@desplega.ai/agent-swarm 2.7.2 → 2.7.3

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 (220) hide show
  1. package/README.md +6 -0
  2. package/dist/{acp-adapter-624yge7a.js → acp-adapter-pdzw76ja.js} +1 -1
  3. package/dist/{actions-rd9xn281.js → actions-a90cfpca.js} +21 -21
  4. package/dist/{amp-adapter-k27hc00p.js → amp-adapter-4vtywcjk.js} +2 -2
  5. package/dist/{app-sjm3xc7g.js → app-8k68gygk.js} +3 -3
  6. package/dist/{assistant-6jbzs8wa.js → assistant-ck01q2yg.js} +32 -21
  7. package/dist/{boot-reembed-e2348eqc.js → boot-reembed-33spavpd.js} +1 -1
  8. package/dist/{boot-reembed-w53pd5xt.js → boot-reembed-xzge83a2.js} +1 -1
  9. package/dist/{boot-scrub-sweep-cpy4yg97.js → boot-scrub-sweep-mxvb8dr3.js} +3 -3
  10. package/dist/{boot-seeds-f6j4t6k6.js → boot-seeds-rpm8bb5r.js} +4 -4
  11. package/dist/{claude-adapter-v3ac8esd.js → claude-adapter-q12bcfj9.js} +3 -3
  12. package/dist/{claude-managed-setup-yyrkqvtm.js → claude-managed-setup-11vb6q89.js} +1 -1
  13. package/dist/{claude-sdk-session-85j69v6d.js → claude-sdk-session-gkv1107j.js} +10325 -10243
  14. package/dist/{cli-rs59z1qx.js → cli-07pcqwaj.js} +1 -1
  15. package/dist/{cli-6vq3ymqm.js → cli-0tkm519n.js} +1 -1
  16. package/dist/{cli-s7cm1927.js → cli-0xzbk52h.js} +4 -4
  17. package/dist/{cli-g0zc1b40.js → cli-0zae6bs6.js} +2 -2
  18. package/dist/{cli-p8yrsmfn.js → cli-10kjx0dm.js} +1 -1
  19. package/dist/{cli-7xsq63pe.js → cli-157ykn37.js} +10 -9
  20. package/dist/{cli-vjcj9k2p.js → cli-2h0rfmqk.js} +1 -1
  21. package/dist/{cli-9wasax9f.js → cli-35nzvqaf.js} +1 -1
  22. package/dist/{cli-e1p7dqe1.js → cli-4aydmmdy.js} +24 -3
  23. package/dist/{cli-942pp3bc.js → cli-54ax65rn.js} +3 -3
  24. package/dist/{cli-10cp92df.js → cli-5ke7gxwr.js} +6 -6
  25. package/dist/{cli-femw4q81.js → cli-6151a7dr.js} +2 -2
  26. package/dist/{cli-3cywb44q.js → cli-77eq8txs.js} +85 -3
  27. package/dist/{cli-atpfk4yd.js → cli-7cwrwb5x.js} +5 -5
  28. package/dist/{cli-bfbpvccx.js → cli-8a1k6mat.js} +61 -49
  29. package/dist/{cli-c18941tx.js → cli-8ch3b2r7.js} +3 -3
  30. package/dist/{cli-rs2s1t05.js → cli-9fk56y8c.js} +4 -4
  31. package/dist/{cli-0wx63vvw.js → cli-9km88w11.js} +1 -1
  32. package/dist/{cli-qy0z3mfj.js → cli-9vttw9nz.js} +56 -46
  33. package/dist/{cli-mzgppb7q.js → cli-a0w50zw9.js} +4 -4
  34. package/dist/{cli-z8a4mfz1.js → cli-aqp59jmv.js} +4 -4
  35. package/dist/{cli-5d8wd50e.js → cli-bs0fwb23.js} +1 -1
  36. package/dist/{cli-fa4kxxx3.js → cli-ct5920jb.js} +2 -2
  37. package/dist/{cli-0awbtmv2.js → cli-dek8hqjc.js} +10 -10
  38. package/dist/{cli-qp6adcmw.js → cli-e24w2mvk.js} +1 -1
  39. package/dist/{cli-t3npd8hm.js → cli-ghfhn1qc.js} +1 -1
  40. package/dist/{cli-jmcf8cnz.js → cli-ja369zab.js} +1 -1
  41. package/dist/{cli-wtjg6je3.js → cli-jhh7tbx6.js} +1 -1
  42. package/dist/{cli-e19bgatr.js → cli-jmk31131.js} +2 -2
  43. package/dist/{cli-2cj8fqv1.js → cli-kjcdzea8.js} +1 -1
  44. package/dist/{cli-1xpr5992.js → cli-kjha1q9k.js} +3 -3
  45. package/dist/{cli-9jfty8px.js → cli-ktcdczth.js} +538 -30
  46. package/dist/{cli-exd52qn4.js → cli-mcmpbpcc.js} +2 -2
  47. package/dist/{cli-2m2p3snc.js → cli-mmf43vcp.js} +6 -6
  48. package/dist/{cli-b2rhbaty.js → cli-pnm99spe.js} +10 -10
  49. package/dist/{cli-9jn9cj0q.js → cli-v9jk2k63.js} +1 -1
  50. package/dist/{cli-mt7bxjfd.js → cli-vhgcwppn.js} +1 -1
  51. package/dist/{cli-qa4hgda3.js → cli-vvz64zc5.js} +14 -13
  52. package/dist/{cli-txr0z97c.js → cli-wmhajw68.js} +171 -32
  53. package/dist/{cli-hzrmv8pg.js → cli-x38rmhap.js} +3 -3
  54. package/dist/{cli-hq9hd4hp.js → cli-xz6qjdqj.js} +1 -1
  55. package/dist/{cli-g9ypd5en.js → cli-z52gq55b.js} +4 -0
  56. package/dist/{cli-2qbmjfta.js → cli-z6219sd3.js} +4 -4
  57. package/dist/cli.js +19 -19
  58. package/dist/{codex-adapter-zm1bkdde.js → codex-adapter-46kbvvkx.js} +1 -1
  59. package/dist/{codex-hook-tkag7fxq.js → codex-hook-tm9ecrd5.js} +1 -1
  60. package/dist/{codex-session-runner-8k02mqw2.js → codex-session-runner-qy93hdrv.js} +1 -1
  61. package/dist/{commands-nw1dn4av.js → commands-hwdhacfx.js} +2 -2
  62. package/dist/{controller-3kkkmps0.js → controller-hjza5p1f.js} +3 -3
  63. package/dist/{controllers-connect-n2c4167r.js → controllers-connect-7m25cay2.js} +20 -20
  64. package/dist/{cursor-adapter-h5028emw.js → cursor-adapter-1qh7ps8w.js} +1 -1
  65. package/dist/{db-76y2msnf.js → db-w23k9301.js} +2 -2
  66. package/dist/{db-8rk0p8j1.js → db-wm8by383.js} +3 -3
  67. package/dist/{dispatcher-xp4wsx30.js → dispatcher-en25ebgc.js} +18 -18
  68. package/dist/{dsh-adapter-vyrr31bn.js → dsh-adapter-zkme32vd.js} +1 -1
  69. package/dist/{e2b-eesfj689.js → e2b-9z9mvt2p.js} +1 -1
  70. package/dist/{e2b-03nha1wz.js → e2b-yhvn70es.js} +1 -1
  71. package/dist/{embedding-space-ejefyep1.js → embedding-space-rgf4aypt.js} +6 -6
  72. package/dist/{embeddings-w297xaw0.js → embeddings-z9cmxhx1.js} +1 -1
  73. package/dist/{external-channel-mf9vp1fe.js → external-channel-g6vv85zv.js} +2 -2
  74. package/dist/{grok-adapter-mpzyyprz.js → grok-adapter-f6n1tpq3.js} +2 -2
  75. package/dist/handlers-ss5948rq.js +42 -0
  76. package/dist/{heartbeat-qnaycjzs.js → heartbeat-5xcdzz45.js} +21 -21
  77. package/dist/{hook-h6sqfzxr.js → hook-6423rme3.js} +2 -2
  78. package/dist/{hook-4zqdgea4.js → hook-8dk1csdd.js} +4 -4
  79. package/dist/{http-65ts210h.js → http-6mfh2ww9.js} +255 -60
  80. package/dist/{index-sqe7f6mj.js → index-0js6bgff.js} +1 -1
  81. package/dist/{index-farm1zsn.js → index-0ng1dd1t.js} +10937 -9527
  82. package/dist/{index-k3ewf1jg.js → index-5eyn1wc5.js} +18 -18
  83. package/dist/{index-vp4wa3qn.js → index-71gnebsn.js} +18 -18
  84. package/dist/{index-hdfad00b.js → index-q5gzpjdd.js} +12 -10
  85. package/dist/index-z3b1vfjx.js +44 -0
  86. package/dist/{keepalive-52k6skha.js → keepalive-cymckeg6.js} +3 -3
  87. package/dist/{lead-y9rj0gr1.js → lead-baknj2dh.js} +29 -29
  88. package/dist/{maintenance-f86wqfq6.js → maintenance-dfrnsx3n.js} +1 -1
  89. package/dist/{oauth-refresh-sweep-vddv9ky7.js → oauth-refresh-sweep-5dztk8x6.js} +2 -2
  90. package/dist/{onboard-8z2qk6gq.js → onboard-h36vja6y.js} +11 -6
  91. package/dist/{otel-impl-epet9j18.js → otel-impl-jerq8vz8.js} +1 -1
  92. package/dist/{pricing-refresh-6wm42a51.js → pricing-refresh-t3nx5krn.js} +3 -3
  93. package/dist/{rbac-roles-2kfcs1sc.js → rbac-roles-dw1awawd.js} +2 -2
  94. package/dist/{rbac-roles-13a0vj6d.js → rbac-roles-km0fc3e8.js} +1 -1
  95. package/dist/{registry-ajhexe44.js → registry-hzfh268v.js} +1 -1
  96. package/dist/{render-v2-cy8zsfsf.js → render-v2-65x4ayj8.js} +3 -3
  97. package/dist/{resume-c783qe6b.js → resume-61znk3hj.js} +6 -6
  98. package/dist/{session-access-s4tv0az8.js → session-access-4yc9htrg.js} +4 -4
  99. package/dist/{session-source-backfill-qzapz0ne.js → session-source-backfill-kr5bdjtp.js} +2 -2
  100. package/dist/{setup-st7htx1k.js → setup-9h61yr2p.js} +1 -1
  101. package/dist/{work-objects-ftj4zw70.js → work-objects-x7rb53qn.js} +27 -22
  102. package/dist/{worker-859fk25k.js → worker-7f3khgn0.js} +29 -29
  103. package/openapi.json +134 -3
  104. package/package.json +5 -5
  105. package/src/be/db/agents.ts +16 -1
  106. package/src/be/db/runtime.ts +25 -15
  107. package/src/be/db/session-summaries.ts +120 -0
  108. package/src/be/db/tasks/read.ts +7 -4
  109. package/src/be/db/tasks/search-index.ts +159 -0
  110. package/src/be/db/tasks/write.ts +3 -0
  111. package/src/be/db-queries/slack-inbound.ts +77 -0
  112. package/src/be/db.ts +27 -8
  113. package/src/be/mcp-overflow-sweep.ts +39 -0
  114. package/src/be/migrations/253_session_summaries.sql +300 -0
  115. package/src/be/migrations/254_query_audit_indexes.sql +25 -0
  116. package/src/be/migrations/256_agent_slack_icon.sql +2 -0
  117. package/src/be/migrations/257_agent_tasks_fts.sql +55 -0
  118. package/src/comb/config.ts +6 -2
  119. package/src/comb/requester.ts +69 -0
  120. package/src/comb/review-batch.ts +24 -2
  121. package/src/commands/onboard/agent-fs-version.ts +1 -1
  122. package/src/commands/onboard/compose-generator.ts +7 -2
  123. package/src/commands/onboard/env-generator.ts +8 -3
  124. package/src/commands/runner.ts +41 -24
  125. package/src/fs/agent-fs-provider.ts +9 -0
  126. package/src/heartbeat/heartbeat.ts +13 -6
  127. package/src/http/agents.ts +10 -2
  128. package/src/http/index.ts +15 -0
  129. package/src/http/integrations.ts +20 -0
  130. package/src/http/list-total-cache.ts +62 -0
  131. package/src/http/sessions.ts +9 -2
  132. package/src/http/slack-inbound.ts +1 -0
  133. package/src/http/status.ts +4 -1
  134. package/src/http/tasks.ts +12 -2
  135. package/src/providers/amp-adapter.ts +1 -1
  136. package/src/providers/cursor-adapter.ts +1 -1
  137. package/src/providers/dsh-adapter.ts +1 -1
  138. package/src/queue-stall-alarm.ts +4 -1
  139. package/src/slack/actions.ts +2 -2
  140. package/src/slack/app-icon.ts +50 -0
  141. package/src/slack/app.ts +13 -1
  142. package/src/slack/assistant.ts +22 -1
  143. package/src/slack/event-dedup.test.ts +11 -0
  144. package/src/slack/event-dedup.ts +8 -4
  145. package/src/slack/inbound-dispatch.ts +58 -0
  146. package/src/slack/render-v2.ts +7 -8
  147. package/src/slack/responses.ts +21 -8
  148. package/src/slack/socket-ack.ts +237 -0
  149. package/src/slack/thread-buffer.ts +3 -5
  150. package/src/slack/watcher.ts +2 -2
  151. package/src/slack/work-objects.ts +5 -1
  152. package/src/tests/agent-fs-release-notes.test.ts +40 -0
  153. package/src/tests/agent-slack-icon.test.ts +85 -0
  154. package/src/tests/approval-cancellation-migration.test.ts +2 -0
  155. package/src/tests/asset-key-migration.test.ts +26 -0
  156. package/src/tests/comb-default.test.ts +159 -0
  157. package/src/tests/comb-review-batch.test.ts +192 -4
  158. package/src/tests/comb-status.test.ts +65 -2
  159. package/src/tests/db-agents.test.ts +13 -0
  160. package/src/tests/db-tasks-read.test.ts +10 -15
  161. package/src/tests/deploy-one-box.test.ts +161 -0
  162. package/src/tests/deploy-stubs.ts +68 -0
  163. package/src/tests/docs-index-footprint.test.js +3 -3
  164. package/src/tests/dsh-adapter.test.ts +1 -1
  165. package/src/tests/e2e-codex-auth-chain.test.ts +18 -1
  166. package/src/tests/fixtures/deploy-stubs/bun +3 -0
  167. package/src/tests/fixtures/deploy-stubs/bunx +27 -0
  168. package/src/tests/fixtures/deploy-stubs/curl +26 -0
  169. package/src/tests/fixtures/deploy-stubs/docker +122 -0
  170. package/src/tests/fixtures/onboard/content.compose.yml.txt +5 -5
  171. package/src/tests/fixtures/onboard/dev.compose.yml.txt +4 -4
  172. package/src/tests/fixtures/onboard/full-all.compose.yml.txt +12 -12
  173. package/src/tests/fixtures/onboard/full.compose.yml.txt +12 -12
  174. package/src/tests/fixtures/onboard/research.compose.yml.txt +4 -4
  175. package/src/tests/fixtures/onboard/solo.compose.yml.txt +2 -2
  176. package/src/tests/heartbeat-expiry.test.ts +151 -25
  177. package/src/tests/http-api-integration.test.ts +31 -0
  178. package/src/tests/human-free-reclassify.test.ts +6 -0
  179. package/src/tests/install-sh-bind.test.ts +242 -0
  180. package/src/tests/install-sh-tunnel.test.ts +89 -0
  181. package/src/tests/kv-storage.test.ts +28 -0
  182. package/src/tests/list-endpoint-slimming.test.ts +3 -0
  183. package/src/tests/list-total-cache.test.ts +63 -0
  184. package/src/tests/mcp-overflow-sweep.test.ts +84 -0
  185. package/src/tests/onboard-compose.test.ts +17 -3
  186. package/src/tests/onboard-env.test.ts +13 -0
  187. package/src/tests/onboarding.test.ts +1 -1
  188. package/src/tests/preload.ts +1 -0
  189. package/src/tests/query-audit-plans.test.ts +314 -0
  190. package/src/tests/script-connections.test.ts +6 -0
  191. package/src/tests/session-summaries.test.ts +384 -0
  192. package/src/tests/sessions-tree.test.ts +5 -1
  193. package/src/tests/slack-app-icon.test.ts +90 -0
  194. package/src/tests/slack-external-channel-egress.test.ts +1 -1
  195. package/src/tests/slack-inbound-receipts.test.ts +34 -0
  196. package/src/tests/slack-inline-output.test.ts +29 -2
  197. package/src/tests/slack-render-v2-query-plans.test.ts +3 -2
  198. package/src/tests/slack-render-v2.test.ts +35 -0
  199. package/src/tests/slack-socket-ack.test.ts +553 -0
  200. package/src/tests/task-lineage.test.ts +3 -0
  201. package/src/tests/task-search-filter.test.ts +4 -4
  202. package/src/tests/task-search-fts.test.ts +320 -0
  203. package/src/tests/update-profile-auth.test.ts +28 -0
  204. package/src/tools/get-swarm.ts +1 -0
  205. package/src/tools/get-tasks.ts +6 -1
  206. package/src/tools/join-swarm.ts +1 -0
  207. package/src/tools/my-agent-info.ts +1 -0
  208. package/src/tools/slack-post.ts +2 -2
  209. package/src/tools/slack-reply.ts +2 -2
  210. package/src/tools/slack-start-thread.ts +2 -2
  211. package/src/tools/slack-update.ts +1 -1
  212. package/src/tools/update-profile.ts +20 -4
  213. package/src/tools/utils.ts +3 -4
  214. package/src/types.ts +47 -0
  215. package/src/utils/constants.ts +127 -5
  216. package/templates/official/lead/HEARTBEAT.md +4 -2
  217. package/templates/official/lead/config.json +2 -2
  218. package/templates/skills/swarm-docs/content.md +2 -0
  219. package/dist/handlers-7x8sywgr.js +0 -42
  220. package/dist/index-mc30r1hs.js +0 -44
@@ -36,7 +36,7 @@ services:
36
36
  - GITHUB_DISABLE=false
37
37
 
38
38
  ports:
39
- - "3999:3013"
39
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3999:3013"
40
40
 
41
41
  volumes:
42
42
  - swarm_data:/app/data
@@ -89,7 +89,7 @@ services:
89
89
  - SENTRY_ORG=${SENTRY_ORG}
90
90
 
91
91
  ports:
92
- - "3201:3000"
92
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3201:3000"
93
93
 
94
94
  volumes:
95
95
  - swarm_logs:/logs
@@ -138,7 +138,7 @@ services:
138
138
  - SENTRY_ORG=${SENTRY_ORG}
139
139
 
140
140
  ports:
141
- - "3202:3000"
141
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3202:3000"
142
142
 
143
143
  volumes:
144
144
  - swarm_logs:/logs
@@ -187,7 +187,7 @@ services:
187
187
  - SENTRY_ORG=${SENTRY_ORG}
188
188
 
189
189
  ports:
190
- - "3203:3000"
190
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3203:3000"
191
191
 
192
192
  volumes:
193
193
  - swarm_logs:/logs
@@ -236,7 +236,7 @@ services:
236
236
  - SENTRY_ORG=${SENTRY_ORG}
237
237
 
238
238
  ports:
239
- - "3204:3000"
239
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3204:3000"
240
240
 
241
241
  volumes:
242
242
  - swarm_logs:/logs
@@ -285,7 +285,7 @@ services:
285
285
  - SENTRY_ORG=${SENTRY_ORG}
286
286
 
287
287
  ports:
288
- - "3205:3000"
288
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3205:3000"
289
289
 
290
290
  volumes:
291
291
  - swarm_logs:/logs
@@ -334,7 +334,7 @@ services:
334
334
  - SENTRY_ORG=${SENTRY_ORG}
335
335
 
336
336
  ports:
337
- - "3206:3000"
337
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3206:3000"
338
338
 
339
339
  volumes:
340
340
  - swarm_logs:/logs
@@ -383,7 +383,7 @@ services:
383
383
  - SENTRY_ORG=${SENTRY_ORG}
384
384
 
385
385
  ports:
386
- - "3207:3000"
386
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3207:3000"
387
387
 
388
388
  volumes:
389
389
  - swarm_logs:/logs
@@ -432,7 +432,7 @@ services:
432
432
  - SENTRY_ORG=${SENTRY_ORG}
433
433
 
434
434
  ports:
435
- - "3208:3000"
435
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3208:3000"
436
436
 
437
437
  volumes:
438
438
  - swarm_logs:/logs
@@ -481,7 +481,7 @@ services:
481
481
  - SENTRY_ORG=${SENTRY_ORG}
482
482
 
483
483
  ports:
484
- - "3209:3000"
484
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3209:3000"
485
485
 
486
486
  volumes:
487
487
  - swarm_logs:/logs
@@ -530,7 +530,7 @@ services:
530
530
  - SENTRY_ORG=${SENTRY_ORG}
531
531
 
532
532
  ports:
533
- - "3210:3000"
533
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3210:3000"
534
534
 
535
535
  volumes:
536
536
  - swarm_logs:/logs
@@ -579,7 +579,7 @@ services:
579
579
  - SENTRY_ORG=${SENTRY_ORG}
580
580
 
581
581
  ports:
582
- - "3211:3000"
582
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3211:3000"
583
583
 
584
584
  volumes:
585
585
  - swarm_logs:/logs
@@ -32,7 +32,7 @@ services:
32
32
  - APP_URL=https://app.agent-swarm.dev
33
33
 
34
34
  ports:
35
- - "3013:3013"
35
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3013:3013"
36
36
 
37
37
  volumes:
38
38
  - swarm_data:/app/data
@@ -73,7 +73,7 @@ services:
73
73
  - MAX_CONCURRENT_TASKS=2
74
74
 
75
75
  ports:
76
- - "3201:3000"
76
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3201:3000"
77
77
 
78
78
  volumes:
79
79
  - swarm_logs:/logs
@@ -109,7 +109,7 @@ services:
109
109
  - MAX_CONCURRENT_TASKS=1
110
110
 
111
111
  ports:
112
- - "3202:3000"
112
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3202:3000"
113
113
 
114
114
  volumes:
115
115
  - swarm_logs:/logs
@@ -145,7 +145,7 @@ services:
145
145
  - MAX_CONCURRENT_TASKS=1
146
146
 
147
147
  ports:
148
- - "3203:3000"
148
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3203:3000"
149
149
 
150
150
  volumes:
151
151
  - swarm_logs:/logs
@@ -181,7 +181,7 @@ services:
181
181
  - MAX_CONCURRENT_TASKS=1
182
182
 
183
183
  ports:
184
- - "3204:3000"
184
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3204:3000"
185
185
 
186
186
  volumes:
187
187
  - swarm_logs:/logs
@@ -217,7 +217,7 @@ services:
217
217
  - MAX_CONCURRENT_TASKS=1
218
218
 
219
219
  ports:
220
- - "3205:3000"
220
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3205:3000"
221
221
 
222
222
  volumes:
223
223
  - swarm_logs:/logs
@@ -253,7 +253,7 @@ services:
253
253
  - MAX_CONCURRENT_TASKS=1
254
254
 
255
255
  ports:
256
- - "3206:3000"
256
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3206:3000"
257
257
 
258
258
  volumes:
259
259
  - swarm_logs:/logs
@@ -289,7 +289,7 @@ services:
289
289
  - MAX_CONCURRENT_TASKS=1
290
290
 
291
291
  ports:
292
- - "3207:3000"
292
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3207:3000"
293
293
 
294
294
  volumes:
295
295
  - swarm_logs:/logs
@@ -325,7 +325,7 @@ services:
325
325
  - MAX_CONCURRENT_TASKS=1
326
326
 
327
327
  ports:
328
- - "3208:3000"
328
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3208:3000"
329
329
 
330
330
  volumes:
331
331
  - swarm_logs:/logs
@@ -361,7 +361,7 @@ services:
361
361
  - MAX_CONCURRENT_TASKS=1
362
362
 
363
363
  ports:
364
- - "3209:3000"
364
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3209:3000"
365
365
 
366
366
  volumes:
367
367
  - swarm_logs:/logs
@@ -397,7 +397,7 @@ services:
397
397
  - MAX_CONCURRENT_TASKS=1
398
398
 
399
399
  ports:
400
- - "3210:3000"
400
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3210:3000"
401
401
 
402
402
  volumes:
403
403
  - swarm_logs:/logs
@@ -433,7 +433,7 @@ services:
433
433
  - MAX_CONCURRENT_TASKS=1
434
434
 
435
435
  ports:
436
- - "3211:3000"
436
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3211:3000"
437
437
 
438
438
  volumes:
439
439
  - swarm_logs:/logs
@@ -32,7 +32,7 @@ services:
32
32
  - APP_URL=https://app.agent-swarm.dev
33
33
 
34
34
  ports:
35
- - "3013:3013"
35
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3013:3013"
36
36
 
37
37
  volumes:
38
38
  - swarm_data:/app/data
@@ -73,7 +73,7 @@ services:
73
73
  - MAX_CONCURRENT_TASKS=2
74
74
 
75
75
  ports:
76
- - "3201:3000"
76
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3201:3000"
77
77
 
78
78
  volumes:
79
79
  - swarm_logs:/logs
@@ -109,7 +109,7 @@ services:
109
109
  - MAX_CONCURRENT_TASKS=1
110
110
 
111
111
  ports:
112
- - "3202:3000"
112
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3202:3000"
113
113
 
114
114
  volumes:
115
115
  - swarm_logs:/logs
@@ -145,7 +145,7 @@ services:
145
145
  - MAX_CONCURRENT_TASKS=1
146
146
 
147
147
  ports:
148
- - "3203:3000"
148
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3203:3000"
149
149
 
150
150
  volumes:
151
151
  - swarm_logs:/logs
@@ -32,7 +32,7 @@ services:
32
32
  - APP_URL=https://app.agent-swarm.dev
33
33
 
34
34
  ports:
35
- - "3013:3013"
35
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3013:3013"
36
36
 
37
37
  volumes:
38
38
  - swarm_data:/app/data
@@ -73,7 +73,7 @@ services:
73
73
  - MAX_CONCURRENT_TASKS=1
74
74
 
75
75
  ports:
76
- - "3201:3000"
76
+ - "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3201:3000"
77
77
 
78
78
  volumes:
79
79
  - swarm_logs:/logs
@@ -8,10 +8,11 @@ import {
8
8
  initDb,
9
9
  updateAgentProfile,
10
10
  } from "../be/db";
11
- import { handleAgentsRest } from "../http/agents";
11
+ import { handleAgentRegister, handleAgentsRest } from "../http/agents";
12
12
  import { getPathSegments, parseQueryParams } from "../http/utils";
13
13
  import { HeartbeatExpiryError, validateHeartbeatExpiry } from "../utils/heartbeat-expiry";
14
14
  import { listenOnFreePort } from "./test-net";
15
+ import { CHILD_PROCESS_TEST_BUDGET_MS, expectChildOk, runChild } from "./test-proc";
15
16
 
16
17
  const section = (...lines: string[]) => `## Tracked Items\n${lines.join("\n")}\n`;
17
18
 
@@ -143,33 +144,158 @@ describe("profile persistence heartbeat gate", () => {
143
144
  await getDbClient().query("SELECT * FROM context_versions WHERE agentId = ?", [agentId]),
144
145
  ).toEqual(versionsBefore);
145
146
  });
146
- test("HTTP returns 400 with the offending line", async () => {
147
- const server = createServer(async (req, res) => {
147
+ test.each(["saved", "HTTP 400", "network failure"] as const)(
148
+ "first-boot runner handles profile %s and continues initializing",
149
+ async (outcome) => {
150
+ const leadId = crypto.randomUUID();
151
+ const secret = "profile-save-test-credential-value";
152
+ expect(await getAgentById(leadId)).toBeFalsy();
153
+ const templateFile = (name: string) =>
154
+ Bun.file(new URL(`../../templates/official/lead/${name}`, import.meta.url)).text();
155
+ const template = {
156
+ config: await Bun.file(
157
+ new URL("../../templates/official/lead/config.json", import.meta.url),
158
+ ).json(),
159
+ files: {
160
+ soulMd: await templateFile("SOUL.md"),
161
+ identityMd: await templateFile("IDENTITY.md"),
162
+ claudeMd: await templateFile("CLAUDE.md"),
163
+ toolsMd: "",
164
+ setupScript: "",
165
+ heartbeatMd:
166
+ outcome === "HTTP 400"
167
+ ? section("- New undated HTTP item")
168
+ : await templateFile("HEARTBEAT.md"),
169
+ },
170
+ };
171
+ let profileBefore: Awaited<ReturnType<typeof getAgentById>>;
172
+ const profileStatuses: number[] = [];
173
+ const server = createServer(async (req, res) => {
174
+ try {
175
+ const path = new URL(req.url ?? "/", "http://localhost").pathname;
176
+ const json = (data: unknown) => {
177
+ res.writeHead(200, { "Content-Type": "application/json" });
178
+ res.end(JSON.stringify(data));
179
+ };
180
+ if (path === "/api/templates/official/lead") return json(template);
181
+ if (path === "/api/config/resolved") return json({ configs: [] });
182
+ if (path === "/api/fs/agent-credentials") return json({ enabled: false });
183
+ if (path.startsWith("/api/active-sessions/")) return json({ recovered: 0 });
184
+ if (path === "/me") {
185
+ profileBefore = await getAgentById(leadId);
186
+ return json(profileBefore);
187
+ }
188
+ if (path === `/api/agents/${leadId}/profile` && req.method === "PUT") {
189
+ res.once("finish", () => profileStatuses.push(res.statusCode));
190
+ }
191
+ if (path === `/api/agents/${leadId}/mcp-servers`) return json({ servers: [] });
192
+ if (await handleAgentRegister(req, res, getPathSegments(req.url ?? "/"), leadId)) return;
193
+ if (
194
+ await handleAgentsRest(
195
+ req,
196
+ res,
197
+ getPathSegments(req.url ?? "/"),
198
+ parseQueryParams(req.url ?? "/"),
199
+ leadId,
200
+ )
201
+ )
202
+ return;
203
+ res.writeHead(404).end();
204
+ } catch {
205
+ res.writeHead(500).end();
206
+ }
207
+ });
208
+ const port = await listenOnFreePort(server);
209
+ const dir = `${process.env.TMPDIR ?? "/tmp"}/runner-profile-boot-${leadId}`;
210
+ await Bun.$`mkdir -p ${dir}`.quiet();
148
211
  try {
149
- await handleAgentsRest(
150
- req,
151
- res,
152
- getPathSegments(req.url ?? "/"),
153
- parseQueryParams(req.url ?? "/"),
154
- agentId,
212
+ await Bun.write(
213
+ `${dir}/boot.ts`,
214
+ `
215
+ import { mock } from "bun:test";
216
+ const fs = { ...await import("node:fs/promises") };
217
+ // A fresh template cache, without touching the host's /workspace.
218
+ const isolated = (fn) => (...args) => {
219
+ if (String(args[0]).startsWith("/workspace")) throw new Error("No fixture workspace");
220
+ return fn(...args);
221
+ };
222
+ mock.module("node:fs/promises", () => ({
223
+ ...fs, stat: isolated(fs.stat), mkdir: isolated(fs.mkdir),
224
+ }));
225
+ const request = globalThis.fetch;
226
+ globalThis.fetch = Object.assign(async (...args) => {
227
+ const path = new URL(args[0] instanceof Request ? args[0].url : args[0]).pathname;
228
+ if (${JSON.stringify(outcome)} === "network failure" && path === "/api/agents/${leadId}/profile") {
229
+ throw new TypeError("fetch failed: " + process.env.AGENT_SWARM_API_KEY);
230
+ }
231
+ const response = await request(...args);
232
+ // This request follows profile initialization and precedes workspace writes.
233
+ if (new URL(response.url).pathname === "/api/agents/${leadId}/mcp-servers") {
234
+ console.log("Boot identity phase completed");
235
+ process.exit(0);
236
+ }
237
+ return response;
238
+ }, { preconnect: request.preconnect });
239
+ const { runAgent } = await import(${JSON.stringify(new URL("../commands/runner.ts", import.meta.url).pathname)});
240
+ await runAgent({ role: "lead", defaultPrompt: "/start-leader", metadataType: "lead_metadata" },
241
+ { logsDir: ${JSON.stringify(`${dir}/logs`)} });
242
+ `,
243
+ );
244
+ const child = expectChildOk(
245
+ await runChild([process.execPath, `${dir}/boot.ts`], {
246
+ cwd: dir,
247
+ env: {
248
+ ...process.env,
249
+ AGENT_ID: leadId,
250
+ AGENT_NAME: "Fresh lead",
251
+ AGENT_SWARM_API_KEY: secret,
252
+ MCP_BASE_URL: `http://localhost:${port}`,
253
+ TEMPLATE_ID: "official/lead",
254
+ TEMPLATE_REGISTRY_URL: `http://localhost:${port}`,
255
+ HARNESS_PROVIDER: "claude",
256
+ CRED_CHECK_DISABLE: "1",
257
+ ANONYMIZED_TELEMETRY: "false",
258
+ BUSINESS_USE_API_KEY: "",
259
+ OTEL_SDK_DISABLED: "true",
260
+ DESPLEGA_TELEMETRY_ENV: "test",
261
+ HOME: dir,
262
+ PATH: dir,
263
+ },
264
+ }),
265
+ "first-boot lead runner",
155
266
  );
156
- } catch {
157
- res.writeHead(500).end();
267
+ expect(child.stdout).toContain("Boot identity phase completed");
268
+ const saved = await getAgentById(leadId);
269
+ expect(saved).toBeTruthy();
270
+ expect(child.stdout + child.stderr).not.toContain(secret);
271
+ if (outcome === "saved") {
272
+ expect(profileStatuses).toEqual([200]);
273
+ expect(saved?.soulMd).toContain("# SOUL.md — Fresh lead");
274
+ expect(saved?.identityMd).toContain("# IDENTITY.md — Fresh lead");
275
+ expect(saved?.claudeMd).toBeTruthy();
276
+ expect(saved?.toolsMd).toBeTruthy();
277
+ expect(saved?.heartbeatMd).toBe(template.files.heartbeatMd);
278
+ expect(child.stdout).toContain("[lead] Generated and saved default identity templates");
279
+ } else {
280
+ expect(saved).toEqual(profileBefore);
281
+ expect(child.stdout).not.toContain("Generated and saved default identity templates");
282
+ expect(child.stderr).toContain("Could not save generated templates to server");
283
+ if (outcome === "HTTP 400") {
284
+ expect(profileStatuses).toEqual([400]);
285
+ expect(child.stderr).toContain("HTTP 400");
286
+ expect(child.stderr).toContain("New undated HTTP item");
287
+ } else {
288
+ expect(profileStatuses).toEqual([]);
289
+ expect(child.stderr).toContain("fetch failed");
290
+ }
291
+ }
292
+ } finally {
293
+ await new Promise<void>((resolve) => server.close(() => resolve()));
294
+ await Bun.$`rm -rf ${dir}`.quiet();
158
295
  }
159
- });
160
- const port = await listenOnFreePort(server);
161
- try {
162
- const response = await fetch(`http://localhost:${port}/api/agents/${agentId}/profile`, {
163
- method: "PUT",
164
- headers: { "Content-Type": "application/json" },
165
- body: JSON.stringify({ heartbeatMd: section("- New undated HTTP item") }),
166
- });
167
- expect(response.status).toBe(400);
168
- expect((await response.json()).error).toContain("New undated HTTP item");
169
- } finally {
170
- await new Promise<void>((resolve) => server.close(() => resolve()));
171
- }
172
- });
296
+ },
297
+ CHILD_PROCESS_TEST_BUDGET_MS,
298
+ );
173
299
  test("persists edits, dated additions, pin durables, and deletions", async () => {
174
300
  const next = section("- **Legacy:** Revised", "- Dated DELETE on 09-30", "📌 Durable");
175
301
  expect((await updateAgentProfile(agentId, { heartbeatMd: next }))?.heartbeatMd).toBe(next);
@@ -308,6 +308,37 @@ describe("Agents", () => {
308
308
  expect(rejected.status).toBe(400);
309
309
  });
310
310
 
311
+ test("PUT /api/agents/:id/profile — sets, validates and resets slackIcon", async () => {
312
+ const path = `/api/agents/${ids.workerAgent}/profile`;
313
+ const owl = { type: "url", url: "https://example.com/owl.png" };
314
+ const set = await put(path, { body: { slackIcon: owl } });
315
+ expect(set.status).toBe(200);
316
+ expect(set.body.slackIcon).toEqual(owl);
317
+
318
+ for (const slackIcon of [
319
+ { type: "emoji", emoji: "owl" },
320
+ { type: "url", url: "http://example.com/owl.png" },
321
+ { type: "url", url: "https://user:pass@example.com/owl.png" },
322
+ { type: "url", url: "https://" },
323
+ ]) {
324
+ expect((await put(path, { body: { slackIcon } })).status).toBe(400);
325
+ }
326
+
327
+ const app = await put(path, { body: { slackIcon: { type: "app" } } });
328
+ expect(app.status).toBe(200);
329
+ expect(app.body.slackIcon).toEqual({ type: "app" });
330
+
331
+ const reset = await put(path, { body: { slackIcon: null } });
332
+ expect(reset.status).toBe(200);
333
+ expect(reset.body.slackIcon).toBeNull();
334
+ });
335
+
336
+ test("GET /api/integrations/slack/app-icon — null with Slack off", async () => {
337
+ const { status, body } = await get("/api/integrations/slack/app-icon");
338
+ expect(status).toBe(200);
339
+ expect(body).toEqual({ imageUrl: null });
340
+ });
341
+
311
342
  test("PUT /api/agents/:id/profile — persists structured filesystem sync rejections", async () => {
312
343
  const current = "s".repeat(SOUL_MD_MAX_CHARS);
313
344
  const accepted = await put(`/api/agents/${ids.workerAgent}/profile`, {
@@ -625,6 +625,9 @@ describe("human-free flag stays correct when a classifying input changes", () =>
625
625
  const third = await task("cycle third", { parentTaskId: second });
626
626
  // Nothing in the product writes a cycle, but the rule (a UNION over ids)
627
627
  // is defined on one, so the reclassification must stay total on it.
628
+ // Product code never rewrites parentTaskId, and migration 253 guards it;
629
+ // this cycle fixture needs the guard off.
630
+ await getDbClient().run("DROP TRIGGER IF EXISTS session_task_parent_immutable");
628
631
  await getDbClient().run("UPDATE agent_tasks SET parentTaskId = ? WHERE id = ?", [third, first]);
629
632
  await getDbClient().run("UPDATE agent_tasks SET tags = ? WHERE id = ?", [
630
633
  JSON.stringify(["heartbeat"]),
@@ -642,6 +645,9 @@ describe("human-free flag stays correct when a classifying input changes", () =>
642
645
  test("a parent cycle longer than one batch stops re-queuing itself", async () => {
643
646
  const ring: string[] = [await task("ring 0")];
644
647
  for (let i = 1; i < 6; i++) ring.push(await task(`ring ${i}`, { parentTaskId: ring[i - 1] }));
648
+ // Product code never rewrites parentTaskId, and migration 253 guards it;
649
+ // this cycle fixture needs the guard off.
650
+ await getDbClient().run("DROP TRIGGER IF EXISTS session_task_parent_immutable");
645
651
  await getDbClient().run("UPDATE agent_tasks SET parentTaskId = ? WHERE id = ?", [
646
652
  ring[5],
647
653
  ring[0],