@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
@@ -13,7 +13,8 @@ import {
13
13
  } from "../be/db";
14
14
  import { ActiveSessionSchema, AgentTaskSchema } from "../types";
15
15
  import { isMultiRuntimeEnabled } from "../utils/multi-runtime";
16
- import { route } from "./route-def";
16
+ import { bodyTask, route } from "./route-def";
17
+ import { readableByFor, resolveSessionActor } from "./session-access";
17
18
  import { jsonError } from "./utils";
18
19
 
19
20
  // ─── Route Definitions ───────────────────────────────────────────────────────
@@ -39,6 +40,7 @@ const createActiveSession = route({
39
40
  method: "post",
40
41
  path: "/api/active-sessions",
41
42
  pattern: ["api", "active-sessions"],
43
+ session: bodyTask("taskId"),
42
44
  summary: "Create a new active session",
43
45
  tags: ["Active Sessions"],
44
46
  body: z.object({
@@ -60,6 +62,7 @@ const deleteSessionByTask = route({
60
62
  method: "delete",
61
63
  path: "/api/active-sessions/by-task/{taskId}",
62
64
  pattern: ["api", "active-sessions", "by-task", null],
65
+ session: { taskParam: "taskId" },
63
66
  summary: "Delete active session by task ID",
64
67
  tags: ["Active Sessions"],
65
68
  params: z.object({ taskId: z.string() }),
@@ -72,6 +75,7 @@ const deleteSessionById = route({
72
75
  method: "delete",
73
76
  path: "/api/active-sessions/{id}",
74
77
  pattern: ["api", "active-sessions", null],
78
+ session: { activeSessionParam: "id" },
75
79
  summary: "Delete active session by ID",
76
80
  tags: ["Active Sessions"],
77
81
  params: z.object({ id: z.string() }),
@@ -84,6 +88,7 @@ const heartbeatSession = route({
84
88
  method: "put",
85
89
  path: "/api/active-sessions/heartbeat/{taskId}",
86
90
  pattern: ["api", "active-sessions", "heartbeat", null],
91
+ session: { taskParam: "taskId" },
87
92
  summary: "Update heartbeat for an active session",
88
93
  tags: ["Active Sessions"],
89
94
  params: z.object({ taskId: z.string() }),
@@ -96,6 +101,7 @@ const updateProviderSession = route({
96
101
  method: "put",
97
102
  path: "/api/active-sessions/provider-session/{taskId}",
98
103
  pattern: ["api", "active-sessions", "provider-session", null],
104
+ session: { taskParam: "taskId" },
99
105
  summary: "Update provider session ID on an active session",
100
106
  tags: ["Active Sessions"],
101
107
  params: z.object({ taskId: z.string() }),
@@ -109,6 +115,7 @@ const cleanupSessions = route({
109
115
  method: "post",
110
116
  path: "/api/active-sessions/cleanup",
111
117
  pattern: ["api", "active-sessions", "cleanup"],
118
+ session: { agentWide: "Deletes every session of an agent, or every stale session." },
112
119
  summary: "Clean up stale sessions",
113
120
  tags: ["Active Sessions"],
114
121
  body: z
@@ -126,6 +133,7 @@ const recoverOrphanedTasks = route({
126
133
  method: "post",
127
134
  path: "/api/active-sessions/recover-orphaned-tasks",
128
135
  pattern: ["api", "active-sessions", "recover-orphaned-tasks"],
136
+ session: { none: "Its handler admits only the calling agent, never a user." },
129
137
  summary: "Recover orphaned in-progress tasks for an agent",
130
138
  tags: ["Active Sessions"],
131
139
  body: z.object({
@@ -153,7 +161,10 @@ export async function handleActiveSessions(
153
161
  if (listActiveSessions.match(req.method, pathSegments)) {
154
162
  const parsed = await listActiveSessions.parse(req, res, pathSegments, queryParams);
155
163
  if (!parsed) return true;
156
- const sessions = await getActiveSessions(parsed.query.agentId || undefined);
164
+ const sessions = await getActiveSessions(
165
+ parsed.query.agentId || undefined,
166
+ readableByFor(await resolveSessionActor(req, myAgentId)),
167
+ );
157
168
  listActiveSessions.respond(res, 200, { sessions });
158
169
  return true;
159
170
  }
@@ -90,6 +90,7 @@ import { getRequestAuth } from "../utils/request-auth-context";
90
90
  import { isRuntimePoolsEnabled } from "../utils/runtime-pools-flag";
91
91
  import { scrubSecrets } from "../utils/secret-scrubber";
92
92
  import { route, runtimeInstanceHeader } from "./route-def";
93
+ import { readableByFor, resolveSessionActor } from "./session-access";
93
94
  import { agentWithCapacity, json, jsonError } from "./utils";
94
95
 
95
96
  function singleHeaderValue(req: IncomingMessage, name: string): string | undefined {
@@ -844,7 +845,7 @@ export async function handleAgentsRest(
844
845
  res: ServerResponse,
845
846
  pathSegments: string[],
846
847
  queryParams: URLSearchParams,
847
- _myAgentId: string | undefined,
848
+ myAgentId: string | undefined,
848
849
  ): Promise<boolean> {
849
850
  if (listAgents.match(req.method, pathSegments)) {
850
851
  const parsed = await listAgents.parse(req, res, pathSegments, queryParams);
@@ -853,7 +854,10 @@ export async function handleAgentsRest(
853
854
  // List responses default to slim (no identity markdown); `?fields=full` restores it.
854
855
  const slim = parsed.query.fields !== "full";
855
856
  const agents = includeTasks
856
- ? await getAllAgentsWithTasks({ slim })
857
+ ? await getAllAgentsWithTasks({
858
+ slim,
859
+ readableBy: readableByFor(await resolveSessionActor(req, myAgentId)),
860
+ })
857
861
  : await getAllAgents({ slim });
858
862
  const [agentsWithCapacity, transports] = await Promise.all([
859
863
  Promise.all(agents.map(agentWithCapacity)),
@@ -1498,6 +1502,7 @@ export async function handleAgentsRest(
1498
1502
  reportedAt: parsed.body.latest_model?.reportedAt ?? parsed.body.acp!.reportedAt,
1499
1503
  reportKind: "post_task" as const,
1500
1504
  bedrock: null,
1505
+ opencodeGo: null,
1501
1506
  acp: null,
1502
1507
  };
1503
1508
  finalAgent =
@@ -1537,7 +1542,10 @@ export async function handleAgentsRest(
1537
1542
  if (!parsed) return true;
1538
1543
  const includeTasks = parsed.query.include === "tasks";
1539
1544
  const agent = includeTasks
1540
- ? await getAgentWithTasks(parsed.params.id)
1545
+ ? await getAgentWithTasks(
1546
+ parsed.params.id,
1547
+ readableByFor(await resolveSessionActor(req, myAgentId)),
1548
+ )
1541
1549
  : await getAgentById(parsed.params.id);
1542
1550
 
1543
1551
  if (!agent) {
@@ -67,6 +67,7 @@ import "./task-templates";
67
67
  import "./telemetry-ui";
68
68
  import "./trackers/jira";
69
69
  import "./trackers/linear";
70
+ import "./ranking";
70
71
  import "./used-by";
71
72
  import "./fleet-benchmark";
72
73
  import "./users";
@@ -20,7 +20,7 @@ import {
20
20
  SUBSCRIPTION_PLANS,
21
21
  SUBSCRIPTION_PLANS_CHECKED_AT,
22
22
  } from "../utils/subscription-plans";
23
- import { route } from "./route-def";
23
+ import { bodyTask, route } from "./route-def";
24
24
  import { clearUsageCache } from "./usage-cache";
25
25
  import { jsonError } from "./utils";
26
26
 
@@ -36,6 +36,7 @@ const reportUsage = route({
36
36
  method: "post",
37
37
  path: "/api/keys/report-usage",
38
38
  pattern: ["api", "keys", "report-usage"],
39
+ session: bodyTask("taskId"),
39
40
  summary: "Record which API key was used for a task",
40
41
  tags: ["API Keys"],
41
42
  body: z.object({
@@ -82,6 +83,7 @@ const reportAuthFailure = route({
82
83
  method: "post",
83
84
  path: "/api/keys/report-auth-failure",
84
85
  pattern: ["api", "keys", "report-auth-failure"],
86
+ session: bodyTask("taskId"),
85
87
  summary: "Record an auth failure for a pooled key; bench it after 2 in a row",
86
88
  tags: ["API Keys"],
87
89
  body: z.object({
@@ -23,7 +23,7 @@ import { getRequestAuth } from "../utils/request-auth-context";
23
23
  import { cancelApprovalRequest } from "../workflows/approval-cancel";
24
24
  import { createApprovalFollowUpTask } from "../workflows/approval-notifications";
25
25
  import { workflowEventBus } from "../workflows/event-bus";
26
- import { route } from "./route-def";
26
+ import { bodyTask, route } from "./route-def";
27
27
  import { jsonError } from "./utils";
28
28
 
29
29
  // ─── Route Definitions ───────────────────────────────────────────────────────
@@ -255,6 +255,7 @@ const createRoute = route({
255
255
  method: "post",
256
256
  path: "/api/approval-requests",
257
257
  pattern: ["api", "approval-requests"],
258
+ session: bodyTask("sourceTaskId"),
258
259
  summary: "Create a new approval request",
259
260
  tags: ["ApprovalRequests"],
260
261
  body: z.object({
package/src/http/apps.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import * as z from "zod";
3
+ import type { AppActionCaller } from "../apps/action-caller";
4
+ import { runAppAction } from "../apps/action-runner";
3
5
  import {
4
6
  type AppDefinition,
5
7
  AppNameSchema,
@@ -60,14 +62,9 @@ import {
60
62
  rollbackApp,
61
63
  snapshotApp,
62
64
  } from "../apps/version";
63
- import { normalizeAssetKey } from "../assets/key";
64
- import { getAgentById, getAppVersion, getAppVersions, getLeadAgent } from "../be/db";
65
- import { getScriptById } from "../be/scripts/db";
66
- import { getSavedScriptOwnerAgentId, runSavedScriptAsAgent } from "../be/scripts/run-saved";
67
- import { resolveTemplate } from "../prompts/resolver";
65
+ import { getAgentById, getAppVersion, getAppVersions } from "../be/db";
68
66
  import type { RbacPrincipal, RbacResource } from "../rbac";
69
67
  import { can } from "../rbac";
70
- import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
71
68
  import { AgentTaskStatusSchema } from "../types";
72
69
  import { getRequestAuth } from "../utils/request-auth-context";
73
70
  import { scrubObject } from "../utils/secret-scrubber";
@@ -76,8 +73,8 @@ import { route } from "./route-def";
76
73
  import { BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError } from "./utils";
77
74
 
78
75
  const MAX_APP_BODY_BYTES = 5 * 1024 * 1024;
79
- const MAX_APP_ROW_BODY_BYTES = 1 * 1024 * 1024;
80
- const MAX_APP_BULK_ROWS_BODY_BYTES = 10 * 1024 * 1024;
76
+ export const MAX_APP_ROW_BODY_BYTES = 1 * 1024 * 1024;
77
+ export const MAX_APP_BULK_ROWS_BODY_BYTES = 10 * 1024 * 1024;
81
78
  const MAX_USER_CONFIG_BODY_BYTES = 64 * 1024;
82
79
  const MAX_USER_CONFIG_VALUES_BYTES = 16 * 1024;
83
80
  const DECIMAL_NUMBER_PATTERN = /^[+-]?\d+(?:\.\d+)?$/;
@@ -660,45 +657,64 @@ const syncAppRoute = route({
660
657
  });
661
658
 
662
659
  /**
663
- * RBAC-gate an app operation and resolve the acting principal's stable actor id
664
- * (`user:<id>`, `agent:<id>`, or `operator`) for row provenance. Returns null
665
- * (after writing the 403) when the write is denied.
660
+ * Check app permissions and resolve server-verified identity for action input
661
+ * and row provenance. Return null after a 403 response when access is denied.
666
662
  */
667
- async function authorizeApp(
663
+ async function authorizeAppCaller(
668
664
  req: IncomingMessage,
669
665
  res: ServerResponse,
670
666
  myAgentId: string | undefined,
671
667
  verb: "app.manage" | "app.use",
672
668
  resource: RbacResource,
673
- ): Promise<string | null> {
669
+ ): Promise<{ actor: string; caller: AppActionCaller } | null> {
674
670
  const auth = getRequestAuth(req);
675
671
  let principal: RbacPrincipal;
676
672
  let actor: string;
673
+ let caller: AppActionCaller;
677
674
  if (auth?.kind === "operator") {
678
675
  principal = { kind: "operator" };
679
- actor = "operator";
676
+ // Page sessions never act as an agent, even if a header names one.
677
+ const agent = myAgentId && !auth.page ? await getAgentById(myAgentId) : null;
678
+ actor = agent ? `agent:${agent.id}` : "operator";
679
+ caller = agent
680
+ ? { kind: "agent", id: agent.id, name: agent.name }
681
+ : { kind: "operator", id: null };
680
682
  } else if (auth?.kind === "user") {
681
683
  principal = { kind: "user", userId: auth.userId };
682
684
  actor = `user:${auth.userId}`;
683
- } else if (auth?.kind === "guest") {
685
+ caller = { kind: "user", id: auth.userId, name: auth.user.name };
686
+ } else if (auth?.kind === "guest" || auth?.kind === "controller") {
684
687
  principal = { kind: "guest" };
685
688
  actor = "guest";
689
+ caller = { kind: "guest", id: null };
686
690
  } else {
687
691
  const agent = myAgentId ? await getAgentById(myAgentId) : null;
688
692
  principal = { kind: "agent", agentId: myAgentId ?? "", isLead: agent?.isLead ?? false };
689
693
  actor = myAgentId ? `agent:${myAgentId}` : "agent";
694
+ caller = { kind: "agent", id: myAgentId ?? null, name: agent?.name };
690
695
  }
696
+ if (auth?.page) caller.pageId = auth.page.id;
691
697
  const decision = can({
692
698
  principal,
693
699
  verb,
694
700
  resource,
695
701
  source: "http",
696
702
  });
697
- if (decision.allow) return actor;
703
+ if (decision.allow) return { actor, caller };
698
704
  jsonError(res, decision.reason, 403);
699
705
  return null;
700
706
  }
701
707
 
708
+ async function authorizeApp(
709
+ req: IncomingMessage,
710
+ res: ServerResponse,
711
+ myAgentId: string | undefined,
712
+ verb: "app.manage" | "app.use",
713
+ resource: RbacResource,
714
+ ): Promise<string | null> {
715
+ return (await authorizeAppCaller(req, res, myAgentId, verb, resource))?.actor ?? null;
716
+ }
717
+
702
718
  async function authorizeAppManage(
703
719
  req: IncomingMessage,
704
720
  res: ServerResponse,
@@ -736,17 +752,6 @@ function definitionNeedsRepair(
736
752
  return true;
737
753
  }
738
754
 
739
- /**
740
- * One `error` string for a sync result, or undefined when every pass succeeded.
741
- * Each failed pair is named so a multi-pair sync says which one broke; the pass
742
- * list in `result.passes` carries the rest.
743
- */
744
- function syncPassError(sync: Awaited<ReturnType<typeof runAppSync>>): string | undefined {
745
- const failed = sync.passes.filter((pass) => pass.error !== undefined);
746
- if (failed.length === 0) return undefined;
747
- return failed.map((pass) => `${pass.model}.${pass.source}: ${pass.error}`).join("; ");
748
- }
749
-
750
755
  function snapshotFailure(res: ServerResponse): void {
751
756
  jsonError(res, "failed to snapshot app", 500);
752
757
  }
@@ -971,6 +976,10 @@ function rowValue(row: AppRow, column: string): unknown {
971
976
  return Object.hasOwn(row, column) ? row[column] : undefined;
972
977
  }
973
978
 
979
+ export function rowMatchesFilters(row: AppRow, filters: RowFilter[]): boolean {
980
+ return filters.every((filter) => rowValue(row, filter.column) === filter.value);
981
+ }
982
+
974
983
  export function applyQuery(
975
984
  rows: AppRow[],
976
985
  query: AppQueryDef,
@@ -999,6 +1008,59 @@ export function applyQuery(
999
1008
  return selected.slice(0, query.limit ?? 200);
1000
1009
  }
1001
1010
 
1011
+ export async function selectAppRows(
1012
+ appId: string,
1013
+ modelName: string,
1014
+ model: ModelDef,
1015
+ filters: RowFilter[],
1016
+ sortRaw?: string,
1017
+ limit = 200,
1018
+ systemColumns = false,
1019
+ ) {
1020
+ // SQL narrows the scan; the `===` re-check keeps the exact filter semantics.
1021
+ const rows = (
1022
+ await listAppRows(
1023
+ appId,
1024
+ modelName,
1025
+ filters.flatMap((filter) =>
1026
+ typeof filter.value === "string" ||
1027
+ typeof filter.value === "number" ||
1028
+ typeof filter.value === "boolean"
1029
+ ? [{ column: filter.column, value: filter.value }]
1030
+ : [],
1031
+ ),
1032
+ )
1033
+ ).filter((row) => rowMatchesFilters(row, filters));
1034
+
1035
+ if (sortRaw) {
1036
+ const [column, dir, extra] = sortRaw.split(":");
1037
+ if (
1038
+ extra !== undefined ||
1039
+ !column ||
1040
+ (dir !== "asc" && dir !== "desc") ||
1041
+ (!SORTABLE_SYSTEM_DATE_COLUMNS.has(column) &&
1042
+ !(systemColumns && Object.hasOwn(SYSTEM_COLUMN_KINDS, column)) &&
1043
+ (!Object.hasOwn(model.columns, column) || model.columns[column]!.hidden === true))
1044
+ ) {
1045
+ throw new AppRowValidationError([
1046
+ { path: "sort", message: "must be <column>:<asc|desc> for a known column" },
1047
+ ]);
1048
+ }
1049
+ rows.sort((a, b) => {
1050
+ const result = compareValues(
1051
+ rowValue(a, column),
1052
+ rowValue(b, column),
1053
+ dir,
1054
+ SORTABLE_SYSTEM_DATE_COLUMNS.has(column) ||
1055
+ (Object.hasOwn(model.columns, column) && model.columns[column]!.kind === "date"),
1056
+ );
1057
+ return result;
1058
+ });
1059
+ }
1060
+ const total = rows.length;
1061
+ return { rows: rows.slice(0, limit), total };
1062
+ }
1063
+
1002
1064
  export async function handleApps(
1003
1065
  req: IncomingMessage,
1004
1066
  res: ServerResponse,
@@ -1169,55 +1231,23 @@ export async function handleApps(
1169
1231
  json(res, { error: "invalid row query", issues }, 400);
1170
1232
  return true;
1171
1233
  }
1172
- // SQL narrows the scan; the `===` re-check keeps the exact filter semantics.
1173
- const rows = (
1174
- await listAppRows(
1175
- parsed.params.id,
1176
- parsed.params.model,
1177
- filters.flatMap((filter) =>
1178
- typeof filter.value === "string" ||
1179
- typeof filter.value === "number" ||
1180
- typeof filter.value === "boolean"
1181
- ? [{ column: filter.column, value: filter.value }]
1182
- : [],
1234
+ try {
1235
+ listRowsRoute.respond(
1236
+ res,
1237
+ 200,
1238
+ await selectAppRows(
1239
+ parsed.params.id,
1240
+ parsed.params.model,
1241
+ resolved.model,
1242
+ filters,
1243
+ parsed.query.sort,
1244
+ parsed.query.limit,
1183
1245
  ),
1184
- )
1185
- ).filter((row) => filters.every((filter) => rowValue(row, filter.column) === filter.value));
1186
- const sortRaw = parsed.query.sort;
1187
- if (sortRaw) {
1188
- const [column, dir, extra] = sortRaw.split(":");
1189
- if (
1190
- extra !== undefined ||
1191
- !column ||
1192
- (dir !== "asc" && dir !== "desc") ||
1193
- (!SORTABLE_SYSTEM_DATE_COLUMNS.has(column) &&
1194
- (!Object.hasOwn(resolved.model.columns, column) ||
1195
- resolved.model.columns[column]!.hidden === true))
1196
- ) {
1197
- json(
1198
- res,
1199
- {
1200
- error: "invalid sort",
1201
- issues: [{ path: "sort", message: "must be <column>:<asc|desc> for a known column" }],
1202
- },
1203
- 400,
1204
- );
1205
- return true;
1206
- }
1207
- rows.sort((a, b) => {
1208
- const result = compareValues(
1209
- rowValue(a, column),
1210
- rowValue(b, column),
1211
- dir,
1212
- SORTABLE_SYSTEM_DATE_COLUMNS.has(column) ||
1213
- (Object.hasOwn(resolved.model.columns, column) &&
1214
- resolved.model.columns[column]!.kind === "date"),
1215
- );
1216
- return result;
1217
- });
1246
+ );
1247
+ } catch (error) {
1248
+ if (!(error instanceof AppRowValidationError)) throw error;
1249
+ json(res, { error: "invalid sort", issues: error.issues }, 400);
1218
1250
  }
1219
- const total = rows.length;
1220
- listRowsRoute.respond(res, 200, { rows: rows.slice(0, parsed.query.limit ?? 200), total });
1221
1251
  return true;
1222
1252
  }
1223
1253
 
@@ -1326,8 +1356,12 @@ export async function handleApps(
1326
1356
  if (enforceContentLengthCap(req, res, MAX_APP_ROW_BODY_BYTES) === BODY_TOO_LARGE) return true;
1327
1357
  const parsed = await runActionRoute.parse(req, res, pathSegments, queryParams);
1328
1358
  if (!parsed) return true;
1329
- const actor = await authorizeAppUse(req, res, myAgentId, parsed.params.id);
1330
- if (!actor) return true;
1359
+ const identity = await authorizeAppCaller(req, res, myAgentId, "app.use", {
1360
+ kind: "app",
1361
+ appId: parsed.params.id,
1362
+ });
1363
+ if (!identity) return true;
1364
+ const { actor, caller } = identity;
1331
1365
 
1332
1366
  const app = await getApp(parsed.params.id);
1333
1367
  if (!app) {
@@ -1335,121 +1369,15 @@ export async function handleApps(
1335
1369
  return true;
1336
1370
  }
1337
1371
  if (definitionNeedsRepair(res, app)) return true;
1338
- const actions = app.definition.actions;
1339
- if (!actions || !Object.hasOwn(actions, parsed.params.name)) {
1340
- jsonError(res, "app action not found", 404);
1341
- return true;
1342
- }
1343
-
1344
- const action = actions[parsed.params.name]!;
1345
- const input = parsed.body.input ?? {};
1346
- if (action.kind === "script") {
1347
- const script = await getScriptById(action.scriptId);
1348
- if (!script) {
1349
- json(
1350
- res,
1351
- {
1352
- error: "invalid app action",
1353
- issues: [
1354
- {
1355
- path: `actions.${parsed.params.name}.scriptId`,
1356
- message: `script "${action.scriptId}" no longer exists`,
1357
- },
1358
- ],
1359
- },
1360
- 400,
1361
- );
1362
- return true;
1363
- }
1364
-
1365
- // Script actions run with the OWNER's bindings by design: wiring a script
1366
- // into an app is the owner's delegation. parseAppDefinition enforces at
1367
- // write time that an agent may only wire scripts it owns (or global ones),
1368
- // so a foreign script id cannot be smuggled in here. Invoker-brokered
1369
- // credentials remain a possible future tightening.
1370
- const runAsAgentId = getSavedScriptOwnerAgentId(script);
1371
- if (!runAsAgentId) {
1372
- jsonError(res, "agentId is required: this script has no owning agent to run as", 400);
1373
- return true;
1374
- }
1375
-
1376
- const output = await runSavedScriptAsAgent({
1377
- script,
1378
- input: { ...action.args, ...input, app: { id: app.id } },
1379
- agentId: runAsAgentId,
1380
- });
1381
- const ok = output.exitCode === 0 && !output.error && !output.runtimeError;
1382
- const error = ok
1383
- ? undefined
1384
- : output.runtimeError
1385
- ? `${output.runtimeError.name}: ${output.runtimeError.message}`
1386
- : (output.error ?? `Script exited with code ${output.exitCode}`);
1387
- runActionRoute.respond(
1388
- res,
1389
- 200,
1390
- scrubObject({
1391
- ok,
1392
- result: output.result,
1393
- stdout: output.stdout,
1394
- ...(error === undefined ? {} : { error }),
1395
- durationMs: output.durationMs,
1396
- }),
1397
- );
1398
- return true;
1399
- }
1400
-
1401
- if (action.kind === "sync") {
1402
- const startedAt = Date.now();
1403
- const sync = await runAppSync({
1404
- appId: app.id,
1405
- ...(action.model === undefined ? {} : { model: action.model }),
1406
- ...(action.source === undefined ? {} : { source: action.source }),
1407
- invokedBy: actor,
1408
- });
1409
- const durationMs = Date.now() - startedAt;
1410
- if (sync.issues && sync.issues.length > 0) {
1411
- json(res, { error: "invalid sync action", issues: sync.issues }, 400);
1412
- return true;
1413
- }
1414
- // Deliberately the SCRIPT-action response shape, minus `taskId`:
1415
- // app-surface.tsx branches on `taskId` first, so omitting it gives the
1416
- // dashboard running -> ok/error + refetchAll() with zero UI changes.
1417
- const error = syncPassError(sync);
1418
- runActionRoute.respond(
1419
- res,
1420
- 200,
1421
- scrubObject({
1422
- ok: sync.ok,
1423
- result: { passes: sync.passes },
1424
- ...(error === undefined ? {} : { error }),
1425
- durationMs,
1426
- }),
1427
- );
1428
- return true;
1429
- }
1430
-
1431
- const lead = action.agentId ? null : await getLeadAgent();
1432
- const taskPrompt = resolveTemplate("task.app.action", {
1433
- prompt: action.prompt,
1434
- app_id: app.id,
1435
- action_name: parsed.params.name,
1436
- input_json: JSON.stringify(input),
1372
+ const output = await runAppAction({
1373
+ app,
1374
+ name: parsed.params.name,
1375
+ input: parsed.body.input,
1376
+ caller,
1377
+ actor,
1437
1378
  });
1438
- const task = await createTaskWithSiblingAwareness(
1439
- taskPrompt.text,
1440
- {
1441
- source: "api",
1442
- agentId: action.agentId ?? lead?.id,
1443
- routingReason: action.agentId ? "human_pinned" : lead ? "skill" : undefined,
1444
- routingSource: action.agentId || lead ? "engine_default" : undefined,
1445
- // App-spawned tasks group under the app's asset namespace so a
1446
- // swarm-tasks source can pull them back via config.assetKey.
1447
- key: normalizeAssetKey(`shared/app:${app.id}/action:${parsed.params.name}/`),
1448
- ...(actor.startsWith("user:") ? { requestedByUserId: actor.slice("user:".length) } : {}),
1449
- },
1450
- { origin: "app" },
1451
- );
1452
- runActionRoute.respond(res, 200, { ok: true, taskId: task.id, status: task.status });
1379
+ if (output.status === 200) runActionRoute.respond(res, 200, output.body);
1380
+ else json(res, output.body, output.status);
1453
1381
  return true;
1454
1382
  }
1455
1383
 
@@ -31,6 +31,7 @@ import { SwarmConfigSchema } from "../types";
31
31
  import { getRequestAuth, type HttpRequestAuth } from "../utils/request-auth-context";
32
32
  import { registerVolatileSecret } from "../utils/secret-scrubber";
33
33
  import { reloadGlobalConfigsAndIntegrations, scheduleIntegrationsReload } from "./core";
34
+ import { syncRankingAfterConfigChange } from "./ranking";
34
35
  import { route } from "./route-def";
35
36
  import { duplicateGuardedQueryKey, jsonError } from "./utils";
36
37
 
@@ -527,6 +528,7 @@ export async function handleConfig(
527
528
  if (scope === "global" && key === "SWARM_ORG_WEBSITE") {
528
529
  await fillOrgLogoFromWebsite(String(value));
529
530
  }
531
+ if (scope === "global") await syncRankingAfterConfigChange(key, String(value));
530
532
  if (scope === "global") {
531
533
  scheduleIntegrationsReload();
532
534
  }
@@ -561,6 +563,7 @@ export async function handleConfig(
561
563
  return true;
562
564
  }
563
565
  if (existing.scope === "global") {
566
+ await syncRankingAfterConfigChange(existing.key, null);
564
567
  scheduleIntegrationsReload();
565
568
  }
566
569
  if (
@@ -43,6 +43,7 @@ const postContext = route({
43
43
  method: "post",
44
44
  path: "/api/tasks/{id}/context",
45
45
  pattern: ["api", "tasks", null, "context"],
46
+ session: { taskParam: "id" },
46
47
  summary: "Record a context usage snapshot for a task",
47
48
  tags: ["Tasks"],
48
49
  params: z.object({ id: z.string() }),
@@ -72,6 +73,7 @@ const getContext = route({
72
73
  method: "get",
73
74
  path: "/api/tasks/{id}/context",
74
75
  pattern: ["api", "tasks", null, "context"],
76
+ session: { taskParam: "id" },
75
77
  summary: "Get context usage history for a task",
76
78
  tags: ["Tasks"],
77
79
  params: z.object({ id: z.string() }),