@desplega.ai/agent-swarm 2.3.0 → 2.4.1

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 (332) hide show
  1. package/README.md +1 -0
  2. package/dist/{acp-adapter-v85a2bqy.js → acp-adapter-zvzr538d.js} +9 -9
  3. package/dist/{actions-zgcxfspj.js → actions-jhtqdw69.js} +21 -19
  4. package/dist/{amp-adapter-jw9xfz42.js → amp-adapter-bd84rhs2.js} +4 -4
  5. package/dist/{app-4e1s4675.js → app-kreq1gwg.js} +3 -3
  6. package/dist/{assistant-v238n9dk.js → assistant-3k22sbsq.js} +23 -21
  7. package/dist/{boot-reembed-p666cw5n.js → boot-reembed-e53x6b9v.js} +1 -1
  8. package/dist/{boot-reembed-w6pm64j4.js → boot-reembed-f6z923ny.js} +1 -1
  9. package/dist/{boot-scrub-sweep-jcyjagn3.js → boot-scrub-sweep-0538vsb7.js} +4 -4
  10. package/dist/{boot-seeds-gbedmamp.js → boot-seeds-6wxqkfk2.js} +4 -4
  11. package/dist/{claude-adapter-q7wspzt8.js → claude-adapter-gfts5044.js} +9 -9
  12. package/dist/{claude-managed-adapter-n7ahjyx6.js → claude-managed-adapter-h2y889ge.js} +3 -3
  13. package/dist/{claude-managed-setup-wza784fb.js → claude-managed-setup-h00hsgtz.js} +2 -2
  14. package/dist/{claude-sdk-session-yf400ptr.js → claude-sdk-session-56besmfz.js} +13 -13
  15. package/dist/{cli-65q70v2v.js → cli-0bh477zn.js} +33 -6
  16. package/dist/{cli-4ync7m60.js → cli-0ebey9hg.js} +1 -1
  17. package/dist/{cli-665j3feb.js → cli-0xk505e8.js} +1 -1
  18. package/dist/{cli-7gh7020n.js → cli-1dmcq2eh.js} +1 -1
  19. package/dist/{cli-gzvb3d4a.js → cli-1jx0srjt.js} +3 -3
  20. package/dist/{cli-zj4d6afs.js → cli-1pa3rz69.js} +21 -6
  21. package/dist/{cli-7ayprxp7.js → cli-2dfbv8qr.js} +8 -0
  22. package/dist/{cli-30m5q8mc.js → cli-3kh4kpqy.js} +6 -6
  23. package/dist/{cli-qrwp40st.js → cli-3p8fqcnh.js} +2 -2
  24. package/dist/{cli-y3dvz6ge.js → cli-3r448dqn.js} +3 -3
  25. package/dist/{cli-a17tzmn0.js → cli-3tnpj2bh.js} +7 -7
  26. package/dist/{cli-ea7chd26.js → cli-471qk6s2.js} +93 -46
  27. package/dist/{cli-a49vpbf3.js → cli-5kqnemen.js} +1 -1
  28. package/dist/{cli-0rh20bp3.js → cli-6ncjvx55.js} +3 -3
  29. package/dist/{cli-fb6t92v8.js → cli-6x9ma5w5.js} +1 -1
  30. package/dist/{cli-k28p6xh7.js → cli-7225gwj7.js} +2 -2
  31. package/dist/{cli-8cvvytad.js → cli-8nyhf7za.js} +3 -3
  32. package/dist/{cli-e09hj50q.js → cli-8wkb09q5.js} +11 -11
  33. package/dist/{cli-j0v75y13.js → cli-a0kcty51.js} +4 -4
  34. package/dist/{cli-zes4tsbb.js → cli-a4gvwttf.js} +4 -4
  35. package/dist/{cli-jhp7w1fy.js → cli-a5ebc95n.js} +2 -2
  36. package/dist/{cli-5d1m1qca.js → cli-ash6xxk7.js} +2 -2
  37. package/dist/{cli-fp4xbw07.js → cli-axh29tyr.js} +14 -12
  38. package/dist/{cli-p22zdvnt.js → cli-bn0rewsy.js} +3 -3
  39. package/dist/{cli-hz64bcnj.js → cli-bn5kevnf.js} +11 -373
  40. package/dist/{cli-8jtrekpy.js → cli-d2mb5s8a.js} +7 -7
  41. package/dist/{cli-c5sbmw1v.js → cli-d82btycs.js} +94 -15
  42. package/dist/{cli-vjfpshn3.js → cli-ejs3m1sx.js} +1 -1
  43. package/dist/{cli-yv0vgscc.js → cli-fpmsqq59.js} +18 -2
  44. package/dist/cli-fz9kewbh.js +382 -0
  45. package/dist/{cli-4s1kqrqg.js → cli-g0hgtz4p.js} +6 -6
  46. package/dist/{cli-hn3pftj2.js → cli-gf0kfg81.js} +7 -7
  47. package/dist/{cli-d43xqzbe.js → cli-hqhzg1qq.js} +2 -2
  48. package/dist/{cli-azs9bhc5.js → cli-j4rqdf2n.js} +2 -2
  49. package/dist/{cli-wtg6edvd.js → cli-jh0qcrga.js} +1 -1
  50. package/dist/{cli-kqypty34.js → cli-k9z4zd4p.js} +6 -5
  51. package/dist/{cli-jhmjwqcg.js → cli-kmqgmcnb.js} +47 -14
  52. package/dist/{cli-8qsx817b.js → cli-kr2pmfqx.js} +583 -769
  53. package/dist/{cli-4g629sgr.js → cli-m2qgh5sv.js} +20 -1
  54. package/dist/{cli-9e9kjqsj.js → cli-mfe7f7b5.js} +2 -2
  55. package/dist/{cli-x3en6ct3.js → cli-nwyw3zhg.js} +1 -1
  56. package/dist/{cli-6vcyw6v7.js → cli-nx462e4v.js} +5 -3
  57. package/dist/{cli-tr24eeny.js → cli-p0rakbrk.js} +9 -9
  58. package/dist/{cli-4hmaheb6.js → cli-p6argxh9.js} +9 -9
  59. package/dist/{cli-3avfh5az.js → cli-pemywhfq.js} +2 -2
  60. package/dist/{cli-etvn5ej2.js → cli-pnp2j5zg.js} +2 -2
  61. package/dist/{cli-wk8ccrz6.js → cli-pw7v69vp.js} +2 -2
  62. package/dist/{cli-ka01m5rb.js → cli-rdq7tzwm.js} +17 -14
  63. package/dist/{cli-re4wkbzx.js → cli-rqqsfz30.js} +677 -104
  64. package/dist/{cli-xgn4rwyv.js → cli-s48yvzb8.js} +4 -4
  65. package/dist/{cli-zv4vy2b0.js → cli-s95dknxh.js} +1 -1
  66. package/dist/{cli-g6p7c2my.js → cli-sjyetj21.js} +2 -2
  67. package/dist/{cli-rdr166ht.js → cli-smvcw9z5.js} +1 -1
  68. package/dist/{cli-fhvqt2fp.js → cli-sv5gb0yb.js} +1 -1
  69. package/dist/{cli-yv3n83x8.js → cli-syhq378v.js} +2 -2
  70. package/dist/{cli-q1812jbw.js → cli-tbhwrxpt.js} +9 -9
  71. package/dist/{cli-w7z94e6c.js → cli-tx00c0h6.js} +4 -2
  72. package/dist/{cli-dxwh4j32.js → cli-vy0fay6k.js} +46 -37
  73. package/dist/{cli-ptkpaqfy.js → cli-wt645v7q.js} +1 -1
  74. package/dist/{cli-ppapy34w.js → cli-wxbn3yfx.js} +2 -2
  75. package/dist/{cli-cby329ny.js → cli-x5mq1fyq.js} +3 -3
  76. package/dist/{cli-rc3r0bnh.js → cli-xbzvet96.js} +6 -6
  77. package/dist/{cli-4mkvwgzr.js → cli-xf234ws8.js} +1 -1
  78. package/dist/{cli-6p7ddg72.js → cli-xftrvxag.js} +5 -5
  79. package/dist/cli-z071fb95.js +848 -0
  80. package/dist/{cli-1v8w782f.js → cli-z1ew8vbq.js} +14 -7
  81. package/dist/{cli-9td5ss65.js → cli-zfg9enh9.js} +3 -3
  82. package/dist/{cli-yxqag76b.js → cli-zw88zjba.js} +1 -1
  83. package/dist/cli.js +19 -19
  84. package/dist/{codex-adapter-nhjj2veb.js → codex-adapter-p6rmm7vv.js} +6 -6
  85. package/dist/{codex-hook-x6tm6y5m.js → codex-hook-x7nejyrr.js} +8 -5
  86. package/dist/{codex-login-35p4sk85.js → codex-login-m819c41r.js} +1 -1
  87. package/dist/{codex-session-runner-6hggrjbx.js → codex-session-runner-8vd5t9t0.js} +7 -7
  88. package/dist/{commands-mrw1ph2w.js → commands-7ems2p13.js} +2 -2
  89. package/dist/{controller-pt1zam5w.js → controller-m263fg43.js} +4 -4
  90. package/dist/{controllers-connect-ymw5541d.js → controllers-connect-bg33ae53.js} +28 -24
  91. package/dist/{cursor-adapter-cf4zh5x0.js → cursor-adapter-cz6nm6mj.js} +3 -3
  92. package/dist/{db-hy80219q.js → db-w505dwa8.js} +2 -2
  93. package/dist/{db-hhpy6m4x.js → db-ys2rbdft.js} +3 -2
  94. package/dist/{devin-adapter-7egzmt0k.js → devin-adapter-zgqadvnm.js} +1 -1
  95. package/dist/{dispatcher-6pdjtkr1.js → dispatcher-0s0mckqr.js} +20 -18
  96. package/dist/{docker-njavd3fb.js → docker-fkgtr75t.js} +2 -2
  97. package/dist/{dsh-adapter-fbx06re2.js → dsh-adapter-3n3net33.js} +3 -3
  98. package/dist/{e2b-5r6a5jeb.js → e2b-7am6tmq6.js} +2 -2
  99. package/dist/{e2b-pw1nvtmh.js → e2b-thdcxtjd.js} +2 -2
  100. package/dist/{easter-egg-3d-6hg2r942.js → easter-egg-3d-5cgynaja.js} +1 -1
  101. package/dist/{event-bus-101fenkh.js → event-bus-9vpk8gmq.js} +1 -1
  102. package/dist/{execute-bcqc69a5.js → execute-t4e6m3rw.js} +1 -1
  103. package/dist/{external-channel-j6vb6ya1.js → external-channel-5b23fv8z.js} +2 -2
  104. package/dist/{google-generative-ai-phpmwxvg.js → google-generative-ai-bmq8gkf3.js} +1 -1
  105. package/dist/{google-vertex-es9ybgdy.js → google-vertex-b1rvp52s.js} +1 -1
  106. package/dist/{grok-adapter-1193sprp.js → grok-adapter-q9fghe5q.js} +10 -10
  107. package/dist/handlers-mqrym347.js +39 -0
  108. package/dist/{heartbeat-h1w1ktce.js → heartbeat-tww6yx1w.js} +23 -21
  109. package/dist/{hook-ejja4bpe.js → hook-dx0pkvar.js} +16 -9
  110. package/dist/{hook-skrbja18.js → hook-pvmh9b6m.js} +6 -6
  111. package/dist/{http-mzh41mfj.js → http-k2ejbt05.js} +975 -292
  112. package/dist/{index-6rmnn89d.js → index-2z710v4x.js} +10 -10
  113. package/dist/{index-xmmkyx82.js → index-dgz61nff.js} +20 -18
  114. package/dist/{index-wgsw9xcz.js → index-g6tbb6ve.js} +1 -1
  115. package/dist/index-h2ek08d6.js +40 -0
  116. package/dist/index-pqfwh9nn.js +41 -0
  117. package/dist/{index-epr2gnbw.js → index-x5aktmp8.js} +1 -1
  118. package/dist/{index-x0q2a4f9.js → index-ysd45kgc.js} +1 -1
  119. package/dist/{index-g909y089.js → index-zgvqzjsw.js} +8 -8
  120. package/dist/{keepalive-4djvbm2j.js → keepalive-nhvpw10z.js} +4 -4
  121. package/dist/lead-jb6sf69z.js +78 -0
  122. package/dist/{maintenance-f3xhnjsm.js → maintenance-agakc519.js} +1 -1
  123. package/dist/{oauth-refresh-sweep-fpd6x6rw.js → oauth-refresh-sweep-4dcbhtn0.js} +3 -3
  124. package/dist/{onboard-196tjgyn.js → onboard-4n2kwa4v.js} +3 -3
  125. package/dist/{opencode-adapter-eeesgfqk.js → opencode-adapter-3s7ax02e.js} +7 -7
  126. package/dist/{otel-impl-g8fh4c21.js → otel-impl-tfa12080.js} +2 -2
  127. package/dist/{pi-mono-adapter-zasp3ze3.js → pi-mono-adapter-ytp7gz8b.js} +24 -13
  128. package/dist/{pricing-refresh-sdcf5yaz.js → pricing-refresh-3cqx3f3x.js} +3 -3
  129. package/dist/{rbac-roles-bsp5657v.js → rbac-roles-9fae5rnv.js} +5 -3
  130. package/dist/{rbac-roles-2x4g1wb5.js → rbac-roles-y6zbfzve.js} +2 -2
  131. package/dist/{render-v2-0pyb39bb.js → render-v2-emawjvc4.js} +3 -3
  132. package/dist/{resume-a86tkj6r.js → resume-zhgts7h7.js} +10 -8
  133. package/dist/{runtime-ar8310ba.js → runtime-xe3aqy4x.js} +1 -1
  134. package/dist/session-access-cm823e06.js +25 -0
  135. package/dist/{setup-ypbzcapd.js → setup-tkp99w1e.js} +1 -1
  136. package/dist/{virtual-modules-wzk6jf3a.js → virtual-modules-4r3zkc20.js} +2 -2
  137. package/dist/{work-objects-yjp8de12.js → work-objects-f722h263.js} +23 -21
  138. package/dist/worker-p7f336rq.js +78 -0
  139. package/dist/{x-8ewt8gxn.js → x-y45mthwn.js} +2 -2
  140. package/openapi.json +1086 -305
  141. package/package.json +2 -2
  142. package/src/apps/action-caller.ts +6 -0
  143. package/src/apps/action-runner.ts +146 -0
  144. package/src/apps/row-store.ts +4 -2
  145. package/src/apps/script-types.ts +17 -4
  146. package/src/be/db/active-sessions.ts +5 -0
  147. package/src/be/db/session-visibility.ts +29 -0
  148. package/src/be/db/tasks/read.ts +61 -3
  149. package/src/be/db/tasks/write.ts +15 -6
  150. package/src/be/db.ts +106 -37
  151. package/src/be/events.ts +11 -0
  152. package/src/be/fleet-benchmark-stats.ts +10 -5
  153. package/src/be/migrations/230_session_shares.sql +23 -0
  154. package/src/be/models-catalog.ts +4 -0
  155. package/src/be/onboarding.ts +6 -0
  156. package/src/be/rbac-roles.ts +2 -0
  157. package/src/be/scripts/typecheck.ts +13 -0
  158. package/src/be/session-shares.ts +244 -0
  159. package/src/be/swarm-config-guard.ts +33 -3
  160. package/src/be/telemetry-enable.ts +1 -1
  161. package/src/commands/provider-credentials.ts +12 -2
  162. package/src/commands/runner.ts +14 -0
  163. package/src/heartbeat/heartbeat.ts +2 -2
  164. package/src/hooks/codex-hook.ts +6 -2
  165. package/src/hooks/hook.ts +7 -0
  166. package/src/hooks/pr-body-guard.ts +38 -11
  167. package/src/hooks/pr-room-guard.ts +88 -0
  168. package/src/http/active-sessions.ts +13 -2
  169. package/src/http/agents.ts +11 -3
  170. package/src/http/all-routes.ts +1 -0
  171. package/src/http/api-keys.ts +3 -1
  172. package/src/http/approval-requests.ts +2 -1
  173. package/src/http/apps.ts +118 -190
  174. package/src/http/config.ts +3 -0
  175. package/src/http/context.ts +2 -0
  176. package/src/http/core.ts +39 -2
  177. package/src/http/events.ts +6 -2
  178. package/src/http/favorite-owner.ts +1 -1
  179. package/src/http/fleet-benchmark.ts +79 -30
  180. package/src/http/fs.ts +38 -3
  181. package/src/http/global-config.ts +7 -0
  182. package/src/http/index.ts +3 -1
  183. package/src/http/memory.ts +3 -1
  184. package/src/http/ranking.ts +351 -0
  185. package/src/http/realtime.ts +26 -21
  186. package/src/http/route-def.ts +77 -11
  187. package/src/http/script-runs.ts +2 -1
  188. package/src/http/scripts.ts +8 -0
  189. package/src/http/session-access.ts +179 -0
  190. package/src/http/session-data.ts +10 -2
  191. package/src/http/sessions.ts +212 -12
  192. package/src/http/stats.ts +7 -3
  193. package/src/http/tasks.ts +85 -8
  194. package/src/http/telemetry-ui.ts +1 -1
  195. package/src/http/used-by.ts +64 -20
  196. package/src/http/users.ts +2 -2
  197. package/src/prompts/session-templates.ts +3 -1
  198. package/src/providers/amp-adapter.ts +1 -1
  199. package/src/providers/pi-mono-adapter.ts +15 -0
  200. package/src/providers/reasoning-effort.ts +2 -1
  201. package/src/providers/types.ts +2 -0
  202. package/src/ranking/telemetry-ranking.ts +119 -0
  203. package/src/ranking/usage.ts +57 -0
  204. package/src/rbac/admission.ts +22 -7
  205. package/src/rbac/legacy-policy.ts +2 -0
  206. package/src/rbac/permissions.ts +9 -0
  207. package/src/realtime/auth.ts +69 -0
  208. package/src/realtime/tickets.ts +4 -1
  209. package/src/realtime/transport.ts +77 -9
  210. package/src/scripts-runtime/sdk-allowlist.ts +7 -0
  211. package/src/scripts-runtime/types/stdlib.d.ts +31 -0
  212. package/src/scripts-runtime/types/swarm-sdk.d.ts +31 -0
  213. package/src/server.ts +4 -0
  214. package/src/slack/blocks.ts +18 -0
  215. package/src/slack/dm.ts +29 -0
  216. package/src/telemetry-context.ts +19 -1
  217. package/src/telemetry-contract/EVENTS.md +45 -8
  218. package/src/telemetry-contract/SOURCE.json +38 -4
  219. package/src/telemetry-contract/catalog.json +294 -5
  220. package/src/telemetry-contract/ranking/fixtures/expected.json +30 -0
  221. package/src/telemetry-contract/ranking/fixtures/invalid/bad-category.json +11 -0
  222. package/src/telemetry-contract/ranking/fixtures/invalid/bad-claim.json +11 -0
  223. package/src/telemetry-contract/ranking/fixtures/invalid/blank-description.json +11 -0
  224. package/src/telemetry-contract/ranking/fixtures/invalid/blank-name.json +11 -0
  225. package/src/telemetry-contract/ranking/fixtures/invalid/description-too-long.json +11 -0
  226. package/src/telemetry-contract/ranking/fixtures/invalid/domain-uppercase.json +11 -0
  227. package/src/telemetry-contract/ranking/fixtures/invalid/domain-with-scheme.json +11 -0
  228. package/src/telemetry-contract/ranking/fixtures/invalid/domain-with-www.json +11 -0
  229. package/src/telemetry-contract/ranking/fixtures/invalid/http-source-url.json +11 -0
  230. package/src/telemetry-contract/ranking/fixtures/invalid/missing-description.json +10 -0
  231. package/src/telemetry-contract/ranking/fixtures/invalid/missing-version.json +10 -0
  232. package/src/telemetry-contract/ranking/fixtures/invalid/name-not-string.json +11 -0
  233. package/src/telemetry-contract/ranking/fixtures/invalid/name-too-long.json +11 -0
  234. package/src/telemetry-contract/ranking/fixtures/invalid/not-object.json +1 -0
  235. package/src/telemetry-contract/ranking/fixtures/invalid/null.json +1 -0
  236. package/src/telemetry-contract/ranking/fixtures/invalid/other-with-subcategory.json +11 -0
  237. package/src/telemetry-contract/ranking/fixtures/invalid/oversize.json +1 -0
  238. package/src/telemetry-contract/ranking/fixtures/invalid/short-claim.json +11 -0
  239. package/src/telemetry-contract/ranking/fixtures/invalid/subcategory-other-category.json +11 -0
  240. package/src/telemetry-contract/ranking/fixtures/invalid/trailing-data.json +1 -0
  241. package/src/telemetry-contract/ranking/fixtures/invalid/unknown-field.json +12 -0
  242. package/src/telemetry-contract/ranking/fixtures/invalid/unsupported-version.json +11 -0
  243. package/src/telemetry-contract/ranking/fixtures/invalid/updated-at-not-date.json +11 -0
  244. package/src/telemetry-contract/ranking/fixtures/invalid/updated-at-offset.json +11 -0
  245. package/src/telemetry-contract/ranking/fixtures/invalid/wrong-case-field.json +11 -0
  246. package/src/telemetry-contract/ranking/fixtures/valid/category-without-subcategory.json +11 -0
  247. package/src/telemetry-contract/ranking/fixtures/valid/full.json +11 -0
  248. package/src/telemetry-contract/ranking/fixtures/valid/minimal.json +11 -0
  249. package/src/telemetry-contract/ranking/fixtures/valid/no-claim.json +10 -0
  250. package/src/telemetry-contract/ranking/fixtures/valid/padded-text.json +11 -0
  251. package/src/telemetry-contract/ranking/fixtures/valid/unicode-name.json +11 -0
  252. package/src/telemetry-contract/ranking/listing.v1.schema.json +61 -0
  253. package/src/telemetry-contract/ranking/taxonomy.json +8 -0
  254. package/src/telemetry-contract/ranking-taxonomy.gen.ts +11 -0
  255. package/src/telemetry-contract/types.gen.ts +963 -58
  256. package/src/telemetry-identity.ts +47 -9
  257. package/src/telemetry-snapshot.ts +9 -1
  258. package/src/telemetry-ui-events.ts +9 -2
  259. package/src/telemetry.ts +40 -5
  260. package/src/tests/app-action-run-tool.test.ts +178 -0
  261. package/src/tests/app-row-tools.test.ts +360 -0
  262. package/src/tests/apps-rbac.test.ts +189 -2
  263. package/src/tests/apps-script-types.test.ts +16 -3
  264. package/src/tests/credential-check.test.ts +38 -0
  265. package/src/tests/db-extraction-contract.test.ts +1 -0
  266. package/src/tests/db-task-read-contract.test.ts +1 -0
  267. package/src/tests/docs-index-footprint.test.js +2 -2
  268. package/src/tests/fleet-benchmark.test.ts +122 -2
  269. package/src/tests/http-users.test.ts +62 -1
  270. package/src/tests/model-catalog-table.test.ts +63 -0
  271. package/src/tests/model-profiles-api.test.ts +19 -7
  272. package/src/tests/models-catalog.test.ts +50 -0
  273. package/src/tests/onboarding.test.ts +21 -0
  274. package/src/tests/org-identity.test.ts +44 -0
  275. package/src/tests/org-website.test.ts +62 -0
  276. package/src/tests/pi-mono-adapter.test.ts +23 -0
  277. package/src/tests/pr-body-leaks.test.ts +4 -0
  278. package/src/tests/pr-room-guard.test.ts +242 -0
  279. package/src/tests/ranking-listing.test.ts +97 -0
  280. package/src/tests/ranking-route.test.ts +321 -0
  281. package/src/tests/ranking-usage.test.ts +50 -0
  282. package/src/tests/rbac-admission-e2e.test.ts +8 -0
  283. package/src/tests/rbac-admission.test.ts +38 -0
  284. package/src/tests/rbac-charact-http.test.ts +4 -3
  285. package/src/tests/rbac-engine.test.ts +2 -0
  286. package/src/tests/rbac-roles.test.ts +2 -0
  287. package/src/tests/realtime-room-surfaces.test.ts +3 -1
  288. package/src/tests/realtime-session-join-race.test.ts +142 -0
  289. package/src/tests/realtime-tickets.test.ts +24 -17
  290. package/src/tests/realtime-transport.test.ts +91 -0
  291. package/src/tests/reasoning-effort.test.ts +39 -3
  292. package/src/tests/runner-harness-telemetry-env.test.ts +56 -1
  293. package/src/tests/session-route-posture.test.ts +639 -0
  294. package/src/tests/session-sharing-routes.test.ts +282 -0
  295. package/src/tests/session-sharing.test.ts +526 -0
  296. package/src/tests/sessions-detail-batch.test.ts +7 -1
  297. package/src/tests/slack-tool-blocks.test.ts +101 -0
  298. package/src/tests/slack-update.test.ts +55 -0
  299. package/src/tests/slack-user-dm.test.ts +177 -0
  300. package/src/tests/swarm-tool-result-gate.test.ts +2 -4
  301. package/src/tests/telemetry-context.test.ts +128 -2
  302. package/src/tests/telemetry-ui-route.test.ts +123 -0
  303. package/src/tests/tool-annotations.test.ts +3 -2
  304. package/src/tests/used-by-route.test.ts +153 -9
  305. package/src/tests/workflow-agent-task-retry.test.ts +130 -4
  306. package/src/tests/workflow-wait-recovery.test.ts +116 -4
  307. package/src/tools/app-action-run.ts +63 -0
  308. package/src/tools/app-rows.ts +234 -0
  309. package/src/tools/model-catalog/index.ts +1 -1
  310. package/src/tools/slack-post.ts +18 -12
  311. package/src/tools/slack-read.ts +13 -1
  312. package/src/tools/slack-reply.ts +7 -10
  313. package/src/tools/slack-start-thread.ts +18 -11
  314. package/src/tools/slack-update.ts +2 -9
  315. package/src/tools/tool-config.ts +8 -0
  316. package/src/tools/utils.ts +1 -1
  317. package/src/tools/workflows/retry-workflow-run.ts +1 -1
  318. package/src/types.ts +52 -0
  319. package/src/utils/company-profile.ts +24 -0
  320. package/src/utils/org-identity.ts +33 -0
  321. package/src/utils/org-website.ts +58 -21
  322. package/src/workflows/recovery.ts +10 -22
  323. package/src/workflows/resume.ts +16 -0
  324. package/src/workflows/task-step-routing.ts +47 -7
  325. package/templates/skills/apps/SKILL.md +58 -2
  326. package/templates/skills/apps/content.md +58 -2
  327. package/dist/handlers-tb3xgjnm.js +0 -37
  328. package/dist/index-nyfcdpky.js +0 -39
  329. package/dist/index-vjn3wbzf.js +0 -38
  330. package/dist/lead-61kqp604.js +0 -76
  331. package/dist/worker-4ptnn1ge.js +0 -76
  332. package/dist/{cli-n5mrsg0f.js → cli-60cgh1rv.js} +3 -3
@@ -69,28 +69,6 @@ import {
69
69
  resolveKvView,
70
70
  toolErr,
71
71
  createToolRegistrar,
72
- duplicateGuardedQueryKey,
73
- warnIfCorsAllowsAnyOrigin,
74
- isOriginAllowedForCredentials,
75
- allowedCredentialRedirect,
76
- setCorsHeaders,
77
- parseQueryParams,
78
- getPathSegments,
79
- wireHttpSpanLifecycle,
80
- safeRequestUrlForLog,
81
- agentWithCapacity,
82
- parseBody,
83
- BODY_TOO_LARGE,
84
- enforceContentLengthCap,
85
- json,
86
- jsonError,
87
- writeUnhandledError,
88
- triggerSchemaErrorResponse,
89
- deriveApiBaseUrl,
90
- httpServerSemconvAttributes,
91
- runtimeInstanceHeader,
92
- describeRequestRoute,
93
- route,
94
72
  handleMcpBridge,
95
73
  Redacted,
96
74
  SCRIPT_SDK_RESPONSE_LIMIT_BYTES,
@@ -199,9 +177,6 @@ import {
199
177
  scheduleIntegrationsReload,
200
178
  handleCore,
201
179
  userCtx,
202
- resolveTaskAuditUserId,
203
- resolveHttpAuditUserId,
204
- resolveHttpFavoriteOwner,
205
180
  handleApps,
206
181
  cancelApprovalRequest,
207
182
  getUserCommsPrefs,
@@ -262,6 +237,8 @@ import {
262
237
  resolveRoomNamespace,
263
238
  roomRequestInfo,
264
239
  authorizeRoomNamespace,
240
+ sessionAccessTopic,
241
+ sessionPresenceRoot,
265
242
  RoomOperationSchema,
266
243
  removeAwarenessStates,
267
244
  roomTopic,
@@ -303,7 +280,7 @@ import {
303
280
  hasCapability,
304
281
  getEnabledCapabilities,
305
282
  createServer
306
- } from "./cli-8qsx817b.js";
283
+ } from "./cli-kr2pmfqx.js";
307
284
  import {
308
285
  chatThreadOfContextKey,
309
286
  agentContextKey,
@@ -311,6 +288,10 @@ import {
311
288
  init_context_key,
312
289
  init_catalog,
313
290
  catalog_default,
291
+ COMPANY_TEAM_SIZES,
292
+ COMPANY_USE_CASES,
293
+ USER_FUNCTIONS,
294
+ init_company_profile,
314
295
  TELEMETRY_PROXY_ORIGIN,
315
296
  normalizeOrgWebsite,
316
297
  orgLogoUrl,
@@ -325,6 +306,7 @@ import {
325
306
  _resolveIntegrationType,
326
307
  _resolveIntegrationProvider,
327
308
  setTelemetryRoleResolver,
309
+ setTelemetryFunctionResolver,
328
310
  isTelemetryReady,
329
311
  trackRelayed,
330
312
  telemetry,
@@ -349,6 +331,7 @@ import {
349
331
  getDbClient2,
350
332
  init_runtime,
351
333
  cachedUserRole,
334
+ cachedUserFunction,
352
335
  configureOrgDomainPersistence,
353
336
  recomputeOrgDomain,
354
337
  init_telemetry_identity,
@@ -440,6 +423,13 @@ import {
440
423
  CHAT_SDK_ADAPTER_NAMES,
441
424
  isAdapterEntryEnabled,
442
425
  init_schema,
426
+ TELEMETRY_RANKING_KEY,
427
+ PUBLIC_RANKING_ENABLED_KEY,
428
+ RANKING_CATEGORIES,
429
+ readTelemetryRanking,
430
+ rankingFormSchema,
431
+ buildListing,
432
+ init_telemetry_ranking,
443
433
  API_DRAINING_HEADER,
444
434
  init_api_drain,
445
435
  parseTaskToolManifest,
@@ -724,26 +714,26 @@ import {
724
714
  countScriptRunJournalSteps2,
725
715
  countScriptRunJournalAgentTaskSteps2,
726
716
  init_db3
727
- } from "./cli-re4wkbzx.js";
717
+ } from "./cli-rqqsfz30.js";
728
718
  import {
729
719
  getUserGrant2,
730
720
  init_rbac_roles
731
- } from "./cli-6vcyw6v7.js";
732
- import"./cli-xgn4rwyv.js";
721
+ } from "./cli-nx462e4v.js";
722
+ import"./cli-s48yvzb8.js";
733
723
  import {
734
724
  normalizeModelKey
735
- } from "./cli-qrwp40st.js";
725
+ } from "./cli-3p8fqcnh.js";
736
726
  import {
737
727
  upsertModelCatalogOverlay,
738
728
  deleteModelCatalogOverlay,
739
729
  loadModelsCatalog,
740
730
  requestModelCatalogRefresh2
741
- } from "./cli-kqypty34.js";
731
+ } from "./cli-k9z4zd4p.js";
742
732
  import {
743
733
  AMP_ESTIMATE_MODELS,
744
734
  ampBillsCacheWrites,
745
735
  resolveAmpModel
746
- } from "./cli-fhvqt2fp.js";
736
+ } from "./cli-sv5gb0yb.js";
747
737
  import {
748
738
  automationIntegrationFixUrl,
749
739
  preflightAutomation,
@@ -755,7 +745,7 @@ import {
755
745
  cancelWorkflowRun2,
756
746
  TriggerSchemaError,
757
747
  startWorkflowExecution2
758
- } from "./cli-jhmjwqcg.js";
748
+ } from "./cli-kmqgmcnb.js";
759
749
  import {
760
750
  require_dist2,
761
751
  isInvalidClientError,
@@ -835,14 +825,14 @@ import {
835
825
  typecheckWithAmbient,
836
826
  typecheckScript,
837
827
  parseSkillContent
838
- } from "./cli-65q70v2v.js";
828
+ } from "./cli-0bh477zn.js";
839
829
  import {
840
830
  buildAuthorizationUrl,
841
831
  exchangeAuthorizationCode,
842
832
  oauthAppRowToProviderConfig,
843
833
  forceRefreshAuthorizationOrThrow,
844
834
  forceRefreshTokenOrThrow
845
- } from "./cli-g6p7c2my.js";
835
+ } from "./cli-sjyetj21.js";
846
836
  import {
847
837
  signalProcessGroup,
848
838
  detachedProcessGroup,
@@ -870,15 +860,15 @@ import {
870
860
  getSlackApp2,
871
861
  startSlackApp2,
872
862
  stopSlackApp2
873
- } from "./cli-dxwh4j32.js";
874
- import"./cli-9e9kjqsj.js";
863
+ } from "./cli-vy0fay6k.js";
864
+ import"./cli-mfe7f7b5.js";
875
865
  import {
876
866
  createApprovalFollowUpTask,
877
867
  generateEdges,
878
868
  definitionNodeIds,
879
869
  validateDefinition
880
- } from "./cli-q1812jbw.js";
881
- import"./cli-8cvvytad.js";
870
+ } from "./cli-tbhwrxpt.js";
871
+ import"./cli-8nyhf7za.js";
882
872
  import {
883
873
  _fetchHolder,
884
874
  CLIENT_ID,
@@ -889,11 +879,53 @@ import {
889
879
  codexOAuthKeyForSlot,
890
880
  loadAllCodexOAuthSlots,
891
881
  getValidCodexOAuth
892
- } from "./cli-vjfpshn3.js";
882
+ } from "./cli-ejs3m1sx.js";
893
883
  import {
894
884
  workflowEventBus2,
895
885
  init_event_bus
896
- } from "./cli-ptkpaqfy.js";
886
+ } from "./cli-wt645v7q.js";
887
+ import {
888
+ duplicateGuardedQueryKey,
889
+ warnIfCorsAllowsAnyOrigin,
890
+ isOriginAllowedForCredentials,
891
+ allowedCredentialRedirect,
892
+ setCorsHeaders,
893
+ parseQueryParams,
894
+ getPathSegments,
895
+ wireHttpSpanLifecycle,
896
+ safeRequestUrlForLog,
897
+ agentWithCapacity,
898
+ parseBody,
899
+ BODY_TOO_LARGE,
900
+ enforceContentLengthCap,
901
+ json,
902
+ jsonError,
903
+ writeUnhandledError,
904
+ triggerSchemaErrorResponse,
905
+ deriveApiBaseUrl,
906
+ httpServerSemconvAttributes,
907
+ hasSessionAccess,
908
+ getSessionRoot,
909
+ sessionAccessForRoot,
910
+ listActiveSessionShares,
911
+ toSessionSharesState,
912
+ setSessionShares,
913
+ resolveTaskAuditUserId,
914
+ resolveHttpAuditUserId,
915
+ dashboardUserIdHeader,
916
+ resolveHttpFavoriteOwner,
917
+ resolveSessionActor2,
918
+ sessionAccessFor2,
919
+ SESSION_READ_DENIED2,
920
+ canReadSession2,
921
+ readableByFor2,
922
+ SESSION_WRITE_DENIED2,
923
+ runtimeInstanceHeader,
924
+ bodyTask,
925
+ bodyListTask,
926
+ describeRequestRoute,
927
+ route
928
+ } from "./cli-z071fb95.js";
897
929
  import {
898
930
  isMultiRuntimeEnabled
899
931
  } from "./cli-7a91w22y.js";
@@ -906,24 +938,6 @@ import {
906
938
  } from "./cli-qje9k2nh.js";
907
939
  import {
908
940
  RuntimePoolError2,
909
- registerSessionTokenConnection,
910
- init_session_token_connections,
911
- findUserById,
912
- getUserIdentities,
913
- listUserEvents,
914
- listUserTokens,
915
- recordIdentityEvent,
916
- linkIdentity,
917
- unlinkIdentity,
918
- mintToken,
919
- revokeToken,
920
- resolveUserByToken,
921
- InvalidRuntimeTokenError,
922
- mintSessionToken,
923
- mintRuntimeToken,
924
- revokeSessionToken,
925
- fingerprintApiKey,
926
- init_users,
927
941
  mintControllerToken,
928
942
  init_tokens,
929
943
  listRuntimeControllers2,
@@ -942,13 +956,13 @@ import {
942
956
  resolvePoolDelivery2,
943
957
  createPoolAgent2,
944
958
  init_db
945
- } from "./cli-hz64bcnj.js";
959
+ } from "./cli-bn5kevnf.js";
946
960
  import {
947
961
  RATING_MODEL_MAX_LENGTH,
948
962
  REFERENCES_SOURCE_MAX_LENGTH,
949
963
  sanitizeReferencesSource
950
964
  } from "./cli-3h26v7yj.js";
951
- import"./cli-rdr166ht.js";
965
+ import"./cli-smvcw9z5.js";
952
966
  import {
953
967
  startPoolRouting,
954
968
  stopPoolRouting,
@@ -958,8 +972,8 @@ import {
958
972
  checkHeartbeatChecklist2,
959
973
  runHeartbeatSweep2,
960
974
  stopHeartbeat2
961
- } from "./cli-ka01m5rb.js";
962
- import"./cli-d43xqzbe.js";
975
+ } from "./cli-rdq7tzwm.js";
976
+ import"./cli-hqhzg1qq.js";
963
977
  import {
964
978
  import_websocket,
965
979
  import_websocket_server
@@ -968,32 +982,34 @@ import {
968
982
  runtimeSupervisorStatus,
969
983
  startRuntimeSupervisor,
970
984
  stopRuntimeSupervisor
971
- } from "./cli-fp4xbw07.js";
985
+ } from "./cli-axh29tyr.js";
972
986
  import"./cli-ns59dc6r.js";
973
987
  import {
974
988
  ACP_TARGET_IDS
975
- } from "./cli-a49vpbf3.js";
989
+ } from "./cli-5kqnemen.js";
976
990
  import {
977
991
  reasoningCapability,
978
992
  supportedReasoningLevels
979
- } from "./cli-p22zdvnt.js";
993
+ } from "./cli-bn0rewsy.js";
980
994
  import {
981
995
  MAX_RATE_LIMIT_RESET_MS
982
996
  } from "./cli-gx4rm0qx.js";
983
- import {
984
- inviteEmailToSharedOrg,
985
- ensureAgentFsCredentialsForAgent
986
- } from "./cli-yv3n83x8.js";
987
- import {
988
- Anthropic
989
- } from "./cli-er83cjbn.js";
990
997
  import {
991
998
  isLeadChannelMonitoringEnabled,
999
+ ORG_IDENTITY_KEYS,
1000
+ orgIdentityStatus,
992
1001
  SIMILARITY_THRESHOLD,
993
1002
  memoryRelevance,
994
1003
  isUnknownModelError,
995
1004
  writeSkillsToFilesystem
996
- } from "./cli-4g629sgr.js";
1005
+ } from "./cli-m2qgh5sv.js";
1006
+ import {
1007
+ inviteEmailToSharedOrg,
1008
+ ensureAgentFsCredentialsForAgent
1009
+ } from "./cli-syhq378v.js";
1010
+ import {
1011
+ Anthropic
1012
+ } from "./cli-er83cjbn.js";
997
1013
  import {
998
1014
  isCodexOAuthConfigKey
999
1015
  } from "./cli-j4bwnpkm.js";
@@ -1005,7 +1021,27 @@ import"./cli-13p25grh.js";
1005
1021
  import"./cli-0adyhvq2.js";
1006
1022
  import {
1007
1023
  require_content_type
1008
- } from "./cli-gzvb3d4a.js";
1024
+ } from "./cli-1jx0srjt.js";
1025
+ import {
1026
+ registerSessionTokenConnection,
1027
+ init_session_token_connections,
1028
+ findUserById,
1029
+ getUserIdentities,
1030
+ listUserEvents,
1031
+ listUserTokens,
1032
+ recordIdentityEvent,
1033
+ linkIdentity,
1034
+ unlinkIdentity,
1035
+ mintToken,
1036
+ revokeToken,
1037
+ resolveUserByToken,
1038
+ InvalidRuntimeTokenError,
1039
+ mintSessionToken,
1040
+ mintRuntimeToken,
1041
+ revokeSessionToken,
1042
+ fingerprintApiKey,
1043
+ init_users
1044
+ } from "./cli-fz9kewbh.js";
1009
1045
  import"./cli-v41kn3cd.js";
1010
1046
  import"./cli-wgtk7xxr.js";
1011
1047
  import {
@@ -1019,7 +1055,7 @@ import {
1019
1055
  clearAuditSink,
1020
1056
  can,
1021
1057
  init_rbac
1022
- } from "./cli-zj4d6afs.js";
1058
+ } from "./cli-1pa3rz69.js";
1023
1059
  import {
1024
1060
  isSafeBundlePath,
1025
1061
  init_bundle_path,
@@ -1039,6 +1075,10 @@ import {
1039
1075
  ProviderNameSchema,
1040
1076
  ModelProfileNameSchema,
1041
1077
  ModelProfileSchema,
1078
+ SessionAccessSchema,
1079
+ SessionOwnerSchema,
1080
+ SessionSharesSchema,
1081
+ SessionSharesUpdateSchema,
1042
1082
  SteerModeSchema,
1043
1083
  SteeringSourceSchema,
1044
1084
  SteeringMessageSchema,
@@ -1093,6 +1133,7 @@ import {
1093
1133
  RuntimePoolSchema,
1094
1134
  RuntimePoolCreateAgentSchema,
1095
1135
  RuntimeInstanceSchema,
1136
+ SUPERSEDED_WORKFLOW_TASK_REASON,
1096
1137
  WorkflowEdgeSchema,
1097
1138
  WorkflowDefinitionSchema,
1098
1139
  WorkflowNodePatchSchema,
@@ -1156,12 +1197,12 @@ import {
1156
1197
  KvKeySchema,
1157
1198
  KvEntrySchema,
1158
1199
  init_types
1159
- } from "./cli-yv0vgscc.js";
1200
+ } from "./cli-fpmsqq59.js";
1160
1201
  import {
1161
1202
  IDENTITY_FIELD_BUDGETS,
1162
1203
  IdentityFieldBudgetError,
1163
1204
  init_identity_field_budget
1164
- } from "./cli-cby329ny.js";
1205
+ } from "./cli-x5mq1fyq.js";
1165
1206
  import {
1166
1207
  getSlackConfiguration,
1167
1208
  init_config
@@ -1179,7 +1220,7 @@ import {
1179
1220
  resolveTemplate,
1180
1221
  init_resolver,
1181
1222
  init_session_templates
1182
- } from "./cli-w7z94e6c.js";
1223
+ } from "./cli-tx00c0h6.js";
1183
1224
  import {
1184
1225
  MODEL_SCOPED_WINDOWS,
1185
1226
  activeModelBlocks,
@@ -1201,6 +1242,7 @@ import {
1201
1242
  import {
1202
1243
  NEVER,
1203
1244
  toJSONSchema,
1245
+ ZodError,
1204
1246
  string2,
1205
1247
  email,
1206
1248
  uuid,
@@ -1228,7 +1270,7 @@ import {
1228
1270
  import {
1229
1271
  init_package,
1230
1272
  package_default
1231
- } from "./cli-5d1m1qca.js";
1273
+ } from "./cli-ash6xxk7.js";
1232
1274
  import {
1233
1275
  realtimeBus,
1234
1276
  init_bus
@@ -1257,11 +1299,11 @@ import {
1257
1299
  scrubObject,
1258
1300
  registerVolatileSecret,
1259
1301
  init_secret_scrubber
1260
- } from "./cli-4mkvwgzr.js";
1302
+ } from "./cli-xf234ws8.js";
1261
1303
  import {
1262
1304
  isAlias,
1263
1305
  init_src2
1264
- } from "./cli-x3en6ct3.js";
1306
+ } from "./cli-nwyw3zhg.js";
1265
1307
  import {
1266
1308
  __toESM
1267
1309
  } from "./cli-242037k3.js";
@@ -2367,7 +2409,8 @@ function identityFromHttpAuth(req, auth) {
2367
2409
  agentId,
2368
2410
  sourceTaskId: auth.kind === "agent" ? auth.taskId ?? undefined : header(req, "x-source-task-id"),
2369
2411
  userId: auth.kind === "user" ? auth.userId : undefined,
2370
- isOperator: auth.kind === "operator"
2412
+ isOperator: auth.kind === "operator",
2413
+ dashboardUserId: auth.kind === "operator" ? auth.dashboardUserId : undefined
2371
2414
  },
2372
2415
  me: {
2373
2416
  userId: auth.kind === "user" ? auth.userId : agentId ?? guestId,
@@ -2560,6 +2603,9 @@ function connect(ws, auth, runtimeId) {
2560
2603
  if (namespace.startsWith("presence:comb:") && msg.name !== "default") {
2561
2604
  throw new Error("Comb presence uses the default room");
2562
2605
  }
2606
+ if (namespace.startsWith("presence:session:") && msg.name !== "default") {
2607
+ throw new Error("Session presence uses the default room");
2608
+ }
2563
2609
  const denied = await authorizeRoomNamespace(namespace, auth.identity, msg.op, msg.name);
2564
2610
  if (denied)
2565
2611
  throw new Error(typeof denied === "string" ? denied : "Forbidden");
@@ -2600,7 +2646,7 @@ function connect(ws, auth, runtimeId) {
2600
2646
  } catch (error) {
2601
2647
  if (!(error instanceof Error) || error.message !== "room does not exist")
2602
2648
  throw error;
2603
- const creationDenied = await authorizeRoomNamespace(namespace, auth.identity, namespace.startsWith("presence:comb:") ? "join" : true);
2649
+ const creationDenied = await authorizeRoomNamespace(namespace, auth.identity, namespace.startsWith("presence:comb:") || namespace.startsWith("presence:session:") ? "join" : true);
2604
2650
  if (creationDenied)
2605
2651
  throw new Error(creationDenied);
2606
2652
  if (closed)
@@ -2610,6 +2656,8 @@ function connect(ws, auth, runtimeId) {
2610
2656
  if (closed)
2611
2657
  return;
2612
2658
  const stopRoom = realtimeBus.subscribe(key, (event) => {
2659
+ if (rooms.get(key)?.suspended)
2660
+ return;
2613
2661
  const view = event;
2614
2662
  const type = view.type;
2615
2663
  if (type === "deleted") {
@@ -2631,15 +2679,62 @@ function connect(ws, auth, runtimeId) {
2631
2679
  }).catch(() => ws.close(1011, "Room reset failed"));
2632
2680
  }
2633
2681
  });
2634
- const stopPresence = realtimeBus.subscribe(`presence:${key}`, (peers) => send({ type: "presence", name: msg.name, namespace, peers }));
2682
+ const stopPresence = realtimeBus.subscribe(`presence:${key}`, (peers) => {
2683
+ if (rooms.get(key)?.suspended)
2684
+ return;
2685
+ send({ type: "presence", name: msg.name, namespace, peers });
2686
+ });
2687
+ const sessionRoot = sessionPresenceRoot(namespace);
2688
+ let accessCheck = 0;
2689
+ const stopAccess = sessionRoot ? realtimeBus.subscribe(sessionAccessTopic(sessionRoot), () => {
2690
+ const joined = rooms.get(key);
2691
+ if (!joined)
2692
+ return;
2693
+ joined.suspended = true;
2694
+ const check = ++accessCheck;
2695
+ authorizeRoomNamespace(namespace, auth.identity, "join").catch(() => "Session access check failed").then((denied) => {
2696
+ const current = rooms.get(key);
2697
+ if (closed || !current || check !== accessCheck)
2698
+ return;
2699
+ if (denied) {
2700
+ leave(key);
2701
+ send({ type: "error", name: msg.name, namespace, error: denied });
2702
+ return;
2703
+ }
2704
+ current.suspended = false;
2705
+ send({
2706
+ type: "presence",
2707
+ name: msg.name,
2708
+ namespace,
2709
+ peers: [...current.room.awareness.getStates().values()]
2710
+ });
2711
+ });
2712
+ }) : undefined;
2635
2713
  rooms.set(key, {
2636
2714
  room,
2637
2715
  version: msg.schemaVersion,
2638
2716
  stop: () => {
2639
2717
  stopRoom();
2640
2718
  stopPresence();
2641
- }
2719
+ stopAccess?.();
2720
+ },
2721
+ suspended: sessionRoot !== undefined
2642
2722
  });
2723
+ if (sessionRoot) {
2724
+ const check = ++accessCheck;
2725
+ const denied = await authorizeRoomNamespace(namespace, auth.identity, "join").catch(() => "Session access check failed");
2726
+ if (closed)
2727
+ return;
2728
+ if (denied) {
2729
+ leave(key);
2730
+ throw new Error(denied);
2731
+ }
2732
+ const current = rooms.get(key);
2733
+ if (!current)
2734
+ throw new Error("You don't have access to this session");
2735
+ if (check === accessCheck)
2736
+ current.suspended = false;
2737
+ }
2643
2738
  room.awareness.states.set(clientId, { ...auth.me, data: {} });
2644
2739
  room.awareness.meta.set(clientId, { clock: 1, lastUpdated: Date.now() });
2645
2740
  publishPresence(room);
@@ -3799,6 +3894,7 @@ function startTelemetryTicker(deps) {
3799
3894
  return;
3800
3895
  stopTelemetryTicker();
3801
3896
  setTelemetryRoleResolver(cachedUserRole);
3897
+ setTelemetryFunctionResolver(cachedUserFunction);
3802
3898
  const tick = async () => {
3803
3899
  try {
3804
3900
  await recomputeOrgDomain();
@@ -3829,6 +3925,7 @@ function stopTelemetryTicker() {
3829
3925
  tickTimer = null;
3830
3926
  firstTimer = null;
3831
3927
  setTelemetryRoleResolver(null);
3928
+ setTelemetryFunctionResolver(null);
3832
3929
  configureOrgDomainPersistence(null);
3833
3930
  }
3834
3931
 
@@ -3863,6 +3960,7 @@ var createActiveSession = route({
3863
3960
  method: "post",
3864
3961
  path: "/api/active-sessions",
3865
3962
  pattern: ["api", "active-sessions"],
3963
+ session: bodyTask("taskId"),
3866
3964
  summary: "Create a new active session",
3867
3965
  tags: ["Active Sessions"],
3868
3966
  body: object2({
@@ -3883,6 +3981,7 @@ var deleteSessionByTask = route({
3883
3981
  method: "delete",
3884
3982
  path: "/api/active-sessions/by-task/{taskId}",
3885
3983
  pattern: ["api", "active-sessions", "by-task", null],
3984
+ session: { taskParam: "taskId" },
3886
3985
  summary: "Delete active session by task ID",
3887
3986
  tags: ["Active Sessions"],
3888
3987
  params: object2({ taskId: string2() }),
@@ -3894,6 +3993,7 @@ var deleteSessionById = route({
3894
3993
  method: "delete",
3895
3994
  path: "/api/active-sessions/{id}",
3896
3995
  pattern: ["api", "active-sessions", null],
3996
+ session: { activeSessionParam: "id" },
3897
3997
  summary: "Delete active session by ID",
3898
3998
  tags: ["Active Sessions"],
3899
3999
  params: object2({ id: string2() }),
@@ -3905,6 +4005,7 @@ var heartbeatSession = route({
3905
4005
  method: "put",
3906
4006
  path: "/api/active-sessions/heartbeat/{taskId}",
3907
4007
  pattern: ["api", "active-sessions", "heartbeat", null],
4008
+ session: { taskParam: "taskId" },
3908
4009
  summary: "Update heartbeat for an active session",
3909
4010
  tags: ["Active Sessions"],
3910
4011
  params: object2({ taskId: string2() }),
@@ -3916,6 +4017,7 @@ var updateProviderSession = route({
3916
4017
  method: "put",
3917
4018
  path: "/api/active-sessions/provider-session/{taskId}",
3918
4019
  pattern: ["api", "active-sessions", "provider-session", null],
4020
+ session: { taskParam: "taskId" },
3919
4021
  summary: "Update provider session ID on an active session",
3920
4022
  tags: ["Active Sessions"],
3921
4023
  params: object2({ taskId: string2() }),
@@ -3928,6 +4030,7 @@ var cleanupSessions = route({
3928
4030
  method: "post",
3929
4031
  path: "/api/active-sessions/cleanup",
3930
4032
  pattern: ["api", "active-sessions", "cleanup"],
4033
+ session: { agentWide: "Deletes every session of an agent, or every stale session." },
3931
4034
  summary: "Clean up stale sessions",
3932
4035
  tags: ["Active Sessions"],
3933
4036
  body: object2({
@@ -3942,6 +4045,7 @@ var recoverOrphanedTasks = route({
3942
4045
  method: "post",
3943
4046
  path: "/api/active-sessions/recover-orphaned-tasks",
3944
4047
  pattern: ["api", "active-sessions", "recover-orphaned-tasks"],
4048
+ session: { none: "Its handler admits only the calling agent, never a user." },
3945
4049
  summary: "Recover orphaned in-progress tasks for an agent",
3946
4050
  tags: ["Active Sessions"],
3947
4051
  body: object2({
@@ -3961,7 +4065,7 @@ async function handleActiveSessions(req, res, pathSegments, queryParams, myAgent
3961
4065
  const parsed = await listActiveSessions.parse(req, res, pathSegments, queryParams);
3962
4066
  if (!parsed)
3963
4067
  return true;
3964
- const sessions = await getActiveSessions2(parsed.query.agentId || undefined);
4068
+ const sessions = await getActiveSessions2(parsed.query.agentId || undefined, readableByFor2(await resolveSessionActor2(req, myAgentId)));
3965
4069
  listActiveSessions.respond(res, 200, { sessions });
3966
4070
  return true;
3967
4071
  }
@@ -4610,14 +4714,17 @@ async function handleAgentRegister(req, res, pathSegments, myAgentId) {
4610
4714
  }
4611
4715
  return false;
4612
4716
  }
4613
- async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId) {
4717
+ async function handleAgentsRest(req, res, pathSegments, queryParams, myAgentId) {
4614
4718
  if (listAgents.match(req.method, pathSegments)) {
4615
4719
  const parsed = await listAgents.parse(req, res, pathSegments, queryParams);
4616
4720
  if (!parsed)
4617
4721
  return true;
4618
4722
  const includeTasks = parsed.query.include === "tasks";
4619
4723
  const slim = parsed.query.fields !== "full";
4620
- const agents = includeTasks ? await getAllAgentsWithTasks2({ slim }) : await getAllAgents2({ slim });
4724
+ const agents = includeTasks ? await getAllAgentsWithTasks2({
4725
+ slim,
4726
+ readableBy: readableByFor2(await resolveSessionActor2(req, myAgentId))
4727
+ }) : await getAllAgents2({ slim });
4621
4728
  const [agentsWithCapacity, transports] = await Promise.all([
4622
4729
  Promise.all(agents.map(agentWithCapacity)),
4623
4730
  resolveListClaudeTransports(agents)
@@ -5118,6 +5225,7 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
5118
5225
  reportedAt: parsed.body.latest_model?.reportedAt ?? parsed.body.acp.reportedAt,
5119
5226
  reportKind: "post_task",
5120
5227
  bedrock: null,
5228
+ opencodeGo: null,
5121
5229
  acp: null
5122
5230
  };
5123
5231
  finalAgent = await updateAgentCredStatus2(parsed.params.id, {
@@ -5155,7 +5263,7 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
5155
5263
  if (!parsed)
5156
5264
  return true;
5157
5265
  const includeTasks = parsed.query.include === "tasks";
5158
- const agent = includeTasks ? await getAgentWithTasks2(parsed.params.id) : await getAgentById2(parsed.params.id);
5266
+ const agent = includeTasks ? await getAgentWithTasks2(parsed.params.id, readableByFor2(await resolveSessionActor2(req, myAgentId))) : await getAgentById2(parsed.params.id);
5159
5267
  if (!agent) {
5160
5268
  jsonError(res, "Agent not found", 404);
5161
5269
  return true;
@@ -5234,6 +5342,7 @@ var reportUsage = route({
5234
5342
  method: "post",
5235
5343
  path: "/api/keys/report-usage",
5236
5344
  pattern: ["api", "keys", "report-usage"],
5345
+ session: bodyTask("taskId"),
5237
5346
  summary: "Record which API key was used for a task",
5238
5347
  tags: ["API Keys"],
5239
5348
  body: object2({
@@ -5277,6 +5386,7 @@ var reportAuthFailure = route({
5277
5386
  method: "post",
5278
5387
  path: "/api/keys/report-auth-failure",
5279
5388
  pattern: ["api", "keys", "report-auth-failure"],
5389
+ session: bodyTask("taskId"),
5280
5390
  summary: "Record an auth failure for a pooled key; bench it after 2 in a row",
5281
5391
  tags: ["API Keys"],
5282
5392
  body: object2({
@@ -5974,6 +6084,7 @@ var createRoute = route({
5974
6084
  method: "post",
5975
6085
  path: "/api/approval-requests",
5976
6086
  pattern: ["api", "approval-requests"],
6087
+ session: bodyTask("sourceTaskId"),
5977
6088
  summary: "Create a new approval request",
5978
6089
  tags: ["ApprovalRequests"],
5979
6090
  body: object2({
@@ -7295,6 +7406,10 @@ function deriveState(state, signals, funnelEvents) {
7295
7406
  if (state.steps.connect.status !== "done") {
7296
7407
  changed = completeStep(state, "connect", "api_key", true, events) || changed;
7297
7408
  }
7409
+ const orgName = process.env.SWARM_ORG_NAME?.trim();
7410
+ if (state.steps.name.status !== "done" && orgName && orgName !== "Your Swarm") {
7411
+ changed = completeStep(state, "name", "custom_name", true, events) || changed;
7412
+ }
7298
7413
  if (state.steps.ai.status !== "done" && signals.providers.some((entry) => entry.state === "verified")) {
7299
7414
  changed = completeStep(state, "ai", inferAiMethod(signals), true, events) || changed;
7300
7415
  }
@@ -7590,6 +7705,417 @@ init_rbac();
7590
7705
  init_types();
7591
7706
  init_request_auth_context();
7592
7707
  init_secret_scrubber();
7708
+
7709
+ // src/http/ranking.ts
7710
+ init_zod();
7711
+ init_db3();
7712
+ init_telemetry_ranking();
7713
+ init_telemetry();
7714
+ init_env_flag();
7715
+
7716
+ // src/http/global-config.ts
7717
+ init_db3();
7718
+ async function globalConfig(key) {
7719
+ const stored = (await getSwarmConfigs2({ scope: "global", key }))?.[0]?.value;
7720
+ return (stored ?? process.env[key])?.trim() || undefined;
7721
+ }
7722
+
7723
+ // src/http/used-by.ts
7724
+ init_zod();
7725
+ init_db3();
7726
+ init_telemetry();
7727
+ init_company_profile();
7728
+ init_org_website();
7729
+ var CONSENT_KEY = "SWARM_USED_BY_CONSENT";
7730
+ var CONSENT_AT_KEY = "SWARM_USED_BY_CONSENT_AT";
7731
+ var GRANTED_NAME_KEY = "SWARM_USED_BY_ORG_NAME";
7732
+ var GRANTED_WEBSITE_KEY = "SWARM_USED_BY_ORG_WEBSITE";
7733
+ var CONTACT_EMAIL_KEY = "SWARM_USED_BY_CONTACT_EMAIL";
7734
+ var CONTACT_FUNCTION_KEY = "SWARM_USED_BY_CONTACT_FUNCTION";
7735
+ var QUOTE_KEY = "SWARM_USED_BY_QUOTE";
7736
+ var TIMEOUT_MS = 1e4;
7737
+ var usedBy = route({
7738
+ method: "post",
7739
+ path: "/api/used-by",
7740
+ pattern: ["api", "used-by"],
7741
+ summary: "Grant or revoke the 'used by' logo consent",
7742
+ description: "Records consent to show the organization's logo on agent-swarm.dev. A grant requires a valid contact email. It sends organization identity, contact details, and an optional quote to the proxy for manual review. This explicit admin action works when telemetry is off. A revoke clears the stored contact details and quote.",
7743
+ tags: ["Telemetry"],
7744
+ body: object2({
7745
+ granted: boolean(),
7746
+ contactEmail: string2().max(320, "Contact email must be at most 320 characters").email("Contact email must be a valid email address").optional(),
7747
+ contactFunction: _enum2(USER_FUNCTIONS).optional(),
7748
+ quote: string2().max(280).optional()
7749
+ }).refine((body) => !body.granted || body.contactEmail !== undefined, {
7750
+ path: ["contactEmail"],
7751
+ message: "Contact email is required when granting logo consent"
7752
+ }),
7753
+ responses: {
7754
+ 200: {
7755
+ description: "Consent recorded",
7756
+ schema: object2({ granted: boolean(), changedAt: string2() })
7757
+ },
7758
+ 400: {
7759
+ description: "Invalid contact details, or missing organization name or website"
7760
+ },
7761
+ 401: { description: "Unauthorized" },
7762
+ 403: { description: "Only a config admin can change the consent" },
7763
+ 502: { description: "The telemetry proxy did not accept the consent" }
7764
+ },
7765
+ rbac: { permission: "config.write.any" }
7766
+ });
7767
+ async function telemetryProxyEndpoint(path) {
7768
+ const feedback = await globalConfig("feedback_endpoint");
7769
+ if (feedback && /^https?:\/\/[^/]+\/v1\/feedback\/?$/.test(feedback)) {
7770
+ return feedback.replace(/\/v1\/feedback\/?$/, path);
7771
+ }
7772
+ return `${TELEMETRY_PROXY_ORIGIN}${path}`;
7773
+ }
7774
+ function usedByEndpoint() {
7775
+ return telemetryProxyEndpoint("/v1/used-by");
7776
+ }
7777
+ async function handleUsedBy(req, res, pathSegments, queryParams, myAgentId) {
7778
+ if (!usedBy.match(req.method, pathSegments))
7779
+ return false;
7780
+ const parsed = await usedBy.parse(req, res, pathSegments, queryParams);
7781
+ if (!parsed)
7782
+ return true;
7783
+ if (!await ensureConfigAdmin(req, res, "config.write.any"))
7784
+ return true;
7785
+ const { granted, contactEmail, contactFunction, quote } = parsed.body;
7786
+ const current = orgIdentityStatus({
7787
+ SWARM_ORG_NAME: await globalConfig("SWARM_ORG_NAME"),
7788
+ SWARM_ORG_WEBSITE: await globalConfig("SWARM_ORG_WEBSITE")
7789
+ });
7790
+ const currentName = current.name ?? undefined;
7791
+ const currentWebsite = current.website;
7792
+ const grantedName = granted ? undefined : await globalConfig(GRANTED_NAME_KEY);
7793
+ const grantedWebsite = granted ? null : normalizeOrgWebsite(await globalConfig(GRANTED_WEBSITE_KEY));
7794
+ const [orgName, orgWebsite] = grantedName && grantedWebsite ? [grantedName, grantedWebsite] : [currentName, currentWebsite];
7795
+ if (!orgName || !orgWebsite) {
7796
+ jsonError(res, "Set the organization name and website first", 400);
7797
+ return true;
7798
+ }
7799
+ const installId = isTelemetryEnabled() ? await globalConfig("telemetry_installation_id") : undefined;
7800
+ const orgDomain = await globalConfig("telemetry_org_domain");
7801
+ const changedAt = new Date().toISOString();
7802
+ try {
7803
+ const response = await fetch(await usedByEndpoint(), {
7804
+ method: "POST",
7805
+ headers: { "Content-Type": "application/json" },
7806
+ body: JSON.stringify({
7807
+ ...installId ? { install_id: installId } : {},
7808
+ org_name: orgName,
7809
+ org_website: orgWebsite,
7810
+ ...orgDomain ? { org_domain: orgDomain } : {},
7811
+ granted,
7812
+ ...granted ? { contact_email: contactEmail, contact_function: contactFunction, quote } : {},
7813
+ changed_at: changedAt
7814
+ }),
7815
+ signal: AbortSignal.timeout(TIMEOUT_MS)
7816
+ });
7817
+ if (!response.ok) {
7818
+ jsonError(res, `The telemetry proxy answered ${response.status}`, 502);
7819
+ return true;
7820
+ }
7821
+ } catch {
7822
+ jsonError(res, "Could not reach the telemetry proxy", 502);
7823
+ return true;
7824
+ }
7825
+ await getDbClient2().transaction(async () => {
7826
+ await upsertSwarmConfig2({ scope: "global", key: CONSENT_KEY, value: String(granted) });
7827
+ await upsertSwarmConfig2({ scope: "global", key: CONSENT_AT_KEY, value: changedAt });
7828
+ if (granted) {
7829
+ await upsertSwarmConfig2({ scope: "global", key: GRANTED_NAME_KEY, value: orgName });
7830
+ await upsertSwarmConfig2({ scope: "global", key: GRANTED_WEBSITE_KEY, value: orgWebsite });
7831
+ }
7832
+ for (const [key, value, isSecret] of [
7833
+ [CONTACT_EMAIL_KEY, contactEmail, true],
7834
+ [CONTACT_FUNCTION_KEY, contactFunction, false],
7835
+ [QUOTE_KEY, quote, false]
7836
+ ]) {
7837
+ await upsertSwarmConfig2({
7838
+ scope: "global",
7839
+ key,
7840
+ value: granted ? value ?? "" : "",
7841
+ isSecret,
7842
+ exportToEnv: false
7843
+ });
7844
+ }
7845
+ });
7846
+ scheduleIntegrationsReload();
7847
+ const owner = await resolveHttpFavoriteOwner(req, myAgentId);
7848
+ trackRelayed("used_by.consent_changed", {
7849
+ granted,
7850
+ has_quote: granted && Boolean(quote),
7851
+ has_function: granted && Boolean(contactFunction)
7852
+ }, { actor: owner?.userId ? { userId: owner.userId } : undefined, emitter: "ui" });
7853
+ usedBy.respond(res, 200, { granted, changedAt });
7854
+ return true;
7855
+ }
7856
+
7857
+ // src/http/ranking.ts
7858
+ var ListingSchema = object2({
7859
+ v: literal2(1),
7860
+ name: string2(),
7861
+ domain: string2(),
7862
+ category: _enum2(RANKING_CATEGORIES),
7863
+ subcategory: string2().nullable(),
7864
+ description: string2().nullable(),
7865
+ sourceCodeUrl: string2().nullable(),
7866
+ updatedAt: string2()
7867
+ });
7868
+ var RankingStateSchema = object2({
7869
+ eligible: boolean(),
7870
+ missing: array2(_enum2(ORG_IDENTITY_KEYS)),
7871
+ telemetryOn: boolean(),
7872
+ enabled: boolean(),
7873
+ name: string2().nullable(),
7874
+ domain: string2().nullable(),
7875
+ listing: ListingSchema.nullable(),
7876
+ listingMatchesDomain: boolean(),
7877
+ verifyUrl: string2().nullable()
7878
+ });
7879
+ var OptInResponseSchema = object2({
7880
+ domain: string2(),
7881
+ listing: ListingSchema,
7882
+ verifyUrl: string2()
7883
+ });
7884
+ var OptOutResponseSchema = object2({
7885
+ enabled: literal2(false)
7886
+ });
7887
+ var getRanking = route({
7888
+ method: "get",
7889
+ path: "/api/ranking",
7890
+ pattern: ["api", "ranking"],
7891
+ summary: "Public ranking state",
7892
+ description: "Whether this install can join the public agent-swarm.dev ranking (telemetry on, organization name and website set), whether it is opted in, and its stored listing. Only a config admin can read it.",
7893
+ tags: ["Telemetry"],
7894
+ responses: {
7895
+ 200: { description: "Ranking state", schema: RankingStateSchema },
7896
+ 401: { description: "Unauthorized" },
7897
+ 403: { description: "Only a config admin can read the ranking state" }
7898
+ },
7899
+ rbac: { permission: "config.write.any" }
7900
+ });
7901
+ var optIn = route({
7902
+ method: "post",
7903
+ path: "/api/ranking/opt-in",
7904
+ pattern: ["api", "ranking", "opt-in"],
7905
+ summary: "Opt this install into the public ranking",
7906
+ description: "Builds the telemetry_ranking listing from the organization name and website plus the chosen category, stores it, turns PUBLIC_RANKING_ENABLED on and sends ranking.opted_in. That is the whole opt-in: the install shows on agent-swarm.dev/ranking at the next hourly refresh.",
7907
+ tags: ["Telemetry"],
7908
+ body: rankingFormSchema,
7909
+ responses: {
7910
+ 200: { description: "Opted in", schema: OptInResponseSchema },
7911
+ 400: { description: "A listing field is invalid" },
7912
+ 401: { description: "Unauthorized" },
7913
+ 403: { description: "Only a config admin can opt in" },
7914
+ 409: {
7915
+ description: "telemetry_off: telemetry is off or the install has no ID. org_identity_missing: the organization name or website is not set (`missing` lists which)."
7916
+ }
7917
+ },
7918
+ rbac: { permission: "config.write.any" }
7919
+ });
7920
+ var optOut = route({
7921
+ method: "post",
7922
+ path: "/api/ranking/opt-out",
7923
+ pattern: ["api", "ranking", "opt-out"],
7924
+ summary: "Leave the public ranking",
7925
+ description: "Turns PUBLIC_RANKING_ENABLED off and sends ranking.opted_out, so workers stop sending ranking.usage. The install leaves agent-swarm.dev/ranking at the next hourly refresh.",
7926
+ tags: ["Telemetry"],
7927
+ responses: {
7928
+ 200: { description: "Opted out", schema: OptOutResponseSchema },
7929
+ 401: { description: "Unauthorized" },
7930
+ 403: { description: "Only a config admin can opt out" }
7931
+ },
7932
+ rbac: { permission: "config.write.any" }
7933
+ });
7934
+ var RANKING_CONFIG_KEYS = new Set([...ORG_IDENTITY_KEYS, PUBLIC_RANKING_ENABLED_KEY]);
7935
+ async function currentIdentity(changed) {
7936
+ const read = async (key) => changed?.key === key ? changed.value : await globalConfig(key);
7937
+ return orgIdentityStatus({
7938
+ SWARM_ORG_NAME: await read("SWARM_ORG_NAME"),
7939
+ SWARM_ORG_WEBSITE: await read("SWARM_ORG_WEBSITE")
7940
+ });
7941
+ }
7942
+ async function storedListing() {
7943
+ return readTelemetryRanking(await globalConfig(TELEMETRY_RANKING_KEY));
7944
+ }
7945
+ async function rankingEnabled() {
7946
+ const raw = await globalConfig(PUBLIC_RANKING_ENABLED_KEY);
7947
+ return isEnvFlagEnabled(PUBLIC_RANKING_ENABLED_KEY, false, {
7948
+ [PUBLIC_RANKING_ENABLED_KEY]: raw
7949
+ });
7950
+ }
7951
+ async function telemetryReady() {
7952
+ return isTelemetryEnabled() && Boolean(await globalConfig("telemetry_installation_id"));
7953
+ }
7954
+ async function verifyUrl(domain) {
7955
+ const params = new URLSearchParams({ domain });
7956
+ return `${await telemetryProxyEndpoint("/v1/ranking/verify")}?${params.toString()}`;
7957
+ }
7958
+ function withoutClaim(listing) {
7959
+ const { claim: _claim, ...rest } = listing;
7960
+ return rest;
7961
+ }
7962
+ async function setEnabled(enabled) {
7963
+ await upsertSwarmConfig2({
7964
+ scope: "global",
7965
+ key: PUBLIC_RANKING_ENABLED_KEY,
7966
+ value: String(enabled)
7967
+ });
7968
+ }
7969
+ function emitOptedIn(listing) {
7970
+ telemetry.ranking("opted_in", { listing: JSON.stringify(withoutClaim(listing)) }, { orgName: listing.name, orgWebsite: listing.domain });
7971
+ }
7972
+ function emitOptedOut(domain) {
7973
+ telemetry.ranking("opted_out", { domain });
7974
+ }
7975
+ async function buildRankingState() {
7976
+ const [identity, listing, enabled, telemetryOn] = await Promise.all([
7977
+ currentIdentity(),
7978
+ storedListing(),
7979
+ rankingEnabled(),
7980
+ telemetryReady()
7981
+ ]);
7982
+ const listingMatchesDomain = Boolean(listing && listing.domain === identity.website);
7983
+ const current = listingMatchesDomain ? listing : null;
7984
+ return {
7985
+ eligible: telemetryOn && identity.ok,
7986
+ missing: identity.missing,
7987
+ telemetryOn,
7988
+ enabled: enabled && listingMatchesDomain,
7989
+ name: identity.name,
7990
+ domain: identity.website,
7991
+ listing: listing ? withoutClaim(listing) : null,
7992
+ listingMatchesDomain,
7993
+ verifyUrl: current ? await verifyUrl(current.domain) : null
7994
+ };
7995
+ }
7996
+ async function syncRankingAfterConfigChange(key, value) {
7997
+ if (!RANKING_CONFIG_KEYS.has(key))
7998
+ return;
7999
+ const listing = await storedListing();
8000
+ if (!listing)
8001
+ return;
8002
+ if (key === PUBLIC_RANKING_ENABLED_KEY) {
8003
+ if (!isEnvFlagEnabled(key, false, { [key]: value ?? undefined })) {
8004
+ emitOptedOut(listing.domain);
8005
+ }
8006
+ return;
8007
+ }
8008
+ if (!await rankingEnabled())
8009
+ return;
8010
+ const identity = await currentIdentity({ key, value });
8011
+ let rebuilt = null;
8012
+ if (identity.ok && identity.website === listing.domain) {
8013
+ if (identity.name === listing.name)
8014
+ return;
8015
+ try {
8016
+ rebuilt = buildListing(identity, listing);
8017
+ } catch {}
8018
+ }
8019
+ if (!rebuilt) {
8020
+ await setEnabled(false);
8021
+ emitOptedOut(listing.domain);
8022
+ return;
8023
+ }
8024
+ await upsertSwarmConfig2({
8025
+ scope: "global",
8026
+ key: TELEMETRY_RANKING_KEY,
8027
+ value: JSON.stringify(rebuilt)
8028
+ });
8029
+ emitOptedIn(rebuilt);
8030
+ }
8031
+ function conflict(res, body) {
8032
+ res.writeHead(409, { "Content-Type": "application/json" });
8033
+ res.end(JSON.stringify(body));
8034
+ }
8035
+ async function handleRanking(req, res, pathSegments, queryParams) {
8036
+ if (getRanking.match(req.method, pathSegments)) {
8037
+ const parsed = await getRanking.parse(req, res, pathSegments, queryParams);
8038
+ if (!parsed)
8039
+ return true;
8040
+ if (!await ensureConfigAdmin(req, res, "config.write.any"))
8041
+ return true;
8042
+ getRanking.respond(res, 200, await buildRankingState());
8043
+ return true;
8044
+ }
8045
+ if (optIn.match(req.method, pathSegments)) {
8046
+ const parsed = await optIn.parse(req, res, pathSegments, queryParams);
8047
+ if (!parsed)
8048
+ return true;
8049
+ if (!await ensureConfigAdmin(req, res, "config.write.any"))
8050
+ return true;
8051
+ if (!await telemetryReady()) {
8052
+ conflict(res, {
8053
+ error: "telemetry_off",
8054
+ message: "Turn telemetry on first: the ranking counts tokens from telemetry."
8055
+ });
8056
+ return true;
8057
+ }
8058
+ const identity = await currentIdentity();
8059
+ if (!identity.ok || !identity.website) {
8060
+ conflict(res, {
8061
+ error: "org_identity_missing",
8062
+ message: "Set the organization name and website first",
8063
+ missing: identity.missing
8064
+ });
8065
+ return true;
8066
+ }
8067
+ const db = getDbClient2();
8068
+ const built = await db.transaction(async () => {
8069
+ let listing;
8070
+ try {
8071
+ listing = buildListing(identity, parsed.body);
8072
+ } catch (error) {
8073
+ return { ok: false, error };
8074
+ }
8075
+ await upsertSwarmConfig2({
8076
+ scope: "global",
8077
+ key: TELEMETRY_RANKING_KEY,
8078
+ value: JSON.stringify(listing)
8079
+ });
8080
+ await setEnabled(true);
8081
+ db.afterCommit(() => emitOptedIn(listing));
8082
+ return { ok: true, listing };
8083
+ });
8084
+ if (!built.ok) {
8085
+ const issue = built.error instanceof ZodError ? built.error.issues[0] : undefined;
8086
+ res.writeHead(400, { "Content-Type": "application/json" });
8087
+ res.end(JSON.stringify({
8088
+ error: issue ? `Invalid ${issue.path.join(".") || "listing"}: ${issue.message}` : "Invalid listing"
8089
+ }));
8090
+ return true;
8091
+ }
8092
+ const { listing } = built;
8093
+ scheduleIntegrationsReload();
8094
+ optIn.respond(res, 200, {
8095
+ domain: listing.domain,
8096
+ listing,
8097
+ verifyUrl: await verifyUrl(listing.domain)
8098
+ });
8099
+ return true;
8100
+ }
8101
+ if (optOut.match(req.method, pathSegments)) {
8102
+ const parsed = await optOut.parse(req, res, pathSegments, queryParams);
8103
+ if (!parsed)
8104
+ return true;
8105
+ if (!await ensureConfigAdmin(req, res, "config.write.any"))
8106
+ return true;
8107
+ const listing = await storedListing();
8108
+ await setEnabled(false);
8109
+ if (listing)
8110
+ emitOptedOut(listing.domain);
8111
+ scheduleIntegrationsReload();
8112
+ optOut.respond(res, 200, { enabled: false });
8113
+ return true;
8114
+ }
8115
+ return false;
8116
+ }
8117
+
8118
+ // src/http/config.ts
7593
8119
  var MAX_ENV_PRESENCE_KEYS = 200;
7594
8120
  var SECRETS_FORCE_MASK_NOTE = " (secret values masked: this token cannot read secrets in this scope)";
7595
8121
  function stripApiOnlyKeys(configs) {
@@ -7959,6 +8485,8 @@ async function handleConfig(req, res, pathSegments, queryParams) {
7959
8485
  if (scope === "global" && key === "SWARM_ORG_WEBSITE") {
7960
8486
  await fillOrgLogoFromWebsite(String(value));
7961
8487
  }
8488
+ if (scope === "global")
8489
+ await syncRankingAfterConfigChange(key, String(value));
7962
8490
  if (scope === "global") {
7963
8491
  scheduleIntegrationsReload();
7964
8492
  }
@@ -7994,6 +8522,7 @@ async function handleConfig(req, res, pathSegments, queryParams) {
7994
8522
  return true;
7995
8523
  }
7996
8524
  if (existing.scope === "global") {
8525
+ await syncRankingAfterConfigChange(existing.key, null);
7997
8526
  scheduleIntegrationsReload();
7998
8527
  }
7999
8528
  if (existing.scope === "agent" && existing.scopeId && existing.key === AGENT_MAX_TASKS_CONFIG_KEY) {
@@ -8830,6 +9359,7 @@ var postContext = route({
8830
9359
  method: "post",
8831
9360
  path: "/api/tasks/{id}/context",
8832
9361
  pattern: ["api", "tasks", null, "context"],
9362
+ session: { taskParam: "id" },
8833
9363
  summary: "Record a context usage snapshot for a task",
8834
9364
  tags: ["Tasks"],
8835
9365
  params: object2({ id: string2() }),
@@ -8856,6 +9386,7 @@ var getContext = route({
8856
9386
  method: "get",
8857
9387
  path: "/api/tasks/{id}/context",
8858
9388
  pattern: ["api", "tasks", null, "context"],
9389
+ session: { taskParam: "id" },
8859
9390
  summary: "Get context usage history for a task",
8860
9391
  tags: ["Tasks"],
8861
9392
  params: object2({ id: string2() }),
@@ -8936,6 +9467,7 @@ var createEventRoute = route({
8936
9467
  method: "post",
8937
9468
  path: "/api/events",
8938
9469
  pattern: ["api", "events"],
9470
+ session: bodyTask("taskId"),
8939
9471
  summary: "Store a single event",
8940
9472
  tags: ["Events"],
8941
9473
  body: eventBodySchema,
@@ -8952,6 +9484,7 @@ var createEventsBatchRoute = route({
8952
9484
  method: "post",
8953
9485
  path: "/api/events/batch",
8954
9486
  pattern: ["api", "events", "batch"],
9487
+ session: bodyListTask("events", "taskId"),
8955
9488
  summary: "Store multiple events in a batch",
8956
9489
  tags: ["Events"],
8957
9490
  body: object2({
@@ -9026,7 +9559,7 @@ var getEventCountsRoute = route({
9026
9559
  auth: { apiKey: true }
9027
9560
  });
9028
9561
  var MAX_LATEST_PAIRS = 50;
9029
- async function handleEvents(req, res, pathSegments, queryParams, _myAgentId) {
9562
+ async function handleEvents(req, res, pathSegments, queryParams, myAgentId) {
9030
9563
  if (createEventsBatchRoute.match(req.method, pathSegments)) {
9031
9564
  const parsed = await createEventsBatchRoute.parse(req, res, pathSegments, queryParams);
9032
9565
  if (!parsed)
@@ -9100,7 +9633,8 @@ async function handleEvents(req, res, pathSegments, queryParams, _myAgentId) {
9100
9633
  until: parsed.query.until || undefined,
9101
9634
  limit: parsed.query.limit ?? 100,
9102
9635
  events: eventFilter,
9103
- latestPerDataField: latestPerDataField ? { events: latestEvents, dataFields } : undefined
9636
+ latestPerDataField: latestPerDataField ? { events: latestEvents, dataFields } : undefined,
9637
+ readableBy: readableByFor2(await resolveSessionActor2(req, myAgentId))
9104
9638
  });
9105
9639
  getEventsRoute.respond(res, 200, latestPerDataField ? { events, latestPerDataField: true } : { events });
9106
9640
  return true;
@@ -12423,7 +12957,6 @@ async function handleFavorites(req, res, pathSegments, queryParams, myAgentId) {
12423
12957
 
12424
12958
  // src/http/fleet-benchmark.ts
12425
12959
  init_zod();
12426
- init_db3();
12427
12960
 
12428
12961
  // src/fleet-benchmark.ts
12429
12962
  var FLEET_WINDOW_DAYS = 30;
@@ -12502,8 +13035,8 @@ function round(value, decimals) {
12502
13035
 
12503
13036
  // src/be/fleet-benchmark-stats.ts
12504
13037
  init_runtime();
12505
- async function getLocalFleetStats(now = new Date) {
12506
- const start = fleetCountStart(await getTelemetryIngestAnchor(), now);
13038
+ async function getLocalFleetStats(now = new Date, { requireIngestAnchor = true } = {}) {
13039
+ const start = requireIngestAnchor ? fleetCountStart(await getTelemetryIngestAnchor(), now) : new Date(now.getTime() - FLEET_WINDOW_DAYS * 86400000);
12507
13040
  if (!start)
12508
13041
  return null;
12509
13042
  const since = start.toISOString();
@@ -12524,110 +13057,8 @@ async function getLocalFleetStats(now = new Date) {
12524
13057
 
12525
13058
  // src/http/fleet-benchmark.ts
12526
13059
  init_telemetry();
12527
-
12528
- // src/http/used-by.ts
12529
- init_zod();
12530
- init_db3();
12531
- init_telemetry();
13060
+ init_company_profile();
12532
13061
  init_org_website();
12533
- var CONSENT_KEY = "SWARM_USED_BY_CONSENT";
12534
- var CONSENT_AT_KEY = "SWARM_USED_BY_CONSENT_AT";
12535
- var GRANTED_NAME_KEY = "SWARM_USED_BY_ORG_NAME";
12536
- var GRANTED_WEBSITE_KEY = "SWARM_USED_BY_ORG_WEBSITE";
12537
- var TIMEOUT_MS = 1e4;
12538
- var usedBy = route({
12539
- method: "post",
12540
- path: "/api/used-by",
12541
- pattern: ["api", "used-by"],
12542
- summary: "Grant or revoke the 'used by' logo consent",
12543
- description: "Records whether agent-swarm.dev may show this organization's logo as a company that uses Agent Swarm. Sends the organization name and website to the telemetry proxy, where a person approves each logo by hand. An explicit admin action, so it is not gated by the telemetry opt-out.",
12544
- tags: ["Telemetry"],
12545
- body: object2({ granted: boolean() }),
12546
- responses: {
12547
- 200: {
12548
- description: "Consent recorded",
12549
- schema: object2({ granted: boolean(), changedAt: string2() })
12550
- },
12551
- 400: {
12552
- description: "Organization name or website is not set (a revoke falls back to the identity of the last grant)"
12553
- },
12554
- 401: { description: "Unauthorized" },
12555
- 403: { description: "Only a config admin can change the consent" },
12556
- 502: { description: "The telemetry proxy did not accept the consent" }
12557
- },
12558
- rbac: { permission: "config.write.any" }
12559
- });
12560
- async function globalConfig(key) {
12561
- const stored = (await getSwarmConfigs2({ scope: "global", key }))?.[0]?.value?.trim();
12562
- return stored || process.env[key]?.trim() || undefined;
12563
- }
12564
- async function telemetryProxyEndpoint(path) {
12565
- const feedback = await globalConfig("feedback_endpoint");
12566
- if (feedback && /^https?:\/\/[^/]+\/v1\/feedback\/?$/.test(feedback)) {
12567
- return feedback.replace(/\/v1\/feedback\/?$/, path);
12568
- }
12569
- return `${TELEMETRY_PROXY_ORIGIN}${path}`;
12570
- }
12571
- function usedByEndpoint() {
12572
- return telemetryProxyEndpoint("/v1/used-by");
12573
- }
12574
- async function handleUsedBy(req, res, pathSegments, queryParams, myAgentId) {
12575
- if (!usedBy.match(req.method, pathSegments))
12576
- return false;
12577
- const parsed = await usedBy.parse(req, res, pathSegments, queryParams);
12578
- if (!parsed)
12579
- return true;
12580
- if (!await ensureConfigAdmin(req, res, "config.write.any"))
12581
- return true;
12582
- const { granted } = parsed.body;
12583
- const currentName = await globalConfig("SWARM_ORG_NAME");
12584
- const currentWebsite = normalizeOrgWebsite(await globalConfig("SWARM_ORG_WEBSITE"));
12585
- const grantedName = granted ? undefined : await globalConfig(GRANTED_NAME_KEY);
12586
- const grantedWebsite = granted ? null : normalizeOrgWebsite(await globalConfig(GRANTED_WEBSITE_KEY));
12587
- const [orgName, orgWebsite] = grantedName && grantedWebsite ? [grantedName, grantedWebsite] : [currentName, currentWebsite];
12588
- if (!orgName || !orgWebsite) {
12589
- jsonError(res, "Set the organization name and website first", 400);
12590
- return true;
12591
- }
12592
- const installId = isTelemetryEnabled() ? await globalConfig("telemetry_installation_id") : undefined;
12593
- const orgDomain = await globalConfig("telemetry_org_domain");
12594
- const changedAt = new Date().toISOString();
12595
- try {
12596
- const response = await fetch(await usedByEndpoint(), {
12597
- method: "POST",
12598
- headers: { "Content-Type": "application/json" },
12599
- body: JSON.stringify({
12600
- ...installId ? { install_id: installId } : {},
12601
- org_name: orgName,
12602
- org_website: orgWebsite,
12603
- ...orgDomain ? { org_domain: orgDomain } : {},
12604
- granted,
12605
- changed_at: changedAt
12606
- }),
12607
- signal: AbortSignal.timeout(TIMEOUT_MS)
12608
- });
12609
- if (!response.ok) {
12610
- jsonError(res, `The telemetry proxy answered ${response.status}`, 502);
12611
- return true;
12612
- }
12613
- } catch {
12614
- jsonError(res, "Could not reach the telemetry proxy", 502);
12615
- return true;
12616
- }
12617
- await upsertSwarmConfig2({ scope: "global", key: CONSENT_KEY, value: String(granted) });
12618
- await upsertSwarmConfig2({ scope: "global", key: CONSENT_AT_KEY, value: changedAt });
12619
- if (granted) {
12620
- await upsertSwarmConfig2({ scope: "global", key: GRANTED_NAME_KEY, value: orgName });
12621
- await upsertSwarmConfig2({ scope: "global", key: GRANTED_WEBSITE_KEY, value: orgWebsite });
12622
- }
12623
- scheduleIntegrationsReload();
12624
- const owner = await resolveHttpFavoriteOwner(req, myAgentId);
12625
- trackRelayed("used_by.consent_changed", { granted }, { actor: owner?.userId ? { userId: owner.userId } : undefined, emitter: "ui" });
12626
- usedBy.respond(res, 200, { granted, changedAt });
12627
- return true;
12628
- }
12629
-
12630
- // src/http/fleet-benchmark.ts
12631
13062
  var FLEET_TTL_MS = 24 * 60 * 60 * 1000;
12632
13063
  var FLEET_FAILURE_TTL_MS = 60 * 60 * 1000;
12633
13064
  var LOCAL_TTL_MS = 10 * 60 * 1000;
@@ -12641,6 +13072,12 @@ var FleetStateSchema = _enum2([
12641
13072
  ]);
12642
13073
  var FleetBenchmarkResponseSchema = object2({
12643
13074
  state: FleetStateSchema,
13075
+ level: _enum2(["basic", "full"]),
13076
+ peerGroup: object2({
13077
+ teamSize: string2().nullable(),
13078
+ useCase: string2().nullable(),
13079
+ orgs: number2().int()
13080
+ }).nullable(),
12644
13081
  local: object2({
12645
13082
  tasksPerWeek: number2(),
12646
13083
  taskSuccessRate: number2().nullable(),
@@ -12664,7 +13101,7 @@ var fleetBenchmark = route({
12664
13101
  path: "/api/fleet-benchmark",
12665
13102
  pattern: ["api", "fleet-benchmark"],
12666
13103
  summary: "Compare this swarm with similar swarms",
12667
- description: "Places this swarm's tasks per week and task success rate (last 30 days) in the fleet percentiles published by the telemetry proxy, against swarms with the same agent count when enough exist. Needs telemetry on and an organization name; otherwise `state` says what is missing and the proxy is not called. `unavailable` also covers an install whose first event the proxy saw cannot be established locally (until 30 days after the first accepted event). The fleet numbers are cached for a day.",
13104
+ description: "Compares local task volume and success rate with fleet percentiles over the last 30 days. Comparisons require telemetry, an organization name, and a known proxy ingest date. A website, team size, and use case enable full comparisons with peer filters. The response includes the proxy's peer group when available. With telemetry off, it returns local DB counts without a proxy request. The fleet cache key includes the peer filters.",
12668
13105
  tags: ["Telemetry"],
12669
13106
  responses: {
12670
13107
  200: {
@@ -12700,48 +13137,64 @@ var ProxyFleetBenchmarksSchema = object2({
12700
13137
  "1": ProxyBlockSchema.optional(),
12701
13138
  "2-5": ProxyBlockSchema.optional(),
12702
13139
  "6+": ProxyBlockSchema.optional()
12703
- })
12704
- });
12705
- async function fleetBenchmarks(now) {
12706
- if (fleetCache && fleetCache.until > now)
13140
+ }),
13141
+ peer_group: object2({
13142
+ team_size: string2().nullable(),
13143
+ use_case: string2().nullable(),
13144
+ orgs: number2().int().nonnegative()
13145
+ }).nullable().optional()
13146
+ });
13147
+ async function fleetBenchmarks(now, peerGroup) {
13148
+ const endpoint = new URL(await telemetryProxyEndpoint("/v1/fleet/benchmarks"));
13149
+ endpoint.searchParams.set("window", "30d");
13150
+ if (peerGroup) {
13151
+ endpoint.searchParams.set("team_size", peerGroup.teamSize);
13152
+ endpoint.searchParams.set("use_case", peerGroup.useCase);
13153
+ }
13154
+ const key = endpoint.toString();
13155
+ if (fleetCache?.key === key && fleetCache.until > now)
12707
13156
  return fleetCache.value;
12708
13157
  let value = null;
12709
13158
  try {
12710
- const response = await fetch(`${await telemetryProxyEndpoint("/v1/fleet/benchmarks")}?window=30d`, {
12711
- signal: AbortSignal.timeout(TIMEOUT_MS2)
12712
- });
13159
+ const response = await fetch(key, { signal: AbortSignal.timeout(TIMEOUT_MS2) });
12713
13160
  if (response.ok) {
12714
13161
  const parsed = ProxyFleetBenchmarksSchema.safeParse(await response.json());
12715
13162
  if (parsed.success)
12716
13163
  value = parsed.data;
12717
13164
  }
12718
13165
  } catch {}
12719
- fleetCache = { value, until: now + (value ? FLEET_TTL_MS : FLEET_FAILURE_TTL_MS) };
13166
+ fleetCache = { key, value, until: now + (value ? FLEET_TTL_MS : FLEET_FAILURE_TTL_MS) };
12720
13167
  return value;
12721
13168
  }
12722
- async function localStats(now) {
12723
- if (localCache && localCache.until > now.getTime())
13169
+ async function localStats(now, requireIngestAnchor = true) {
13170
+ if (localCache?.requireIngestAnchor === requireIngestAnchor && localCache.until > now.getTime())
12724
13171
  return localCache.value;
12725
- const value = await getLocalFleetStats(now);
12726
- localCache = { value, until: now.getTime() + LOCAL_TTL_MS };
13172
+ const value = await getLocalFleetStats(now, { requireIngestAnchor });
13173
+ localCache = { requireIngestAnchor, value, until: now.getTime() + LOCAL_TTL_MS };
12727
13174
  return value;
12728
13175
  }
12729
- async function orgName() {
12730
- const stored = (await getSwarmConfigs2({ scope: "global", key: "SWARM_ORG_NAME" }))?.[0]?.value?.trim();
12731
- return stored || process.env.SWARM_ORG_NAME?.trim() || undefined;
12732
- }
12733
13176
  async function buildFleetBenchmarkResponse(now = new Date) {
13177
+ const [name, website, teamSize, useCase] = await Promise.all([
13178
+ globalConfig("SWARM_ORG_NAME"),
13179
+ globalConfig("SWARM_ORG_WEBSITE"),
13180
+ globalConfig("SWARM_ORG_TEAM_SIZE"),
13181
+ globalConfig("SWARM_ORG_USE_CASE")
13182
+ ]);
13183
+ const team = _enum2(COMPANY_TEAM_SIZES).safeParse(teamSize);
13184
+ const use = _enum2(COMPANY_USE_CASES).safeParse(useCase);
13185
+ const peerGroup = normalizeOrgWebsite(website) && team.success && use.success ? { teamSize: team.data, useCase: use.data } : null;
12734
13186
  const empty = {
13187
+ level: peerGroup ? "full" : "basic",
13188
+ peerGroup: null,
12735
13189
  local: null,
12736
13190
  minFinishedTasks: FLEET_MIN_FINISHED_TASKS,
12737
13191
  generatedAt: null,
12738
13192
  comparisons: []
12739
13193
  };
12740
- if (!isTelemetryEnabled())
12741
- return { state: "telemetry_off", ...empty };
12742
- if (!await orgName())
13194
+ const telemetryEnabled = isTelemetryEnabled();
13195
+ if (telemetryEnabled && !name)
12743
13196
  return { state: "no_org_name", ...empty };
12744
- const stats = await localStats(now);
13197
+ const stats = await localStats(now, telemetryEnabled);
12745
13198
  if (!stats)
12746
13199
  return { state: "unavailable", ...empty };
12747
13200
  const finishedTasks = stats.tasksCompleted + stats.tasksFailed;
@@ -12752,14 +13205,23 @@ async function buildFleetBenchmarkResponse(now = new Date) {
12752
13205
  agents: stats.agents,
12753
13206
  bucket: fleetBucketFor(stats.agents)
12754
13207
  };
13208
+ if (!telemetryEnabled)
13209
+ return { ...empty, state: "telemetry_off", local };
12755
13210
  if (finishedTasks < FLEET_MIN_FINISHED_TASKS)
12756
13211
  return { ...empty, state: "not_enough_tasks", local };
12757
- const fleet = await fleetBenchmarks(now.getTime());
13212
+ const fleet = await fleetBenchmarks(now.getTime(), peerGroup);
13213
+ const responsePeerGroup = peerGroup && fleet?.peer_group ? {
13214
+ teamSize: fleet.peer_group.team_size,
13215
+ useCase: fleet.peer_group.use_case,
13216
+ orgs: fleet.peer_group.orgs
13217
+ } : null;
12758
13218
  const comparisons = fleet ? compareWithFleet(stats, fleet, now) : [];
12759
13219
  if (!fleet || comparisons.length === 0)
12760
- return { ...empty, state: "unavailable", local };
13220
+ return { ...empty, state: "unavailable", local, peerGroup: responsePeerGroup };
12761
13221
  return {
13222
+ ...empty,
12762
13223
  state: "ready",
13224
+ peerGroup: responsePeerGroup,
12763
13225
  local,
12764
13226
  minFinishedTasks: FLEET_MIN_FINISHED_TASKS,
12765
13227
  generatedAt: fleet.generatedAt,
@@ -12886,6 +13348,7 @@ var listTaskFilesRoute = route({
12886
13348
  method: "get",
12887
13349
  path: "/api/fs/tasks/{taskId}/files",
12888
13350
  pattern: ["api", "fs", "tasks", null, "files"],
13351
+ session: { taskParam: "taskId" },
12889
13352
  summary: "List task file attachments",
12890
13353
  tags: ["FS"],
12891
13354
  params: taskParams,
@@ -12901,6 +13364,7 @@ var uploadTaskFileRoute = route({
12901
13364
  method: "post",
12902
13365
  path: "/api/fs/tasks/{taskId}/files",
12903
13366
  pattern: ["api", "fs", "tasks", null, "files"],
13367
+ session: { taskParam: "taskId" },
12904
13368
  summary: "Upload a binary task file attachment",
12905
13369
  description: "Accepts a raw binary request body. Pass the display/path name as the `name` query parameter.",
12906
13370
  tags: ["FS"],
@@ -12920,6 +13384,7 @@ var getTaskFileRoute = route({
12920
13384
  method: "get",
12921
13385
  path: "/api/fs/tasks/{taskId}/files/{attachmentId}",
12922
13386
  pattern: ["api", "fs", "tasks", null, "files", null],
13387
+ session: { taskParam: "taskId" },
12923
13388
  summary: "Get task file attachment metadata",
12924
13389
  tags: ["FS"],
12925
13390
  params: attachmentParams,
@@ -12932,6 +13397,7 @@ var downloadTaskFileRoute = route({
12932
13397
  method: "get",
12933
13398
  path: "/api/fs/tasks/{taskId}/files/{attachmentId}/raw",
12934
13399
  pattern: ["api", "fs", "tasks", null, "files", null, "raw"],
13400
+ session: { taskParam: "taskId" },
12935
13401
  summary: "Download raw task file bytes",
12936
13402
  description: "Streams raw bytes. File content is not secret-scrubbed.",
12937
13403
  tags: ["FS"],
@@ -12948,6 +13414,7 @@ var signedUrlTaskFileRoute = route({
12948
13414
  method: "get",
12949
13415
  path: "/api/fs/tasks/{taskId}/files/{attachmentId}/signed-url",
12950
13416
  pattern: ["api", "fs", "tasks", null, "files", null, "signed-url"],
13417
+ session: { taskParam: "taskId" },
12951
13418
  summary: "Create a provider signed GET URL for a task file",
12952
13419
  tags: ["FS"],
12953
13420
  params: attachmentParams,
@@ -12962,6 +13429,7 @@ var deleteTaskFileRoute = route({
12962
13429
  method: "delete",
12963
13430
  path: "/api/fs/tasks/{taskId}/files/{attachmentId}",
12964
13431
  pattern: ["api", "fs", "tasks", null, "files", null],
13432
+ session: { taskParam: "taskId" },
12965
13433
  summary: "Delete a task file attachment",
12966
13434
  tags: ["FS"],
12967
13435
  params: attachmentParams,
@@ -13010,7 +13478,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
13010
13478
  const parsed = await downloadTaskFileRoute.parse(req, res, pathSegments, queryParams);
13011
13479
  if (!parsed)
13012
13480
  return true;
13013
- const attachment = await findAttachment(parsed.params.taskId, parsed.params.attachmentId, res);
13481
+ const attachment = await findAttachment(req, myAgentId, parsed.params.taskId, parsed.params.attachmentId, res);
13014
13482
  if (!attachment)
13015
13483
  return true;
13016
13484
  return sendDownload(res, attachment);
@@ -13019,7 +13487,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
13019
13487
  const parsed = await signedUrlTaskFileRoute.parse(req, res, pathSegments, queryParams);
13020
13488
  if (!parsed)
13021
13489
  return true;
13022
- const attachment = await findAttachment(parsed.params.taskId, parsed.params.attachmentId, res);
13490
+ const attachment = await findAttachment(req, myAgentId, parsed.params.taskId, parsed.params.attachmentId, res);
13023
13491
  if (!attachment)
13024
13492
  return true;
13025
13493
  return sendSignedUrl(res, attachment, parsed.query.expiresIn);
@@ -13028,7 +13496,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
13028
13496
  const parsed = await getTaskFileRoute.parse(req, res, pathSegments, queryParams);
13029
13497
  if (!parsed)
13030
13498
  return true;
13031
- const attachment = await findAttachment(parsed.params.taskId, parsed.params.attachmentId, res);
13499
+ const attachment = await findAttachment(req, myAgentId, parsed.params.taskId, parsed.params.attachmentId, res);
13032
13500
  if (!attachment)
13033
13501
  return true;
13034
13502
  getTaskFileRoute.respond(res, 200, attachment);
@@ -13067,6 +13535,10 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
13067
13535
  return true;
13068
13536
  if (!await getTaskById2(parsed.params.taskId))
13069
13537
  return notFound(res, "Task not found");
13538
+ if (!await canReadSession2(req, myAgentId, parsed.params.taskId)) {
13539
+ jsonError(res, SESSION_READ_DENIED2, 403);
13540
+ return true;
13541
+ }
13070
13542
  listTaskFilesRoute.respond(res, 200, {
13071
13543
  attachments: await getTaskAttachments2(parsed.params.taskId)
13072
13544
  });
@@ -13202,11 +13674,15 @@ function backingProviderId(attachment) {
13202
13674
  return "local-fs";
13203
13675
  return null;
13204
13676
  }
13205
- async function findAttachment(taskId, attachmentId, res) {
13677
+ async function findAttachment(req, myAgentId, taskId, attachmentId, res) {
13206
13678
  if (!await getTaskById2(taskId)) {
13207
13679
  notFound(res, "Task not found");
13208
13680
  return null;
13209
13681
  }
13682
+ if (!await canReadSession2(req, myAgentId, taskId)) {
13683
+ jsonError(res, SESSION_READ_DENIED2, 403);
13684
+ return null;
13685
+ }
13210
13686
  const attachment = (await getTaskAttachments2(taskId)).find((item) => item.id === attachmentId);
13211
13687
  if (!attachment) {
13212
13688
  notFound(res, "Attachment not found");
@@ -17846,6 +18322,7 @@ var indexMemory = route({
17846
18322
  method: "post",
17847
18323
  path: "/api/memory/index",
17848
18324
  pattern: ["api", "memory", "index"],
18325
+ session: bodyTask("sourceTaskId"),
17849
18326
  summary: "Ingest content into memory system (async embedding)",
17850
18327
  tags: ["Memory"],
17851
18328
  rbac: { permission: "memory.write.consolidated" },
@@ -18323,6 +18800,7 @@ var rateMemory = route({
18323
18800
  method: "post",
18324
18801
  path: "/api/memory/rate",
18325
18802
  pattern: ["api", "memory", "rate"],
18803
+ session: { none: "worker-only: it needs X-Agent-ID, which a user token cannot send" },
18326
18804
  summary: "Submit RatingEvents to update memory usefulness posteriors",
18327
18805
  tags: ["Memory"],
18328
18806
  auth: { apiKey: true, agentId: true },
@@ -21770,7 +22248,7 @@ var listTasks = route({
21770
22248
  path: "/api/tasks",
21771
22249
  pattern: ["api", "tasks"],
21772
22250
  summary: "List tasks with filters",
21773
- description: "Returns tasks with the full `task` text replaced by a bounded `taskPreview` and completion/integration blobs dropped by default — list views only need the preview. Pass `fields=full` to restore the full `AgentTask`, or `fields=timeline` for the narrow shape the dashboard timeline draws. Fetch a single task in full via `GET /api/tasks/{id}`. `total` (the filtered row count, ignoring limit/offset) is computed only with `includeTotal=true`.",
22251
+ description: "Returns tasks with the full `task` text replaced by a bounded `taskPreview` and completion/integration blobs dropped by default — list views only need the preview. Pass `fields=full` to restore the full `AgentTask`, or `fields=timeline` for the narrow shape the dashboard timeline draws. Every shape gives a user caller only tasks of sessions they can read. Fetch a single task in full via `GET /api/tasks/{id}`. `total` (the filtered row count, ignoring limit/offset) is computed only with `includeTotal=true`.",
21774
22252
  tags: ["Tasks"],
21775
22253
  query: object2({
21776
22254
  status: string2().optional(),
@@ -21809,6 +22287,7 @@ var createTask = route({
21809
22287
  method: "post",
21810
22288
  path: "/api/tasks",
21811
22289
  pattern: ["api", "tasks"],
22290
+ session: bodyTask("parentTaskId"),
21812
22291
  summary: "Create a new task",
21813
22292
  tags: ["Tasks"],
21814
22293
  body: object2({
@@ -21864,6 +22343,7 @@ var promoteDraftTaskRoute = route({
21864
22343
  method: "post",
21865
22344
  path: "/api/tasks/{id}/promote-draft",
21866
22345
  pattern: ["api", "tasks", null, "promote-draft"],
22346
+ session: { taskParam: "id" },
21867
22347
  summary: "Promote a draft task out of the pre-dispatch draft state",
21868
22348
  description: "Transitions a `draft` task (#1240 — created with attachments still uploading) to its normal dispatch-eligible status: `offered` if it was offered to an agent, `pending` if it has an owning agent (the common case — UI-composer tasks default to Lead), otherwise `unassigned`. Called by the UI composer once its attachment upload batch settles, whether every file uploaded, some failed, or all failed — a draft must never be stranded by an upload error. Idempotent: calling it on a task that already left `draft` returns the current task unchanged rather than erroring, so a retried request is safe.",
21869
22349
  tags: ["Tasks"],
@@ -21879,6 +22359,7 @@ var updateSession = route({
21879
22359
  method: "put",
21880
22360
  path: "/api/tasks/{id}/session",
21881
22361
  pattern: ["api", "tasks", null, "session"],
22362
+ session: { taskParam: "id" },
21882
22363
  summary: "Update provider session ID and harness metadata for a task",
21883
22364
  tags: ["Tasks"],
21884
22365
  params: object2({ id: string2() }),
@@ -21912,6 +22393,7 @@ var cancelTaskRoute = route({
21912
22393
  method: "post",
21913
22394
  path: "/api/tasks/{id}/cancel",
21914
22395
  pattern: ["api", "tasks", null, "cancel"],
22396
+ session: { taskParam: "id" },
21915
22397
  summary: "Cancel a pending or in-progress task",
21916
22398
  tags: ["Tasks"],
21917
22399
  params: object2({ id: string2() }),
@@ -21926,6 +22408,7 @@ var steerTaskRoute = route({
21926
22408
  method: "post",
21927
22409
  path: "/api/tasks/{id}/steer",
21928
22410
  pattern: ["api", "tasks", null, "steer"],
22411
+ session: { taskParam: "id" },
21929
22412
  summary: "Deliver a steering message to a running task",
21930
22413
  tags: ["Tasks"],
21931
22414
  params: object2({ id: string2() }),
@@ -21954,6 +22437,7 @@ var getTaskSteeringMessagesRoute = route({
21954
22437
  method: "get",
21955
22438
  path: "/api/tasks/{id}/steering-messages",
21956
22439
  pattern: ["api", "tasks", null, "steering-messages"],
22440
+ session: { taskParam: "id" },
21957
22441
  summary: "List steering messages for a task",
21958
22442
  tags: ["Tasks"],
21959
22443
  params: object2({ id: string2() }),
@@ -21980,6 +22464,7 @@ var markSteeringDeliveredRoute = route({
21980
22464
  method: "post",
21981
22465
  path: "/api/steering-messages/{id}/delivered",
21982
22466
  pattern: ["api", "steering-messages", null, "delivered"],
22467
+ session: { steeringParam: "id" },
21983
22468
  summary: "Mark a steering message delivered",
21984
22469
  tags: ["Tasks"],
21985
22470
  params: object2({ id: string2() }),
@@ -22003,6 +22488,7 @@ var markSteeringHandledRoute = route({
22003
22488
  method: "post",
22004
22489
  path: "/api/steering-messages/{id}/handled",
22005
22490
  pattern: ["api", "steering-messages", null, "handled"],
22491
+ session: { steeringParam: "id" },
22006
22492
  summary: "Mark a steering message handled",
22007
22493
  description: "Optionally accepts a JSON body `{ note?: string }` — a short acceptance note describing how the steering was incorporated, persisted as `handledNote`.",
22008
22494
  tags: ["Tasks"],
@@ -22025,6 +22511,7 @@ var markSteeringUndeliverableRoute = route({
22025
22511
  method: "post",
22026
22512
  path: "/api/steering-messages/{id}/undeliverable",
22027
22513
  pattern: ["api", "steering-messages", null, "undeliverable"],
22514
+ session: { steeringParam: "id" },
22028
22515
  summary: "Promote an undeliverable steering message",
22029
22516
  tags: ["Tasks"],
22030
22517
  params: object2({ id: string2() }),
@@ -22048,6 +22535,7 @@ var getTask = route({
22048
22535
  method: "get",
22049
22536
  path: "/api/tasks/{id}",
22050
22537
  pattern: ["api", "tasks", null],
22538
+ session: { taskParam: "id" },
22051
22539
  summary: "Get task details with logs and attachments",
22052
22540
  description: "Returns the full `AgentTask` row decorated with `logs` (capped by `logsLimit`) and `attachments` (pointer-based artifacts stored on the task, ordered by `created_at`).",
22053
22541
  tags: ["Tasks"],
@@ -22057,6 +22545,7 @@ var getTask = route({
22057
22545
  }),
22058
22546
  responses: {
22059
22547
  200: { description: "Task with logs and attachments", schema: GetTaskResponseSchema },
22548
+ 403: { description: "A user caller cannot read the task's session" },
22060
22549
  404: { description: "Task not found" }
22061
22550
  }
22062
22551
  });
@@ -22064,6 +22553,7 @@ var updateTaskProgressRoute = route({
22064
22553
  method: "post",
22065
22554
  path: "/api/tasks/{id}/progress",
22066
22555
  pattern: ["api", "tasks", null, "progress"],
22556
+ session: { taskParam: "id" },
22067
22557
  summary: "Update task progress text",
22068
22558
  tags: ["Tasks"],
22069
22559
  params: object2({ id: string2() }),
@@ -22081,6 +22571,7 @@ var finishTask = route({
22081
22571
  method: "post",
22082
22572
  path: "/api/tasks/{id}/finish",
22083
22573
  pattern: ["api", "tasks", null, "finish"],
22574
+ session: { taskParam: "id" },
22084
22575
  summary: "Mark task as completed or failed (runner endpoint)",
22085
22576
  tags: ["Tasks"],
22086
22577
  params: object2({ id: string2() }),
@@ -22126,6 +22617,7 @@ var pauseTaskRoute = route({
22126
22617
  method: "post",
22127
22618
  path: "/api/tasks/{id}/pause",
22128
22619
  pattern: ["api", "tasks", null, "pause"],
22620
+ session: { taskParam: "id" },
22129
22621
  summary: "Pause an in-progress task",
22130
22622
  tags: ["Tasks"],
22131
22623
  params: object2({ id: string2() }),
@@ -22140,6 +22632,7 @@ var resumeTaskRoute = route({
22140
22632
  method: "post",
22141
22633
  path: "/api/tasks/{id}/resume",
22142
22634
  pattern: ["api", "tasks", null, "resume"],
22635
+ session: { taskParam: "id" },
22143
22636
  summary: "Resume a paused task",
22144
22637
  tags: ["Tasks"],
22145
22638
  params: object2({ id: string2() }),
@@ -22154,6 +22647,7 @@ var supersedeTaskRoute = route({
22154
22647
  method: "post",
22155
22648
  path: "/api/tasks/{id}/supersede",
22156
22649
  pattern: ["api", "tasks", null, "supersede"],
22650
+ session: { taskParam: "id" },
22157
22651
  summary: "Supersede an in-progress task (terminate + spawn resume follow-up)",
22158
22652
  description: 'Marks the original task `superseded` (terminal) and creates a fresh `taskType="resume"` follow-up so a worker can pick up the work in a new provider session. Workflow-step tasks (those with `workflowRunStepId`) are carved out: the original is marked `failed` with reason `superseded_workflow_task` and no follow-up is created — the workflow engine\'s retry/failure policy applies.',
22159
22653
  tags: ["Tasks"],
@@ -22174,6 +22668,7 @@ var updateTaskVcsRoute = route({
22174
22668
  method: "patch",
22175
22669
  path: "/api/tasks/{id}/vcs",
22176
22670
  pattern: ["api", "tasks", null, "vcs"],
22671
+ session: { taskParam: "id" },
22177
22672
  summary: "Update VCS (PR/MR) info for a task",
22178
22673
  tags: ["Tasks"],
22179
22674
  params: object2({ id: string2() }),
@@ -22194,6 +22689,7 @@ var updateTaskTitleRoute = route({
22194
22689
  method: "patch",
22195
22690
  path: "/api/tasks/{id}/title",
22196
22691
  pattern: ["api", "tasks", null, "title"],
22692
+ session: { taskParam: "id" },
22197
22693
  summary: "Set or clear a task's display title (session rename)",
22198
22694
  description: "Sets a human-facing display title override on a task. The sessions UI only reads this from root tasks (session list items), but titles on child tasks are harmless. Pass `title: null` (or an empty string) to clear the override and fall back to the task prompt.",
22199
22695
  tags: ["Tasks"],
@@ -22210,17 +22706,21 @@ var updateTaskTitleRoute = route({
22210
22706
  ungated: "mirrors the pre-RBAC PATCH /api/tasks/{id}/vcs sibling route: bearer auth only"
22211
22707
  }
22212
22708
  });
22213
- async function canSteerTask(req, myAgentId, task) {
22709
+ async function hasSessionWrite(req, myAgentId, taskId) {
22710
+ const access = await sessionAccessFor2(await resolveSessionActor2(req, myAgentId), taskId);
22711
+ return hasSessionAccess(access, "write");
22712
+ }
22713
+ async function canSteerTask(req, myAgentId, task, sessionAccess = "none") {
22714
+ const auth = getRequestAuth(req);
22214
22715
  const resource = {
22215
22716
  kind: "task",
22216
22717
  taskId: task.id,
22217
- requestedByUserId: task.requestedByUserId,
22718
+ requestedByUserId: auth?.kind === "user" && sessionAccess === "write" ? auth.userId : task.requestedByUserId,
22218
22719
  creatorAgentId: task.creatorAgentId,
22219
22720
  agentId: task.agentId
22220
22721
  };
22221
22722
  let principal;
22222
22723
  let verb;
22223
- const auth = getRequestAuth(req);
22224
22724
  if (auth?.kind === "operator") {
22225
22725
  principal = { kind: "operator" };
22226
22726
  verb = "task.steer.own";
@@ -22329,6 +22829,7 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
22329
22829
  if (validated.length > 0)
22330
22830
  source = validated;
22331
22831
  }
22832
+ const actor = await resolveSessionActor2(req, myAgentId);
22332
22833
  const filters = {
22333
22834
  status,
22334
22835
  agentId: parsed.query.agentId || undefined,
@@ -22344,7 +22845,8 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
22344
22845
  requestedByUserIdIsNull: parsed.query.requestedByUserId === "none" || undefined,
22345
22846
  orderBy: parsed.query.orderBy,
22346
22847
  limit: parsed.query.limit,
22347
- offset: parsed.query.offset
22848
+ offset: parsed.query.offset,
22849
+ readableBy: actor.kind === "user" ? { userId: actor.userId } : undefined
22348
22850
  };
22349
22851
  const tasks = parsed.query.fields === "full" ? await getAllTasks2(filters) : parsed.query.fields === "timeline" ? await getAllTasks2(filters, { fields: "timeline" }) : await getAllTasks2(filters, { slim: true });
22350
22852
  if (parsed.query.includeTotal === "true") {
@@ -22402,6 +22904,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
22402
22904
  jsonError(res, `You don't have context for parent task ${parentTask.id}.`, 403);
22403
22905
  return true;
22404
22906
  }
22907
+ if (parentTask && !await hasSessionWrite(req, myAgentId, parentTask.id)) {
22908
+ jsonError(res, SESSION_WRITE_DENIED2, 403);
22909
+ return true;
22910
+ }
22405
22911
  if (principal.kind === "agent" && parsed.body.contextKey && parsed.body.contextKey !== parentTask?.contextKey && chatThreadOfContextKey(parsed.body.contextKey) && !can({ principal, verb: "integration.chat.post", resource: { kind: "none" }, source: "http" }).allow) {
22406
22912
  jsonError(res, "Permission denied: only a lead can bind a task to a chat thread.", 403);
22407
22913
  return true;
@@ -22601,7 +23107,13 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
22601
23107
  jsonError(res, "Task not found", 404);
22602
23108
  return true;
22603
23109
  }
22604
- if (!await canSteerTask(req, myAgentId, task)) {
23110
+ const sessionActor = await resolveSessionActor2(req, myAgentId);
23111
+ const steerAccess = await sessionAccessFor2(sessionActor, task.id);
23112
+ if (!hasSessionAccess(steerAccess, "write")) {
23113
+ jsonError(res, SESSION_WRITE_DENIED2, 403);
23114
+ return true;
23115
+ }
23116
+ if (!await canSteerTask(req, myAgentId, task, steerAccess)) {
22605
23117
  jsonError(res, "Forbidden: caller cannot steer this task", 403);
22606
23118
  return true;
22607
23119
  }
@@ -22641,6 +23153,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
22641
23153
  jsonError(res, "Task not found", 404);
22642
23154
  return true;
22643
23155
  }
23156
+ if (!await canReadSession2(req, myAgentId, parsed.params.id)) {
23157
+ jsonError(res, SESSION_READ_DENIED2, 403);
23158
+ return true;
23159
+ }
22644
23160
  getTaskSteeringMessagesRoute.respond(res, 200, {
22645
23161
  messages: await getSteeringMessagesForTask2(parsed.params.id)
22646
23162
  });
@@ -22659,13 +23175,18 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
22659
23175
  return true;
22660
23176
  }
22661
23177
  if (parsed.query.taskId) {
23178
+ if (!await canReadSession2(req, myAgentId, parsed.query.taskId)) {
23179
+ jsonError(res, SESSION_READ_DENIED2, 403);
23180
+ return true;
23181
+ }
22662
23182
  const task = await getTaskById2(parsed.query.taskId);
22663
23183
  const messages = task?.agentId === myAgentId ? await getPendingSteeringForTask2(parsed.query.taskId) : [];
22664
23184
  getPendingSteeringMessagesRoute.respond(res, 200, { messages });
22665
23185
  return true;
22666
23186
  }
23187
+ const actor = await resolveSessionActor2(req, myAgentId);
22667
23188
  getPendingSteeringMessagesRoute.respond(res, 200, {
22668
- messages: await getPendingSteeringForAgent2(myAgentId)
23189
+ messages: await getPendingSteeringForAgent2(myAgentId, readableByFor2(actor))
22669
23190
  });
22670
23191
  return true;
22671
23192
  }
@@ -22806,6 +23327,11 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
22806
23327
  jsonError(res, "Task not found", 404);
22807
23328
  return true;
22808
23329
  }
23330
+ const readAccess = await sessionAccessFor2(await resolveSessionActor2(req, myAgentId), task.id);
23331
+ if (!hasSessionAccess(readAccess, "read")) {
23332
+ jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
23333
+ return true;
23334
+ }
22809
23335
  const logs = await getLogsByTaskId2(parsed.params.id, parsed.query.logsLimit ?? 200);
22810
23336
  const attachments = await getTaskAttachments2(parsed.params.id);
22811
23337
  getTask.respond(res, 200, {
@@ -22957,7 +23483,7 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
22957
23483
  jsonError(res, "Missing X-Agent-ID header", 400);
22958
23484
  return true;
22959
23485
  }
22960
- const pausedTasks = await getPausedTasksForAgent2(myAgentId);
23486
+ const pausedTasks = await getPausedTasksForAgent2(myAgentId, readableByFor2(await resolveSessionActor2(req, myAgentId)));
22961
23487
  const tasks = await Promise.all(pausedTasks.map(async (task) => {
22962
23488
  const requestedBy = await buildTaskRequestedBy(task);
22963
23489
  return {
@@ -23112,7 +23638,7 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
23112
23638
  return true;
23113
23639
  }
23114
23640
  if (task.workflowRunStepId != null) {
23115
- const failed = await failTask2(parsed.params.id, "superseded_workflow_task");
23641
+ const failed = await failTask2(parsed.params.id, SUPERSEDED_WORKFLOW_TASK_REASON);
23116
23642
  ensure({
23117
23643
  id: "task.workflow_step_failed_on_supersede",
23118
23644
  flow: "task",
@@ -26820,6 +27346,7 @@ ${templateBody}` : templateBody;
26820
27346
 
26821
27347
  // src/http/realtime.ts
26822
27348
  init_zod();
27349
+ init_users();
26823
27350
  init_rbac();
26824
27351
 
26825
27352
  // src/realtime/browser.generated.txt
@@ -26833,14 +27360,14 @@ var realtimeTicketRoute = route({
26833
27360
  pattern: ["api", "realtime", "ticket"],
26834
27361
  summary: "Issue a one-shot dashboard realtime ticket",
26835
27362
  tags: ["Rooms"],
26836
- rbac: { permission: "comb.presence" },
27363
+ description: "The ticket authorizes one dashboard socket for presence rooms: Comb drives (`comb.presence`) and sessions (`session.presence`). An operator-key tab's picked user (`X-Swarm-User-Id`) rides on the ticket so session presence can check that user's access.",
27364
+ rbac: { permission: "session.presence", alsoAdmits: ["comb.presence"] },
26837
27365
  responses: {
26838
27366
  200: {
26839
27367
  description: "One-shot realtime ticket",
26840
27368
  schema: object2({ ticket: string2(), expiresAt: number2().int() })
26841
27369
  },
26842
- 403: { description: "Comb presence is not allowed" },
26843
- 404: { description: "Comb is not enabled" }
27370
+ 403: { description: "Presence is not allowed" }
26844
27371
  }
26845
27372
  });
26846
27373
  route({
@@ -26857,29 +27384,28 @@ route({
26857
27384
  async function handleRealtimeTicket(req, res, pathSegments, queryParams) {
26858
27385
  if (!realtimeTicketRoute.match(req.method, pathSegments))
26859
27386
  return false;
26860
- if (!getCombConfig().enabled) {
26861
- jsonError(res, "Comb is not enabled", 404);
26862
- return true;
26863
- }
26864
27387
  const parsed = await realtimeTicketRoute.parse(req, res, pathSegments, queryParams);
26865
27388
  if (!parsed)
26866
27389
  return true;
26867
27390
  const auth = getRequestAuth(req);
26868
27391
  if (!auth || auth.kind === "agent" || auth.kind === "guest" || auth.kind === "controller") {
26869
- jsonError(res, "Comb presence requires dashboard authentication", 403);
27392
+ jsonError(res, "Presence requires dashboard authentication", 403);
26870
27393
  return true;
26871
27394
  }
26872
27395
  const principal = auth.kind === "operator" ? { kind: "operator" } : { kind: "user", userId: auth.userId };
26873
- if (!can({
26874
- principal,
26875
- verb: "comb.presence",
26876
- resource: { kind: "none" },
26877
- source: "http"
26878
- }).allow) {
26879
- jsonError(res, "Comb presence requires the comb.presence permission", 403);
27396
+ const allowed = (verb) => can({ principal, verb, resource: { kind: "none" }, source: "http" }).allow;
27397
+ if (!allowed("comb.presence") && !allowed("session.presence")) {
27398
+ jsonError(res, "Presence requires the comb.presence or session.presence permission", 403);
26880
27399
  return true;
26881
27400
  }
26882
- realtimeTicketRoute.respond(res, 200, issueRealtimeTicket(auth));
27401
+ let dashboardUserId;
27402
+ if (auth.kind === "operator" && !auth.page) {
27403
+ const picked = dashboardUserIdHeader(req);
27404
+ const user = picked ? await findUserById(picked) : null;
27405
+ if (user?.status === "active")
27406
+ dashboardUserId = user.id;
27407
+ }
27408
+ realtimeTicketRoute.respond(res, 200, issueRealtimeTicket(dashboardUserId ? { ...auth, dashboardUserId } : auth));
26883
27409
  return true;
26884
27410
  }
26885
27411
  async function handleRealtimeAsset(req, res) {
@@ -30768,6 +31294,7 @@ var agentTaskRoute = route({
30768
31294
  method: "post",
30769
31295
  path: "/api/internal/script-runs/{runId}/agent-task",
30770
31296
  pattern: ["api", "internal", "script-runs", null, "agent-task"],
31297
+ session: bodyTask("parentTaskId"),
30771
31298
  operationId: "script_runs_internal_agent_task",
30772
31299
  summary: "Create or wait for a script workflow agent task step",
30773
31300
  tags: ["Script Runs"],
@@ -31247,6 +31774,9 @@ var upsertRoute2 = route({
31247
31774
  method: "post",
31248
31775
  path: "/api/scripts/upsert",
31249
31776
  pattern: ["api", "scripts", "upsert"],
31777
+ auth: {
31778
+ userNamesAgent: "dashboard script playground: a user runs or saves a script in a chosen agent's scope"
31779
+ },
31250
31780
  operationId: "scripts_upsert",
31251
31781
  summary: "Create or update a reusable script",
31252
31782
  description: "Explicit script upserts run a TypeScript typecheck before writing.",
@@ -31271,6 +31801,9 @@ var runRoute = route({
31271
31801
  method: "post",
31272
31802
  path: "/api/scripts/run",
31273
31803
  pattern: ["api", "scripts", "run"],
31804
+ auth: {
31805
+ userNamesAgent: "dashboard script playground: a user runs or saves a script in a chosen agent's scope"
31806
+ },
31274
31807
  operationId: "scripts_run",
31275
31808
  summary: "Run a reusable or inline script",
31276
31809
  description: "Inline source skips typecheck and is auto-saved as a scratch script only on success.",
@@ -32241,6 +32774,7 @@ var createSessionLogsRoute = route({
32241
32774
  method: "post",
32242
32775
  path: "/api/session-logs",
32243
32776
  pattern: ["api", "session-logs"],
32777
+ session: bodyTask("taskId"),
32244
32778
  summary: "Store session logs",
32245
32779
  tags: ["Session Data"],
32246
32780
  body: object2({
@@ -32262,6 +32796,7 @@ var getSessionLogsByTask = route({
32262
32796
  method: "get",
32263
32797
  path: "/api/tasks/{taskId}/session-logs",
32264
32798
  pattern: ["api", "tasks", null, "session-logs"],
32799
+ session: { taskParam: "taskId" },
32265
32800
  summary: "Get session logs for a task",
32266
32801
  tags: ["Session Data"],
32267
32802
  params: object2({ taskId: string2() }),
@@ -32280,6 +32815,7 @@ var createSessionCostRoute = route({
32280
32815
  method: "post",
32281
32816
  path: "/api/session-costs",
32282
32817
  pattern: ["api", "session-costs"],
32818
+ session: bodyTask("taskId"),
32283
32819
  summary: "Store session cost record",
32284
32820
  tags: ["Session Data"],
32285
32821
  body: object2({
@@ -32378,7 +32914,7 @@ var listSessionCosts = route({
32378
32914
  }
32379
32915
  }
32380
32916
  });
32381
- async function handleSessionData(req, res, pathSegments, queryParams, _myAgentId) {
32917
+ async function handleSessionData(req, res, pathSegments, queryParams, myAgentId) {
32382
32918
  if (createSessionLogsRoute.match(req.method, pathSegments)) {
32383
32919
  const parsed = await createSessionLogsRoute.parse(req, res, pathSegments, queryParams);
32384
32920
  if (!parsed)
@@ -32417,6 +32953,10 @@ async function handleSessionData(req, res, pathSegments, queryParams, _myAgentId
32417
32953
  jsonError(res, "Task not found", 404);
32418
32954
  return true;
32419
32955
  }
32956
+ if (!await canReadSession2(req, myAgentId, task.id)) {
32957
+ jsonError(res, SESSION_READ_DENIED2, 403);
32958
+ return true;
32959
+ }
32420
32960
  const logs = await getSessionLogsByTaskId2(parsed.params.taskId, parsed.query?.limit);
32421
32961
  getSessionLogsByTask.respond(res, 200, { logs });
32422
32962
  return true;
@@ -32563,6 +33103,8 @@ init_zod();
32563
33103
  init_db3();
32564
33104
  init_steering();
32565
33105
  init_users();
33106
+ init_rbac();
33107
+ init_bus();
32566
33108
  init_types();
32567
33109
  init_request_auth_context();
32568
33110
  init_runtime_pools_flag();
@@ -32611,6 +33153,9 @@ var mintSessionTokenRoute = route({
32611
33153
  method: "post",
32612
33154
  path: "/api/sessions/tokens",
32613
33155
  pattern: ["api", "sessions", "tokens"],
33156
+ session: {
33157
+ none: "user tokens cannot mint: the handler admits only the operator key or the task's own runtime"
33158
+ },
32614
33159
  summary: "Mint an ephemeral task or runtime token",
32615
33160
  description: "Operators can mint task or runtime tokens. Runtime tokens can mint task tokens for their own assigned tasks. Child tokens expire with their parent runtime.",
32616
33161
  tags: ["Sessions"],
@@ -32673,7 +33218,8 @@ var listSessions = route({
32673
33218
  q: string2().optional(),
32674
33219
  requestedByUserId: string2().min(1).optional(),
32675
33220
  contextKeyPrefix: string2().min(1).optional(),
32676
- fields: _enum2(["full", "slim"]).optional()
33221
+ fields: _enum2(["full", "slim"]).optional(),
33222
+ scope: _enum2(["mine", "shared"]).optional()
32677
33223
  }),
32678
33224
  responses: {
32679
33225
  200: {
@@ -32693,23 +33239,68 @@ var getSession = route({
32693
33239
  method: "get",
32694
33240
  path: "/api/sessions/{rootTaskId}",
32695
33241
  pattern: ["api", "sessions", null],
33242
+ session: { taskParam: "rootTaskId" },
32696
33243
  summary: "Get a session — root task + the entire descendant chain",
32697
33244
  tags: ["Sessions"],
32698
33245
  params: object2({ rootTaskId: string2() }),
32699
33246
  responses: {
32700
33247
  200: {
32701
- description: "Root task + chain (ordered by createdAt)",
33248
+ description: "Root task + chain (ordered by createdAt), plus the caller's `access` and the session `owner`",
32702
33249
  schema: object2({
32703
33250
  root: TaskWithSteeringSchema,
32704
- chain: array2(TaskWithSteeringSchema)
33251
+ chain: array2(TaskWithSteeringSchema),
33252
+ access: SessionAccessSchema,
33253
+ owner: SessionOwnerSchema.nullable()
32705
33254
  })
32706
33255
  },
32707
33256
  401: { description: "Unauthorized" },
33257
+ 403: { description: "The caller has no access to this session" },
33258
+ 404: { description: "Root task not found" }
33259
+ },
33260
+ auth: { apiKey: true }
33261
+ });
33262
+ var getSessionShares = route({
33263
+ method: "get",
33264
+ path: "/api/sessions/{rootTaskId}/shares",
33265
+ pattern: ["api", "sessions", null, "shares"],
33266
+ session: { taskParam: "rootTaskId" },
33267
+ summary: "List a session's active shares",
33268
+ description: "Active shares only. `everyone` is the access every user has; `users` are per-person shares. A session with no shares is private to its owner.",
33269
+ tags: ["Sessions"],
33270
+ params: object2({ rootTaskId: string2() }),
33271
+ responses: {
33272
+ 200: { description: "Active shares", schema: SessionSharesSchema },
33273
+ 401: { description: "Unauthorized" },
33274
+ 403: { description: "The caller has no access to this session" },
32708
33275
  404: { description: "Root task not found" }
32709
33276
  },
32710
33277
  auth: { apiKey: true }
32711
33278
  });
32712
- async function handleSessions(req, res, pathSegments, queryParams) {
33279
+ var putSessionShares = route({
33280
+ method: "put",
33281
+ path: "/api/sessions/{rootTaskId}/shares",
33282
+ pattern: ["api", "sessions", null, "shares"],
33283
+ session: { taskParam: "rootTaskId" },
33284
+ summary: "Set who can view or edit a session",
33285
+ description: "Owner only. The body is the full desired state: users left out lose access. Changed targets have their active share revoked and a new one inserted in one transaction; shares are never deleted.",
33286
+ tags: ["Sessions"],
33287
+ params: object2({ rootTaskId: string2() }),
33288
+ body: SessionSharesUpdateSchema,
33289
+ responses: {
33290
+ 200: { description: "Active shares after the change", schema: SessionSharesSchema },
33291
+ 400: { description: "Validation error or unknown user" },
33292
+ 401: { description: "Unauthorized" },
33293
+ 403: { description: "Only the session owner can share it" },
33294
+ 404: { description: "Root task not found" },
33295
+ 409: { description: "The session has no owner, so it is already open to everyone" }
33296
+ },
33297
+ auth: { apiKey: true },
33298
+ rbac: { permission: "session.share" }
33299
+ });
33300
+ async function accessForRoot(actor, root) {
33301
+ return actor.kind === "bypass" ? "owner" : sessionAccessForRoot(actor.userId, root);
33302
+ }
33303
+ async function handleSessions(req, res, pathSegments, queryParams, myAgentId) {
32713
33304
  if (mintSessionTokenRoute.match(req.method, pathSegments)) {
32714
33305
  const auth = getRequestAuth(req);
32715
33306
  if (auth?.kind !== "operator" && !(auth?.kind === "agent" && auth.runtimeId)) {
@@ -32786,21 +33377,30 @@ async function handleSessions(req, res, pathSegments, queryParams) {
32786
33377
  if (!parsed)
32787
33378
  return true;
32788
33379
  const sources = parsed.query.source ? parsed.query.source.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
32789
- const baseOpts = {
32790
- limit: parsed.query.limit,
32791
- offset: parsed.query.offset,
33380
+ const actor = await resolveSessionActor2(req, myAgentId);
33381
+ if (actor.kind === "bypass" && parsed.query.scope) {
33382
+ listSessions.respond(res, 200, {
33383
+ sessions: [],
33384
+ total: 0,
33385
+ limit: parsed.query.limit ?? 25,
33386
+ offset: parsed.query.offset ?? 0
33387
+ });
33388
+ return true;
33389
+ }
33390
+ const filterOpts = {
32792
33391
  source: sources,
32793
33392
  q: parsed.query.q,
32794
33393
  requestedByUserId: parsed.query.requestedByUserId,
32795
- contextKeyPrefix: parsed.query.contextKeyPrefix
33394
+ contextKeyPrefix: parsed.query.contextKeyPrefix,
33395
+ ...actor.kind === "user" ? { viewer: { userId: actor.userId, scope: parsed.query.scope ?? "all" } } : {}
33396
+ };
33397
+ const baseOpts = {
33398
+ ...filterOpts,
33399
+ limit: parsed.query.limit,
33400
+ offset: parsed.query.offset
32796
33401
  };
32797
33402
  const sessions = parsed.query.fields === "full" ? await listRecentSessions2(baseOpts) : await listRecentSessions2({ ...baseOpts, slim: true });
32798
- const total = await countSessions2({
32799
- source: sources,
32800
- q: parsed.query.q,
32801
- requestedByUserId: parsed.query.requestedByUserId,
32802
- contextKeyPrefix: parsed.query.contextKeyPrefix
32803
- });
33403
+ const total = await countSessions2(filterOpts);
32804
33404
  listSessions.respond(res, 200, {
32805
33405
  sessions,
32806
33406
  total,
@@ -32818,6 +33418,20 @@ async function handleSessions(req, res, pathSegments, queryParams) {
32818
33418
  jsonError(res, "Root task not found", 404);
32819
33419
  return true;
32820
33420
  }
33421
+ const sessionRoot = await getSessionRoot(root.id) ?? {
33422
+ id: root.id,
33423
+ requestedByUserId: root.requestedByUserId ?? null
33424
+ };
33425
+ const access = await accessForRoot(await resolveSessionActor2(req, myAgentId), sessionRoot);
33426
+ if (access === "none") {
33427
+ jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
33428
+ return true;
33429
+ }
33430
+ const ownerUser = sessionRoot.requestedByUserId ? await findUserById(sessionRoot.requestedByUserId) : null;
33431
+ const owner = sessionRoot.requestedByUserId ? {
33432
+ id: sessionRoot.requestedByUserId,
33433
+ name: ownerUser?.name ?? sessionRoot.requestedByUserId
33434
+ } : null;
32821
33435
  const chain = await getRootTaskChain2(parsed.params.rootTaskId);
32822
33436
  const tasks = [root, ...chain];
32823
33437
  const [steering, citations] = await Promise.all([
@@ -32829,7 +33443,69 @@ async function handleSessions(req, res, pathSegments, queryParams) {
32829
33443
  ...steering.get(task.id),
32830
33444
  citations: citations.get(task.id)
32831
33445
  });
32832
- getSession.respond(res, 200, { root: decorate(root), chain: chain.map(decorate) });
33446
+ getSession.respond(res, 200, {
33447
+ root: decorate(root),
33448
+ chain: chain.map(decorate),
33449
+ access,
33450
+ owner
33451
+ });
33452
+ return true;
33453
+ }
33454
+ if (getSessionShares.match(req.method, pathSegments)) {
33455
+ const parsed = await getSessionShares.parse(req, res, pathSegments, queryParams);
33456
+ if (!parsed)
33457
+ return true;
33458
+ const root = await getSessionRoot(parsed.params.rootTaskId);
33459
+ if (!root) {
33460
+ jsonError(res, "Root task not found", 404);
33461
+ return true;
33462
+ }
33463
+ const access = await accessForRoot(await resolveSessionActor2(req, myAgentId), root);
33464
+ if (access === "none") {
33465
+ jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
33466
+ return true;
33467
+ }
33468
+ getSessionShares.respond(res, 200, toSessionSharesState(await listActiveSessionShares(root.id)));
33469
+ return true;
33470
+ }
33471
+ if (putSessionShares.match(req.method, pathSegments)) {
33472
+ const parsed = await putSessionShares.parse(req, res, pathSegments, queryParams);
33473
+ if (!parsed)
33474
+ return true;
33475
+ const auth = getRequestAuth(req);
33476
+ const actor = await resolveSessionActor2(req, myAgentId);
33477
+ const principal = auth?.kind === "user" ? { kind: "user", userId: auth.userId } : auth?.kind === "operator" && !myAgentId ? { kind: "operator" } : null;
33478
+ if (!principal || !can({ principal, verb: "session.share", resource: { kind: "none" }, source: "http" }).allow) {
33479
+ jsonError(res, "Only the session owner can share it", 403);
33480
+ return true;
33481
+ }
33482
+ const root = await getSessionRoot(parsed.params.rootTaskId);
33483
+ if (!root) {
33484
+ jsonError(res, "Root task not found", 404);
33485
+ return true;
33486
+ }
33487
+ if (!root.requestedByUserId) {
33488
+ jsonError(res, "This session has no owner, so it is already open to everyone", 409);
33489
+ return true;
33490
+ }
33491
+ if (actor.kind === "user" && actor.userId !== root.requestedByUserId) {
33492
+ jsonError(res, "Only the session owner can share it", 403);
33493
+ return true;
33494
+ }
33495
+ const userIds = [...new Set(parsed.body.users.map((user) => user.userId))];
33496
+ if (userIds.length !== parsed.body.users.length) {
33497
+ jsonError(res, "Each user may appear only once", 400);
33498
+ return true;
33499
+ }
33500
+ for (const userId of userIds) {
33501
+ if (!await findUserById(userId)) {
33502
+ jsonError(res, `Unknown user ${userId}`, 400);
33503
+ return true;
33504
+ }
33505
+ }
33506
+ const state = await setSessionShares(root, parsed.body, actor.kind === "user" ? actor.userId : null);
33507
+ realtimeBus.publish(sessionAccessTopic(root.id), null);
33508
+ putSessionShares.respond(res, 200, state);
32833
33509
  return true;
32834
33510
  }
32835
33511
  return false;
@@ -34231,11 +34907,12 @@ async function handleStats(req, res, pathSegments, queryParams, myAgentId) {
34231
34907
  return true;
34232
34908
  const limit = parsed.query.limit ?? 100;
34233
34909
  const agentId = parsed.query.agentId;
34910
+ const readableBy = readableByFor2(await resolveSessionActor2(req, myAgentId));
34234
34911
  let logs = [];
34235
34912
  if (agentId) {
34236
- logs = (await getLogsByAgentId2(agentId)).slice(0, limit);
34913
+ logs = (await getLogsByAgentId2(agentId, readableBy)).slice(0, limit);
34237
34914
  } else {
34238
- logs = await getAllLogs2(limit);
34915
+ logs = await getAllLogs2(limit, readableBy);
34239
34916
  }
34240
34917
  listLogs.respond(res, 200, { logs });
34241
34918
  return true;
@@ -34291,7 +34968,7 @@ async function handleStats(req, res, pathSegments, queryParams, myAgentId) {
34291
34968
  return true;
34292
34969
  }
34293
34970
  if (getConcurrentContextRoute.match(req.method, pathSegments)) {
34294
- const context = await getConcurrentContext2();
34971
+ const context = await getConcurrentContext2(readableByFor2(await resolveSessionActor2(req, myAgentId)));
34295
34972
  getConcurrentContextRoute.respond(res, 200, context);
34296
34973
  return true;
34297
34974
  }
@@ -34354,9 +35031,8 @@ function uiEventEmitter(event) {
34354
35031
  return relayable.get(event)?.source;
34355
35032
  }
34356
35033
  function resolveSpec(spec) {
34357
- if (!spec.ref)
34358
- return spec;
34359
- return { ...file.definitions[spec.ref], ...spec };
35034
+ const resolved = spec.ref ? { ...file.definitions[spec.ref], ...spec } : spec;
35035
+ return resolved.items ? { ...resolved, items: resolveSpec(resolved.items) } : resolved;
34360
35036
  }
34361
35037
  function typeMatches(type, value) {
34362
35038
  switch (type) {
@@ -34401,6 +35077,12 @@ function validateUiEvent(event, properties) {
34401
35077
  if (spec.enum && !spec.enum.includes(value)) {
34402
35078
  problems.push(`"${key}" must be one of ${spec.enum.join(", ")}`);
34403
35079
  }
35080
+ if (Array.isArray(value) && spec.items?.enum) {
35081
+ const allowed = spec.items.enum;
35082
+ if (value.some((item) => !allowed.includes(item))) {
35083
+ problems.push(`"${key}" items must be one of ${allowed.join(", ")}`);
35084
+ }
35085
+ }
34404
35086
  if (typeof spec.minimum === "number" && typeof value === "number" && value < spec.minimum) {
34405
35087
  problems.push(`"${key}" must be at least ${spec.minimum}`);
34406
35088
  }
@@ -34441,7 +35123,7 @@ var enableTelemetry = route({
34441
35123
  summary: "Turn telemetry on",
34442
35124
  description: "Stores `ANONYMIZED_TELEMETRY=true` and applies it to the running server: the install ID is minted if needed and `telemetry.enabled` is sent once, so no restart is needed.",
34443
35125
  tags: ["Telemetry"],
34444
- body: object2({ via: _enum2(["banner", "settings"]).default("banner") }),
35126
+ body: object2({ via: _enum2(["banner", "settings", "benchmark"]).default("banner") }),
34445
35127
  responses: {
34446
35128
  200: {
34447
35129
  description: "Telemetry is on",
@@ -35392,7 +36074,7 @@ var createUserRoute = route({
35392
36074
  tags: ["Users"],
35393
36075
  body: object2({
35394
36076
  name: string2().min(1),
35395
- email: string2().optional(),
36077
+ email: string2().email("Email must be a valid email address").optional(),
35396
36078
  role: string2().optional(),
35397
36079
  notes: string2().optional(),
35398
36080
  emailAliases: array2(string2()).optional(),
@@ -35487,7 +36169,7 @@ var updateUserRoute = route({
35487
36169
  params: object2({ id: string2() }),
35488
36170
  body: object2({
35489
36171
  name: string2().min(1).optional(),
35490
- email: string2().optional(),
36172
+ email: string2().email("Email must be a valid email address").nullable().optional(),
35491
36173
  role: string2().optional(),
35492
36174
  notes: string2().optional(),
35493
36175
  emailAliases: array2(string2()).optional(),
@@ -38362,10 +39044,11 @@ var httpServer = createHttpServer(async (req, res) => {
38362
39044
  () => handleExtensions(req, res, pathSegments, queryParams, myAgentId),
38363
39045
  () => handleFavorites(req, res, pathSegments, queryParams, myAgentId),
38364
39046
  () => handleUsers(req, res, pathSegments, queryParams),
38365
- () => handleSessions(req, res, pathSegments, queryParams),
39047
+ () => handleSessions(req, res, pathSegments, queryParams, myAgentId),
38366
39048
  () => handleInboxState(req, res, pathSegments, queryParams),
38367
39049
  () => handleTelemetryUi(req, res, pathSegments, queryParams, myAgentId),
38368
39050
  () => handleUsedBy(req, res, pathSegments, queryParams, myAgentId),
39051
+ () => handleRanking(req, res, pathSegments, queryParams),
38369
39052
  () => handleFleetBenchmark(req, res, pathSegments, queryParams),
38370
39053
  () => handleTaskTemplates(req, res, pathSegments, queryParams),
38371
39054
  () => handleMcp(req, res, transports, transportActivity, mcpSessionAgents),
@@ -38397,7 +39080,7 @@ var httpServer = createHttpServer(async (req, res) => {
38397
39080
  });
38398
39081
  });
38399
39082
  var detachRealtime = attachRealtimeTransport(httpServer);
38400
- var { attachControllerTransport } = await import("./controllers-connect-ymw5541d.js");
39083
+ var { attachControllerTransport } = await import("./controllers-connect-bg33ae53.js");
38401
39084
  var detachControllers = attachControllerTransport(httpServer);
38402
39085
  var stopWorkerNudges = startWorkerNudgeListener();
38403
39086
  var stopRoomDeletion = realtimeBus.subscribe("room:namespace-deleted", (namespace) => {
@@ -38426,7 +39109,7 @@ async function shutdown() {
38426
39109
  sessionsProcessed: getServerSessionsProcessed()
38427
39110
  });
38428
39111
  if (hasCapability("scheduling")) {
38429
- const { stopScheduler } = await import("./index-vjn3wbzf.js");
39112
+ const { stopScheduler } = await import("./index-h2ek08d6.js");
38430
39113
  stopScheduler();
38431
39114
  }
38432
39115
  stopHeartbeat2();
@@ -38438,7 +39121,7 @@ async function shutdown() {
38438
39121
  await stopSlackApp2();
38439
39122
  await stopChatSdk();
38440
39123
  if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
38441
- const { stopOAuthKeepalive } = await import("./keepalive-4djvbm2j.js");
39124
+ const { stopOAuthKeepalive } = await import("./keepalive-nhvpw10z.js");
38442
39125
  await stopOAuthKeepalive();
38443
39126
  }
38444
39127
  stopOAuthPendingGc();
@@ -38518,21 +39201,21 @@ try {
38518
39201
  console.warn("[startup] CAPABILITIES upgrade seed failed (non-fatal):", err);
38519
39202
  }
38520
39203
  try {
38521
- const { ensurePricingSeeded } = await import("./boot-seeds-gbedmamp.js");
39204
+ const { ensurePricingSeeded } = await import("./boot-seeds-6wxqkfk2.js");
38522
39205
  ensurePricingSeeded();
38523
- const { startPricingRefreshLoop } = await import("./pricing-refresh-sdcf5yaz.js");
39206
+ const { startPricingRefreshLoop } = await import("./pricing-refresh-3cqx3f3x.js");
38524
39207
  startPricingRefreshLoop();
38525
39208
  } catch (err) {
38526
39209
  console.error("[startup] Failed to seed pricing rows:", err);
38527
39210
  }
38528
39211
  try {
38529
- const { ensureModelProfilesSeeded } = await import("./boot-seeds-gbedmamp.js");
39212
+ const { ensureModelProfilesSeeded } = await import("./boot-seeds-6wxqkfk2.js");
38530
39213
  await ensureModelProfilesSeeded();
38531
39214
  } catch (err) {
38532
39215
  console.error("[startup] Failed to seed model profiles:", err);
38533
39216
  }
38534
39217
  try {
38535
- const { ensureRbacSeeded } = await import("./boot-seeds-gbedmamp.js");
39218
+ const { ensureRbacSeeded } = await import("./boot-seeds-6wxqkfk2.js");
38536
39219
  ensureRbacSeeded();
38537
39220
  } catch (err) {
38538
39221
  console.error("[startup] Failed to sync RBAC seed rows:", err);
@@ -38540,7 +39223,7 @@ try {
38540
39223
  throw err;
38541
39224
  }
38542
39225
  try {
38543
- const { runAllSeeders } = await import("./index-6rmnn89d.js");
39226
+ const { runAllSeeders } = await import("./index-2z710v4x.js");
38544
39227
  await runAllSeeders({ scriptEmbeddingMode: "skip" });
38545
39228
  } catch (err) {
38546
39229
  console.error("[startup] Failed to seed built-in entities:", err);
@@ -38602,39 +39285,39 @@ httpServer.listen(port, async () => {
38602
39285
  await initWorkflows2();
38603
39286
  await startScriptRunSupervisor(getMcpBaseUrl());
38604
39287
  if (hasCapability("scheduling")) {
38605
- const { startScheduler } = await import("./index-vjn3wbzf.js");
38606
- const { getExecutorRegistry } = await import("./index-xmmkyx82.js");
39288
+ const { startScheduler } = await import("./index-h2ek08d6.js");
39289
+ const { getExecutorRegistry } = await import("./index-dgz61nff.js");
38607
39290
  const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
38608
39291
  startScheduler(getExecutorRegistry(), intervalMs, {
38609
39292
  runId: globalState.__runId
38610
39293
  });
38611
39294
  }
38612
39295
  if (!isEnvFlagEnabled("HEARTBEAT_DISABLE", false)) {
38613
- const { startHeartbeat } = await import("./index-nyfcdpky.js");
39296
+ const { startHeartbeat } = await import("./index-pqfwh9nn.js");
38614
39297
  const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
38615
39298
  startHeartbeat(heartbeatMs);
38616
39299
  }
38617
39300
  startPoolRouting();
38618
39301
  startRuntimeSupervisor();
38619
39302
  if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
38620
- const { startOAuthKeepalive } = await import("./keepalive-4djvbm2j.js");
39303
+ const { startOAuthKeepalive } = await import("./keepalive-nhvpw10z.js");
38621
39304
  startOAuthKeepalive();
38622
39305
  }
38623
39306
  if (process.env.OAUTH_REFRESH_SWEEP_DISABLE !== "true") {
38624
- const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-fpd6x6rw.js");
39307
+ const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-4dcbhtn0.js");
38625
39308
  startOAuthRefreshSweep();
38626
39309
  }
38627
39310
  startOAuthPendingGc();
38628
39311
  await startMemoryGc();
38629
39312
  await startDbRetention();
38630
39313
  startHumanFreeDrain();
38631
- import("./boot-reembed-p666cw5n.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
39314
+ import("./boot-reembed-e53x6b9v.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
38632
39315
  console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
38633
39316
  });
38634
- import("./boot-reembed-w6pm64j4.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
39317
+ import("./boot-reembed-f6z923ny.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
38635
39318
  console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
38636
39319
  });
38637
- import("./boot-scrub-sweep-jcyjagn3.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
39320
+ import("./boot-scrub-sweep-0538vsb7.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
38638
39321
  console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
38639
39322
  });
38640
39323
  }).on("error", (err) => {