@desplega.ai/agent-swarm 2.4.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 (222) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-kedstxx6.js → acp-adapter-zvzr538d.js} +9 -9
  3. package/dist/{actions-vget6r1k.js → actions-jhtqdw69.js} +21 -21
  4. package/dist/{amp-adapter-xeb5mktv.js → amp-adapter-bd84rhs2.js} +4 -4
  5. package/dist/{app-mqap8t8m.js → app-kreq1gwg.js} +3 -3
  6. package/dist/{assistant-h0gv97mv.js → assistant-3k22sbsq.js} +22 -22
  7. package/dist/{boot-reembed-qr8a894z.js → boot-reembed-e53x6b9v.js} +1 -1
  8. package/dist/{boot-reembed-rj85jjhw.js → boot-reembed-f6z923ny.js} +1 -1
  9. package/dist/{boot-scrub-sweep-eyfvn6v9.js → boot-scrub-sweep-0538vsb7.js} +4 -4
  10. package/dist/{boot-seeds-xs9a8dx1.js → boot-seeds-6wxqkfk2.js} +4 -4
  11. package/dist/{claude-adapter-bm23cye7.js → claude-adapter-gfts5044.js} +9 -9
  12. package/dist/{claude-managed-adapter-1mwrsm9d.js → claude-managed-adapter-h2y889ge.js} +2 -2
  13. package/dist/{claude-managed-setup-gkfgks4j.js → claude-managed-setup-h00hsgtz.js} +2 -2
  14. package/dist/{claude-sdk-session-apb4ypz3.js → claude-sdk-session-56besmfz.js} +13 -13
  15. package/dist/{cli-nrrg0fvy.js → cli-0bh477zn.js} +18 -5
  16. package/dist/{cli-7gh7020n.js → cli-1dmcq2eh.js} +1 -1
  17. package/dist/{cli-7ayprxp7.js → cli-2dfbv8qr.js} +8 -0
  18. package/dist/{cli-1cevz46c.js → cli-3kh4kpqy.js} +6 -6
  19. package/dist/{cli-tg9ksbn8.js → cli-3p8fqcnh.js} +2 -2
  20. package/dist/{cli-x1p5dhq2.js → cli-3r448dqn.js} +3 -3
  21. package/dist/{cli-ecshf504.js → cli-3tnpj2bh.js} +7 -7
  22. package/dist/{cli-ffbrkh1r.js → cli-471qk6s2.js} +46 -38
  23. package/dist/{cli-a49vpbf3.js → cli-5kqnemen.js} +1 -1
  24. package/dist/{cli-zvze9rbm.js → cli-6ncjvx55.js} +3 -3
  25. package/dist/{cli-fb6t92v8.js → cli-6x9ma5w5.js} +1 -1
  26. package/dist/{cli-c8fwpgqb.js → cli-7225gwj7.js} +2 -2
  27. package/dist/{cli-bn26xbcs.js → cli-8nyhf7za.js} +1 -1
  28. package/dist/{cli-y83zk5sb.js → cli-a0kcty51.js} +4 -4
  29. package/dist/{cli-8b9wvjps.js → cli-a4gvwttf.js} +4 -4
  30. package/dist/{cli-jhp7w1fy.js → cli-a5ebc95n.js} +2 -2
  31. package/dist/{cli-mz1ezzzh.js → cli-ash6xxk7.js} +2 -2
  32. package/dist/{cli-rsd7ga7v.js → cli-axh29tyr.js} +11 -11
  33. package/dist/{cli-p22zdvnt.js → cli-bn0rewsy.js} +3 -3
  34. package/dist/{cli-6ftrrwm1.js → cli-bn5kevnf.js} +5 -5
  35. package/dist/{cli-p1m2b6gc.js → cli-d2mb5s8a.js} +7 -7
  36. package/dist/{cli-c5sbmw1v.js → cli-d82btycs.js} +94 -15
  37. package/dist/{cli-vjfpshn3.js → cli-ejs3m1sx.js} +1 -1
  38. package/dist/{cli-a1esmmee.js → cli-fpmsqq59.js} +7 -2
  39. package/dist/{cli-8cxkw1f5.js → cli-fz9kewbh.js} +2 -2
  40. package/dist/{cli-8rwvksbc.js → cli-g0hgtz4p.js} +5 -5
  41. package/dist/{cli-kz116kvw.js → cli-gf0kfg81.js} +7 -7
  42. package/dist/{cli-eqgfs202.js → cli-hqhzg1qq.js} +2 -2
  43. package/dist/{cli-3wjs22x3.js → cli-j4rqdf2n.js} +2 -2
  44. package/dist/{cli-ywyrffff.js → cli-jh0qcrga.js} +1 -1
  45. package/dist/{cli-ttaz6c7m.js → cli-k9z4zd4p.js} +6 -5
  46. package/dist/{cli-nme7wery.js → cli-kmqgmcnb.js} +46 -13
  47. package/dist/{cli-x719j3vs.js → cli-kr2pmfqx.js} +392 -168
  48. package/dist/{cli-wrv09v7q.js → cli-m2qgh5sv.js} +1 -1
  49. package/dist/{cli-xfe63zx3.js → cli-mfe7f7b5.js} +2 -2
  50. package/dist/{cli-x3en6ct3.js → cli-nwyw3zhg.js} +1 -1
  51. package/dist/{cli-5s5hx57y.js → cli-nx462e4v.js} +1 -1
  52. package/dist/{cli-bfc3k1x5.js → cli-p0rakbrk.js} +9 -9
  53. package/dist/{cli-avmqhatn.js → cli-p6argxh9.js} +8 -8
  54. package/dist/{cli-3avfh5az.js → cli-pemywhfq.js} +2 -2
  55. package/dist/{cli-sppc0cmw.js → cli-pnp2j5zg.js} +2 -2
  56. package/dist/{cli-wk8ccrz6.js → cli-pw7v69vp.js} +2 -2
  57. package/dist/{cli-bhwa8rzc.js → cli-rdq7tzwm.js} +13 -12
  58. package/dist/{cli-rg7ntypm.js → cli-rqqsfz30.js} +379 -87
  59. package/dist/{cli-kk2r80n5.js → cli-s48yvzb8.js} +4 -4
  60. package/dist/{cli-zv4vy2b0.js → cli-s95dknxh.js} +1 -1
  61. package/dist/{cli-b80a1100.js → cli-sjyetj21.js} +2 -2
  62. package/dist/{cli-rdr166ht.js → cli-smvcw9z5.js} +1 -1
  63. package/dist/{cli-2rwet3r7.js → cli-sv5gb0yb.js} +1 -1
  64. package/dist/{cli-5ff2sjyt.js → cli-syhq378v.js} +2 -2
  65. package/dist/{cli-tj4j2ts4.js → cli-tbhwrxpt.js} +8 -8
  66. package/dist/{cli-4fhyq711.js → cli-vy0fay6k.js} +13 -13
  67. package/dist/{cli-ptkpaqfy.js → cli-wt645v7q.js} +1 -1
  68. package/dist/{cli-f3nx1h8z.js → cli-wxbn3yfx.js} +2 -2
  69. package/dist/{cli-wyrr38xm.js → cli-x5mq1fyq.js} +1 -1
  70. package/dist/{cli-ccn0eybf.js → cli-xbzvet96.js} +6 -6
  71. package/dist/{cli-4mkvwgzr.js → cli-xf234ws8.js} +1 -1
  72. package/dist/{cli-tgvpmnwz.js → cli-xftrvxag.js} +5 -5
  73. package/dist/{cli-02vhhssk.js → cli-z071fb95.js} +5 -5
  74. package/dist/{cli-mvg2hf8p.js → cli-z1ew8vbq.js} +14 -7
  75. package/dist/{cli-dsgj3e7a.js → cli-zfg9enh9.js} +2 -2
  76. package/dist/{cli-yxqag76b.js → cli-zw88zjba.js} +1 -1
  77. package/dist/cli.js +19 -19
  78. package/dist/{codex-adapter-pp2gq8gr.js → codex-adapter-p6rmm7vv.js} +6 -6
  79. package/dist/{codex-hook-qwzccd64.js → codex-hook-x7nejyrr.js} +7 -4
  80. package/dist/{codex-login-35p4sk85.js → codex-login-m819c41r.js} +1 -1
  81. package/dist/{codex-session-runner-0vzn09t8.js → codex-session-runner-8vd5t9t0.js} +7 -7
  82. package/dist/{commands-psqhkttb.js → commands-7ems2p13.js} +2 -2
  83. package/dist/{controller-t20vz2k8.js → controller-m263fg43.js} +4 -4
  84. package/dist/{controllers-connect-gkxmqw9d.js → controllers-connect-bg33ae53.js} +23 -23
  85. package/dist/{cursor-adapter-3v542qe5.js → cursor-adapter-cz6nm6mj.js} +3 -3
  86. package/dist/{db-2xbyenjr.js → db-w505dwa8.js} +2 -2
  87. package/dist/{db-nc1rd869.js → db-ys2rbdft.js} +3 -3
  88. package/dist/{devin-adapter-7egzmt0k.js → devin-adapter-zgqadvnm.js} +1 -1
  89. package/dist/{dispatcher-ewceb8ts.js → dispatcher-0s0mckqr.js} +20 -20
  90. package/dist/{docker-njavd3fb.js → docker-fkgtr75t.js} +2 -2
  91. package/dist/{dsh-adapter-75xwh78t.js → dsh-adapter-3n3net33.js} +3 -3
  92. package/dist/{e2b-5r6a5jeb.js → e2b-7am6tmq6.js} +2 -2
  93. package/dist/{e2b-pw1nvtmh.js → e2b-thdcxtjd.js} +2 -2
  94. package/dist/{easter-egg-3d-xsc47jwf.js → easter-egg-3d-5cgynaja.js} +1 -1
  95. package/dist/{event-bus-101fenkh.js → event-bus-9vpk8gmq.js} +1 -1
  96. package/dist/{execute-gzmw1mnw.js → execute-t4e6m3rw.js} +1 -1
  97. package/dist/{external-channel-a5dsmxn4.js → external-channel-5b23fv8z.js} +2 -2
  98. package/dist/{grok-adapter-vj88xr23.js → grok-adapter-q9fghe5q.js} +10 -10
  99. package/dist/handlers-mqrym347.js +39 -0
  100. package/dist/{heartbeat-t2m9r3wa.js → heartbeat-tww6yx1w.js} +23 -23
  101. package/dist/{hook-xa677f8r.js → hook-dx0pkvar.js} +16 -9
  102. package/dist/{hook-8femx1bg.js → hook-pvmh9b6m.js} +6 -6
  103. package/dist/{http-xpmmq028.js → http-k2ejbt05.js} +199 -131
  104. package/dist/{index-s7ckdajq.js → index-2z710v4x.js} +10 -10
  105. package/dist/{index-dg2tmj99.js → index-dgz61nff.js} +20 -20
  106. package/dist/{index-syztqdt6.js → index-g6tbb6ve.js} +1 -1
  107. package/dist/index-h2ek08d6.js +40 -0
  108. package/dist/index-pqfwh9nn.js +41 -0
  109. package/dist/{index-cdzyfrsp.js → index-x5aktmp8.js} +1 -1
  110. package/dist/{keepalive-3nr4st3r.js → keepalive-nhvpw10z.js} +4 -4
  111. package/dist/lead-jb6sf69z.js +78 -0
  112. package/dist/{maintenance-verwpe2c.js → maintenance-agakc519.js} +1 -1
  113. package/dist/{oauth-refresh-sweep-9q2t0y3q.js → oauth-refresh-sweep-4dcbhtn0.js} +3 -3
  114. package/dist/{onboard-aytqedmq.js → onboard-4n2kwa4v.js} +3 -3
  115. package/dist/{opencode-adapter-f4hryjny.js → opencode-adapter-3s7ax02e.js} +7 -7
  116. package/dist/{otel-impl-hckw4wt1.js → otel-impl-tfa12080.js} +2 -2
  117. package/dist/{pi-mono-adapter-hxby0ce1.js → pi-mono-adapter-ytp7gz8b.js} +22 -11
  118. package/dist/{pricing-refresh-5nvme3sm.js → pricing-refresh-3cqx3f3x.js} +3 -3
  119. package/dist/{rbac-roles-pd8c26m1.js → rbac-roles-9fae5rnv.js} +1 -1
  120. package/dist/{rbac-roles-vbpy21mg.js → rbac-roles-y6zbfzve.js} +2 -2
  121. package/dist/{render-v2-7nqgvk2q.js → render-v2-emawjvc4.js} +3 -3
  122. package/dist/{resume-b6djcj3c.js → resume-zhgts7h7.js} +10 -8
  123. package/dist/{runtime-txrxc9za.js → runtime-xe3aqy4x.js} +1 -1
  124. package/dist/{session-access-fc3f9y12.js → session-access-cm823e06.js} +4 -4
  125. package/dist/{setup-n39szm6b.js → setup-tkp99w1e.js} +1 -1
  126. package/dist/{virtual-modules-wyp2k6z3.js → virtual-modules-4r3zkc20.js} +2 -2
  127. package/dist/{work-objects-jem2fre0.js → work-objects-f722h263.js} +23 -23
  128. package/dist/worker-p7f336rq.js +78 -0
  129. package/dist/{x-8ewt8gxn.js → x-y45mthwn.js} +2 -2
  130. package/openapi.json +99 -52
  131. package/package.json +2 -2
  132. package/src/apps/action-runner.ts +146 -0
  133. package/src/apps/row-store.ts +4 -2
  134. package/src/apps/script-types.ts +1 -1
  135. package/src/be/db.ts +14 -4
  136. package/src/be/fleet-benchmark-stats.ts +10 -5
  137. package/src/be/models-catalog.ts +4 -0
  138. package/src/be/onboarding.ts +6 -0
  139. package/src/be/scripts/typecheck.ts +13 -0
  140. package/src/be/swarm-config-guard.ts +10 -1
  141. package/src/be/telemetry-enable.ts +1 -1
  142. package/src/commands/provider-credentials.ts +12 -2
  143. package/src/commands/runner.ts +3 -0
  144. package/src/heartbeat/heartbeat.ts +2 -2
  145. package/src/hooks/codex-hook.ts +6 -2
  146. package/src/hooks/hook.ts +7 -0
  147. package/src/hooks/pr-body-guard.ts +38 -11
  148. package/src/hooks/pr-room-guard.ts +88 -0
  149. package/src/http/agents.ts +1 -0
  150. package/src/http/apps.ts +84 -181
  151. package/src/http/fleet-benchmark.ts +79 -30
  152. package/src/http/global-config.ts +7 -0
  153. package/src/http/ranking.ts +24 -40
  154. package/src/http/tasks.ts +2 -1
  155. package/src/http/telemetry-ui.ts +1 -1
  156. package/src/http/used-by.ts +55 -19
  157. package/src/http/users.ts +2 -2
  158. package/src/providers/amp-adapter.ts +1 -1
  159. package/src/providers/pi-mono-adapter.ts +15 -0
  160. package/src/providers/reasoning-effort.ts +2 -1
  161. package/src/providers/types.ts +2 -0
  162. package/src/ranking/telemetry-ranking.ts +5 -33
  163. package/src/scripts-runtime/sdk-allowlist.ts +7 -0
  164. package/src/scripts-runtime/types/stdlib.d.ts +31 -0
  165. package/src/scripts-runtime/types/swarm-sdk.d.ts +31 -0
  166. package/src/server.ts +4 -0
  167. package/src/telemetry-context.ts +19 -1
  168. package/src/telemetry-contract/EVENTS.md +42 -8
  169. package/src/telemetry-contract/SOURCE.json +6 -5
  170. package/src/telemetry-contract/catalog.json +225 -5
  171. package/src/telemetry-contract/ranking/fixtures/valid/no-claim.json +10 -0
  172. package/src/telemetry-contract/ranking/listing.v1.schema.json +2 -2
  173. package/src/telemetry-contract/types.gen.ts +225 -27
  174. package/src/telemetry-identity.ts +47 -9
  175. package/src/telemetry-snapshot.ts +9 -1
  176. package/src/telemetry-ui-events.ts +9 -2
  177. package/src/telemetry.ts +14 -3
  178. package/src/tests/app-action-run-tool.test.ts +178 -0
  179. package/src/tests/app-row-tools.test.ts +360 -0
  180. package/src/tests/check-pr-body.test.ts +0 -23
  181. package/src/tests/credential-check.test.ts +38 -0
  182. package/src/tests/fleet-benchmark.test.ts +122 -2
  183. package/src/tests/http-users.test.ts +62 -1
  184. package/src/tests/model-catalog-table.test.ts +63 -0
  185. package/src/tests/model-profiles-api.test.ts +19 -7
  186. package/src/tests/models-catalog.test.ts +50 -0
  187. package/src/tests/onboarding.test.ts +21 -0
  188. package/src/tests/org-website.test.ts +62 -0
  189. package/src/tests/pi-mono-adapter.test.ts +23 -0
  190. package/src/tests/pr-body-leaks.test.ts +4 -0
  191. package/src/tests/pr-room-guard.test.ts +242 -0
  192. package/src/tests/ranking-listing.test.ts +8 -23
  193. package/src/tests/ranking-route.test.ts +49 -47
  194. package/src/tests/reasoning-effort.test.ts +39 -3
  195. package/src/tests/runner-harness-telemetry-env.test.ts +56 -1
  196. package/src/tests/swarm-tool-result-gate.test.ts +2 -4
  197. package/src/tests/telemetry-context.test.ts +128 -2
  198. package/src/tests/telemetry-ui-route.test.ts +123 -0
  199. package/src/tests/tool-annotations.test.ts +3 -2
  200. package/src/tests/used-by-route.test.ts +144 -10
  201. package/src/tests/workflow-agent-task-retry.test.ts +130 -4
  202. package/src/tests/workflow-wait-recovery.test.ts +116 -4
  203. package/src/tools/app-action-run.ts +63 -0
  204. package/src/tools/app-rows.ts +234 -0
  205. package/src/tools/model-catalog/index.ts +1 -1
  206. package/src/tools/tool-config.ts +8 -0
  207. package/src/tools/utils.ts +1 -1
  208. package/src/tools/workflows/retry-workflow-run.ts +1 -1
  209. package/src/types.ts +17 -0
  210. package/src/utils/company-profile.ts +24 -0
  211. package/src/utils/org-website.ts +58 -21
  212. package/src/workflows/recovery.ts +10 -22
  213. package/src/workflows/resume.ts +16 -0
  214. package/src/workflows/task-step-routing.ts +47 -7
  215. package/templates/skills/apps/SKILL.md +41 -2
  216. package/templates/skills/apps/content.md +41 -2
  217. package/dist/handlers-zbs90bny.js +0 -39
  218. package/dist/index-8pwc1jc8.js +0 -40
  219. package/dist/index-nxa6me0q.js +0 -41
  220. package/dist/lead-td8h2sfn.js +0 -78
  221. package/dist/worker-wr6xgfr2.js +0 -78
  222. package/src/tests/check-ranking-entry.test.ts +0 -146
package/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Agent Swarm API",
5
- "version": "2.4.0",
5
+ "version": "2.4.1",
6
6
  "description": "Multi-agent orchestration API for Claude Code, Codex, and Gemini CLI. Enables task distribution, agent communication, and service discovery.\n\nMCP tools are documented separately in [MCP.md](./MCP.md)."
7
7
  },
8
8
  "servers": [
@@ -613,6 +613,9 @@
613
613
  "bedrock": {
614
614
  "$ref": "#/components/schemas/AgentBedrockStatus"
615
615
  },
616
+ "opencodeGo": {
617
+ "$ref": "#/components/schemas/AgentOpencodeGoStatus"
618
+ },
616
619
  "acp": {
617
620
  "$ref": "#/components/schemas/AgentAcpStatus"
618
621
  }
@@ -766,6 +769,28 @@
766
769
  "ready"
767
770
  ]
768
771
  },
772
+ "AgentOpencodeGoStatus": {
773
+ "type": [
774
+ "object",
775
+ "null"
776
+ ],
777
+ "properties": {
778
+ "probedAt": {
779
+ "type": "number"
780
+ },
781
+ "models": {
782
+ "type": "array",
783
+ "items": {
784
+ "type": "string"
785
+ },
786
+ "default": []
787
+ }
788
+ },
789
+ "default": null,
790
+ "required": [
791
+ "probedAt"
792
+ ]
793
+ },
769
794
  "AgentAcpStatus": {
770
795
  "type": [
771
796
  "object",
@@ -15336,7 +15361,7 @@
15336
15361
  "/api/used-by": {
15337
15362
  "post": {
15338
15363
  "summary": "Grant or revoke the 'used by' logo consent",
15339
- "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.",
15364
+ "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.",
15340
15365
  "tags": [
15341
15366
  "Telemetry"
15342
15367
  ],
@@ -15353,6 +15378,26 @@
15353
15378
  "properties": {
15354
15379
  "granted": {
15355
15380
  "type": "boolean"
15381
+ },
15382
+ "contactEmail": {
15383
+ "type": "string",
15384
+ "maxLength": 320,
15385
+ "format": "email"
15386
+ },
15387
+ "contactFunction": {
15388
+ "type": "string",
15389
+ "enum": [
15390
+ "engineer",
15391
+ "eng_lead",
15392
+ "founder",
15393
+ "product",
15394
+ "ops",
15395
+ "other"
15396
+ ]
15397
+ },
15398
+ "quote": {
15399
+ "type": "string",
15400
+ "maxLength": 280
15356
15401
  }
15357
15402
  },
15358
15403
  "required": [
@@ -15386,7 +15431,7 @@
15386
15431
  }
15387
15432
  },
15388
15433
  "400": {
15389
- "description": "Organization name or website is not set (a revoke falls back to the identity of the last grant)",
15434
+ "description": "Invalid contact details, or missing organization name or website",
15390
15435
  "content": {
15391
15436
  "application/json": {
15392
15437
  "schema": {
@@ -15431,7 +15476,7 @@
15431
15476
  "/api/ranking": {
15432
15477
  "get": {
15433
15478
  "summary": "Public ranking state",
15434
- "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. The listing carries the private ranking claim, so only a config admin can read it.",
15479
+ "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.",
15435
15480
  "tags": [
15436
15481
  "Telemetry"
15437
15482
  ],
@@ -15491,9 +15536,6 @@
15491
15536
  1
15492
15537
  ]
15493
15538
  },
15494
- "claim": {
15495
- "type": "string"
15496
- },
15497
15539
  "name": {
15498
15540
  "type": "string"
15499
15541
  },
@@ -15535,7 +15577,6 @@
15535
15577
  },
15536
15578
  "required": [
15537
15579
  "v",
15538
- "claim",
15539
15580
  "name",
15540
15581
  "domain",
15541
15582
  "category",
@@ -15548,18 +15589,6 @@
15548
15589
  "listingMatchesDomain": {
15549
15590
  "type": "boolean"
15550
15591
  },
15551
- "fileJson": {
15552
- "type": [
15553
- "string",
15554
- "null"
15555
- ]
15556
- },
15557
- "prUrl": {
15558
- "type": [
15559
- "string",
15560
- "null"
15561
- ]
15562
- },
15563
15592
  "verifyUrl": {
15564
15593
  "type": [
15565
15594
  "string",
@@ -15576,8 +15605,6 @@
15576
15605
  "domain",
15577
15606
  "listing",
15578
15607
  "listingMatchesDomain",
15579
- "fileJson",
15580
- "prUrl",
15581
15608
  "verifyUrl"
15582
15609
  ]
15583
15610
  }
@@ -15610,7 +15637,7 @@
15610
15637
  "/api/ranking/opt-in": {
15611
15638
  "post": {
15612
15639
  "summary": "Opt this install into the public ranking",
15613
- "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. The install is ranked only after a ranking PR with the returned file is merged into desplega-ai/agent-swarm.",
15640
+ "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.",
15614
15641
  "tags": [
15615
15642
  "Telemetry"
15616
15643
  ],
@@ -15673,9 +15700,6 @@
15673
15700
  "domain": {
15674
15701
  "type": "string"
15675
15702
  },
15676
- "claim": {
15677
- "type": "string"
15678
- },
15679
15703
  "listing": {
15680
15704
  "type": "object",
15681
15705
  "properties": {
@@ -15685,9 +15709,6 @@
15685
15709
  1
15686
15710
  ]
15687
15711
  },
15688
- "claim": {
15689
- "type": "string"
15690
- },
15691
15712
  "name": {
15692
15713
  "type": "string"
15693
15714
  },
@@ -15729,7 +15750,6 @@
15729
15750
  },
15730
15751
  "required": [
15731
15752
  "v",
15732
- "claim",
15733
15753
  "name",
15734
15754
  "domain",
15735
15755
  "category",
@@ -15739,22 +15759,13 @@
15739
15759
  "updatedAt"
15740
15760
  ]
15741
15761
  },
15742
- "fileJson": {
15743
- "type": "string"
15744
- },
15745
- "prUrl": {
15746
- "type": "string"
15747
- },
15748
15762
  "verifyUrl": {
15749
15763
  "type": "string"
15750
15764
  }
15751
15765
  },
15752
15766
  "required": [
15753
15767
  "domain",
15754
- "claim",
15755
15768
  "listing",
15756
- "fileJson",
15757
- "prUrl",
15758
15769
  "verifyUrl"
15759
15770
  ]
15760
15771
  }
@@ -15807,7 +15818,7 @@
15807
15818
  "/api/ranking/opt-out": {
15808
15819
  "post": {
15809
15820
  "summary": "Leave the public ranking",
15810
- "description": "Turns PUBLIC_RANKING_ENABLED off and sends ranking.opted_out, so workers stop sending ranking.usage. Returns the GitHub page that deletes the ranking entry.",
15821
+ "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.",
15811
15822
  "tags": [
15812
15823
  "Telemetry"
15813
15824
  ],
@@ -15829,17 +15840,10 @@
15829
15840
  "enum": [
15830
15841
  false
15831
15842
  ]
15832
- },
15833
- "leaveUrl": {
15834
- "type": [
15835
- "string",
15836
- "null"
15837
- ]
15838
15843
  }
15839
15844
  },
15840
15845
  "required": [
15841
- "enabled",
15842
- "leaveUrl"
15846
+ "enabled"
15843
15847
  ]
15844
15848
  }
15845
15849
  }
@@ -54216,7 +54220,8 @@
54216
54220
  "type": "string",
54217
54221
  "enum": [
54218
54222
  "banner",
54219
- "settings"
54223
+ "settings",
54224
+ "benchmark"
54220
54225
  ],
54221
54226
  "default": "banner"
54222
54227
  }
@@ -55323,7 +55328,7 @@
55323
55328
  "/api/fleet-benchmark": {
55324
55329
  "get": {
55325
55330
  "summary": "Compare this swarm with similar swarms",
55326
- "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.",
55331
+ "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.",
55327
55332
  "tags": [
55328
55333
  "Telemetry"
55329
55334
  ],
@@ -55350,6 +55355,41 @@
55350
55355
  "unavailable"
55351
55356
  ]
55352
55357
  },
55358
+ "level": {
55359
+ "type": "string",
55360
+ "enum": [
55361
+ "basic",
55362
+ "full"
55363
+ ]
55364
+ },
55365
+ "peerGroup": {
55366
+ "type": [
55367
+ "object",
55368
+ "null"
55369
+ ],
55370
+ "properties": {
55371
+ "teamSize": {
55372
+ "type": [
55373
+ "string",
55374
+ "null"
55375
+ ]
55376
+ },
55377
+ "useCase": {
55378
+ "type": [
55379
+ "string",
55380
+ "null"
55381
+ ]
55382
+ },
55383
+ "orgs": {
55384
+ "type": "integer"
55385
+ }
55386
+ },
55387
+ "required": [
55388
+ "teamSize",
55389
+ "useCase",
55390
+ "orgs"
55391
+ ]
55392
+ },
55353
55393
  "local": {
55354
55394
  "type": [
55355
55395
  "object",
@@ -55455,6 +55495,8 @@
55455
55495
  },
55456
55496
  "required": [
55457
55497
  "state",
55498
+ "level",
55499
+ "peerGroup",
55458
55500
  "local",
55459
55501
  "minFinishedTasks",
55460
55502
  "generatedAt",
@@ -55744,7 +55786,8 @@
55744
55786
  "minLength": 1
55745
55787
  },
55746
55788
  "email": {
55747
- "type": "string"
55789
+ "type": "string",
55790
+ "format": "email"
55748
55791
  },
55749
55792
  "role": {
55750
55793
  "type": "string"
@@ -56524,7 +56567,11 @@
56524
56567
  "minLength": 1
56525
56568
  },
56526
56569
  "email": {
56527
- "type": "string"
56570
+ "type": [
56571
+ "string",
56572
+ "null"
56573
+ ],
56574
+ "format": "email"
56528
56575
  },
56529
56576
  "role": {
56530
56577
  "type": "string"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@desplega.ai/agent-swarm",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "description": "agent-swarm.dev — an open-source operating system for AI work: a lead agent breaks goals into tasks, routes them to specialized workers such as Claude Code or Codex, runs each worker in an isolated container, and preserves shared memory, tools, schedules, and review gates so delegated work compounds across sessions.",
5
5
  "keywords": [
6
6
  "agent-swarm.dev",
@@ -200,7 +200,7 @@
200
200
  "dependencies": {
201
201
  "@agentclientprotocol/sdk": "1.9.0",
202
202
  "@ai-sdk/openai": "^3.0.41",
203
- "@anthropic-ai/claude-agent-sdk": "0.3.302",
203
+ "@anthropic-ai/claude-agent-sdk": "0.3.303",
204
204
  "@anthropic-ai/sdk": "^0.135.0",
205
205
  "@asteasolutions/zod-to-openapi": "^8.0.0",
206
206
  "@aws-sdk/client-bedrock": "3.1048.0",
@@ -0,0 +1,146 @@
1
+ import { normalizeAssetKey } from "../assets/key";
2
+ import { getLeadAgent } from "../be/db";
3
+ import { getScriptById } from "../be/scripts/db";
4
+ import { getSavedScriptOwnerAgentId, runSavedScriptAsAgent } from "../be/scripts/run-saved";
5
+ import { resolveTemplate } from "../prompts/resolver";
6
+ import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
7
+ import { scrubObject } from "../utils/secret-scrubber";
8
+ import type { AppActionCaller } from "./action-caller";
9
+ import type { AppRecord } from "./store";
10
+ import { runAppSync } from "./sync";
11
+
12
+ /**
13
+ * One `error` string for a sync result, or undefined when every pass succeeded.
14
+ * Each failed pair is named so a multi-pair sync says which one broke; the pass
15
+ * list in `result.passes` carries the rest.
16
+ */
17
+ function syncPassError(sync: Awaited<ReturnType<typeof runAppSync>>): string | undefined {
18
+ const failed = sync.passes.filter((pass) => pass.error !== undefined);
19
+ if (failed.length === 0) return undefined;
20
+ return failed.map((pass) => `${pass.model}.${pass.source}: ${pass.error}`).join("; ");
21
+ }
22
+
23
+ export async function runAppAction({
24
+ app,
25
+ name,
26
+ input = {},
27
+ caller,
28
+ actor,
29
+ }: {
30
+ app: AppRecord;
31
+ name: string;
32
+ input?: Record<string, unknown>;
33
+ caller: AppActionCaller;
34
+ actor: string;
35
+ }) {
36
+ const actions = app.definition.actions;
37
+ if (!actions || !Object.hasOwn(actions, name)) {
38
+ return { status: 404 as const, body: { error: "app action not found" } };
39
+ }
40
+
41
+ const action = actions[name]!;
42
+
43
+ if (action.kind === "script") {
44
+ const script = await getScriptById(action.scriptId);
45
+ if (!script) {
46
+ return {
47
+ status: 400 as const,
48
+ body: {
49
+ error: "invalid app action",
50
+ issues: [
51
+ {
52
+ path: `actions.${name}.scriptId`,
53
+ message: `script "${action.scriptId}" no longer exists`,
54
+ },
55
+ ],
56
+ },
57
+ };
58
+ }
59
+
60
+ // Script actions use the owner's bindings. Definition validation checks
61
+ // which scripts an agent can wire into an app.
62
+ const runAsAgentId = getSavedScriptOwnerAgentId(script);
63
+ if (!runAsAgentId) {
64
+ return {
65
+ status: 400 as const,
66
+ body: { error: "agentId is required: this script has no owning agent to run as" },
67
+ };
68
+ }
69
+
70
+ const output = await runSavedScriptAsAgent({
71
+ script,
72
+ input: { ...action.args, ...input, app: { id: app.id }, caller },
73
+ agentId: runAsAgentId,
74
+ });
75
+ const ok = output.exitCode === 0 && !output.error && !output.runtimeError;
76
+ const error = ok
77
+ ? undefined
78
+ : output.runtimeError
79
+ ? `${output.runtimeError.name}: ${output.runtimeError.message}`
80
+ : (output.error ?? `Script exited with code ${output.exitCode}`);
81
+ return {
82
+ status: 200 as const,
83
+ body: scrubObject({
84
+ ok,
85
+ result: output.result,
86
+ stdout: output.stdout,
87
+ ...(error === undefined ? {} : { error }),
88
+ durationMs: output.durationMs,
89
+ }),
90
+ };
91
+ }
92
+
93
+ if (action.kind === "sync") {
94
+ const startedAt = Date.now();
95
+ const sync = await runAppSync({
96
+ appId: app.id,
97
+ ...(action.model === undefined ? {} : { model: action.model }),
98
+ ...(action.source === undefined ? {} : { source: action.source }),
99
+ invokedBy: actor,
100
+ });
101
+ const durationMs = Date.now() - startedAt;
102
+ if (sync.issues && sync.issues.length > 0) {
103
+ return { status: 400 as const, body: { error: "invalid sync action", issues: sync.issues } };
104
+ }
105
+ // Deliberately the SCRIPT-action response shape, minus `taskId`:
106
+ // app-surface.tsx branches on `taskId` first, so omitting it gives the
107
+ // dashboard running -> ok/error + refetchAll() with zero UI changes.
108
+ const error = syncPassError(sync);
109
+ return {
110
+ status: 200 as const,
111
+ body: scrubObject({
112
+ ok: sync.ok,
113
+ result: { passes: sync.passes },
114
+ ...(error === undefined ? {} : { error }),
115
+ durationMs,
116
+ }),
117
+ };
118
+ }
119
+
120
+ const lead = action.agentId ? null : await getLeadAgent();
121
+ const taskPrompt = resolveTemplate("task.app.action", {
122
+ prompt: action.prompt,
123
+ app_id: app.id,
124
+ action_name: name,
125
+ input_json: JSON.stringify(input),
126
+ caller_json: JSON.stringify(caller),
127
+ });
128
+ const task = await createTaskWithSiblingAwareness(
129
+ taskPrompt.text,
130
+ {
131
+ source: "api",
132
+ agentId: action.agentId ?? lead?.id,
133
+ routingReason: action.agentId ? "human_pinned" : lead ? "skill" : undefined,
134
+ routingSource: action.agentId || lead ? "engine_default" : undefined,
135
+ // App-spawned tasks group under the app's asset namespace so a
136
+ // swarm-tasks source can pull them back via config.assetKey.
137
+ key: normalizeAssetKey(`shared/app:${app.id}/action:${name}/`),
138
+ ...(actor.startsWith("user:") ? { requestedByUserId: actor.slice("user:".length) } : {}),
139
+ },
140
+ { origin: "app" },
141
+ );
142
+ return {
143
+ status: 200 as const,
144
+ body: { ok: true as const, taskId: task.id, status: task.status },
145
+ };
146
+ }
@@ -109,7 +109,7 @@ function indexKeys(model: string, definition: ModelDef, row: AppRow): string[] {
109
109
  return keys;
110
110
  }
111
111
 
112
- function validValue(column: ColumnDef, value: unknown): boolean {
112
+ export function validValue(column: ColumnDef, value: unknown): boolean {
113
113
  if (value === null) return column.required !== true;
114
114
  if (column.kind === "string") return typeof value === "string";
115
115
  if (column.kind === "number") return typeof value === "number" && Number.isFinite(value);
@@ -526,18 +526,20 @@ export async function rebuildAppColumnIndexUnlocked(
526
526
  }
527
527
  }
528
528
 
529
+ /** `matches` re-checks the row under the lock; a row that no longer matches is kept. */
529
530
  export function deleteAppRow(
530
531
  appId: string,
531
532
  model: string,
532
533
  _definition: ModelDef,
533
534
  rowId: string,
535
+ matches?: (row: AppRow) => boolean,
534
536
  ): Promise<boolean> {
535
537
  return withMutationLock(appId, model, async () => {
536
538
  const currentDefinition = await currentModelDefinition(appId, model);
537
539
  if (!currentDefinition) return false;
538
540
  const namespace = appsNamespace(appId);
539
541
  const row = await getAppRow(appId, model, rowId);
540
- if (!row) return false;
542
+ if (!row || (matches && !matches(row))) return false;
541
543
  for (const key of indexKeys(model, currentDefinition, row)) await deleteKv(namespace, key);
542
544
  await deleteKv(namespace, rowKey(model, rowId));
543
545
  return true;
@@ -206,7 +206,7 @@ function renderApp(app: AppRecord, namespace: string): string {
206
206
  export namespace ${namespace} {
207
207
  ${renderedModels.map((model) => model.source).join("\n\n")}
208
208
 
209
- /** Declared actions. Invocation is REST-only: POST /api/apps/<id>/actions/<name>. */
209
+ /** Scripts can write rows. Declared actions are callable through REST or MCP only. */
210
210
  export type ActionName = ${actionType};
211
211
 
212
212
  /** Script action arguments include server-stamped app and caller context. */
package/src/be/db.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  import { realtimeBus } from "../realtime/bus";
11
11
  import { slackChannelFromContextKey } from "../tasks/slack-routing";
12
12
  import { _resolveIntegrationType, emitIntegrationConnected, telemetry } from "../telemetry";
13
- import { scheduleOrgDomainRecompute } from "../telemetry-identity";
13
+ import { invalidateTelemetryUserRole, scheduleOrgDomainRecompute } from "../telemetry-identity";
14
14
  import {
15
15
  emitTaskTelemetry,
16
16
  type TaskTelemetryEvent,
@@ -12354,7 +12354,7 @@ export async function updateUser(
12354
12354
  id: string,
12355
12355
  data: Partial<{
12356
12356
  name: string;
12357
- email: string;
12357
+ email: string | null;
12358
12358
  role: string;
12359
12359
  notes: string;
12360
12360
  emailAliases: string[];
@@ -12419,7 +12419,12 @@ export async function updateUser(
12419
12419
  `UPDATE users SET ${sets.join(", ")} WHERE id = ? RETURNING *`,
12420
12420
  params,
12421
12421
  );
12422
- if (row) getDbClient().afterCommit(scheduleOrgDomainRecompute);
12422
+ if (row) {
12423
+ getDbClient().afterCommit(() => {
12424
+ invalidateTelemetryUserRole(id);
12425
+ scheduleOrgDomainRecompute();
12426
+ });
12427
+ }
12423
12428
  return row ? rowToUser(row) : null;
12424
12429
  }
12425
12430
 
@@ -12556,7 +12561,12 @@ export async function deleteUser(id: string, replacementUserId?: string): Promis
12556
12561
  await reclassifyTaskHumanFree(reclassifySeedIds);
12557
12562
 
12558
12563
  const result = await tx.run("DELETE FROM users WHERE id = ?", [id]);
12559
- if (result.changes > 0) getDbClient().afterCommit(scheduleOrgDomainRecompute);
12564
+ if (result.changes > 0) {
12565
+ getDbClient().afterCommit(() => {
12566
+ invalidateTelemetryUserRole(id);
12567
+ scheduleOrgDomainRecompute();
12568
+ });
12569
+ }
12560
12570
  return result.changes > 0;
12561
12571
  });
12562
12572
  }
@@ -5,15 +5,20 @@
5
5
  * registered agent count (the same count `org.snapshot` reports as
6
6
  * `agents_total`).
7
7
  *
8
- * Null when that start is unknown: no accepted event is recorded yet, or the
9
- * earliest one is inside the window.
8
+ * Comparisons require a known ingest start. With telemetry off, callers can
9
+ * use the local 30-day window without an ingest anchor.
10
10
  */
11
- import { fleetCountStart, type LocalFleetStats } from "../fleet-benchmark";
11
+ import { FLEET_WINDOW_DAYS, fleetCountStart, type LocalFleetStats } from "../fleet-benchmark";
12
12
  import { getDbClient } from "./db/runtime";
13
13
  import { getTelemetryIngestAnchor } from "./telemetry-ingest-anchor";
14
14
 
15
- export async function getLocalFleetStats(now = new Date()): Promise<LocalFleetStats | null> {
16
- const start = fleetCountStart(await getTelemetryIngestAnchor(), now);
15
+ export async function getLocalFleetStats(
16
+ now = new Date(),
17
+ { requireIngestAnchor = true } = {},
18
+ ): Promise<LocalFleetStats | null> {
19
+ const start = requireIngestAnchor
20
+ ? fleetCountStart(await getTelemetryIngestAnchor(), now)
21
+ : new Date(now.getTime() - FLEET_WINDOW_DAYS * 86_400_000);
17
22
  if (!start) return null;
18
23
  const since = start.toISOString();
19
24
  const client = getDbClient();
@@ -27,6 +27,10 @@ export const CATALOG_PROVIDER_IDS = [
27
27
  "openai",
28
28
  "amazon-bedrock",
29
29
  "opencode",
30
+ // OpenCode Go is opencode's first-party subscription provider, a sibling of
31
+ // its Zen section (`opencode`). models.dev lists it, so projecting it gives
32
+ // `opencode-go/<model>` ids their context window and reasoning facts.
33
+ "opencode-go",
30
34
  ...openAICompatibleProviderIds(),
31
35
  ] as const;
32
36
 
@@ -498,6 +498,12 @@ function deriveState(
498
498
  if (state.steps.connect.status !== "done") {
499
499
  changed = completeStep(state, "connect", "api_key", true, events) || changed;
500
500
  }
501
+ // A name saved anywhere (setup, the org-name popup, Settings, the API) names
502
+ // the swarm. The old setup default "Your Swarm" does not count.
503
+ const orgName = process.env.SWARM_ORG_NAME?.trim();
504
+ if (state.steps.name.status !== "done" && orgName && orgName !== "Your Swarm") {
505
+ changed = completeStep(state, "name", "custom_name", true, events) || changed;
506
+ }
501
507
  if (
502
508
  state.steps.ai.status !== "done" &&
503
509
  signals.providers.some((entry) => entry.state === "verified")
@@ -419,6 +419,19 @@ export interface SwarmSdk {
419
419
  query: string;
420
420
  params?: Record<string, string | number | boolean>;
421
421
  }): Promise<unknown>;
422
+ app_rows_list(args: {
423
+ appId: string;
424
+ model: string;
425
+ equals?: Record<string, string | number | boolean>;
426
+ sort?: \`\${string}:asc\` | \`\${string}:desc\`;
427
+ limit?: number;
428
+ }): Promise<unknown>;
429
+ app_row_get(args: { appId: string; model: string; rowId: string }): Promise<unknown>;
430
+ app_row_create(args: { appId: string; model: string; values: Record<string, unknown> }): Promise<unknown>;
431
+ app_rows_create(args: { appId: string; model: string; rows: Record<string, unknown>[] }): Promise<unknown>;
432
+ app_row_patch(args: { appId: string; model: string; rowId: string; values: Record<string, unknown> }): Promise<unknown>;
433
+ app_row_delete(args: { appId: string; model: string; rowId: string }): Promise<unknown>;
434
+ app_rows_delete(args: { appId: string; model: string; equals: Record<string, string | number | boolean>; limit?: number }): Promise<unknown>;
422
435
  app_rollback(args: {
423
436
  appId: string;
424
437
  version: number;