@desplega.ai/agent-swarm 1.145.0 → 1.147.0

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 (221) hide show
  1. package/README.md +2 -1
  2. package/dist/{acp-adapter-580t41c0.js → acp-adapter-9n319wqc.js} +61 -6
  3. package/dist/{actions-m662tjng.js → actions-2vxqvpr9.js} +9 -8
  4. package/dist/{app-zb3rndm8.js → app-w066xfy0.js} +6 -5
  5. package/dist/assistant-kt31dj2h.js +236 -0
  6. package/dist/{boot-reembed-072x00vn.js → boot-reembed-az5rassp.js} +7 -6
  7. package/dist/{boot-reembed-65zz2smt.js → boot-reembed-j3mm3rfz.js} +8 -7
  8. package/dist/{boot-scrub-logs-yz0ams04.js → boot-scrub-logs-b6h54817.js} +4 -4
  9. package/dist/{claude-adapter-paj270y2.js → claude-adapter-w1z04ann.js} +8 -6
  10. package/dist/{claude-managed-setup-w619jncn.js → claude-managed-setup-f5pmsyrp.js} +24 -3
  11. package/dist/{claude-sdk-session-swt80j9z.js → claude-sdk-session-f0cc1kec.js} +8 -6
  12. package/dist/{cli-dt553ktb.js → cli-0b6y7kxm.js} +1 -1
  13. package/dist/{cli-bxdhw615.js → cli-12fz972k.js} +4 -4
  14. package/dist/{cli-s2f20r5w.js → cli-1th1728g.js} +2 -2
  15. package/dist/{cli-rs6ec3b9.js → cli-3tgwnf43.js} +3 -3
  16. package/dist/{cli-gxnzc6wh.js → cli-4nr988h6.js} +1 -1
  17. package/dist/{cli-axs8e6d4.js → cli-53s590z8.js} +1 -1
  18. package/dist/{cli-ygbjp3z8.js → cli-54wwve05.js} +5 -4
  19. package/dist/{cli-v0asr5yv.js → cli-719k9j2c.js} +2 -2
  20. package/dist/{cli-anktweqr.js → cli-89xfd7f9.js} +21 -47
  21. package/dist/{cli-q34a0e10.js → cli-8v0g1yc8.js} +1 -1
  22. package/dist/cli-9d8ntetj.js +42 -0
  23. package/dist/{cli-z9fprcrf.js → cli-atcve9y8.js} +881 -738
  24. package/dist/{cli-jmb7g47h.js → cli-b5bq9crk.js} +14 -31
  25. package/dist/cli-d8brzxjb.js +190 -0
  26. package/dist/{cli-gzky0gkr.js → cli-de14znh8.js} +2 -2
  27. package/dist/{cli-xytwff7f.js → cli-dttph1bw.js} +2 -2
  28. package/dist/cli-ep19k3mb.js +19 -0
  29. package/dist/{cli-m1xa8be7.js → cli-f146mn5s.js} +7 -50
  30. package/dist/{cli-1fj0chdk.js → cli-f7dsy61y.js} +5 -3
  31. package/dist/{cli-f567d5c1.js → cli-ftht3zzj.js} +2782 -2589
  32. package/dist/{cli-a0g1gbt5.js → cli-hbv0kq6w.js} +2 -2
  33. package/dist/{cli-fhsz3twr.js → cli-jsp14b6c.js} +171 -1
  34. package/dist/{cli-7fnkj1hj.js → cli-kpv03zhj.js} +21 -17
  35. package/dist/{cli-m4j6trn4.js → cli-m8yrsg97.js} +2 -2
  36. package/dist/cli-m9sxbkhm.js +779 -0
  37. package/dist/cli-mmemxxdc.js +823 -0
  38. package/dist/{cli-tqtvj5pt.js → cli-n8508nre.js} +1 -1
  39. package/dist/{cli-z96t0thj.js → cli-p1d5b073.js} +37 -2
  40. package/dist/{cli-gh8y8f36.js → cli-rw51bq3j.js} +13 -10
  41. package/dist/{cli-59ej1c07.js → cli-smby5m17.js} +7 -6
  42. package/dist/{cli-65vqzz3v.js → cli-tzvk9haz.js} +94 -372
  43. package/dist/{cli-dc9k2ba9.js → cli-w9kb3bk1.js} +1 -1
  44. package/dist/{cli-kpev657q.js → cli-y6b6fb76.js} +25 -3
  45. package/dist/{cli-123q2get.js → cli-yz3djrm0.js} +9 -9
  46. package/dist/{cli-wvp6bvy4.js → cli-zhjmqxzh.js} +1 -1
  47. package/dist/cli.js +13 -13
  48. package/dist/{codex-adapter-k98vaw2r.js → codex-adapter-zw4dwsx7.js} +2 -2
  49. package/dist/{codex-hook-9pf19cks.js → codex-hook-bw8p5nh4.js} +2 -2
  50. package/dist/{codex-session-runner-tngtphx7.js → codex-session-runner-k981s65v.js} +2 -2
  51. package/dist/{commands-y8nbr3fv.js → commands-sykrxx7e.js} +4 -4
  52. package/dist/{db-30p4h2t1.js → db-sfbsc8wt.js} +6 -4
  53. package/dist/handlers-0aspcejm.js +603 -0
  54. package/dist/{hook-dcw4jfbj.js → hook-4nvqj2sb.js} +7 -5
  55. package/dist/{hook-21jexvfx.js → hook-hxccs7m5.js} +24 -49
  56. package/dist/{http-r4kq5g8d.js → http-y06z517d.js} +230 -94
  57. package/dist/{index-q3qja6ta.js → index-hssww7kj.js} +15 -14
  58. package/dist/{index-gay66b9w.js → index-k8znfx0z.js} +13 -12
  59. package/dist/{index-53zyht4z.js → index-tce1rss4.js} +371 -110
  60. package/dist/{index-nqvf26za.js → index-x5j894zf.js} +14 -13
  61. package/dist/{keepalive-xs8fa55y.js → keepalive-w733ax66.js} +6 -6
  62. package/dist/{lead-d9m2t4s0.js → lead-75bjqa9d.js} +29 -27
  63. package/dist/{maintenance-3zh82g6z.js → maintenance-w4f1zjck.js} +8 -7
  64. package/dist/{oauth-refresh-sweep-03snsye5.js → oauth-refresh-sweep-wdetky26.js} +5 -5
  65. package/dist/{onboard-0cg1925x.js → onboard-bb5net8s.js} +2 -2
  66. package/dist/{otel-impl-0ab95z27.js → otel-impl-0w7c14ap.js} +1 -1
  67. package/dist/{pi-mono-adapter-dtkz4e7j.js → pi-mono-adapter-2has4gnp.js} +12 -2
  68. package/dist/{pricing-refresh-e5jhzz4j.js → pricing-refresh-9nzsrgcq.js} +6 -6
  69. package/dist/{rbac-roles-p1mf40z3.js → rbac-roles-r3hqjp09.js} +5 -5
  70. package/dist/{rbac-roles-2w3cmrkc.js → rbac-roles-z1jrffbt.js} +4 -4
  71. package/dist/{registry-9kdck5ye.js → registry-v5wytg0s.js} +1 -2
  72. package/dist/{render-v2-xhawry3k.js → render-v2-vbr89fnb.js} +6 -5
  73. package/dist/{seed-pricing-t223sw91.js → seed-pricing-g62hy1pk.js} +7 -5
  74. package/dist/{setup-zcs0rce7.js → setup-gnfqnptp.js} +2 -2
  75. package/dist/{worker-qnp2ka62.js → worker-kznxp4qm.js} +29 -27
  76. package/openapi.json +263 -12
  77. package/package.json +1 -1
  78. package/plugin/commands/start-worker.md +4 -9
  79. package/plugin/pi-skills/start-worker/SKILL.md +4 -9
  80. package/src/be/db/agents.ts +556 -0
  81. package/src/be/db/context-versions.ts +117 -0
  82. package/src/be/db/runtime.ts +406 -0
  83. package/src/be/db/tasks/read.ts +1167 -0
  84. package/src/be/db/tasks/write.ts +953 -0
  85. package/src/be/db.ts +584 -3429
  86. package/src/be/migrations/147_scheduled_tasks_parent_task.sql +7 -0
  87. package/src/be/migrations/148_pages_svg.sql +123 -0
  88. package/src/be/migrations/149_session_tokens.sql +24 -0
  89. package/src/be/scripts/typecheck.ts +1 -0
  90. package/src/be/seed-pricing.ts +13 -8
  91. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  92. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  93. package/src/be/seed-scripts/catalog/star-history-refresh.ts +101 -0
  94. package/src/be/seed-scripts/catalog/star-history-renderer.ts +119 -0
  95. package/src/be/seed-scripts/index.ts +17 -0
  96. package/src/be/seed-skills/index.ts +14 -0
  97. package/src/be/swarm-config-guard.ts +5 -2
  98. package/src/be/task-attachment-store.ts +70 -0
  99. package/src/be/users.ts +77 -0
  100. package/src/commands/attachments-section.ts +37 -0
  101. package/src/commands/claude-managed-setup.ts +45 -3
  102. package/src/commands/claude-md-session.ts +408 -0
  103. package/src/commands/context-preamble.ts +15 -3
  104. package/src/commands/profile-sync.ts +205 -5
  105. package/src/commands/provider-credentials.ts +60 -27
  106. package/src/commands/runner.ts +63 -58
  107. package/src/commands/templates.ts +25 -2
  108. package/src/hooks/hook.ts +26 -67
  109. package/src/http/agents.ts +40 -1
  110. package/src/http/auth.ts +13 -1
  111. package/src/http/fs.ts +21 -51
  112. package/src/http/index.ts +2 -0
  113. package/src/http/mcp.ts +32 -0
  114. package/src/http/pages-public.ts +20 -4
  115. package/src/http/pages.ts +11 -3
  116. package/src/http/poll.ts +4 -0
  117. package/src/http/repos.ts +3 -3
  118. package/src/http/sessions.ts +82 -0
  119. package/src/http/tasks.ts +10 -2
  120. package/src/http/utils.ts +79 -1
  121. package/src/prompts/session-templates.ts +12 -8
  122. package/src/providers/acp-adapter.ts +62 -8
  123. package/src/providers/pi-mono-adapter.ts +23 -6
  124. package/src/providers/pricing-sources.md +11 -0
  125. package/src/scheduler/scheduler.test.ts +49 -2
  126. package/src/scheduler/scheduler.ts +4 -0
  127. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  128. package/src/scripts-runtime/types/stdlib.d.ts +8 -0
  129. package/src/scripts-runtime/types/swarm-sdk.d.ts +8 -0
  130. package/src/server.ts +2 -0
  131. package/src/slack/assistant.ts +87 -46
  132. package/src/slack/handlers.test.ts +1 -3
  133. package/src/slack/handlers.ts +102 -87
  134. package/src/slack/inbound-files.ts +466 -0
  135. package/src/slack/templates.ts +14 -0
  136. package/src/tasks/task-terminal-effects.ts +158 -0
  137. package/src/tests/acp-adapter.test.ts +369 -117
  138. package/src/tests/budget-claim-gate.test.ts +1 -0
  139. package/src/tests/build-pi-skills.test.ts +5 -7
  140. package/src/tests/claude-managed-setup.test.ts +52 -4
  141. package/src/tests/claude-md-session.test.ts +603 -0
  142. package/src/tests/codex-skill-resolver.test.ts +19 -0
  143. package/src/tests/context-versioning.test.ts +107 -0
  144. package/src/tests/cors-allowlist.test.ts +309 -0
  145. package/src/tests/credential-check.test.ts +157 -14
  146. package/src/tests/db-agents.test.ts +91 -0
  147. package/src/tests/db-context-versions.test.ts +37 -0
  148. package/src/tests/db-extraction-contract.test.ts +45 -0
  149. package/src/tests/db-runtime.test.ts +70 -0
  150. package/src/tests/db-task-read-contract.test.ts +624 -0
  151. package/src/tests/db-task-write-contract.test.ts +653 -0
  152. package/src/tests/db-tasks-read.test.ts +183 -0
  153. package/src/tests/db-tasks-write.test.ts +230 -0
  154. package/src/tests/defer-task.test.ts +389 -0
  155. package/src/tests/docs-index-footprint.test.js +2 -2
  156. package/src/tests/e2e-visuals-stage.test.ts +151 -0
  157. package/src/tests/file-lock.test.ts +144 -0
  158. package/src/tests/fixtures/file-lock-holder.ts +13 -0
  159. package/src/tests/fixtures/hold-file-lock.ts +25 -0
  160. package/src/tests/heartbeat-expiry.test.ts +188 -0
  161. package/src/tests/http-api-integration.test.ts +78 -1
  162. package/src/tests/pages-public-html.test.ts +43 -0
  163. package/src/tests/pages-svg-migration.test.ts +42 -0
  164. package/src/tests/preload.ts +4 -1
  165. package/src/tests/profile-sync-warning.test.ts +16 -0
  166. package/src/tests/profile-sync.test.ts +126 -0
  167. package/src/tests/prompt-template-session.test.ts +20 -4
  168. package/src/tests/provider-registration.test.ts +93 -0
  169. package/src/tests/rbac-e2e-helpers.test.ts +53 -0
  170. package/src/tests/rbac-e2e-helpers.ts +19 -2
  171. package/src/tests/reload-config.test.ts +26 -0
  172. package/src/tests/routing-affinity-inventory.test.ts +1 -1
  173. package/src/tests/runner-attachments-section.test.ts +22 -2
  174. package/src/tests/runner-context-preamble.test.ts +30 -0
  175. package/src/tests/runner-deployment-resume.test.ts +107 -0
  176. package/src/tests/runner-repo-autostash.test.ts +12 -0
  177. package/src/tests/script-connections.test.ts +4 -0
  178. package/src/tests/seed-scripts.test.ts +2 -2
  179. package/src/tests/session-tokens.test.ts +279 -0
  180. package/src/tests/slack-file-tools-attachments.test.ts +408 -0
  181. package/src/tests/slack-inbound-files.test.ts +847 -0
  182. package/src/tests/star-history-refresh.test.ts +102 -0
  183. package/src/tests/swarm-config-reserved-keys.test.ts +21 -0
  184. package/src/tests/swarm-repos.test.ts +20 -0
  185. package/src/tests/tool-annotations.test.ts +4 -3
  186. package/src/tests/ui-logs-parser.test.ts +176 -0
  187. package/src/tools/create-page.ts +4 -2
  188. package/src/tools/defer-task.ts +239 -0
  189. package/src/tools/repos/update-repo.ts +2 -2
  190. package/src/tools/schedules/create-schedule.ts +1 -0
  191. package/src/tools/schedules/list-schedules.ts +1 -0
  192. package/src/tools/schedules/patch-schedule.ts +1 -0
  193. package/src/tools/schedules/update-schedule.ts +1 -0
  194. package/src/tools/slack-download-file.ts +60 -29
  195. package/src/tools/slack-read.ts +73 -32
  196. package/src/tools/store-progress.ts +16 -146
  197. package/src/tools/tool-config.ts +1 -0
  198. package/src/tools/utils.ts +21 -0
  199. package/src/types.ts +37 -4
  200. package/src/utils/acp-session-token.ts +56 -0
  201. package/src/utils/file-lock.ts +111 -0
  202. package/src/utils/heartbeat-expiry.ts +78 -0
  203. package/src/utils/request-auth-context.ts +2 -0
  204. package/src/utils/task-attachment-links.ts +22 -0
  205. package/templates/skills/code-quality/content.md +8 -5
  206. package/templates/skills/download-task-attachment/content.md +2 -1
  207. package/{plugin/pi-skills → templates/skills}/review-offered-task/SKILL.md +5 -5
  208. package/templates/skills/review-offered-task/config.json +14 -0
  209. package/{plugin/commands/review-offered-task.md → templates/skills/review-offered-task/content.md} +3 -8
  210. package/templates/skills/scheduling/content.md +4 -0
  211. package/templates/skills/slack-interaction/content.md +2 -2
  212. package/templates/skills/work-on-task/SKILL.md +16 -0
  213. package/templates/skills/work-on-task/config.json +14 -0
  214. package/templates/skills/work-on-task/content.md +11 -0
  215. package/dist/assistant-0qt42nzg.js +0 -212
  216. package/dist/cli-fs1kkqgr.js +0 -317
  217. package/dist/cli-jnf5zghe.js +0 -269
  218. package/dist/cli-n1q8q14d.js +0 -177
  219. package/dist/handlers-yfp1qn81.js +0 -592
  220. package/plugin/commands/work-on-task.md +0 -39
  221. package/plugin/pi-skills/work-on-task/SKILL.md +0 -39
@@ -0,0 +1,603 @@
1
+ import {
2
+ bufferThreadMessage,
3
+ formatSlackSteeringAck,
4
+ getBufferMessageCount,
5
+ instantFlush,
6
+ requestSlackThreadSteering,
7
+ wasEventSeen
8
+ } from "./cli-f146mn5s.js";
9
+ import {
10
+ bufferedFileFailures,
11
+ buildEffectiveText,
12
+ createSlackTaskWithFiles,
13
+ extractSlackMessageText,
14
+ extractTaskFromMessage,
15
+ fetchSlackFiles,
16
+ hasOtherUserMention,
17
+ notifySlackFileFailures,
18
+ routeMessage
19
+ } from "./cli-mmemxxdc.js";
20
+ import"./cli-jsp14b6c.js";
21
+ import {
22
+ enrichSlackUserEmail,
23
+ resolveSlackUserId,
24
+ rewriteSlackMentions
25
+ } from "./cli-dttph1bw.js";
26
+ import"./cli-p1d5b073.js";
27
+ import"./cli-53s590z8.js";
28
+ import {
29
+ ackSlackMessage,
30
+ buildTreeBlocks,
31
+ ensureSlackThreadTree,
32
+ isSlackRenderV2Enabled,
33
+ registerTreeMessage
34
+ } from "./cli-b5bq9crk.js";
35
+ import"./cli-q51yprfq.js";
36
+ import"./cli-9d8ntetj.js";
37
+ import"./cli-yatxqynm.js";
38
+ import {
39
+ getAgentById,
40
+ getAgentWorkingOnThread,
41
+ getLeadAgent,
42
+ getMostRecentTaskInThread,
43
+ getTasksByAgentId,
44
+ init_context_key,
45
+ init_db,
46
+ reactionName,
47
+ slackContextKey
48
+ } from "./cli-ftht3zzj.js";
49
+ import"./cli-y6b6fb76.js";
50
+ import {
51
+ init_resolver,
52
+ resolveTemplate
53
+ } from "./cli-kpv03zhj.js";
54
+ import"./cli-e29gmdmw.js";
55
+ import"./cli-c2mk1kgf.js";
56
+ import {
57
+ init_env_flag,
58
+ isEnvFlagEnabled
59
+ } from "./cli-ej6gbxfb.js";
60
+ import"./cli-sd0mxa3c.js";
61
+ import"./cli-w5e1d1vv.js";
62
+ import"./cli-w9kb3bk1.js";
63
+ import"./cli-xqcq9y5e.js";
64
+ import"./cli-pzxba7gw.js";
65
+ import {
66
+ init_event_bus,
67
+ workflowEventBus
68
+ } from "./cli-f7kvq46p.js";
69
+ import"./cli-zf8jjwex.js";
70
+ import"./cli-bd957dgb.js";
71
+ import {
72
+ __callDispose,
73
+ __using
74
+ } from "./cli-bc05qbss.js";
75
+
76
+ // src/slack/handlers.ts
77
+ init_db();
78
+ init_resolver();
79
+ init_context_key();
80
+ init_event_bus();
81
+ init_env_flag();
82
+ var allowedEmailDomains = (process.env.SLACK_ALLOWED_EMAIL_DOMAINS || "").split(",").map((d) => d.trim().toLowerCase()).filter(Boolean);
83
+ var allowedUserIds = (process.env.SLACK_ALLOWED_USER_IDS || "").split(",").map((id) => id.trim()).filter(Boolean);
84
+ var filteringEnabled = allowedEmailDomains.length > 0 || allowedUserIds.length > 0;
85
+ function checkUserAccess(userId, email, config) {
86
+ const { allowedEmailDomains: domains, allowedUserIds: userIds } = config;
87
+ if (domains.length === 0 && userIds.length === 0) {
88
+ return true;
89
+ }
90
+ if (userIds.includes(userId)) {
91
+ return true;
92
+ }
93
+ if (domains.length === 0) {
94
+ return false;
95
+ }
96
+ if (!email) {
97
+ return false;
98
+ }
99
+ const domain = email.split("@")[1]?.toLowerCase();
100
+ if (!domain) {
101
+ return false;
102
+ }
103
+ return domains.includes(domain);
104
+ }
105
+ async function isUserAllowed(client, userId) {
106
+ if (!filteringEnabled) {
107
+ return true;
108
+ }
109
+ if (allowedUserIds.includes(userId)) {
110
+ return true;
111
+ }
112
+ if (allowedEmailDomains.length === 0) {
113
+ return false;
114
+ }
115
+ const email = await enrichSlackUserEmail(client, userId);
116
+ if (!email) {
117
+ console.log(`[Slack] User ${userId} has no email, denying access`);
118
+ return false;
119
+ }
120
+ const domain = email.split("@")[1]?.toLowerCase();
121
+ if (!domain) {
122
+ console.log(`[Slack] User ${userId} has invalid email format, denying access`);
123
+ return false;
124
+ }
125
+ const allowed = allowedEmailDomains.includes(domain);
126
+ if (!allowed) {
127
+ console.log(`[Slack] User ${userId} email domain "${domain}" not in allowed list`);
128
+ }
129
+ return allowed;
130
+ }
131
+ function isBotMessage(event, botUserId) {
132
+ if (event.subtype === "bot_message")
133
+ return true;
134
+ if (event.bot_id)
135
+ return true;
136
+ if (botUserId && event.user === botUserId)
137
+ return true;
138
+ return false;
139
+ }
140
+ var cachedBotUserId = null;
141
+ var cachedBotId = null;
142
+ var swarmThreadRootCache = new Map;
143
+ var SWARM_THREAD_ROOT_CACHE_MAX = 1000;
144
+ function resetSlackHandlerCachesForTesting() {
145
+ cachedBotUserId = null;
146
+ cachedBotId = null;
147
+ swarmThreadRootCache.clear();
148
+ }
149
+ function isSwarmThreadRoot(root, botUserId, botId) {
150
+ if (!root)
151
+ return false;
152
+ if (botUserId && root.user === botUserId)
153
+ return true;
154
+ if (botId && root.bot_id === botId)
155
+ return true;
156
+ return false;
157
+ }
158
+ async function wasThreadStartedBySwarm(client, channelId, threadTs) {
159
+ const key = `${channelId}:${threadTs}`;
160
+ const cached = swarmThreadRootCache.get(key);
161
+ if (cached !== undefined)
162
+ return cached;
163
+ let startedBySwarm = false;
164
+ try {
165
+ const resp = await client.conversations.replies({
166
+ channel: channelId,
167
+ ts: threadTs,
168
+ limit: 1,
169
+ inclusive: true
170
+ });
171
+ const root = resp.messages?.[0];
172
+ startedBySwarm = isSwarmThreadRoot(root, cachedBotUserId, cachedBotId);
173
+ } catch (error) {
174
+ console.error("[Slack] Failed to check whether thread was started by swarm:", error);
175
+ }
176
+ if (swarmThreadRootCache.size >= SWARM_THREAD_ROOT_CACHE_MAX) {
177
+ const oldest = swarmThreadRootCache.keys().next().value;
178
+ if (oldest !== undefined)
179
+ swarmThreadRootCache.delete(oldest);
180
+ }
181
+ swarmThreadRootCache.set(key, startedBySwarm);
182
+ return startedBySwarm;
183
+ }
184
+ async function hasSwarmThreadActivity(client, channelId, threadTs) {
185
+ return await getAgentWorkingOnThread(channelId, threadTs) !== null || await wasThreadStartedBySwarm(client, channelId, threadTs);
186
+ }
187
+ async function getThreadContext(client, channel, threadTs, currentTs, botUserId) {
188
+ if (!threadTs)
189
+ return "";
190
+ try {
191
+ const result = await client.conversations.replies({
192
+ channel,
193
+ ts: threadTs,
194
+ limit: 20
195
+ });
196
+ const messages = result.messages || [];
197
+ const previousMessages = messages.filter((m) => {
198
+ if (m.ts === currentTs)
199
+ return false;
200
+ return extractSlackMessageText(m) !== "";
201
+ });
202
+ if (previousMessages.length === 0)
203
+ return "";
204
+ const formattedMessages = [];
205
+ for (const m of previousMessages) {
206
+ const isBotMessage2 = m.user === botUserId || m.bot_id !== undefined || m.subtype === "bot_message";
207
+ const text = extractSlackMessageText(m);
208
+ if (isBotMessage2) {
209
+ const truncatedText = text.length > 500 ? `${text.slice(0, 500)}...` : text;
210
+ formattedMessages.push(`[Agent]: ${truncatedText}`);
211
+ } else {
212
+ const authorTag = m.user ? `<@${m.user}>` : "Unknown";
213
+ formattedMessages.push(`${authorTag}: ${text}`);
214
+ }
215
+ }
216
+ return await rewriteSlackMentions(formattedMessages.join(`
217
+ `), botUserId);
218
+ } catch (error) {
219
+ console.error("[Slack] Failed to fetch thread context:", error);
220
+ return "";
221
+ }
222
+ }
223
+ var processedMessages = new Set;
224
+ var MESSAGE_DEDUP_TTL = 60000;
225
+ function isMessageProcessed(messageKey) {
226
+ if (processedMessages.has(messageKey)) {
227
+ console.log(`[Slack] Duplicate event detected: ${messageKey}`);
228
+ return true;
229
+ }
230
+ processedMessages.add(messageKey);
231
+ setTimeout(() => processedMessages.delete(messageKey), MESSAGE_DEDUP_TTL);
232
+ console.log(`[Slack] Processing new message: ${messageKey}`);
233
+ return false;
234
+ }
235
+ var rateLimitMap = new Map;
236
+ var RATE_LIMIT_WINDOW = 60000;
237
+ var MAX_REQUESTS_PER_WINDOW = 10;
238
+ function checkRateLimit(userId) {
239
+ const userRequests = rateLimitMap.get(userId) || 0;
240
+ if (userRequests >= MAX_REQUESTS_PER_WINDOW) {
241
+ return false;
242
+ }
243
+ rateLimitMap.set(userId, userRequests + 1);
244
+ setTimeout(() => {
245
+ const current = rateLimitMap.get(userId) || 0;
246
+ if (current > 0) {
247
+ rateLimitMap.set(userId, current - 1);
248
+ }
249
+ }, RATE_LIMIT_WINDOW);
250
+ return true;
251
+ }
252
+ function registerMessageHandler(app) {
253
+ app.event("message", async ({ event, body, client, say }) => {
254
+ let __stack2 = [];
255
+ try {
256
+ const eventId = body?.event_id;
257
+ if (wasEventSeen(eventId)) {
258
+ console.log(`[Slack] dropping Slack retry: event_id=${eventId}`);
259
+ return;
260
+ }
261
+ const msg = event;
262
+ if (msg.subtype === "message_changed")
263
+ return;
264
+ if (!cachedBotUserId) {
265
+ try {
266
+ const authResult = await client.auth.test();
267
+ cachedBotUserId = authResult.user_id;
268
+ cachedBotId = authResult.bot_id ?? null;
269
+ } catch (error) {
270
+ console.error("[Slack] Failed to cache bot user ID:", error);
271
+ }
272
+ }
273
+ if (isBotMessage(msg, cachedBotUserId)) {
274
+ return;
275
+ }
276
+ const hasText = !!msg.text?.trim();
277
+ const hasFiles = !!(msg.files && msg.files.length > 0);
278
+ if (!hasText && !hasFiles || !msg.user)
279
+ return;
280
+ const messageKey = `${msg.channel}:${msg.ts}`;
281
+ if (isMessageProcessed(messageKey)) {
282
+ return;
283
+ }
284
+ if (!await isUserAllowed(client, msg.user)) {
285
+ console.log(`[Slack] Ignoring message from unauthorized user ${msg.user}`);
286
+ return;
287
+ }
288
+ const requestedByUserId = await resolveSlackUserId(client, msg.user, {
289
+ sampleEventType: "message",
290
+ sampleContext: msg.text ?? ""
291
+ });
292
+ workflowEventBus.emit("slack.message", {
293
+ channel: msg.channel,
294
+ text: msg.text,
295
+ user: msg.user,
296
+ ts: msg.ts,
297
+ threadTs: msg.thread_ts
298
+ });
299
+ const effectiveText = buildEffectiveText(msg.text, msg.files);
300
+ if (!cachedBotUserId) {
301
+ console.error("[Slack] Bot user ID unavailable — skipping message to avoid silent misbehavior");
302
+ return;
303
+ }
304
+ const botUserId = cachedBotUserId;
305
+ const botMentioned = !!msg.text?.includes(`<@${botUserId}>`);
306
+ const isAssistantThread = !!msg.assistant_thread;
307
+ const isImplicitMention = isAssistantThread && !botMentioned && !hasOtherUserMention(effectiveText, botUserId);
308
+ const additiveSlack = isEnvFlagEnabled("ADDITIVE_SLACK", false);
309
+ const requireMentionForThreadFollowup = isEnvFlagEnabled("SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION", false);
310
+ if (additiveSlack && msg.thread_ts) {
311
+ const unattached2 = bufferedFileFailures(msg.files);
312
+ const stripped = buildEffectiveText(msg.text, msg.files, unattached2).replace(/<@[A-Z0-9]+>/g, "").trim();
313
+ if (stripped.startsWith("!now") && await hasSwarmThreadActivity(client, msg.channel, msg.thread_ts)) {
314
+ const nowMessage = stripped.replace(/^!now\s*/, "").trim();
315
+ const threadKey = `${msg.channel}:${msg.thread_ts}`;
316
+ console.log(`[Slack] !now command detected in thread ${threadKey}${nowMessage ? ` with message: "${nowMessage}"` : ""}`);
317
+ if (nowMessage) {
318
+ bufferThreadMessage(msg.channel, msg.thread_ts, nowMessage, msg.user, msg.ts);
319
+ }
320
+ await instantFlush(threadKey);
321
+ await ackSlackMessage(client, msg.channel, msg.ts, reactionName("now"), "now");
322
+ await notifySlackFileFailures(client, msg.channel, msg.thread_ts, unattached2);
323
+ return;
324
+ }
325
+ }
326
+ if (additiveSlack && !botMentioned && msg.thread_ts && !requireMentionForThreadFollowup) {
327
+ if (hasOtherUserMention(effectiveText, botUserId)) {
328
+ console.log(`[Slack] Skipping ADDITIVE buffer in ${msg.channel}/${msg.thread_ts}: message mentions another user`);
329
+ return;
330
+ }
331
+ const hasSwarmActivity = await hasSwarmThreadActivity(client, msg.channel, msg.thread_ts);
332
+ if (hasSwarmActivity) {
333
+ const threadKey = `${msg.channel}:${msg.thread_ts}`;
334
+ const unattached2 = bufferedFileFailures(msg.files);
335
+ bufferThreadMessage(msg.channel, msg.thread_ts, buildEffectiveText(msg.text, msg.files, unattached2), msg.user, msg.ts);
336
+ const count = getBufferMessageCount(threadKey);
337
+ const event2 = count === 1 ? "accepted" : "buffered";
338
+ const name = reactionName(event2);
339
+ console.log(`[Slack] Additive buffer: ${threadKey} (message #${count}, reaction: ${name})`);
340
+ await ackSlackMessage(client, msg.channel, msg.ts, name, event2);
341
+ await notifySlackFileFailures(client, msg.channel, msg.thread_ts, unattached2);
342
+ return;
343
+ }
344
+ }
345
+ const routingText = msg.text || effectiveText;
346
+ const routingThreadContext = msg.thread_ts ? { channelId: msg.channel, threadTs: msg.thread_ts } : undefined;
347
+ const matches = await routeMessage(routingText, botUserId, botMentioned || isImplicitMention, routingThreadContext);
348
+ if (matches.length === 0) {
349
+ let __stack = [];
350
+ try {
351
+ if (!botMentioned && !isImplicitMention)
352
+ return;
353
+ if (!checkRateLimit(msg.user)) {
354
+ if (!isSlackRenderV2Enabled()) {
355
+ await say({
356
+ text: ":satellite: _You're sending too many requests. Please slow down._",
357
+ thread_ts: msg.thread_ts || msg.ts
358
+ });
359
+ }
360
+ return;
361
+ }
362
+ const inbound2 = __using(__stack, await fetchSlackFiles(client, msg.files), 1);
363
+ const taskText = buildEffectiveText(msg.text, inbound2.files, inbound2.failed);
364
+ const taskDescription2 = isImplicitMention ? taskText : extractTaskFromMessage(taskText, botUserId);
365
+ if (!taskDescription2) {
366
+ if (!isImplicitMention && !isSlackRenderV2Enabled()) {
367
+ await say({
368
+ text: ":satellite: _Please provide a task description after mentioning an agent._",
369
+ thread_ts: msg.thread_ts || msg.ts
370
+ });
371
+ }
372
+ return;
373
+ }
374
+ const threadTs2 = msg.thread_ts || msg.ts;
375
+ const threadContext2 = await getThreadContext(client, msg.channel, msg.thread_ts, msg.ts, botUserId);
376
+ let fullTaskDescription2;
377
+ if (threadContext2) {
378
+ const ctxResult = resolveTemplate("slack.message.thread_context", {
379
+ thread_messages: threadContext2
380
+ });
381
+ fullTaskDescription2 = `${ctxResult.text}
382
+
383
+ ${taskDescription2}`;
384
+ } else {
385
+ fullTaskDescription2 = taskDescription2;
386
+ }
387
+ const lead = await getLeadAgent();
388
+ const { task, unattached: unattached2 } = await createSlackTaskWithFiles(fullTaskDescription2, {
389
+ agentId: lead?.id,
390
+ source: "slack",
391
+ slackChannelId: msg.channel,
392
+ slackThreadTs: threadTs2,
393
+ slackTriggerMessageTs: msg.ts,
394
+ slackUserId: msg.user,
395
+ requestedByUserId,
396
+ contextKey: slackContextKey({ channelId: msg.channel, threadTs: threadTs2 })
397
+ }, inbound2);
398
+ await ackSlackMessage(client, msg.channel, msg.ts, reactionName("accepted"), "accepted");
399
+ await notifySlackFileFailures(client, msg.channel, threadTs2, unattached2);
400
+ if (isSlackRenderV2Enabled()) {
401
+ await ensureSlackThreadTree([task.id]);
402
+ } else {
403
+ await say({
404
+ text: ":satellite: _No agents are online right now. Your request has been queued and will be processed when agents come back up._",
405
+ thread_ts: threadTs2
406
+ });
407
+ }
408
+ return;
409
+ } catch (_catch) {
410
+ var _err = _catch, _hasErr = 1;
411
+ } finally {
412
+ var _promise = __callDispose(__stack, _err, _hasErr);
413
+ _promise && await _promise;
414
+ }
415
+ }
416
+ if (!checkRateLimit(msg.user)) {
417
+ if (!isSlackRenderV2Enabled()) {
418
+ await say({
419
+ text: ":satellite: _You're sending too many requests. Please slow down._",
420
+ thread_ts: msg.thread_ts || msg.ts
421
+ });
422
+ }
423
+ return;
424
+ }
425
+ const inbound = __using(__stack2, await fetchSlackFiles(client, msg.files), 1);
426
+ const taskDescription = extractTaskFromMessage(buildEffectiveText(msg.text, inbound.files, inbound.failed), botUserId);
427
+ if (!taskDescription) {
428
+ if (!isSlackRenderV2Enabled()) {
429
+ await say({
430
+ text: ":satellite: _Please provide a task description after mentioning an agent._",
431
+ thread_ts: msg.thread_ts || msg.ts
432
+ });
433
+ }
434
+ return;
435
+ }
436
+ const threadTs = msg.thread_ts || msg.ts;
437
+ const threadContext = await getThreadContext(client, msg.channel, msg.thread_ts, msg.ts, botUserId);
438
+ let fullTaskDescription;
439
+ if (threadContext) {
440
+ const ctxResult = resolveTemplate("slack.message.thread_context", {
441
+ thread_messages: threadContext
442
+ });
443
+ fullTaskDescription = `${ctxResult.text}
444
+
445
+ ${taskDescription}`;
446
+ } else {
447
+ fullTaskDescription = taskDescription;
448
+ }
449
+ const results = { assigned: [], queued: [], steered: [], failed: [] };
450
+ const unattached = [];
451
+ for (const match of matches) {
452
+ const agent = await getAgentById(match.agent.id);
453
+ if (!agent) {
454
+ results.failed.push({ agentName: match.agent.name, reason: "not found" });
455
+ continue;
456
+ }
457
+ try {
458
+ const latestTask = await getMostRecentTaskInThread(msg.channel, threadTs);
459
+ if (agent.isLead) {
460
+ const steering = msg.thread_ts && inbound.files.length === 0 ? await requestSlackThreadSteering({
461
+ channelId: msg.channel,
462
+ threadTs,
463
+ message: taskDescription,
464
+ messageTimestamps: [msg.ts],
465
+ requestedByUserId
466
+ }) : null;
467
+ if (steering) {
468
+ await ackSlackMessage(client, msg.channel, msg.ts, reactionName("steered"), "steered");
469
+ results.steered.push({
470
+ agentName: agent.name,
471
+ acknowledgement: formatSlackSteeringAck(steering.result)
472
+ });
473
+ continue;
474
+ }
475
+ const created2 = await createSlackTaskWithFiles(fullTaskDescription, {
476
+ agentId: agent.id,
477
+ source: "slack",
478
+ slackChannelId: msg.channel,
479
+ slackThreadTs: threadTs,
480
+ slackTriggerMessageTs: msg.ts,
481
+ slackUserId: msg.user,
482
+ parentTaskId: latestTask?.id,
483
+ requestedByUserId,
484
+ contextKey: slackContextKey({ channelId: msg.channel, threadTs })
485
+ }, inbound);
486
+ const { task: task2 } = created2;
487
+ unattached.push(...created2.unattached);
488
+ await ackSlackMessage(client, msg.channel, msg.ts, reactionName("accepted"), "accepted");
489
+ results.assigned.push({ agentName: agent.name, taskId: task2.id });
490
+ continue;
491
+ }
492
+ const created = await createSlackTaskWithFiles(fullTaskDescription, {
493
+ agentId: agent.id,
494
+ source: "slack",
495
+ slackChannelId: msg.channel,
496
+ slackThreadTs: threadTs,
497
+ slackTriggerMessageTs: msg.ts,
498
+ slackUserId: msg.user,
499
+ requestedByUserId,
500
+ contextKey: slackContextKey({ channelId: msg.channel, threadTs })
501
+ }, inbound);
502
+ const { task } = created;
503
+ unattached.push(...created.unattached);
504
+ await ackSlackMessage(client, msg.channel, msg.ts, reactionName("accepted"), "accepted");
505
+ const agentTasks = await getTasksByAgentId(agent.id);
506
+ const inProgressInThread = agentTasks.find((t) => t.id !== task.id && t.status === "in_progress" && t.slackThreadTs === threadTs);
507
+ if (inProgressInThread) {
508
+ results.queued.push({ agentName: agent.name, taskId: task.id });
509
+ } else {
510
+ results.assigned.push({ agentName: agent.name, taskId: task.id });
511
+ }
512
+ } catch {
513
+ results.failed.push({ agentName: agent.name, reason: "error" });
514
+ }
515
+ }
516
+ if (results.assigned.length + results.queued.length > 0) {
517
+ await notifySlackFileFailures(client, msg.channel, threadTs, unattached);
518
+ }
519
+ const totalResults = results.assigned.length + results.queued.length + results.failed.length;
520
+ if (totalResults > 0) {
521
+ const successfulTaskIds = [...results.assigned, ...results.queued].map(({ taskId }) => taskId);
522
+ if (isSlackRenderV2Enabled()) {
523
+ if (successfulTaskIds.length > 0)
524
+ await ensureSlackThreadTree(successfulTaskIds);
525
+ } else {
526
+ const initialNodes = results.assigned.map(({ agentName, taskId }) => ({
527
+ taskId,
528
+ agentName,
529
+ status: "in_progress",
530
+ children: []
531
+ }));
532
+ for (const q of results.queued) {
533
+ initialNodes.push({
534
+ taskId: q.taskId,
535
+ agentName: q.agentName,
536
+ status: "pending",
537
+ children: []
538
+ });
539
+ }
540
+ const blocks = buildTreeBlocks(initialNodes);
541
+ if (results.failed.length > 0) {
542
+ const failedLines = results.failed.map((f) => `⚠️ Could not assign to: *${f.agentName}* — ${f.reason}`).join(`
543
+ `);
544
+ blocks.push({
545
+ type: "context",
546
+ elements: [{ type: "mrkdwn", text: failedLines }]
547
+ });
548
+ }
549
+ const parts = [];
550
+ if (results.assigned.length > 0) {
551
+ const names = results.assigned.map((a) => `${a.agentName}`).join(", ");
552
+ parts.push(`Task assigned to: ${names}`);
553
+ }
554
+ if (results.queued.length > 0) {
555
+ const names = results.queued.map((q) => `${q.agentName}`).join(", ");
556
+ parts.push(`Task queued for: ${names}`);
557
+ }
558
+ if (results.failed.length > 0) {
559
+ const names = results.failed.map((f) => `${f.agentName}`).join(", ");
560
+ parts.push(`Could not assign to: ${names}`);
561
+ }
562
+ console.log(`[Slack] Posting initial tree message with ${initialNodes.length} node(s)${results.failed.length > 0 ? ` and ${results.failed.length} failed assignment(s)` : ""}`);
563
+ const resp = await say({
564
+ text: parts.join(". "),
565
+ blocks,
566
+ thread_ts: msg.thread_ts || msg.ts
567
+ });
568
+ if (resp?.ts) {
569
+ for (const { taskId } of results.assigned) {
570
+ await registerTreeMessage(taskId, msg.channel, threadTs, resp.ts);
571
+ }
572
+ for (const { taskId } of results.queued) {
573
+ await registerTreeMessage(taskId, msg.channel, threadTs, resp.ts);
574
+ }
575
+ }
576
+ }
577
+ }
578
+ if (!isSlackRenderV2Enabled()) {
579
+ for (const steered of results.steered) {
580
+ await say({
581
+ text: `${steered.acknowledgement} *${steered.agentName}* will receive it.`,
582
+ thread_ts: threadTs
583
+ });
584
+ }
585
+ }
586
+ } catch (_catch2) {
587
+ var _err2 = _catch2, _hasErr2 = 1;
588
+ } finally {
589
+ var _promise2 = __callDispose(__stack2, _err2, _hasErr2);
590
+ _promise2 && await _promise2;
591
+ }
592
+ });
593
+ app.event("app_mention", async ({ event }) => {
594
+ console.log(`[Slack] App mentioned in channel ${event.channel}`);
595
+ });
596
+ }
597
+ export {
598
+ checkUserAccess,
599
+ isBotMessage,
600
+ isSwarmThreadRoot,
601
+ registerMessageHandler,
602
+ resetSlackHandlerCachesForTesting
603
+ };
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  handleHook
3
- } from "./cli-anktweqr.js";
3
+ } from "./cli-89xfd7f9.js";
4
+ import"./cli-d8brzxjb.js";
4
5
  import"./cli-rvqz34y5.js";
5
6
  import"./cli-hp1n6zq3.js";
6
7
  import"./cli-xvkmedgn.js";
@@ -8,15 +9,16 @@ import"./cli-1ebpar1q.js";
8
9
  import"./cli-e7d20cty.js";
9
10
  import"./cli-3dpj7atn.js";
10
11
  import"./cli-f14fvzag.js";
11
- import"./cli-fs1kkqgr.js";
12
+ import"./cli-m9sxbkhm.js";
12
13
  import"./cli-bsakbbkm.js";
13
- import"./cli-kpev657q.js";
14
- import"./cli-7fnkj1hj.js";
14
+ import"./cli-9d8ntetj.js";
15
+ import"./cli-y6b6fb76.js";
16
+ import"./cli-kpv03zhj.js";
15
17
  import"./cli-e29gmdmw.js";
16
18
  import"./cli-ej6gbxfb.js";
17
19
  import"./cli-sd0mxa3c.js";
18
20
  import"./cli-w5e1d1vv.js";
19
- import"./cli-dc9k2ba9.js";
21
+ import"./cli-w9kb3bk1.js";
20
22
  import"./cli-xqcq9y5e.js";
21
23
  import"./cli-c4eeb5q2.js";
22
24
  import"./cli-btbzvq95.js";