@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
@@ -2,18 +2,22 @@ import {
2
2
  getAgentById2,
3
3
  getTaskById2,
4
4
  init_db3
5
- } from "./cli-txr0z97c.js";
5
+ } from "./cli-wmhajw68.js";
6
6
  import {
7
+ isAdmittedSlackDelivery,
7
8
  slackTaskOutput,
8
9
  getTaskUrl
9
- } from "./cli-9jfty8px.js";
10
+ } from "./cli-ktcdczth.js";
11
+ import {
12
+ wasEventSeen
13
+ } from "./cli-157ykn37.js";
10
14
  import {
11
15
  shouldBlockChannel2
12
- } from "./cli-6vq3ymqm.js";
13
- import"./cli-qy0z3mfj.js";
14
- import"./cli-b2rhbaty.js";
15
- import"./cli-10cp92df.js";
16
- import"./cli-t3npd8hm.js";
16
+ } from "./cli-0tkm519n.js";
17
+ import"./cli-9vttw9nz.js";
18
+ import"./cli-pnm99spe.js";
19
+ import"./cli-5ke7gxwr.js";
20
+ import"./cli-ghfhn1qc.js";
17
21
  import"./cli-3ggwdtb0.js";
18
22
  import"./cli-e8xyjvfc.js";
19
23
  import"./cli-n0ysjjtv.js";
@@ -22,28 +26,27 @@ import"./cli-kvzgak7c.js";
22
26
  import"./cli-445c1vp7.js";
23
27
  import"./cli-jbgrwv4y.js";
24
28
  import"./cli-eny54typ.js";
25
- import"./cli-1xpr5992.js";
26
- import"./cli-qp6adcmw.js";
27
- import"./cli-p8yrsmfn.js";
28
- import"./cli-e19bgatr.js";
29
+ import"./cli-kjha1q9k.js";
30
+ import"./cli-e24w2mvk.js";
31
+ import"./cli-10kjx0dm.js";
32
+ import"./cli-jmk31131.js";
29
33
  import"./cli-qz93y3x5.js";
30
- import"./cli-jmcf8cnz.js";
31
- import"./cli-mzgppb7q.js";
32
- import"./cli-g9ypd5en.js";
33
- import"./cli-2qbmjfta.js";
34
- import"./cli-s7cm1927.js";
34
+ import"./cli-ja369zab.js";
35
+ import"./cli-a0w50zw9.js";
36
+ import"./cli-z52gq55b.js";
37
+ import"./cli-z6219sd3.js";
38
+ import"./cli-0xzbk52h.js";
35
39
  import"./cli-3h26v7yj.js";
36
40
  import"./cli-vpq0n13a.js";
37
- import"./cli-942pp3bc.js";
38
- import"./cli-7xsq63pe.js";
41
+ import"./cli-54ax65rn.js";
39
42
  import {
40
43
  isUserAllowed2
41
- } from "./cli-2m2p3snc.js";
44
+ } from "./cli-mmf43vcp.js";
42
45
  import"./cli-13p25grh.js";
43
46
  import"./cli-0adyhvq2.js";
44
- import"./cli-9jn9cj0q.js";
47
+ import"./cli-v9jk2k63.js";
45
48
  import"./cli-rycdaj0a.js";
46
- import"./cli-2cj8fqv1.js";
49
+ import"./cli-kjcdzea8.js";
47
50
  import {
48
51
  isEnvFlagEnabled,
49
52
  init_env_flag
@@ -125,9 +128,11 @@ async function getDetails(event, client) {
125
128
  };
126
129
  }
127
130
  function registerWorkObjectHandlers(app) {
128
- app.event("entity_details_requested", async ({ event, client, logger }) => {
131
+ app.event("entity_details_requested", async ({ event, body, client, logger }) => {
129
132
  if (!isEnvFlagEnabled("SLACK_WORK_OBJECTS_ENABLED", false))
130
133
  return;
134
+ if (!isAdmittedSlackDelivery() && wasEventSeen(body?.event_id))
135
+ return;
131
136
  let details;
132
137
  try {
133
138
  details = await getDetails(event, client);
@@ -1,69 +1,69 @@
1
1
  import"./cli-qz93y3x5.js";
2
2
  import {
3
3
  getEnabledCapabilities
4
- } from "./cli-qy0z3mfj.js";
5
- import"./cli-txr0z97c.js";
6
- import"./cli-jmcf8cnz.js";
7
- import"./cli-mzgppb7q.js";
8
- import"./cli-p8yrsmfn.js";
9
- import"./cli-e19bgatr.js";
10
- import"./cli-qp6adcmw.js";
4
+ } from "./cli-9vttw9nz.js";
5
+ import"./cli-wmhajw68.js";
6
+ import"./cli-ja369zab.js";
7
+ import"./cli-a0w50zw9.js";
8
+ import"./cli-10kjx0dm.js";
9
+ import"./cli-jmk31131.js";
10
+ import"./cli-e24w2mvk.js";
11
11
  import"./cli-eny54typ.js";
12
- import"./cli-b2rhbaty.js";
13
- import"./cli-10cp92df.js";
14
- import"./cli-t3npd8hm.js";
12
+ import"./cli-pnm99spe.js";
13
+ import"./cli-5ke7gxwr.js";
14
+ import"./cli-ghfhn1qc.js";
15
15
  import"./cli-3ggwdtb0.js";
16
16
  import"./cli-e8xyjvfc.js";
17
- import"./cli-9jfty8px.js";
18
- import"./cli-6vq3ymqm.js";
17
+ import"./cli-ktcdczth.js";
18
+ import"./cli-0tkm519n.js";
19
19
  import"./cli-n0ysjjtv.js";
20
20
  import"./cli-2nf3b08t.js";
21
21
  import"./cli-kvzgak7c.js";
22
22
  import"./cli-445c1vp7.js";
23
23
  import"./cli-jbgrwv4y.js";
24
- import"./cli-1xpr5992.js";
25
- import"./cli-s7cm1927.js";
26
- import"./cli-g9ypd5en.js";
27
- import"./cli-2qbmjfta.js";
24
+ import"./cli-kjha1q9k.js";
25
+ import"./cli-0xzbk52h.js";
26
+ import"./cli-z52gq55b.js";
27
+ import"./cli-z6219sd3.js";
28
28
  import"./cli-3h26v7yj.js";
29
29
  import"./cli-vpq0n13a.js";
30
30
  import"./cli-8wd3mbyj.js";
31
31
  import"./cli-vqgnyh76.js";
32
32
  import"./cli-77cd6gy8.js";
33
- import"./cli-femw4q81.js";
33
+ import"./cli-6151a7dr.js";
34
34
  import {
35
35
  runAgent
36
- } from "./cli-bfbpvccx.js";
36
+ } from "./cli-8a1k6mat.js";
37
37
  import"./cli-4887xyd1.js";
38
38
  import"./cli-9cctx1tn.js";
39
39
  import"./cli-cr3k93hd.js";
40
40
  import"./cli-jc1g9ndz.js";
41
- import"./cli-fa4kxxx3.js";
42
- import"./cli-vjcj9k2p.js";
41
+ import"./cli-ct5920jb.js";
42
+ import"./cli-2h0rfmqk.js";
43
43
  import"./cli-5xbma5yx.js";
44
44
  import"./cli-d82btycs.js";
45
45
  import"./cli-0726awxw.js";
46
- import"./cli-z8a4mfz1.js";
47
- import"./cli-c18941tx.js";
46
+ import"./cli-aqp59jmv.js";
47
+ import"./cli-8ch3b2r7.js";
48
48
  import"./cli-4bd1rck6.js";
49
49
  import"./cli-h3e7bhvn.js";
50
50
  import"./cli-prcyzecg.js";
51
51
  import"./cli-dehgvj25.js";
52
52
  import"./cli-kr5tjjtz.js";
53
- import"./cli-exd52qn4.js";
54
- import"./cli-rs2s1t05.js";
53
+ import"./cli-mcmpbpcc.js";
54
+ import"./cli-9fk56y8c.js";
55
55
  import"./cli-9y354g3j.js";
56
- import"./cli-g0zc1b40.js";
57
- import"./cli-hq9hd4hp.js";
56
+ import"./cli-0zae6bs6.js";
57
+ import"./cli-xz6qjdqj.js";
58
58
  import"./cli-0n39ee9n.js";
59
59
  import"./cli-5z6xz9xs.js";
60
- import"./cli-rs59z1qx.js";
60
+ import"./cli-07pcqwaj.js";
61
61
  import"./cli-4zsenjhy.js";
62
62
  import"./cli-13p25grh.js";
63
63
  import"./cli-0adyhvq2.js";
64
- import"./cli-9jn9cj0q.js";
64
+ import"./cli-v9jk2k63.js";
65
65
  import"./cli-rycdaj0a.js";
66
- import"./cli-2cj8fqv1.js";
66
+ import"./cli-kjcdzea8.js";
67
67
 
68
68
  // src/commands/worker.ts
69
69
  var workerConfig = {
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.7.2",
5
+ "version": "2.7.3",
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": [
@@ -1016,6 +1016,64 @@
1016
1016
  "reportedAt"
1017
1017
  ]
1018
1018
  },
1019
+ "AgentSlackIcon": {
1020
+ "oneOf": [
1021
+ {
1022
+ "type": "object",
1023
+ "properties": {
1024
+ "type": {
1025
+ "type": "string",
1026
+ "enum": [
1027
+ "emoji"
1028
+ ]
1029
+ },
1030
+ "emoji": {
1031
+ "type": "string",
1032
+ "maxLength": 102,
1033
+ "pattern": "^:[a-z0-9_+'-]+:$"
1034
+ }
1035
+ },
1036
+ "required": [
1037
+ "type",
1038
+ "emoji"
1039
+ ]
1040
+ },
1041
+ {
1042
+ "type": "object",
1043
+ "properties": {
1044
+ "type": {
1045
+ "type": "string",
1046
+ "enum": [
1047
+ "url"
1048
+ ]
1049
+ },
1050
+ "url": {
1051
+ "type": "string",
1052
+ "maxLength": 2048,
1053
+ "format": "uri"
1054
+ }
1055
+ },
1056
+ "required": [
1057
+ "type",
1058
+ "url"
1059
+ ]
1060
+ },
1061
+ {
1062
+ "type": "object",
1063
+ "properties": {
1064
+ "type": {
1065
+ "type": "string",
1066
+ "enum": [
1067
+ "app"
1068
+ ]
1069
+ }
1070
+ },
1071
+ "required": [
1072
+ "type"
1073
+ ]
1074
+ }
1075
+ ]
1076
+ },
1019
1077
  "Agent": {
1020
1078
  "type": "object",
1021
1079
  "properties": {
@@ -1189,6 +1247,16 @@
1189
1247
  }
1190
1248
  ]
1191
1249
  },
1250
+ "slackIcon": {
1251
+ "oneOf": [
1252
+ {
1253
+ "$ref": "#/components/schemas/AgentSlackIcon"
1254
+ },
1255
+ {
1256
+ "type": "null"
1257
+ }
1258
+ ]
1259
+ },
1192
1260
  "createdAt": {
1193
1261
  "type": "string",
1194
1262
  "format": "date-time"
@@ -11804,6 +11872,16 @@
11804
11872
  }
11805
11873
  ]
11806
11874
  },
11875
+ "slackIcon": {
11876
+ "oneOf": [
11877
+ {
11878
+ "$ref": "#/components/schemas/AgentSlackIcon"
11879
+ },
11880
+ {
11881
+ "type": "null"
11882
+ }
11883
+ ]
11884
+ },
11807
11885
  "changeSource": {
11808
11886
  "type": "string"
11809
11887
  },
@@ -23619,6 +23697,42 @@
23619
23697
  }
23620
23698
  }
23621
23699
  },
23700
+ "/api/integrations/slack/app-icon": {
23701
+ "get": {
23702
+ "summary": "The Slack app's own avatar, shown on posts with no icon override",
23703
+ "tags": [
23704
+ "Integrations"
23705
+ ],
23706
+ "security": [
23707
+ {
23708
+ "bearerAuth": []
23709
+ }
23710
+ ],
23711
+ "responses": {
23712
+ "200": {
23713
+ "description": "`imageUrl` is null when Slack is off or the lookup fails.",
23714
+ "content": {
23715
+ "application/json": {
23716
+ "schema": {
23717
+ "type": "object",
23718
+ "properties": {
23719
+ "imageUrl": {
23720
+ "type": [
23721
+ "string",
23722
+ "null"
23723
+ ]
23724
+ }
23725
+ },
23726
+ "required": [
23727
+ "imageUrl"
23728
+ ]
23729
+ }
23730
+ }
23731
+ }
23732
+ }
23733
+ }
23734
+ }
23735
+ },
23622
23736
  "/api/integrations/chat/status": {
23623
23737
  "get": {
23624
23738
  "summary": "Chat SDK channel status: enabled flag, Slack clash, per-adapter health and webhook URL.",
@@ -30586,7 +30700,7 @@
30586
30700
  "/status": {
30587
30701
  "get": {
30588
30702
  "summary": "Identity + setup readiness + live activity for the swarm dashboard",
30589
- "description": "Single source of truth consumed by the UI home page. Identity comes from SWARM_* envs; setup milestones each emit `unverified | configured | verified`; automations report `running | needs_setup` from the same runtime preflight used at dispatch; activity counts agents alive in the last 5 min, leads that can take work (status idle or busy), and tasks created in the last 24h; agent_fs reports whether AGENT_FS_API_URL is set, plus the Comb settings (COMB_ENABLED, the browser-facing agent-fs URL, the shared org and drive ids, and the agent-fs user id of the swarm service account); telemetry reports the effective ANONYMIZED_TELEMETRY opt-out state.",
30703
+ "description": "Single source of truth consumed by the UI home page. Identity comes from SWARM_* envs; setup milestones each emit `unverified | configured | verified`; automations report `running | needs_setup` from the same runtime preflight used at dispatch; activity counts agents alive in the last 5 min, leads that can take work (status idle or busy), and tasks created in the last 24h; agent_fs reports whether AGENT_FS_API_URL is set, plus the Comb settings (whether Files is on and, when off, what is missing; the browser-facing agent-fs URL, the shared org and drive ids, and the agent-fs user id of the swarm service account); telemetry reports the effective ANONYMIZED_TELEMETRY opt-out state.",
30590
30704
  "tags": [
30591
30705
  "Status"
30592
30706
  ],
@@ -30820,6 +30934,12 @@
30820
30934
  "enabled": {
30821
30935
  "type": "boolean"
30822
30936
  },
30937
+ "reason": {
30938
+ "type": [
30939
+ "string",
30940
+ "null"
30941
+ ]
30942
+ },
30823
30943
  "api_url": {
30824
30944
  "type": [
30825
30945
  "string",
@@ -30850,6 +30970,7 @@
30850
30970
  },
30851
30971
  "required": [
30852
30972
  "enabled",
30973
+ "reason",
30853
30974
  "api_url",
30854
30975
  "live_url",
30855
30976
  "org_id",
@@ -33807,9 +33928,11 @@
33807
33928
  },
33808
33929
  {
33809
33930
  "schema": {
33810
- "type": "string"
33931
+ "type": "string",
33932
+ "description": "Search task text and title by word or phrase, with a prefix match on the last word, or a task id by prefix. Not a substring match: '2443' does not find '12443', and '%' and '_' are not wildcards."
33811
33933
  },
33812
33934
  "required": false,
33935
+ "description": "Search task text and title by word or phrase, with a prefix match on the last word, or a task id by prefix. Not a substring match: '2443' does not find '12443', and '%' and '_' are not wildcards.",
33813
33936
  "name": "search",
33814
33937
  "in": "query"
33815
33938
  },
@@ -54576,6 +54699,9 @@
54576
54699
  "uncertain": {
54577
54700
  "type": "number"
54578
54701
  },
54702
+ "ackNotReady": {
54703
+ "type": "number"
54704
+ },
54579
54705
  "lastOutcomeAt": {
54580
54706
  "type": [
54581
54707
  "string",
@@ -54606,6 +54732,7 @@
54606
54732
  "ignored",
54607
54733
  "failed",
54608
54734
  "uncertain",
54735
+ "ackNotReady",
54609
54736
  "lastOutcomeAt",
54610
54737
  "lastFailure"
54611
54738
  ]
@@ -54625,6 +54752,9 @@
54625
54752
  "uncertain": {
54626
54753
  "type": "number"
54627
54754
  },
54755
+ "ackNotReady": {
54756
+ "type": "number"
54757
+ },
54628
54758
  "lastOutcomeAt": {
54629
54759
  "type": [
54630
54760
  "string",
@@ -54655,6 +54785,7 @@
54655
54785
  "ignored",
54656
54786
  "failed",
54657
54787
  "uncertain",
54788
+ "ackNotReady",
54658
54789
  "lastOutcomeAt",
54659
54790
  "lastFailure"
54660
54791
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@desplega.ai/agent-swarm",
3
- "version": "2.7.2",
3
+ "version": "2.7.3",
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",
@@ -203,7 +203,7 @@
203
203
  "dependencies": {
204
204
  "@agentclientprotocol/sdk": "2.0.2",
205
205
  "@ai-sdk/openai": "^3.0.41",
206
- "@anthropic-ai/claude-agent-sdk": "0.3.311",
206
+ "@anthropic-ai/claude-agent-sdk": "0.3.312",
207
207
  "@anthropic-ai/sdk": "^1.3.0",
208
208
  "@asteasolutions/zod-to-openapi": "^8.0.0",
209
209
  "@aws-sdk/client-bedrock": "3.1048.0",
@@ -211,8 +211,8 @@
211
211
  "@chat-adapter/slack": "4.41.1",
212
212
  "@chat-adapter/teams": "4.41.1",
213
213
  "@chat-adapter/telegram": "4.41.1",
214
- "@cursor/sdk": "1.1.0",
215
- "@desplega.ai/agent-fs-just-bash": "0.18.11",
214
+ "@cursor/sdk": "1.2.0",
215
+ "@desplega.ai/agent-fs-just-bash": "0.18.12",
216
216
  "@desplega.ai/business-use": "^0.4.2",
217
217
  "@earendil-works/pi-agent-core": "^1.4.0",
218
218
  "@earendil-works/pi-ai": "^1.4.0",
@@ -221,7 +221,7 @@
221
221
  "@jitl/quickjs-singlefile-mjs-release-sync": "0.32.0",
222
222
  "@linear/sdk": "^77.0.0",
223
223
  "@modelcontextprotocol/sdk": "1.32.1",
224
- "@openai/codex": "0.164.0",
224
+ "@openai/codex": "0.165.0",
225
225
  "@openfort/openfort-node": "^0.9.1",
226
226
  "@opentelemetry/api": "^1.9.1",
227
227
  "@opentelemetry/api-logs": "^0.218.0",
@@ -5,12 +5,13 @@ import type {
5
5
  AgentLog,
6
6
  AgentLogEventType,
7
7
  AgentSkill,
8
+ AgentSlackIcon,
8
9
  AgentStatus,
9
10
  AgentTask,
10
11
  ProviderName,
11
12
  RoutingAffinity,
12
13
  } from "../../types";
13
- import { AgentAvatarSchema } from "../../types";
14
+ import { AgentAvatarSchema, AgentSlackIconSchema } from "../../types";
14
15
  import { isEnvFlagEnabled } from "../../utils/env-flag";
15
16
  import { getDbClient } from "./runtime";
16
17
 
@@ -63,6 +64,8 @@ export type AgentRow = {
63
64
  cred_status: string | null;
64
65
  /** Migration 119: custom avatar (JSON of AgentAvatar). NULL = deterministic hash-derived fallback. */
65
66
  avatar: string | null;
67
+ /** Migration 256: JSON of AgentSlackIcon. */
68
+ slack_icon: string | null;
66
69
  runtime_pool_id: string | null;
67
70
  runtime_provision_attempts: number;
68
71
  runtime_provision_retry_at: string | null;
@@ -81,6 +84,17 @@ function parseAgentAvatar(raw: string | null): AgentAvatar | null {
81
84
  }
82
85
  }
83
86
 
87
+ /** Malformed content falls back to `null`, like `parseAgentAvatar`. */
88
+ function parseAgentSlackIcon(raw: string | null): AgentSlackIcon | null {
89
+ if (!raw) return null;
90
+ try {
91
+ const parsed = AgentSlackIconSchema.safeParse(JSON.parse(raw));
92
+ return parsed.success ? parsed.data : null;
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+
84
98
  /**
85
99
  * Map an agent row to the `Agent` shape. When `slim` is true the six identity
86
100
  * markdown blobs (`claudeMd`/`soulMd`/`identityMd`/`toolsMd`/`heartbeatMd`/
@@ -112,6 +126,7 @@ export function rowToAgent(row: AgentRow, slim = false): Agent {
112
126
  : null,
113
127
  credStatus: row.cred_status ? (JSON.parse(row.cred_status) as AgentCredStatus) : null,
114
128
  avatar: parseAgentAvatar(row.avatar),
129
+ slackIcon: parseAgentSlackIcon(row.slack_icon),
115
130
  };
116
131
  if (slim) return base;
117
132
  return {
@@ -201,25 +201,35 @@ function bootOpenedDb(
201
201
  .all()
202
202
  .map((column) => `"${column.name.replaceAll('"', '""')}"`)
203
203
  .join(", ");
204
+ // Triggers on other tables that read agent_tasks (the session summary
205
+ // recompute, migration 253) would fail the rename's schema check while
206
+ // agent_tasks is dropped, so they are dropped and re-created too.
204
207
  const schemaObjects = database
205
- .prepare<{ sql: string }, []>(
206
- `SELECT sql FROM sqlite_master
207
- WHERE tbl_name = 'agent_tasks'
208
- AND type IN ('index', 'trigger')
208
+ .prepare<{ name: string; tbl_name: string; sql: string }, []>(
209
+ `SELECT name, tbl_name, sql FROM sqlite_master
210
+ WHERE type IN ('index', 'trigger')
209
211
  AND sql IS NOT NULL
212
+ AND (tbl_name = 'agent_tasks' OR (type = 'trigger' AND sql LIKE '%agent_tasks%'))
210
213
  ORDER BY type, name`,
211
214
  )
212
- .all()
213
- .map((row) => row.sql);
214
-
215
- database.transaction(() => {
216
- database.run("DROP TABLE IF EXISTS agent_tasks_new");
217
- database.run(rebuiltSchemaSql);
218
- database.run(`INSERT INTO agent_tasks_new (${columns}) SELECT ${columns} FROM agent_tasks`);
219
- database.run("DROP TABLE agent_tasks");
220
- database.run("ALTER TABLE agent_tasks_new RENAME TO agent_tasks");
221
- for (const sql of schemaObjects) database.run(sql);
222
- })();
215
+ .all();
216
+
217
+ database
218
+ .transaction(() => {
219
+ database.run("DROP TABLE IF EXISTS agent_tasks_new");
220
+ database.run(rebuiltSchemaSql);
221
+ // Copy rowids too: agent_tasks_fts (migration 256) is keyed by them.
222
+ database.run(
223
+ `INSERT INTO agent_tasks_new (rowid, ${columns}) SELECT rowid, ${columns} FROM agent_tasks`,
224
+ );
225
+ for (const object of schemaObjects) {
226
+ if (object.tbl_name !== "agent_tasks") database.run(`DROP TRIGGER "${object.name}"`);
227
+ }
228
+ database.run("DROP TABLE agent_tasks");
229
+ database.run("ALTER TABLE agent_tasks_new RENAME TO agent_tasks");
230
+ for (const object of schemaObjects) database.run(object.sql);
231
+ })
232
+ .immediate();
223
233
 
224
234
  database.run("PRAGMA foreign_keys=on");
225
235
  console.log("[Migration] Successfully removed CHECK constraint on agent_tasks.status");
@@ -0,0 +1,120 @@
1
+ import { getDbClient } from "./runtime";
2
+
3
+ /**
4
+ * `session_summaries` and `session_members` (migration 253) store the session
5
+ * chain walk: one row per root with its chain aggregates, and the root of
6
+ * every chain task. Triggers keep them exact on every task, steering and
7
+ * approval write. The one exception is a deleted task's descendants: a
8
+ * trigger body cannot walk the chain, so `deleteTask` calls
9
+ * {@link dropSessionDescendants} before the DELETE.
10
+ */
11
+
12
+ /**
13
+ * Drop the members rows of a task's descendants. Once the task is gone, the
14
+ * chain walk no longer reaches them, so they belong to no session. Call it in
15
+ * the delete's transaction, before the DELETE; the delete trigger then
16
+ * recomputes the chain's summary.
17
+ */
18
+ export async function dropSessionDescendants(taskId: string): Promise<void> {
19
+ // UNION, not UNION ALL: a parentTaskId cycle written before migration 253
20
+ // must not loop.
21
+ await getDbClient().run(
22
+ `WITH RECURSIVE descendant(id) AS (
23
+ SELECT id FROM agent_tasks WHERE parentTaskId = ?
24
+ UNION SELECT t.id FROM descendant d JOIN agent_tasks t ON t.parentTaskId = d.id
25
+ ) DELETE FROM session_members WHERE taskId IN (SELECT id FROM descendant)`,
26
+ [taskId],
27
+ );
28
+ }
29
+
30
+ export interface SessionSummaryDrift {
31
+ rootId: string;
32
+ /** The field that differs, or `member` for a chain task whose members row is wrong. */
33
+ field:
34
+ | "summary-missing"
35
+ | "summary-extra"
36
+ | "chainCount"
37
+ | "lastActivityAt"
38
+ | "latestStatus"
39
+ | "pendingApproval"
40
+ | "lastHumanAt"
41
+ | "member";
42
+ expected: string | number | null;
43
+ stored: string | number | null;
44
+ }
45
+
46
+ const HUMAN_TASK_SQL = `t.requestedByUserId IS NOT NULL AND t.requestedByUserIdInherited = 0
47
+ AND COALESCE(t.source, '') NOT IN ('mcp', 'system', 'schedule', 'workflow')`;
48
+
49
+ /**
50
+ * Read-only drift check: compares the stored summaries and members with a
51
+ * fresh chain walk (the rule `sessionSummaryQuery` applies today). Returns one
52
+ * row per mismatch, so an empty result means the tables are exact. With
53
+ * `since`, only roots whose chain activity is at or after it are compared.
54
+ * `latestStatus` is skipped when two chain tasks tie on the newest
55
+ * `lastUpdatedAt`: the walk then picks an arbitrary one.
56
+ */
57
+ export function sessionSummaryDriftQuery(since: string | null = null): {
58
+ sql: string;
59
+ params: (string | null)[];
60
+ } {
61
+ return {
62
+ sql: `WITH RECURSIVE chain(root_id, id) AS (
63
+ SELECT id, id FROM agent_tasks WHERE parentTaskId IS NULL
64
+ UNION ALL SELECT c.root_id, t.id FROM chain c JOIN agent_tasks t ON t.parentTaskId = c.id
65
+ ), walked AS (
66
+ SELECT c.root_id, COUNT(*) AS chainCount, t.status AS latestStatus,
67
+ MAX(t.lastUpdatedAt) AS lastActivityAt
68
+ FROM chain c JOIN agent_tasks t ON t.id = c.id GROUP BY c.root_id
69
+ ), expected AS (
70
+ SELECT w.*,
71
+ (SELECT COUNT(*) FROM chain c JOIN agent_tasks t ON t.id = c.id
72
+ WHERE c.root_id = w.root_id AND t.lastUpdatedAt = w.lastActivityAt) AS newest,
73
+ EXISTS (SELECT 1 FROM chain c JOIN approval_requests ar ON ar.sourceTaskId = c.id
74
+ WHERE c.root_id = w.root_id AND ar.status = 'pending') AS pendingApproval,
75
+ (SELECT MAX(at) FROM (
76
+ SELECT t.createdAt AS at FROM chain c JOIN agent_tasks t ON t.id = c.id
77
+ WHERE c.root_id = w.root_id AND ${HUMAN_TASK_SQL}
78
+ UNION ALL SELECT sm.created_at FROM chain c JOIN task_steering_messages sm ON sm.task_id = c.id
79
+ WHERE c.root_id = w.root_id AND sm.created_by_kind = 'user' AND sm.status != 'cancelled'
80
+ UNION ALL SELECT ar.createdAt FROM chain c JOIN approval_requests ar ON ar.sourceTaskId = c.id
81
+ WHERE c.root_id = w.root_id AND ar.status = 'pending')) AS lastHumanAt
82
+ FROM walked w WHERE (? IS NULL OR w.lastActivityAt >= ?)
83
+ ), stored AS (
84
+ SELECT * FROM session_summaries s WHERE (? IS NULL OR s.lastActivityAt >= ?)
85
+ )
86
+ SELECT e.root_id AS rootId, 'summary-missing' AS field, e.chainCount AS expected, NULL AS stored
87
+ FROM expected e LEFT JOIN session_summaries s ON s.rootId = e.root_id WHERE s.rootId IS NULL
88
+ UNION ALL SELECT s.rootId, 'summary-extra', NULL, s.chainCount
89
+ FROM stored s LEFT JOIN walked w ON w.root_id = s.rootId WHERE w.root_id IS NULL
90
+ UNION ALL SELECT e.root_id, 'chainCount', e.chainCount, s.chainCount
91
+ FROM expected e JOIN session_summaries s ON s.rootId = e.root_id WHERE s.chainCount IS NOT e.chainCount
92
+ UNION ALL SELECT e.root_id, 'lastActivityAt', e.lastActivityAt, s.lastActivityAt
93
+ FROM expected e JOIN session_summaries s ON s.rootId = e.root_id
94
+ WHERE s.lastActivityAt IS NOT e.lastActivityAt
95
+ UNION ALL SELECT e.root_id, 'latestStatus', e.latestStatus, s.latestStatus
96
+ FROM expected e JOIN session_summaries s ON s.rootId = e.root_id
97
+ WHERE e.newest = 1 AND s.latestStatus IS NOT e.latestStatus
98
+ UNION ALL SELECT e.root_id, 'pendingApproval', e.pendingApproval, s.pendingApprovalCount > 0
99
+ FROM expected e JOIN session_summaries s ON s.rootId = e.root_id
100
+ WHERE (s.pendingApprovalCount > 0) IS NOT e.pendingApproval
101
+ UNION ALL SELECT e.root_id, 'lastHumanAt', e.lastHumanAt, s.lastHumanAt
102
+ FROM expected e JOIN session_summaries s ON s.rootId = e.root_id
103
+ WHERE s.lastHumanAt IS NOT e.lastHumanAt
104
+ UNION ALL SELECT c.root_id, 'member', c.id, m.rootId
105
+ FROM chain c JOIN expected e ON e.root_id = c.root_id
106
+ LEFT JOIN session_members m ON m.taskId = c.id WHERE m.rootId IS NOT c.root_id
107
+ UNION ALL SELECT m.rootId, 'member', NULL, m.taskId
108
+ FROM session_members m LEFT JOIN chain c ON c.id = m.taskId AND c.root_id = m.rootId
109
+ WHERE c.id IS NULL AND (? IS NULL OR m.rootId IN (SELECT rootId FROM stored))`,
110
+ params: [since, since, since, since, since],
111
+ };
112
+ }
113
+
114
+ /** Run {@link sessionSummaryDriftQuery}. */
115
+ export async function findSessionSummaryDrift(
116
+ since: string | null = null,
117
+ ): Promise<SessionSummaryDrift[]> {
118
+ const query = sessionSummaryDriftQuery(since);
119
+ return await getDbClient().query<SessionSummaryDrift>(query.sql, query.params);
120
+ }