@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
@@ -4,6 +4,8 @@
4
4
  *
5
5
  * - `context.org_domain`: the email DOMAIN of the org's admin (never the email)
6
6
  * - `context.user_role`: `admin` when the user's grants include everything, else `member`
7
+ * - `context.user_function`: a canonical job function from `users.metadata.jobFunction`
8
+ * (not `users.role`: the dashboard reads an empty or `admin` role as admin-like)
7
9
  *
8
10
  * This file must not import `./be/db` (`be/db.ts` imports the trigger module
9
11
  * that imports this one). It reads through the DB client and lazy-imports RBAC.
@@ -11,26 +13,36 @@
11
13
  import { getDbClient } from "./be/db/runtime";
12
14
  import { setTelemetryOrgDomain } from "./telemetry";
13
15
  import { emailDomain, type TelemetryUserRole } from "./telemetry-context";
16
+ import { USER_FUNCTIONS } from "./utils/company-profile";
14
17
 
15
18
  const ROLE_TTL_MS = 60 * 60_000;
16
19
  const ROLE_CACHE_LIMIT = 1_000;
17
20
  const DOMAIN_RECOMPUTE_DEBOUNCE_MS = 2_000;
18
21
 
19
- const roleCache = new Map<string, { role: TelemetryUserRole; at: number }>();
22
+ interface UserRoleEntry {
23
+ role: TelemetryUserRole;
24
+ userFunction?: (typeof USER_FUNCTIONS)[number];
25
+ at: number;
26
+ }
27
+
28
+ const roleCache = new Map<string, UserRoleEntry>();
20
29
  const roleLoads = new Map<string, Promise<TelemetryUserRole | undefined>>();
21
30
 
22
- async function loadUserRole(userId: string): Promise<TelemetryUserRole | undefined> {
31
+ async function loadUserRole(userId: string): Promise<UserRoleEntry | undefined> {
23
32
  try {
24
33
  // Lazy: be/rbac-roles imports be/db, which (transitively) imports this file.
25
34
  const { getUserGrant } = await import("./be/rbac-roles");
26
35
  const grant = await getUserGrant(userId);
27
36
  const role: TelemetryUserRole = grant.grantsAll ? "admin" : "member";
28
- if (roleCache.size >= ROLE_CACHE_LIMIT) {
29
- const oldest = roleCache.keys().next().value;
30
- if (oldest !== undefined) roleCache.delete(oldest);
31
- }
32
- roleCache.set(userId, { role, at: Date.now() });
33
- return role;
37
+ const user = await getDbClient().get<{ jobFunction: string | null }>(
38
+ "SELECT json_extract(metadata, '$.jobFunction') AS jobFunction FROM users WHERE id = ? AND json_valid(metadata)",
39
+ [userId],
40
+ );
41
+ return {
42
+ role,
43
+ userFunction: USER_FUNCTIONS.find((value) => value === user?.jobFunction),
44
+ at: Date.now(),
45
+ };
34
46
  } catch {
35
47
  return undefined;
36
48
  }
@@ -42,7 +54,20 @@ export function primeUserRole(userId: string): Promise<TelemetryUserRole | undef
42
54
  if (hit && Date.now() - hit.at < ROLE_TTL_MS) return Promise.resolve(hit.role);
43
55
  let load = roleLoads.get(userId);
44
56
  if (!load) {
45
- load = loadUserRole(userId).finally(() => roleLoads.delete(userId));
57
+ load = loadUserRole(userId)
58
+ .then((entry) => {
59
+ if (entry && roleLoads.get(userId) === load) {
60
+ if (roleCache.size >= ROLE_CACHE_LIMIT) {
61
+ const oldest = roleCache.keys().next().value;
62
+ if (oldest !== undefined) roleCache.delete(oldest);
63
+ }
64
+ roleCache.set(userId, entry);
65
+ }
66
+ return entry?.role;
67
+ })
68
+ .finally(() => {
69
+ if (roleLoads.get(userId) === load) roleLoads.delete(userId);
70
+ });
46
71
  roleLoads.set(userId, load);
47
72
  }
48
73
  return load;
@@ -59,6 +84,19 @@ export function cachedUserRole(userId: string): TelemetryUserRole | undefined {
59
84
  return hit?.role;
60
85
  }
61
86
 
87
+ /** Resolve the job function from the same cache and lookup as the RBAC role. */
88
+ export function cachedUserFunction(userId: string): UserRoleEntry["userFunction"] {
89
+ const hit = roleCache.get(userId);
90
+ if (!hit || Date.now() - hit.at >= ROLE_TTL_MS) void primeUserRole(userId);
91
+ return hit?.userFunction;
92
+ }
93
+
94
+ /** Invalidate after a user mutation commits, including any lookup already in progress. */
95
+ export function invalidateTelemetryUserRole(userId: string): void {
96
+ roleCache.delete(userId);
97
+ roleLoads.delete(userId);
98
+ }
99
+
62
100
  /**
63
101
  * The email domain of the org's earliest-created active admin, falling back to
64
102
  * the earliest active user with an email. A candidate whose address has no
@@ -9,9 +9,15 @@
9
9
  */
10
10
 
11
11
  import { getDbClient } from "./be/db/runtime";
12
- import { isTelemetryReady, setTelemetryRoleResolver, telemetry } from "./telemetry";
12
+ import {
13
+ isTelemetryReady,
14
+ setTelemetryFunctionResolver,
15
+ setTelemetryRoleResolver,
16
+ telemetry,
17
+ } from "./telemetry";
13
18
  import type { PropsFor } from "./telemetry-contract";
14
19
  import {
20
+ cachedUserFunction,
15
21
  cachedUserRole,
16
22
  configureOrgDomainPersistence,
17
23
  recomputeOrgDomain,
@@ -169,6 +175,7 @@ export function startTelemetryTicker(deps: SnapshotDeps): void {
169
175
  if (!isTelemetryReady()) return;
170
176
  stopTelemetryTicker();
171
177
  setTelemetryRoleResolver(cachedUserRole);
178
+ setTelemetryFunctionResolver(cachedUserFunction);
172
179
 
173
180
  const tick = async () => {
174
181
  try {
@@ -207,5 +214,6 @@ export function stopTelemetryTicker(): void {
207
214
  tickTimer = null;
208
215
  firstTimer = null;
209
216
  setTelemetryRoleResolver(null);
217
+ setTelemetryFunctionResolver(null);
210
218
  configureOrgDomainPersistence(null);
211
219
  }
@@ -15,6 +15,7 @@ interface PropertySpec {
15
15
  minimum?: number;
16
16
  required?: boolean;
17
17
  nullable?: boolean;
18
+ items?: PropertySpec;
18
19
  }
19
20
 
20
21
  interface CatalogEntry {
@@ -62,8 +63,8 @@ export function uiEventEmitter(event: string): string | undefined {
62
63
  }
63
64
 
64
65
  function resolveSpec(spec: PropertySpec): PropertySpec {
65
- if (!spec.ref) return spec;
66
- return { ...file.definitions[spec.ref], ...spec };
66
+ const resolved = spec.ref ? { ...file.definitions[spec.ref], ...spec } : spec;
67
+ return resolved.items ? { ...resolved, items: resolveSpec(resolved.items) } : resolved;
67
68
  }
68
69
 
69
70
  function typeMatches(type: string | undefined, value: unknown): boolean {
@@ -117,6 +118,12 @@ export function validateUiEvent(event: string, properties: Record<string, unknow
117
118
  if (spec.enum && !spec.enum.includes(value as string)) {
118
119
  problems.push(`"${key}" must be one of ${spec.enum.join(", ")}`);
119
120
  }
121
+ if (Array.isArray(value) && spec.items?.enum) {
122
+ const allowed = spec.items.enum;
123
+ if (value.some((item) => !allowed.includes(item))) {
124
+ problems.push(`"${key}" items must be one of ${allowed.join(", ")}`);
125
+ }
126
+ }
120
127
  if (typeof spec.minimum === "number" && typeof value === "number" && value < spec.minimum) {
121
128
  problems.push(`"${key}" must be at least ${spec.minimum}`);
122
129
  }
package/src/telemetry.ts CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  validOrgDomain,
23
23
  validOrgId,
24
24
  } from "./telemetry-context";
25
- import type { PropsFor, TelemetryEventName } from "./telemetry-contract";
25
+ import type { EventContext, PropsFor, TelemetryEventName } from "./telemetry-contract";
26
26
  import { isEnvFlagEnabled } from "./utils/env-flag";
27
27
  import { getNodeEnv } from "./utils/node-env";
28
28
  import { normalizeOrgWebsite } from "./utils/org-website";
@@ -68,6 +68,7 @@ let cachedOrgId: string | undefined;
68
68
  /** Email domain of the org's admin, set by the API server (`setTelemetryOrgDomain`). */
69
69
  let cachedOrgDomain: string | undefined;
70
70
  let roleResolver: ((userId: string) => TelemetryUserRole | undefined) | null = null;
71
+ let functionResolver: ((userId: string) => EventContext["user_function"]) | null = null;
71
72
  let identityReloader: (() => Promise<void>) | null = null;
72
73
  let lastIdentityLoadAt = 0;
73
74
  let source = "unknown";
@@ -510,6 +511,13 @@ export function setTelemetryRoleResolver(
510
511
  roleResolver = resolver;
511
512
  }
512
513
 
514
+ /** API server only: cache-backed lookup of the actor's job function. */
515
+ export function setTelemetryFunctionResolver(
516
+ resolver: ((userId: string) => EventContext["user_function"]) | null,
517
+ ): void {
518
+ functionResolver = resolver;
519
+ }
520
+
513
521
  /**
514
522
  * True when `track()` would send: telemetry is on, an install ID exists and an
515
523
  * org ID resolved. Emitters that record "already sent" state check this first.
@@ -630,6 +638,7 @@ function emit(
630
638
  metadata?: Record<string, unknown>,
631
639
  actor?: TelemetryActor,
632
640
  emitter: string = source,
641
+ identity?: { orgName: string; orgWebsite: string },
633
642
  ): void {
634
643
  if (!isTelemetryEnabled() || !installationId) return;
635
644
  maybeRefreshOrgIdentity();
@@ -639,7 +648,9 @@ function emit(
639
648
  const occurredAt = new Date().toISOString();
640
649
  const sentFor = installationId;
641
650
  const isCloud = cachedIsCloud || isCloudDeployment();
642
- const orgIdentity = getOrgIdentity();
651
+ const orgIdentity = identity
652
+ ? { ...getOrgIdentity(), organization_name: identity.orgName }
653
+ : getOrgIdentity();
643
654
  const userId = actor?.userId?.trim() || undefined;
644
655
  const payload = {
645
656
  schema_version: 2 as const,
@@ -653,7 +664,7 @@ function emit(
653
664
  orgId,
654
665
  orgName: orgIdentity.organization_name,
655
666
  orgDomain: cachedOrgDomain,
656
- orgWebsite: normalizeOrgWebsite(process.env.SWARM_ORG_WEBSITE),
667
+ orgWebsite: identity?.orgWebsite ?? normalizeOrgWebsite(process.env.SWARM_ORG_WEBSITE),
657
668
  installationId,
658
669
  actor: userId ? { userId, role: actor?.role ?? roleResolver?.(userId) ?? null } : undefined,
659
670
  isCloud,
@@ -661,8 +672,10 @@ function emit(
661
672
  swarmVersion: pkg.version,
662
673
  installMethod: cachedInstallMethod,
663
674
  installPreset: cachedInstallPreset,
664
- // Stays null until the dashboard reports where the visitor came from.
665
- acquisitionSource: null,
675
+ acquisitionSource: process.env.SWARM_ORG_ACQUISITION_SOURCE,
676
+ teamSize: process.env.SWARM_ORG_TEAM_SIZE,
677
+ useCase: process.env.SWARM_ORG_USE_CASE,
678
+ userFunction: userId ? functionResolver?.(userId) : undefined,
666
679
  }),
667
680
  properties: normalizeProperties(event, {
668
681
  ...properties,
@@ -742,6 +755,7 @@ export function _resetTelemetryStateForTests(): void {
742
755
  cachedOrgId = undefined;
743
756
  cachedOrgDomain = undefined;
744
757
  roleResolver = null;
758
+ functionResolver = null;
745
759
  identityReloader = null;
746
760
  lastIdentityLoadAt = 0;
747
761
  }
@@ -820,6 +834,27 @@ export const telemetry = {
820
834
  );
821
835
  },
822
836
 
837
+ /**
838
+ * Public-ranking events. The API passes the org identity it just read from
839
+ * config, so a listing built right after an identity edit still matches the
840
+ * envelope's `org_name` / `org_website` (the proxy rejects a mismatch) before
841
+ * the debounced config reload updates `process.env`.
842
+ */
843
+ ranking<S extends EventSuffix<"ranking">>(
844
+ event: S,
845
+ props: FamilyProps<"ranking", S>,
846
+ identity?: { orgName: string; orgWebsite: string },
847
+ ): void {
848
+ emit(
849
+ `ranking.${event}`,
850
+ props as Record<string, unknown>,
851
+ undefined,
852
+ undefined,
853
+ source,
854
+ identity,
855
+ );
856
+ },
857
+
823
858
  org<S extends EventSuffix<"org">>(event: S, props: FamilyProps<"org", S>): void {
824
859
  emit(`org.${event}`, props as Record<string, unknown>, undefined);
825
860
  },
@@ -0,0 +1,178 @@
1
+ import { afterAll, beforeAll, expect, test } from "bun:test";
2
+ import { unlink } from "node:fs/promises";
3
+ import { createServer as createHttpServer } from "node:http";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { type AppRecord, createApp } from "../apps/store";
6
+ import { closeDb, createAgent, getTaskById, initDb } from "../be/db";
7
+ import { upsertScriptByName } from "../be/scripts/db";
8
+ import { handleCore } from "../http/core";
9
+ import { handleMcpBridge } from "../http/mcp-bridge";
10
+ import { getPathSegments, parseQueryParams } from "../http/utils";
11
+ import { LEGACY_POLICY, type LegacyRule } from "../rbac";
12
+ import { isMcpToolAllowedForScripts } from "../scripts-runtime/sdk-allowlist";
13
+ import { registerAppActionRunTool } from "../tools/app-action-run";
14
+ import { markExtensionRequestOrigin, markScriptSdkRequestOrigin } from "../tools/utils";
15
+ import { SKIP_SANDBOX_SPAWN_TESTS } from "./sandbox-spawn-test-helpers";
16
+ import { listenOnFreePort } from "./test-net";
17
+ import { CHILD_PROCESS_TEST_BUDGET_MS } from "./test-proc";
18
+
19
+ const dbPath = `/private/tmp/test-app-action-run-tool-${process.pid}.sqlite`;
20
+ const agentId = crypto.randomUUID();
21
+ const ownerId = crypto.randomUUID();
22
+ const apiKey = "example-app-action-run-key";
23
+ const previousKey = process.env.AGENT_SWARM_API_KEY;
24
+ const previousBase = process.env.MCP_BASE_URL;
25
+ const definition: AppRecord["definition"] = {
26
+ schemaVersion: 1,
27
+ models: { note: { columns: { title: { kind: "string" } } } },
28
+ actions: { triage: { kind: "task", prompt: "Triage the notes." } },
29
+ pages: { main: { root: "root", elements: { root: { type: "Container", props: {} } } } },
30
+ defaultPage: "main",
31
+ };
32
+ type ToolResult = {
33
+ isError?: boolean;
34
+ structuredContent: {
35
+ message: string;
36
+ data: { ok: boolean; taskId: string; status: string; result?: unknown };
37
+ };
38
+ };
39
+ let handler: (args: unknown, extra: unknown) => Promise<ToolResult>;
40
+ let appId: string;
41
+
42
+ async function removeDb() {
43
+ for (const suffix of ["", "-wal", "-shm"]) await unlink(dbPath + suffix).catch(() => undefined);
44
+ }
45
+ beforeAll(async () => {
46
+ process.env.AGENT_SWARM_API_KEY = apiKey;
47
+ await removeDb();
48
+ initDb(dbPath);
49
+ await createAgent({ id: agentId, name: "Action invoker", isLead: false, status: "idle" });
50
+ await createAgent({ id: ownerId, name: "Script owner", isLead: true, status: "idle" });
51
+ appId = (await createApp({ name: "Action tool", definition })).id;
52
+ const server = new McpServer({ name: "app-action-run-tool", version: "1.0.0" });
53
+ registerAppActionRunTool(server);
54
+ handler = (server as unknown as { _registeredTools: Record<string, { handler: typeof handler }> })
55
+ ._registeredTools["app-action-run"]!.handler;
56
+ });
57
+ afterAll(async () => {
58
+ closeDb();
59
+ await removeDb();
60
+ if (previousKey === undefined) delete process.env.AGENT_SWARM_API_KEY;
61
+ else process.env.AGENT_SWARM_API_KEY = previousKey;
62
+ if (previousBase === undefined) delete process.env.MCP_BASE_URL;
63
+ else process.env.MCP_BASE_URL = previousBase;
64
+ });
65
+ function call(
66
+ action: string,
67
+ input: Record<string, unknown> = {},
68
+ origin?: "script-sdk" | "extension",
69
+ id = agentId,
70
+ target = appId,
71
+ ) {
72
+ const extra = { sessionId: "action-tool", requestInfo: { headers: { "x-agent-id": id } } };
73
+ if (origin === "script-sdk") markScriptSdkRequestOrigin(extra);
74
+ if (origin === "extension") markExtensionRequestOrigin(extra);
75
+ return handler({ appId: target, action, input }, extra);
76
+ }
77
+
78
+ test("runs task actions with the verified caller and the REST response shape", async () => {
79
+ const output = await call("triage", { caller: { kind: "guest", id: "forged" } });
80
+ expect(output.isError).not.toBe(true);
81
+ expect(output.structuredContent).toMatchObject({ ok: true, status: "pending" });
82
+ const task = await getTaskById(output.structuredContent.taskId);
83
+ expect(task?.task).toContain(`"kind":"agent","id":"${agentId}","name":"Action invoker"`);
84
+ });
85
+
86
+ test("refuses script and extension origins and stays outside the SDK allowlist", async () => {
87
+ for (const origin of ["script-sdk", "extension"] as const) {
88
+ const output = await call("triage", {}, origin);
89
+ expect(output.isError).toBe(true);
90
+ expect(output.structuredContent.message).toContain("cannot run app actions");
91
+ }
92
+ expect(isMcpToolAllowedForScripts("app-action-run")).toBe(false);
93
+ });
94
+
95
+ test("rejects missing agents, apps, actions, and denied principals", async () => {
96
+ expect((await call("triage", {}, undefined, "")).isError).toBe(true);
97
+ expect((await call("triage", {}, undefined, agentId, "missing")).isError).toBe(true);
98
+ expect((await call("missing")).isError).toBe(true);
99
+ const policy = LEGACY_POLICY as unknown as Record<"app.use", LegacyRule>;
100
+ const original = policy["app.use"];
101
+ policy["app.use"] = { name: "deny-actions", denyReason: "denied actions", evaluate: () => false };
102
+ try {
103
+ expect((await call("triage")).isError).toBe(true);
104
+ } finally {
105
+ policy["app.use"] = original;
106
+ }
107
+ });
108
+
109
+ test.skipIf(SKIP_SANDBOX_SPAWN_TESTS)(
110
+ "runs script actions as the owner while preserving the invoking agent caller",
111
+ async () => {
112
+ const server = createHttpServer(async (req, res) => {
113
+ const id = req.headers["x-agent-id"] as string | undefined;
114
+ if (await handleCore(req, res, id, apiKey)) return;
115
+ if (
116
+ await handleMcpBridge(
117
+ req,
118
+ res,
119
+ getPathSegments(req.url ?? ""),
120
+ parseQueryParams(req.url ?? ""),
121
+ id,
122
+ )
123
+ )
124
+ return;
125
+ res.writeHead(404).end();
126
+ });
127
+ try {
128
+ const port = await listenOnFreePort(server);
129
+ process.env.MCP_BASE_URL = `http://localhost:${port}`;
130
+ const saved = await upsertScriptByName({
131
+ name: "action_tool_owner",
132
+ scope: "agent",
133
+ scopeId: ownerId,
134
+ agentId: ownerId,
135
+ source: `export default async function(args, ctx) {
136
+ const response = await ctx.swarm.app_row_create({ appId: args.app.id, model: "note", values: { title: "owner-write" } });
137
+ return { caller: args.caller, row: (response?.data ?? response).row };
138
+ }`,
139
+ description: "Owner action fixture",
140
+ intent: "Verify script owner and caller",
141
+ signatureJson: "{}",
142
+ typeChecked: true,
143
+ embeddingMode: "skip",
144
+ });
145
+ const app = await createApp({
146
+ name: "Script action",
147
+ definition: {
148
+ ...definition,
149
+ actions: {
150
+ write: {
151
+ kind: "script",
152
+ scriptId: saved.script.id,
153
+ args: { caller: { kind: "guest", id: "default-forgery" } },
154
+ },
155
+ },
156
+ },
157
+ });
158
+ const output = await call(
159
+ "write",
160
+ { caller: { kind: "operator", id: "forged" } },
161
+ undefined,
162
+ agentId,
163
+ app.id,
164
+ );
165
+ expect(output.isError).not.toBe(true);
166
+ expect(output.structuredContent).toMatchObject({
167
+ ok: true,
168
+ result: {
169
+ caller: { kind: "agent", id: agentId, name: "Action invoker" },
170
+ row: { title: "owner-write", createdBy: `agent:${ownerId}` },
171
+ },
172
+ });
173
+ } finally {
174
+ if (server.listening) await new Promise<void>((resolve) => server.close(() => resolve()));
175
+ }
176
+ },
177
+ CHILD_PROCESS_TEST_BUDGET_MS,
178
+ );