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