@desplega.ai/agent-swarm 1.49.0 → 1.52.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 (547) hide show
  1. package/README.md +1 -1
  2. package/openapi.json +2070 -728
  3. package/package.json +10 -1
  4. package/src/agentmail/handlers.ts +65 -10
  5. package/src/agentmail/templates.ts +111 -0
  6. package/src/be/db.ts +1233 -7
  7. package/src/be/migrations/014_prompt_templates.sql +33 -0
  8. package/src/be/migrations/015_workflow_workspace.sql +3 -0
  9. package/src/be/migrations/016_active_session_runner_session.sql +4 -0
  10. package/src/be/migrations/017_channel_activity_cursors.sql +6 -0
  11. package/src/be/migrations/018_fix_seed_double_version.sql +30 -0
  12. package/src/be/migrations/019_skills.sql +65 -0
  13. package/src/be/migrations/020_approval_requests.sql +41 -0
  14. package/src/be/seed.ts +62 -0
  15. package/src/be/skill-parser.ts +70 -0
  16. package/src/be/skill-sync.ts +106 -0
  17. package/src/commands/runner.ts +320 -132
  18. package/src/commands/templates.ts +172 -0
  19. package/src/github/handlers.ts +292 -77
  20. package/src/github/mentions-aliases.test.ts +73 -0
  21. package/src/github/mentions.test.ts +3 -3
  22. package/src/github/mentions.ts +32 -6
  23. package/src/github/templates.ts +398 -0
  24. package/src/gitlab/handlers.ts +63 -22
  25. package/src/gitlab/templates.ts +140 -0
  26. package/src/heartbeat/heartbeat.ts +19 -10
  27. package/src/heartbeat/templates.ts +30 -0
  28. package/src/http/active-sessions.ts +27 -0
  29. package/src/http/approval-requests.ts +247 -0
  30. package/src/http/config.ts +3 -3
  31. package/src/http/index.ts +9 -2
  32. package/src/http/poll.ts +135 -14
  33. package/src/http/prompt-templates.ts +412 -0
  34. package/src/http/schedules.ts +35 -0
  35. package/src/http/skills.ts +479 -0
  36. package/src/http/workflows.ts +8 -0
  37. package/src/linear/sync.ts +28 -4
  38. package/src/linear/templates.ts +47 -0
  39. package/src/prompts/base-prompt.ts +41 -490
  40. package/src/prompts/registry.ts +57 -0
  41. package/src/prompts/resolver.ts +296 -0
  42. package/src/prompts/session-templates.ts +604 -0
  43. package/src/providers/claude-adapter.ts +15 -2
  44. package/src/providers/pi-mono-extension.ts +5 -1
  45. package/src/scheduler/scheduler.ts +125 -91
  46. package/src/server.ts +44 -0
  47. package/src/slack/assistant.ts +7 -4
  48. package/src/slack/channel-activity.ts +177 -0
  49. package/src/slack/handlers.ts +21 -6
  50. package/src/slack/templates.ts +55 -0
  51. package/src/tests/approval-requests.test.ts +735 -0
  52. package/src/tests/artifact-sdk.test.ts +12 -12
  53. package/src/tests/base-prompt.test.ts +49 -49
  54. package/src/tests/channel-activity.test.ts +363 -0
  55. package/src/tests/heartbeat.test.ts +1 -0
  56. package/src/tests/linear-webhook.test.ts +7 -3
  57. package/src/tests/pool-session-logs.test.ts +199 -0
  58. package/src/tests/prompt-template-github.test.ts +682 -0
  59. package/src/tests/prompt-template-remaining.test.ts +504 -0
  60. package/src/tests/prompt-template-resolver.test.ts +621 -0
  61. package/src/tests/prompt-template-session.test.ts +363 -0
  62. package/src/tests/prompt-templates-db.test.ts +616 -0
  63. package/src/tests/self-improvement.test.ts +8 -7
  64. package/src/tests/skill-parser.test.ts +178 -0
  65. package/src/tests/skill-sync.test.ts +171 -0
  66. package/src/tests/slack-metadata-inheritance.test.ts +1 -1
  67. package/src/tests/slack-thread-followups.test.ts +1 -1
  68. package/src/tests/structured-output.test.ts +0 -4
  69. package/src/tests/tool-annotations.test.ts +2 -1
  70. package/src/tests/update-profile-agentid.test.ts +248 -0
  71. package/src/tests/update-profile-auth.test.ts +195 -0
  72. package/src/tests/workflow-async-v2.test.ts +126 -4
  73. package/src/tests/workflow-definition-validation.test.ts +76 -0
  74. package/src/tests/workflow-executors.test.ts +4 -2
  75. package/src/tests/workflow-retry-v2.test.ts +1 -1
  76. package/src/tests/workflow-schedule-trigger.test.ts +104 -0
  77. package/src/tests/workflow-workspace.test.ts +272 -0
  78. package/src/tools/prompt-templates/delete.ts +86 -0
  79. package/src/tools/prompt-templates/get.ts +89 -0
  80. package/src/tools/prompt-templates/index.ts +5 -0
  81. package/src/tools/prompt-templates/list.ts +95 -0
  82. package/src/tools/prompt-templates/preview.ts +84 -0
  83. package/src/tools/prompt-templates/set.ts +117 -0
  84. package/src/tools/request-human-input.ts +106 -0
  85. package/src/tools/skills/index.ts +11 -0
  86. package/src/tools/skills/skill-create.ts +105 -0
  87. package/src/tools/skills/skill-delete.ts +67 -0
  88. package/src/tools/skills/skill-get.ts +75 -0
  89. package/src/tools/skills/skill-install-remote.ts +152 -0
  90. package/src/tools/skills/skill-install.ts +101 -0
  91. package/src/tools/skills/skill-list.ts +77 -0
  92. package/src/tools/skills/skill-publish.ts +123 -0
  93. package/src/tools/skills/skill-search.ts +43 -0
  94. package/src/tools/skills/skill-sync-remote.ts +128 -0
  95. package/src/tools/skills/skill-uninstall.ts +60 -0
  96. package/src/tools/skills/skill-update.ts +128 -0
  97. package/src/tools/store-progress.ts +22 -4
  98. package/src/tools/task-action.ts +20 -0
  99. package/src/tools/templates.ts +53 -0
  100. package/src/tools/tool-config.ts +23 -0
  101. package/src/tools/update-profile.ts +106 -34
  102. package/src/tools/workflows/create-workflow.ts +19 -1
  103. package/src/tools/workflows/update-workflow.ts +16 -1
  104. package/src/types.ts +109 -2
  105. package/src/workflows/definition.ts +30 -12
  106. package/src/workflows/engine.ts +40 -14
  107. package/src/workflows/executors/agent-task.ts +14 -3
  108. package/src/workflows/executors/human-in-the-loop.ts +160 -0
  109. package/src/workflows/executors/registry.ts +2 -0
  110. package/src/workflows/index.ts +1 -1
  111. package/src/workflows/recovery.ts +72 -0
  112. package/src/workflows/resume.ts +162 -12
  113. package/src/workflows/triggers.ts +31 -2
  114. package/src/workflows/version.ts +2 -0
  115. package/.claude/settings.json +0 -84
  116. package/.claude/settings.local.json +0 -117
  117. package/.dockerignore +0 -61
  118. package/.editorconfig +0 -15
  119. package/.entire/settings.json +0 -4
  120. package/.env.docker.example +0 -56
  121. package/.env.example +0 -78
  122. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -78
  123. package/.github/ISSUE_TEMPLATE/community-template.yml +0 -77
  124. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  125. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -60
  126. package/.github/PULL_REQUEST_TEMPLATE/community-template.md +0 -29
  127. package/.github/workflows/ci.yml +0 -52
  128. package/.github/workflows/docker-and-deploy.yml +0 -132
  129. package/.github/workflows/merge-gate.yml +0 -233
  130. package/.opencode/plugins/entire.ts +0 -133
  131. package/.superset/config.json +0 -6
  132. package/.wts-config.json +0 -4
  133. package/.wts-setup.ts +0 -171
  134. package/CHANGELOG.md +0 -447
  135. package/CLAUDE.md +0 -521
  136. package/CONTRIBUTING.md +0 -315
  137. package/DEPLOYMENT.md +0 -622
  138. package/Dockerfile +0 -65
  139. package/Dockerfile.worker +0 -189
  140. package/MCP.md +0 -841
  141. package/UI.md +0 -40
  142. package/api-entrypoint.sh +0 -56
  143. package/assets/agent-swarm-logo-orange.png +0 -0
  144. package/assets/agent-swarm-logo.png +0 -0
  145. package/assets/agent-swarm.mp4 +0 -0
  146. package/assets/agent-swarm.png +0 -0
  147. package/biome.json +0 -39
  148. package/deploy/DEPLOY.md +0 -60
  149. package/deploy/agent-swarm.service +0 -17
  150. package/deploy/docker-push.ts +0 -30
  151. package/deploy/install.ts +0 -85
  152. package/deploy/prod-db.ts +0 -42
  153. package/deploy/uninstall.ts +0 -12
  154. package/deploy/update.ts +0 -21
  155. package/depot.json +0 -1
  156. package/docker-compose.example.yml +0 -350
  157. package/docker-compose.local.yml +0 -119
  158. package/docker-entrypoint.sh +0 -632
  159. package/docs-site/app/api/search/route.ts +0 -4
  160. package/docs-site/app/docs/[[...slug]]/page.tsx +0 -87
  161. package/docs-site/app/docs/layout.tsx +0 -12
  162. package/docs-site/app/globals.css +0 -24
  163. package/docs-site/app/layout.config.tsx +0 -34
  164. package/docs-site/app/layout.tsx +0 -119
  165. package/docs-site/app/llms-full.txt/route.ts +0 -11
  166. package/docs-site/app/llms.mdx/docs/[[...slug]]/route.ts +0 -24
  167. package/docs-site/app/llms.txt/route.ts +0 -8
  168. package/docs-site/app/page.tsx +0 -5
  169. package/docs-site/app/robots.ts +0 -13
  170. package/docs-site/app/sitemap.ts +0 -37
  171. package/docs-site/components/api-page.client.tsx +0 -4
  172. package/docs-site/components/api-page.tsx +0 -7
  173. package/docs-site/components/mdx/mermaid.tsx +0 -55
  174. package/docs-site/content/docs/(documentation)/architecture/agents.mdx +0 -117
  175. package/docs-site/content/docs/(documentation)/architecture/hooks.mdx +0 -77
  176. package/docs-site/content/docs/(documentation)/architecture/memory.mdx +0 -96
  177. package/docs-site/content/docs/(documentation)/architecture/meta.json +0 -4
  178. package/docs-site/content/docs/(documentation)/architecture/overview.mdx +0 -172
  179. package/docs-site/content/docs/(documentation)/concepts/epics.mdx +0 -98
  180. package/docs-site/content/docs/(documentation)/concepts/meta.json +0 -4
  181. package/docs-site/content/docs/(documentation)/concepts/scheduling.mdx +0 -136
  182. package/docs-site/content/docs/(documentation)/concepts/services.mdx +0 -104
  183. package/docs-site/content/docs/(documentation)/concepts/task-lifecycle.mdx +0 -148
  184. package/docs-site/content/docs/(documentation)/concepts/workflows.mdx +0 -209
  185. package/docs-site/content/docs/(documentation)/contributing.mdx +0 -158
  186. package/docs-site/content/docs/(documentation)/getting-started.mdx +0 -157
  187. package/docs-site/content/docs/(documentation)/guides/agentmail-integration.mdx +0 -79
  188. package/docs-site/content/docs/(documentation)/guides/deployment.mdx +0 -171
  189. package/docs-site/content/docs/(documentation)/guides/github-integration.mdx +0 -81
  190. package/docs-site/content/docs/(documentation)/guides/gitlab-integration.mdx +0 -93
  191. package/docs-site/content/docs/(documentation)/guides/linear-integration.mdx +0 -98
  192. package/docs-site/content/docs/(documentation)/guides/meta.json +0 -13
  193. package/docs-site/content/docs/(documentation)/guides/sentry-integration.mdx +0 -52
  194. package/docs-site/content/docs/(documentation)/guides/slack-integration.mdx +0 -179
  195. package/docs-site/content/docs/(documentation)/guides/x402-payments.mdx +0 -154
  196. package/docs-site/content/docs/(documentation)/index.mdx +0 -65
  197. package/docs-site/content/docs/(documentation)/meta.json +0 -19
  198. package/docs-site/content/docs/(documentation)/reference/cli.mdx +0 -241
  199. package/docs-site/content/docs/(documentation)/reference/environment-variables.mdx +0 -205
  200. package/docs-site/content/docs/(documentation)/reference/mcp-tools.mdx +0 -449
  201. package/docs-site/content/docs/(documentation)/reference/meta.json +0 -4
  202. package/docs-site/content/docs/api-reference/active-sessions.mdx +0 -9
  203. package/docs-site/content/docs/api-reference/agents.mdx +0 -9
  204. package/docs-site/content/docs/api-reference/channels.mdx +0 -9
  205. package/docs-site/content/docs/api-reference/config.mdx +0 -9
  206. package/docs-site/content/docs/api-reference/debug.mdx +0 -9
  207. package/docs-site/content/docs/api-reference/ecosystem.mdx +0 -9
  208. package/docs-site/content/docs/api-reference/epics.mdx +0 -9
  209. package/docs-site/content/docs/api-reference/index.mdx +0 -32
  210. package/docs-site/content/docs/api-reference/memory.mdx +0 -9
  211. package/docs-site/content/docs/api-reference/meta.json +0 -25
  212. package/docs-site/content/docs/api-reference/poll.mdx +0 -9
  213. package/docs-site/content/docs/api-reference/repos.mdx +0 -9
  214. package/docs-site/content/docs/api-reference/schedules.mdx +0 -9
  215. package/docs-site/content/docs/api-reference/session-data.mdx +0 -9
  216. package/docs-site/content/docs/api-reference/stats.mdx +0 -9
  217. package/docs-site/content/docs/api-reference/tasks.mdx +0 -9
  218. package/docs-site/content/docs/api-reference/trackers.mdx +0 -9
  219. package/docs-site/content/docs/api-reference/webhooks.mdx +0 -9
  220. package/docs-site/content/docs/api-reference/workflows.mdx +0 -9
  221. package/docs-site/content/docs/meta.json +0 -3
  222. package/docs-site/lib/get-llm-text.ts +0 -10
  223. package/docs-site/lib/openapi.ts +0 -23
  224. package/docs-site/lib/source.ts +0 -8
  225. package/docs-site/mdx-components.tsx +0 -13
  226. package/docs-site/next.config.mjs +0 -29
  227. package/docs-site/package.json +0 -35
  228. package/docs-site/pnpm-lock.yaml +0 -5407
  229. package/docs-site/postcss.config.mjs +0 -8
  230. package/docs-site/public/logo.png +0 -0
  231. package/docs-site/scripts/generate-docs.ts +0 -171
  232. package/docs-site/source.config.ts +0 -17
  233. package/docs-site/tsconfig.json +0 -46
  234. package/ecosystem.config.cjs +0 -66
  235. package/landing/next.config.ts +0 -14
  236. package/landing/package.json +0 -31
  237. package/landing/pnpm-lock.yaml +0 -1091
  238. package/landing/postcss.config.mjs +0 -8
  239. package/landing/public/apple-touch-icon.png +0 -0
  240. package/landing/public/favicon.ico +0 -0
  241. package/landing/public/logo.png +0 -0
  242. package/landing/public/og-image.png +0 -0
  243. package/landing/public/omghost-desplega.svg +0 -30
  244. package/landing/public/omghost-openfort.svg +0 -9
  245. package/landing/src/app/actions/waitlist.ts +0 -25
  246. package/landing/src/app/blog/openfort-hackathon/page.tsx +0 -863
  247. package/landing/src/app/blog/page.tsx +0 -162
  248. package/landing/src/app/blog/swarm-metrics/page.tsx +0 -685
  249. package/landing/src/app/examples/page.tsx +0 -174
  250. package/landing/src/app/examples/x402/page.tsx +0 -456
  251. package/landing/src/app/globals.css +0 -122
  252. package/landing/src/app/layout.tsx +0 -134
  253. package/landing/src/app/page.tsx +0 -27
  254. package/landing/src/app/robots.ts +0 -13
  255. package/landing/src/app/sitemap.ts +0 -44
  256. package/landing/src/components/architecture.tsx +0 -163
  257. package/landing/src/components/cta.tsx +0 -52
  258. package/landing/src/components/features.tsx +0 -160
  259. package/landing/src/components/footer.tsx +0 -100
  260. package/landing/src/components/hero.tsx +0 -217
  261. package/landing/src/components/how-it-works.tsx +0 -165
  262. package/landing/src/components/navbar.tsx +0 -147
  263. package/landing/src/components/waitlist.tsx +0 -110
  264. package/landing/src/components/why-choose.tsx +0 -149
  265. package/landing/src/components/workshops.tsx +0 -328
  266. package/landing/src/lib/utils.ts +0 -6
  267. package/landing/tsconfig.json +0 -41
  268. package/misc/transcripts/2026-03-09-pi-mono-e2e-verification.md +0 -154
  269. package/new-ui/CLAUDE.md +0 -92
  270. package/new-ui/README.md +0 -73
  271. package/new-ui/biome.json +0 -42
  272. package/new-ui/components.json +0 -21
  273. package/new-ui/index.html +0 -25
  274. package/new-ui/package.json +0 -49
  275. package/new-ui/pnpm-lock.yaml +0 -4845
  276. package/new-ui/public/logo.png +0 -0
  277. package/new-ui/src/api/client.ts +0 -814
  278. package/new-ui/src/api/hooks/index.ts +0 -64
  279. package/new-ui/src/api/hooks/use-agents.ts +0 -58
  280. package/new-ui/src/api/hooks/use-channels.ts +0 -115
  281. package/new-ui/src/api/hooks/use-config-api.ts +0 -46
  282. package/new-ui/src/api/hooks/use-costs.ts +0 -122
  283. package/new-ui/src/api/hooks/use-db-query.ts +0 -29
  284. package/new-ui/src/api/hooks/use-epics.ts +0 -75
  285. package/new-ui/src/api/hooks/use-repos.ts +0 -61
  286. package/new-ui/src/api/hooks/use-schedules.ts +0 -81
  287. package/new-ui/src/api/hooks/use-services.ts +0 -16
  288. package/new-ui/src/api/hooks/use-stats.ts +0 -27
  289. package/new-ui/src/api/hooks/use-tasks.ts +0 -89
  290. package/new-ui/src/api/hooks/use-workflows.ts +0 -109
  291. package/new-ui/src/api/types.ts +0 -549
  292. package/new-ui/src/app/App.tsx +0 -13
  293. package/new-ui/src/app/providers.tsx +0 -32
  294. package/new-ui/src/app/router.tsx +0 -52
  295. package/new-ui/src/components/layout/app-header.tsx +0 -47
  296. package/new-ui/src/components/layout/app-sidebar.tsx +0 -128
  297. package/new-ui/src/components/layout/breadcrumbs.tsx +0 -57
  298. package/new-ui/src/components/layout/config-guard.tsx +0 -22
  299. package/new-ui/src/components/layout/root-layout.tsx +0 -40
  300. package/new-ui/src/components/layout/swarm-switcher.tsx +0 -85
  301. package/new-ui/src/components/shared/command-menu.tsx +0 -131
  302. package/new-ui/src/components/shared/data-grid.tsx +0 -141
  303. package/new-ui/src/components/shared/empty-state.tsx +0 -24
  304. package/new-ui/src/components/shared/error-boundary.tsx +0 -72
  305. package/new-ui/src/components/shared/json-viewer.tsx +0 -47
  306. package/new-ui/src/components/shared/name-connection-modal.tsx +0 -99
  307. package/new-ui/src/components/shared/page-skeleton.tsx +0 -16
  308. package/new-ui/src/components/shared/session-log-viewer.tsx +0 -364
  309. package/new-ui/src/components/shared/stats-bar.tsx +0 -132
  310. package/new-ui/src/components/shared/status-badge.tsx +0 -131
  311. package/new-ui/src/components/shared/usage-summary.tsx +0 -179
  312. package/new-ui/src/components/ui/alert-dialog.tsx +0 -176
  313. package/new-ui/src/components/ui/alert.tsx +0 -60
  314. package/new-ui/src/components/ui/avatar.tsx +0 -96
  315. package/new-ui/src/components/ui/badge.tsx +0 -46
  316. package/new-ui/src/components/ui/button.tsx +0 -62
  317. package/new-ui/src/components/ui/card.tsx +0 -75
  318. package/new-ui/src/components/ui/command.tsx +0 -160
  319. package/new-ui/src/components/ui/dialog.tsx +0 -143
  320. package/new-ui/src/components/ui/dropdown-menu.tsx +0 -226
  321. package/new-ui/src/components/ui/input.tsx +0 -21
  322. package/new-ui/src/components/ui/label.tsx +0 -19
  323. package/new-ui/src/components/ui/progress.tsx +0 -26
  324. package/new-ui/src/components/ui/scroll-area.tsx +0 -54
  325. package/new-ui/src/components/ui/select.tsx +0 -175
  326. package/new-ui/src/components/ui/separator.tsx +0 -28
  327. package/new-ui/src/components/ui/sheet.tsx +0 -132
  328. package/new-ui/src/components/ui/sidebar.tsx +0 -691
  329. package/new-ui/src/components/ui/skeleton.tsx +0 -13
  330. package/new-ui/src/components/ui/sonner.tsx +0 -35
  331. package/new-ui/src/components/ui/switch.tsx +0 -33
  332. package/new-ui/src/components/ui/table.tsx +0 -92
  333. package/new-ui/src/components/ui/tabs.tsx +0 -79
  334. package/new-ui/src/components/ui/textarea.tsx +0 -18
  335. package/new-ui/src/components/ui/tooltip.tsx +0 -51
  336. package/new-ui/src/components/workflows/action-node.tsx +0 -53
  337. package/new-ui/src/components/workflows/condition-node.tsx +0 -50
  338. package/new-ui/src/components/workflows/graph-utils.ts +0 -124
  339. package/new-ui/src/components/workflows/json-tree.tsx +0 -189
  340. package/new-ui/src/components/workflows/node-styles.ts +0 -10
  341. package/new-ui/src/components/workflows/step-detail-sheet.tsx +0 -87
  342. package/new-ui/src/components/workflows/trigger-node.tsx +0 -41
  343. package/new-ui/src/components/workflows/workflow-graph.tsx +0 -65
  344. package/new-ui/src/hooks/use-auto-scroll.ts +0 -82
  345. package/new-ui/src/hooks/use-config.ts +0 -203
  346. package/new-ui/src/hooks/use-keyboard-shortcuts.ts +0 -41
  347. package/new-ui/src/hooks/use-mobile.ts +0 -19
  348. package/new-ui/src/hooks/use-theme.ts +0 -60
  349. package/new-ui/src/lib/config.ts +0 -188
  350. package/new-ui/src/lib/slugs.ts +0 -71
  351. package/new-ui/src/lib/utils.ts +0 -120
  352. package/new-ui/src/main.tsx +0 -11
  353. package/new-ui/src/pages/agents/[id]/page.tsx +0 -492
  354. package/new-ui/src/pages/agents/page.tsx +0 -134
  355. package/new-ui/src/pages/chat/page.tsx +0 -674
  356. package/new-ui/src/pages/config/page.tsx +0 -1109
  357. package/new-ui/src/pages/dashboard/page.tsx +0 -454
  358. package/new-ui/src/pages/debug/page.tsx +0 -275
  359. package/new-ui/src/pages/epics/[id]/page.tsx +0 -809
  360. package/new-ui/src/pages/epics/page.tsx +0 -321
  361. package/new-ui/src/pages/not-found/page.tsx +0 -18
  362. package/new-ui/src/pages/repos/page.tsx +0 -369
  363. package/new-ui/src/pages/schedules/[id]/page.tsx +0 -664
  364. package/new-ui/src/pages/schedules/page.tsx +0 -477
  365. package/new-ui/src/pages/services/page.tsx +0 -128
  366. package/new-ui/src/pages/tasks/[id]/page.tsx +0 -670
  367. package/new-ui/src/pages/tasks/page.tsx +0 -592
  368. package/new-ui/src/pages/usage/page.tsx +0 -195
  369. package/new-ui/src/pages/workflow-runs/[id]/page.tsx +0 -363
  370. package/new-ui/src/pages/workflows/[id]/page.tsx +0 -417
  371. package/new-ui/src/pages/workflows/page.tsx +0 -266
  372. package/new-ui/src/styles/ag-grid.css +0 -36
  373. package/new-ui/src/styles/globals.css +0 -213
  374. package/new-ui/test-results/.last-run.json +0 -4
  375. package/new-ui/tsconfig.app.json +0 -34
  376. package/new-ui/tsconfig.json +0 -4
  377. package/new-ui/tsconfig.node.json +0 -26
  378. package/new-ui/vercel.json +0 -4
  379. package/new-ui/vite.config.ts +0 -28
  380. package/plugin/README.md +0 -1
  381. package/plugin/build-pi-skills.ts +0 -233
  382. package/plugin/hooks/hooks.json +0 -71
  383. package/prek.toml +0 -75
  384. package/pyproject.toml +0 -9
  385. package/scripts/check-db-boundary.sh +0 -60
  386. package/scripts/e2e-docker-provider.ts +0 -820
  387. package/scripts/e2e-io-schemas-test.ts +0 -807
  388. package/scripts/e2e-provider-test.ts +0 -220
  389. package/scripts/e2e-workflow-redesign.sh +0 -229
  390. package/scripts/e2e-workflow-test.sh +0 -285
  391. package/scripts/e2e-workflow-test.ts +0 -857
  392. package/scripts/generate-mcp-docs.ts +0 -415
  393. package/scripts/generate-openapi.ts +0 -26
  394. package/scripts/measure-tool-tokens.ts +0 -118
  395. package/scripts/x402-e2e-test.ts +0 -195
  396. package/scripts/x402-test-server.ts +0 -236
  397. package/scripts/x402-testnet-e2e.ts +0 -668
  398. package/slack-manifest.json +0 -88
  399. package/templates-ui/README.md +0 -46
  400. package/templates-ui/components.json +0 -17
  401. package/templates-ui/eslint.config.mjs +0 -18
  402. package/templates-ui/next.config.ts +0 -7
  403. package/templates-ui/package.json +0 -35
  404. package/templates-ui/pnpm-lock.yaml +0 -4571
  405. package/templates-ui/postcss.config.mjs +0 -7
  406. package/templates-ui/public/file.svg +0 -1
  407. package/templates-ui/public/globe.svg +0 -1
  408. package/templates-ui/public/logo.png +0 -0
  409. package/templates-ui/public/next.svg +0 -1
  410. package/templates-ui/public/vercel.svg +0 -1
  411. package/templates-ui/public/window.svg +0 -1
  412. package/templates-ui/src/app/[category]/[name]/page.tsx +0 -89
  413. package/templates-ui/src/app/api/templates/[...slug]/route.ts +0 -52
  414. package/templates-ui/src/app/api/templates/route.ts +0 -18
  415. package/templates-ui/src/app/builder/page.tsx +0 -37
  416. package/templates-ui/src/app/globals.css +0 -94
  417. package/templates-ui/src/app/layout.tsx +0 -79
  418. package/templates-ui/src/app/page.tsx +0 -38
  419. package/templates-ui/src/app/robots.ts +0 -11
  420. package/templates-ui/src/app/sitemap.ts +0 -31
  421. package/templates-ui/src/components/compose-builder.tsx +0 -442
  422. package/templates-ui/src/components/compose-preview.tsx +0 -117
  423. package/templates-ui/src/components/file-preview.tsx +0 -77
  424. package/templates-ui/src/components/footer.tsx +0 -40
  425. package/templates-ui/src/components/header.tsx +0 -41
  426. package/templates-ui/src/components/template-card.tsx +0 -87
  427. package/templates-ui/src/components/template-detail.tsx +0 -125
  428. package/templates-ui/src/components/template-gallery.tsx +0 -263
  429. package/templates-ui/src/components/ui/badge.tsx +0 -36
  430. package/templates-ui/src/components/ui/button.tsx +0 -57
  431. package/templates-ui/src/components/ui/card.tsx +0 -76
  432. package/templates-ui/src/components/ui/separator.tsx +0 -31
  433. package/templates-ui/src/components/ui/tooltip.tsx +0 -32
  434. package/templates-ui/src/lib/compose-generator.ts +0 -241
  435. package/templates-ui/src/lib/templates.ts +0 -137
  436. package/templates-ui/src/lib/utils.ts +0 -6
  437. package/templates-ui/tsconfig.json +0 -34
  438. package/thoughts/research/2026-02-28-openfort-viem-x402-research.md +0 -679
  439. package/thoughts/research/2026-02-28-x402-payments-research.md +0 -686
  440. package/thoughts/researcher/plans/2026-02-20-agent-self-improvement-plan.md +0 -282
  441. package/thoughts/researcher/research/2026-02-20-agent-self-improvement.md +0 -492
  442. package/thoughts/shared/plans/.gitkeep +0 -0
  443. package/thoughts/shared/plans/2025-12-18-slack-integration.md +0 -1195
  444. package/thoughts/shared/plans/2025-12-19-agent-log-streaming.md +0 -732
  445. package/thoughts/shared/plans/2025-12-19-role-based-swarm-plugin.md +0 -361
  446. package/thoughts/shared/plans/2025-12-20-mobile-responsive-ui.md +0 -501
  447. package/thoughts/shared/plans/2025-12-20-startup-team-swarm.md +0 -560
  448. package/thoughts/shared/plans/2025-12-23-runner-level-polling.md +0 -934
  449. package/thoughts/shared/plans/2025-12-23-runner-session-logs.md +0 -1000
  450. package/thoughts/shared/plans/2025-12-23-worker-lead-spawn-triggers.md +0 -568
  451. package/thoughts/shared/plans/2026-01-09-inverse-teleport.md +0 -1516
  452. package/thoughts/shared/plans/2026-01-12-agent-rename-pm2-control.md +0 -1133
  453. package/thoughts/shared/plans/2026-01-12-github-app-integration.md +0 -380
  454. package/thoughts/shared/plans/2026-01-12-lead-inbox-model.md +0 -876
  455. package/thoughts/shared/plans/2026-01-12-ralph-wiggum-integration.md +0 -463
  456. package/thoughts/shared/plans/2026-01-13-agent-concurrency.md +0 -691
  457. package/thoughts/shared/plans/2026-01-13-github-assignment-handling.md +0 -690
  458. package/thoughts/shared/plans/2026-01-13-prevent-duplicate-trigger-processing.md +0 -1071
  459. package/thoughts/shared/plans/2026-01-14-fix-slack-thread-context.md +0 -507
  460. package/thoughts/shared/plans/2026-01-15-scheduled-tasks-implementation.md +0 -565
  461. package/thoughts/shared/plans/2026-01-15-usage-cost-tracking-ui.md +0 -1479
  462. package/thoughts/shared/plans/2026-01-16-epics-feature-implementation.md +0 -1230
  463. package/thoughts/shared/plans/2026-02-26-mcp-tool-context-reduction.md +0 -282
  464. package/thoughts/shared/plans/2026-03-02-claude-context-mode-integration.md +0 -328
  465. package/thoughts/shared/plans/2026-03-02-code-level-heartbeat.md +0 -224
  466. package/thoughts/shared/research/.gitkeep +0 -0
  467. package/thoughts/shared/research/2025-01-09-inverse-teleport-plan-review.md +0 -420
  468. package/thoughts/shared/research/2025-12-18-slack-integration.md +0 -442
  469. package/thoughts/shared/research/2025-12-19-agent-log-streaming.md +0 -339
  470. package/thoughts/shared/research/2025-12-19-agent-secrets-cli-research.md +0 -390
  471. package/thoughts/shared/research/2025-12-21-gemini-cli-integration.md +0 -376
  472. package/thoughts/shared/research/2025-12-22-runner-loop-architecture.md +0 -582
  473. package/thoughts/shared/research/2025-12-22-setup-experience-improvements.md +0 -264
  474. package/thoughts/shared/research/2026-01-13-lead-duplicate-trigger-processing.md +0 -223
  475. package/thoughts/shared/research/2026-01-14-lead-slack-thread-context.md +0 -277
  476. package/thoughts/shared/research/2026-01-15-ai-tracker-agent-swarm-integration.md +0 -376
  477. package/thoughts/shared/research/2026-01-15-auto-starting-processes-in-worker-containers.md +0 -787
  478. package/thoughts/shared/research/2026-01-15-scheduled-tasks.md +0 -390
  479. package/thoughts/shared/research/2026-01-16-epics-feature-research.md +0 -437
  480. package/thoughts/shared/research/2026-02-26-cliffy-mcp-tools.md +0 -159
  481. package/thoughts/shared/research/2026-03-03-database-migration-system-refactor.md +0 -337
  482. package/thoughts/swarm-researcher/plans/2026-02-23-openclaw-improvements-plan.md +0 -778
  483. package/thoughts/swarm-researcher/plans/2026-02-26-artifacts-localtunnel-plan.md +0 -1269
  484. package/thoughts/swarm-researcher/research/2026-02-23-openclaw-vs-agent-swarm-comparison.md +0 -411
  485. package/thoughts/swarm-researcher/research/2026-02-26-artifacts-localtunnel.md +0 -724
  486. package/thoughts/taras/brainstorms/2026-03-20-prompt-template-registry.md +0 -443
  487. package/thoughts/taras/brainstorms/2026-03-20-setup-cli-onboarding.md +0 -307
  488. package/thoughts/taras/plans/2026-01-22-agent-swarm-schemas.md +0 -98
  489. package/thoughts/taras/plans/2026-01-28-per-worker-claude-md.md +0 -617
  490. package/thoughts/taras/plans/2026-01-28-sentry-cli-integration.md +0 -214
  491. package/thoughts/taras/plans/2026-02-20-auto-improvement.md +0 -803
  492. package/thoughts/taras/plans/2026-02-20-env-management.md +0 -538
  493. package/thoughts/taras/plans/2026-02-20-memory-system.md +0 -882
  494. package/thoughts/taras/plans/2026-02-20-repos-knowledge.md +0 -806
  495. package/thoughts/taras/plans/2026-02-20-session-attach.md +0 -647
  496. package/thoughts/taras/plans/2026-02-20-worker-identity.md +0 -820
  497. package/thoughts/taras/plans/2026-02-25-feat-new-ui-visual-redesign-plan.md +0 -768
  498. package/thoughts/taras/plans/2026-03-04-fix-buildSystemPrompt-missing-fields.md +0 -77
  499. package/thoughts/taras/plans/2026-03-04-new-ui-missing-actions.md +0 -543
  500. package/thoughts/taras/plans/2026-03-06-one-time-scheduled-tasks.md +0 -373
  501. package/thoughts/taras/plans/2026-03-08-memory-self-improvement-enhancements.md +0 -512
  502. package/thoughts/taras/plans/2026-03-08-pi-mono-provider-implementation.md +0 -919
  503. package/thoughts/taras/plans/2026-03-09-templates-registry.md +0 -723
  504. package/thoughts/taras/plans/2026-03-10-task-working-directory.md +0 -371
  505. package/thoughts/taras/plans/2026-03-11-archil-per-agent-write-strategy.md +0 -621
  506. package/thoughts/taras/plans/2026-03-12-eliminate-inbox-route-to-tasks.md +0 -61
  507. package/thoughts/taras/plans/2026-03-12-slack-thread-followup-additive.md +0 -488
  508. package/thoughts/taras/plans/2026-03-13-slack-ai-improvements.md +0 -644
  509. package/thoughts/taras/plans/2026-03-16-route-wrapper-openapi.md +0 -636
  510. package/thoughts/taras/plans/2026-03-17-multi-api-config.md +0 -444
  511. package/thoughts/taras/plans/2026-03-18-agent-fs-integration.md +0 -591
  512. package/thoughts/taras/plans/2026-03-18-debug-db-explorer.md +0 -446
  513. package/thoughts/taras/plans/2026-03-18-workflow-redesign.md +0 -987
  514. package/thoughts/taras/plans/2026-03-19-compound-learnings.md +0 -403
  515. package/thoughts/taras/plans/2026-03-19-ticket-tracker-linear-integration.md +0 -860
  516. package/thoughts/taras/plans/2026-03-19-workflow-io-schemas-and-bugs.md +0 -899
  517. package/thoughts/taras/plans/2026-03-20-setup-cli-onboarding.md +0 -874
  518. package/thoughts/taras/plans/2026-03-20-workflow-structured-output-validation-workspace.md +0 -723
  519. package/thoughts/taras/research/2026-01-22-vercel-cli-integration.md +0 -287
  520. package/thoughts/taras/research/2026-01-27-excessive-polling-issue.md +0 -311
  521. package/thoughts/taras/research/2026-01-28-per-worker-claude-md.md +0 -383
  522. package/thoughts/taras/research/2026-01-28-sentry-cli-integration.md +0 -240
  523. package/thoughts/taras/research/2026-02-19-agent-native-swarm-architecture.md +0 -390
  524. package/thoughts/taras/research/2026-02-19-swarm-gaps-implementation.md +0 -594
  525. package/thoughts/taras/research/2026-02-25-dashboard-ui-design-best-practices.md +0 -825
  526. package/thoughts/taras/research/2026-02-26-task-detail-page-redesign.md +0 -393
  527. package/thoughts/taras/research/2026-03-03-new-ui-missing-actions.md +0 -168
  528. package/thoughts/taras/research/2026-03-05-pi-mono-provider-research.md +0 -230
  529. package/thoughts/taras/research/2026-03-06-workflow-engine-design.md +0 -445
  530. package/thoughts/taras/research/2026-03-08-drive-loop-concept.md +0 -375
  531. package/thoughts/taras/research/2026-03-08-pi-mono-deep-dive.md +0 -869
  532. package/thoughts/taras/research/2026-03-09-templates-registry.md +0 -373
  533. package/thoughts/taras/research/2026-03-10-agent-working-directory.md +0 -223
  534. package/thoughts/taras/research/2026-03-10-configurable-event-prompts.md +0 -339
  535. package/thoughts/taras/research/2026-03-11-archil-production-setup.md +0 -181
  536. package/thoughts/taras/research/2026-03-11-archil-shared-disk-write-strategies.md +0 -437
  537. package/thoughts/taras/research/2026-03-13-slack-ai-features.md +0 -258
  538. package/thoughts/taras/research/2026-03-16-openapi-docs-generation.md +0 -335
  539. package/thoughts/taras/research/2026-03-16-route-wrapper-openapi.md +0 -670
  540. package/thoughts/taras/research/2026-03-16-slack-thread-followups-e2e.md +0 -54
  541. package/thoughts/taras/research/2026-03-18-agent-fs-integration.md +0 -558
  542. package/thoughts/taras/research/2026-03-18-linear-integration-finalization.md +0 -526
  543. package/thoughts/taras/research/2026-03-18-workflow-redesign.md +0 -797
  544. package/thoughts/taras/research/2026-03-19-workflow-node-io-schemas-and-bugs.md +0 -563
  545. package/thoughts/taras/research/2026-03-19-workflow-structured-output-validation-workspace.md +0 -486
  546. package/thoughts/taras/research/2026-03-20-prompt-template-registry.md +0 -469
  547. package/tsconfig.json +0 -37
@@ -1,1195 +0,0 @@
1
- # Slack Multi-Agent Bot Integration Implementation Plan
2
-
3
- ## Overview
4
-
5
- Integrate Slack as a communication interface for the Agent Swarm MCP, allowing users to interact with agents via Slack messages. Agents will respond with custom personas, and tasks can be created directly from Slack conversations.
6
-
7
- ## Current State Analysis
8
-
9
- The Agent Swarm MCP currently provides:
10
- - HTTP server with REST API and MCP transport (`src/http.ts`)
11
- - SQLite database with `agents`, `agent_tasks`, and `agent_log` tables (`src/be/db.ts`)
12
- - 8 MCP tools for agent coordination (`src/tools/*.ts`)
13
- - CLI runners for lead/worker agents (`src/commands/*.ts`)
14
- - React dashboard UI (`ui/src/`)
15
-
16
- **What's Missing:**
17
- - No Slack integration code exists
18
- - No task source tracking (can't distinguish MCP vs Slack-created tasks)
19
- - No mechanism for external systems to create tasks directly
20
-
21
- ### Key Discoveries:
22
- - Socket Mode enabled in `slack-manifest.json:61` - no webhook endpoints needed
23
- - Task creation flows through `send-task` tool only (`src/tools/send-task.ts`)
24
- - Database uses `CREATE TABLE IF NOT EXISTS` for idempotent schema updates (`src/be/db.ts:23-65`)
25
- - Zod v4 used for schema validation (`src/types.ts`)
26
-
27
- ## Desired End State
28
-
29
- After implementation:
30
- 1. Slack bot connects via Socket Mode on server startup
31
- 2. Users can mention agents by name in Slack messages to create tasks
32
- 3. Agents respond in Slack with custom personas matching their swarm identity
33
- 4. Task progress/completion updates appear in the original Slack thread
34
- 5. `/agent-swarm-status` slash command shows current swarm state
35
-
36
- ### Verification:
37
- - Bot appears online in Slack workspace
38
- - Mentioning an agent name creates a task visible in the dashboard
39
- - Agent completion messages appear in Slack with correct persona
40
- - Slash command returns agent list with statuses
41
-
42
- ## What We're NOT Doing
43
-
44
- - **Multi-workspace OAuth flow** - Single workspace only (env vars for tokens)
45
- - **Interactive components** - No buttons, modals, or block kit interactions
46
- - **App Home tab** - Disabled in manifest, not implementing
47
- - **Message threading for sub-tasks** - Each mention = one task, no hierarchy
48
- - **Slack-side task management** - No editing/canceling tasks from Slack
49
- - **Rate limiting** - Rely on Slack's built-in rate limits
50
- - **Message history sync** - Only process new messages, not historical
51
-
52
- ## Implementation Approach
53
-
54
- Use Slack's Bolt SDK with Socket Mode for real-time event delivery. This avoids exposing public endpoints and simplifies deployment. The integration will:
55
-
56
- 1. Run alongside the existing HTTP server (not replace it)
57
- 2. Create tasks directly in the database (bypassing MCP tools)
58
- 3. Poll for task completion to send Slack responses
59
- 4. Use the agent's name as the Slack display name via `chat:write.customize`
60
-
61
- ## Phase 1: Database Schema Updates
62
-
63
- ### Overview
64
- Add task source tracking and prepare schema for Slack token storage.
65
-
66
- ### Changes Required:
67
-
68
- #### 1. Types (`src/types.ts`)
69
-
70
- Add task source enum and extend task schema:
71
-
72
- ```typescript
73
- // After line 3 (AgentTaskStatusSchema)
74
- export const AgentTaskSourceSchema = z.enum(["mcp", "slack", "api"]);
75
- export type AgentTaskSource = z.infer<typeof AgentTaskSourceSchema>;
76
- ```
77
-
78
- Update `AgentTaskSchema` to include source:
79
-
80
- ```typescript
81
- // Modify lines 5-19
82
- export const AgentTaskSchema = z.object({
83
- id: z.uuid(),
84
- agentId: z.uuid(),
85
- task: z.string().min(1),
86
- status: AgentTaskStatusSchema,
87
- source: AgentTaskSourceSchema.default("mcp"), // NEW
88
-
89
- createdAt: z.iso.datetime().default(() => new Date().toISOString()),
90
- lastUpdatedAt: z.iso.datetime().default(() => new Date().toISOString()),
91
-
92
- finishedAt: z.iso.datetime().optional(),
93
-
94
- failureReason: z.string().optional(),
95
- output: z.string().optional(),
96
- progress: z.string().optional(),
97
-
98
- // Slack-specific metadata (optional)
99
- slackChannelId: z.string().optional(),
100
- slackThreadTs: z.string().optional(),
101
- slackUserId: z.string().optional(),
102
- });
103
- ```
104
-
105
- #### 2. Database Schema (`src/be/db.ts`)
106
-
107
- Add `source` column and Slack metadata to `agent_tasks` table. Update the CREATE TABLE statement:
108
-
109
- ```sql
110
- -- Modify lines 33-45
111
- CREATE TABLE IF NOT EXISTS agent_tasks (
112
- id TEXT PRIMARY KEY,
113
- agentId TEXT NOT NULL,
114
- task TEXT NOT NULL,
115
- status TEXT NOT NULL CHECK(status IN ('pending', 'in_progress', 'completed', 'failed')),
116
- source TEXT NOT NULL DEFAULT 'mcp' CHECK(source IN ('mcp', 'slack', 'api')),
117
- slackChannelId TEXT,
118
- slackThreadTs TEXT,
119
- slackUserId TEXT,
120
- createdAt TEXT NOT NULL,
121
- lastUpdatedAt TEXT NOT NULL,
122
- finishedAt TEXT,
123
- failureReason TEXT,
124
- output TEXT,
125
- progress TEXT,
126
- FOREIGN KEY (agentId) REFERENCES agents(id) ON DELETE CASCADE
127
- );
128
- ```
129
-
130
- Add migration for existing databases (add after table creation, before indexes):
131
-
132
- ```sql
133
- -- Add column if it doesn't exist (SQLite doesn't support IF NOT EXISTS for columns)
134
- -- We'll handle this in code with a try/catch
135
- ```
136
-
137
- #### 3. Database Functions (`src/be/db.ts`)
138
-
139
- Update `AgentTaskRow` type:
140
-
141
- ```typescript
142
- // Modify lines 178-189
143
- type AgentTaskRow = {
144
- id: string;
145
- agentId: string;
146
- task: string;
147
- status: AgentTaskStatus;
148
- source: AgentTaskSource;
149
- slackChannelId: string | null;
150
- slackThreadTs: string | null;
151
- slackUserId: string | null;
152
- createdAt: string;
153
- lastUpdatedAt: string;
154
- finishedAt: string | null;
155
- failureReason: string | null;
156
- output: string | null;
157
- progress: string | null;
158
- };
159
- ```
160
-
161
- Update `rowToAgentTask` converter:
162
-
163
- ```typescript
164
- // Modify lines 191-204
165
- function rowToAgentTask(row: AgentTaskRow): AgentTask {
166
- return {
167
- id: row.id,
168
- agentId: row.agentId,
169
- task: row.task,
170
- status: row.status,
171
- source: row.source,
172
- slackChannelId: row.slackChannelId ?? undefined,
173
- slackThreadTs: row.slackThreadTs ?? undefined,
174
- slackUserId: row.slackUserId ?? undefined,
175
- createdAt: row.createdAt,
176
- lastUpdatedAt: row.lastUpdatedAt,
177
- finishedAt: row.finishedAt ?? undefined,
178
- failureReason: row.failureReason ?? undefined,
179
- output: row.output ?? undefined,
180
- progress: row.progress ?? undefined,
181
- };
182
- }
183
- ```
184
-
185
- Update `createTask` function signature and query:
186
-
187
- ```typescript
188
- // Modify lines 249-257
189
- export function createTask(
190
- agentId: string,
191
- task: string,
192
- options?: {
193
- source?: AgentTaskSource;
194
- slackChannelId?: string;
195
- slackThreadTs?: string;
196
- slackUserId?: string;
197
- }
198
- ): AgentTask {
199
- const id = crypto.randomUUID();
200
- const source = options?.source ?? "mcp";
201
- const row = getDb()
202
- .prepare<AgentTaskRow, [string, string, string, AgentTaskStatus, AgentTaskSource, string | null, string | null, string | null]>(
203
- `INSERT INTO agent_tasks (id, agentId, task, status, source, slackChannelId, slackThreadTs, slackUserId, createdAt, lastUpdatedAt)
204
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) RETURNING *`
205
- )
206
- .get(id, agentId, task, "pending", source, options?.slackChannelId ?? null, options?.slackThreadTs ?? null, options?.slackUserId ?? null);
207
- if (!row) throw new Error("Failed to create task");
208
- try {
209
- createLogEntry({ eventType: "task_created", agentId, taskId: id, newValue: "pending", metadata: { source } });
210
- } catch { }
211
- return rowToAgentTask(row);
212
- }
213
- ```
214
-
215
- Add function to get Slack tasks awaiting response:
216
-
217
- ```typescript
218
- // Add after getAllTasks function (around line 335)
219
- export function getCompletedSlackTasks(): AgentTask[] {
220
- return getDb()
221
- .prepare<AgentTaskRow, []>(
222
- `SELECT * FROM agent_tasks
223
- WHERE source = 'slack'
224
- AND slackChannelId IS NOT NULL
225
- AND status IN ('completed', 'failed')
226
- ORDER BY lastUpdatedAt DESC`
227
- )
228
- .all()
229
- .map(rowToAgentTask);
230
- }
231
- ```
232
-
233
- ### Success Criteria:
234
-
235
- #### Automated Verification:
236
- - [x] TypeScript compiles: `bun run tsc:check`
237
- - [x] Server starts without errors: `bun run dev:http`
238
- - [x] Existing tasks still work (backward compatible)
239
-
240
- #### Manual Verification:
241
- - [x] New task created via MCP has `source: "mcp"`
242
- - [x] Database schema updated correctly (check with sqlite3 CLI)
243
-
244
- **Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation from the human that the database changes work correctly before proceeding to the next phase.
245
-
246
- ---
247
-
248
- ## Phase 2: Slack Dependencies and Configuration
249
-
250
- ### Overview
251
- Add Slack Bolt SDK and configure environment variables.
252
-
253
- ### Changes Required:
254
-
255
- #### 1. Install Dependencies
256
-
257
- ```bash
258
- bun add @slack/bolt
259
- ```
260
-
261
- #### 2. Environment Variables
262
-
263
- Create `.env.example` update (document required vars):
264
-
265
- ```bash
266
- # Slack Bot Configuration (Socket Mode)
267
- SLACK_BOT_TOKEN=xoxb-... # Bot User OAuth Token
268
- SLACK_APP_TOKEN=xapp-... # App-Level Token (for Socket Mode)
269
- SLACK_SIGNING_SECRET=... # Signing Secret (optional for Socket Mode)
270
- ```
271
-
272
- #### 3. Slack Types (`src/slack/types.ts`)
273
-
274
- **File**: `src/slack/types.ts` (NEW)
275
-
276
- ```typescript
277
- import type { Agent } from "../types";
278
-
279
- export interface SlackMessageContext {
280
- channelId: string;
281
- threadTs?: string;
282
- userId: string;
283
- text: string;
284
- botUserId: string;
285
- }
286
-
287
- export interface AgentMatch {
288
- agent: Agent;
289
- matchedText: string;
290
- }
291
-
292
- export interface SlackConfig {
293
- botToken: string;
294
- appToken: string;
295
- signingSecret?: string;
296
- }
297
- ```
298
-
299
- ### Success Criteria:
300
-
301
- #### Automated Verification:
302
- - [x] Dependencies install: `bun install`
303
- - [x] TypeScript compiles: `bun run tsc:check`
304
- - [x] No runtime errors on import
305
-
306
- #### Manual Verification:
307
- - [x] `.env` file has required Slack tokens configured
308
- - [ ] Tokens are valid (can be verified in Phase 3)
309
-
310
- **Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation before proceeding to the next phase.
311
-
312
- ---
313
-
314
- ## Phase 3: Slack Bot Core Implementation
315
-
316
- ### Overview
317
- Initialize Bolt app with Socket Mode and implement basic message handling.
318
-
319
- ### Changes Required:
320
-
321
- #### 1. Slack App Initialization (`src/slack/app.ts`)
322
-
323
- **File**: `src/slack/app.ts` (NEW)
324
-
325
- ```typescript
326
- import { App, LogLevel } from "@slack/bolt";
327
-
328
- let app: App | null = null;
329
-
330
- export function getSlackApp(): App | null {
331
- return app;
332
- }
333
-
334
- export async function initSlackApp(): Promise<App | null> {
335
- const botToken = process.env.SLACK_BOT_TOKEN;
336
- const appToken = process.env.SLACK_APP_TOKEN;
337
-
338
- if (!botToken || !appToken) {
339
- console.log("[Slack] Missing SLACK_BOT_TOKEN or SLACK_APP_TOKEN, Slack integration disabled");
340
- return null;
341
- }
342
-
343
- app = new App({
344
- token: botToken,
345
- appToken: appToken,
346
- socketMode: true,
347
- logLevel: process.env.NODE_ENV === "development" ? LogLevel.DEBUG : LogLevel.INFO,
348
- });
349
-
350
- // Register handlers
351
- const { registerMessageHandler } = await import("./handlers");
352
- const { registerCommandHandler } = await import("./commands");
353
-
354
- registerMessageHandler(app);
355
- registerCommandHandler(app);
356
-
357
- return app;
358
- }
359
-
360
- export async function startSlackApp(): Promise<void> {
361
- if (!app) {
362
- await initSlackApp();
363
- }
364
-
365
- if (app) {
366
- await app.start();
367
- console.log("[Slack] Bot connected via Socket Mode");
368
- }
369
- }
370
-
371
- export async function stopSlackApp(): Promise<void> {
372
- if (app) {
373
- await app.stop();
374
- app = null;
375
- console.log("[Slack] Bot disconnected");
376
- }
377
- }
378
- ```
379
-
380
- #### 2. Agent Router (`src/slack/router.ts`)
381
-
382
- **File**: `src/slack/router.ts` (NEW)
383
-
384
- ```typescript
385
- import type { Agent } from "../types";
386
- import { getAllAgents, getAgentById } from "../be/db";
387
- import type { AgentMatch } from "./types";
388
-
389
- /**
390
- * Routes a Slack message to the appropriate agent(s) based on mentions.
391
- *
392
- * Routing rules:
393
- * - `swarm#<uuid>` → exact agent by ID
394
- * - `swarm#all` → all non-lead agents
395
- * - Partial name match (words >3 chars) → agent by name
396
- * - Bot @mention only → lead agent
397
- */
398
- export function routeMessage(
399
- text: string,
400
- botUserId: string,
401
- botMentioned: boolean
402
- ): AgentMatch[] {
403
- const matches: AgentMatch[] = [];
404
- const agents = getAllAgents().filter(a => a.status !== "offline");
405
-
406
- // Check for explicit swarm#<id> syntax
407
- const idMatches = text.matchAll(/swarm#([a-f0-9-]{36})/gi);
408
- for (const match of idMatches) {
409
- const agent = getAgentById(match[1]);
410
- if (agent && agent.status !== "offline") {
411
- matches.push({ agent, matchedText: match[0] });
412
- }
413
- }
414
-
415
- // Check for swarm#all broadcast
416
- if (/swarm#all/i.test(text)) {
417
- const nonLeadAgents = agents.filter(a => !a.isLead);
418
- for (const agent of nonLeadAgents) {
419
- if (!matches.some(m => m.agent.id === agent.id)) {
420
- matches.push({ agent, matchedText: "swarm#all" });
421
- }
422
- }
423
- }
424
-
425
- // Check for partial name matches (words > 3 chars)
426
- if (matches.length === 0) {
427
- for (const agent of agents) {
428
- const nameWords = agent.name.split(/\s+/).filter(w => w.length > 3);
429
- for (const word of nameWords) {
430
- const regex = new RegExp(`\\b${escapeRegex(word)}\\b`, "i");
431
- if (regex.test(text)) {
432
- if (!matches.some(m => m.agent.id === agent.id)) {
433
- matches.push({ agent, matchedText: word });
434
- }
435
- break;
436
- }
437
- }
438
- }
439
- }
440
-
441
- // If only bot was mentioned and no agents matched, route to lead
442
- if (matches.length === 0 && botMentioned) {
443
- const lead = agents.find(a => a.isLead);
444
- if (lead) {
445
- matches.push({ agent: lead, matchedText: "@bot" });
446
- }
447
- }
448
-
449
- return matches;
450
- }
451
-
452
- function escapeRegex(str: string): string {
453
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
454
- }
455
-
456
- /**
457
- * Extracts the task description from a message, removing bot mentions and agent references.
458
- */
459
- export function extractTaskFromMessage(text: string, botUserId: string): string {
460
- return text
461
- .replace(new RegExp(`<@${botUserId}>`, "g"), "") // Remove bot mentions
462
- .replace(/swarm#[a-f0-9-]{36}/gi, "") // Remove swarm#<id>
463
- .replace(/swarm#all/gi, "") // Remove swarm#all
464
- .trim();
465
- }
466
- ```
467
-
468
- #### 3. Message Handlers (`src/slack/handlers.ts`)
469
-
470
- **File**: `src/slack/handlers.ts` (NEW)
471
-
472
- ```typescript
473
- import type { App, GenericMessageEvent } from "@slack/bolt";
474
- import { createTask, getAgentById } from "../be/db";
475
- import { routeMessage, extractTaskFromMessage } from "./router";
476
-
477
- export function registerMessageHandler(app: App): void {
478
- // Handle all message events
479
- app.event("message", async ({ event, client, say }) => {
480
- // Ignore bot messages and message_changed events
481
- if (event.subtype === "bot_message" || event.subtype === "message_changed") {
482
- return;
483
- }
484
-
485
- const msg = event as GenericMessageEvent;
486
- if (!msg.text || !msg.user) return;
487
-
488
- // Get bot's user ID
489
- const authResult = await client.auth.test();
490
- const botUserId = authResult.user_id as string;
491
-
492
- // Check if bot was mentioned
493
- const botMentioned = msg.text.includes(`<@${botUserId}>`);
494
-
495
- // Route message to agents
496
- const matches = routeMessage(msg.text, botUserId, botMentioned);
497
-
498
- if (matches.length === 0) {
499
- // No agents matched - ignore message unless bot was directly mentioned
500
- if (botMentioned) {
501
- await say({
502
- text: "No agents are currently available. Use `/agent-swarm-status` to check the swarm.",
503
- thread_ts: msg.thread_ts || msg.ts,
504
- });
505
- }
506
- return;
507
- }
508
-
509
- // Extract task description
510
- const taskDescription = extractTaskFromMessage(msg.text, botUserId);
511
- if (!taskDescription) {
512
- await say({
513
- text: "Please provide a task description after mentioning an agent.",
514
- thread_ts: msg.thread_ts || msg.ts,
515
- });
516
- return;
517
- }
518
-
519
- // Create tasks for each matched agent
520
- const createdTasks: string[] = [];
521
- for (const match of matches) {
522
- // Check agent is still idle
523
- const agent = getAgentById(match.agent.id);
524
- if (!agent || agent.status !== "idle") {
525
- await say({
526
- text: `Agent "${match.agent.name}" is currently ${agent?.status || "unavailable"} and cannot accept tasks.`,
527
- thread_ts: msg.thread_ts || msg.ts,
528
- });
529
- continue;
530
- }
531
-
532
- const task = createTask(match.agent.id, taskDescription, {
533
- source: "slack",
534
- slackChannelId: msg.channel,
535
- slackThreadTs: msg.thread_ts || msg.ts,
536
- slackUserId: msg.user,
537
- });
538
-
539
- createdTasks.push(`${match.agent.name} (${task.id.slice(0, 8)})`);
540
- }
541
-
542
- if (createdTasks.length > 0) {
543
- await say({
544
- text: `Task created for: ${createdTasks.join(", ")}`,
545
- thread_ts: msg.thread_ts || msg.ts,
546
- });
547
- }
548
- });
549
-
550
- // Handle app_mention events specifically
551
- app.event("app_mention", async ({ event, client, say }) => {
552
- // app_mention is already handled by the message event above
553
- // but we can add specific behavior here if needed
554
- console.log(`[Slack] App mentioned in channel ${event.channel}`);
555
- });
556
- }
557
- ```
558
-
559
- #### 4. Slash Commands (`src/slack/commands.ts`)
560
-
561
- **File**: `src/slack/commands.ts` (NEW)
562
-
563
- ```typescript
564
- import type { App } from "@slack/bolt";
565
- import { getAllAgents, getAllTasks } from "../be/db";
566
-
567
- export function registerCommandHandler(app: App): void {
568
- app.command("/agent-swarm-status", async ({ command, ack, respond }) => {
569
- await ack();
570
-
571
- const agents = getAllAgents();
572
- const tasks = getAllTasks({ status: "in_progress" });
573
-
574
- const statusEmoji: Record<string, string> = {
575
- idle: ":white_circle:",
576
- busy: ":large_blue_circle:",
577
- offline: ":black_circle:",
578
- };
579
-
580
- const agentLines = agents.map(agent => {
581
- const emoji = statusEmoji[agent.status] || ":question:";
582
- const role = agent.isLead ? " (Lead)" : "";
583
- const activeTask = tasks.find(t => t.agentId === agent.id);
584
- const taskInfo = activeTask ? ` - Working on: ${activeTask.task.slice(0, 50)}...` : "";
585
- return `${emoji} *${agent.name}*${role}: ${agent.status}${taskInfo}`;
586
- });
587
-
588
- const summary = {
589
- total: agents.length,
590
- idle: agents.filter(a => a.status === "idle").length,
591
- busy: agents.filter(a => a.status === "busy").length,
592
- offline: agents.filter(a => a.status === "offline").length,
593
- };
594
-
595
- await respond({
596
- response_type: "ephemeral",
597
- blocks: [
598
- {
599
- type: "header",
600
- text: { type: "plain_text", text: "Agent Swarm Status" },
601
- },
602
- {
603
- type: "section",
604
- text: {
605
- type: "mrkdwn",
606
- text: `*Summary:* ${summary.total} agents (${summary.idle} idle, ${summary.busy} busy, ${summary.offline} offline)`,
607
- },
608
- },
609
- {
610
- type: "divider",
611
- },
612
- {
613
- type: "section",
614
- text: {
615
- type: "mrkdwn",
616
- text: agentLines.join("\n") || "_No agents registered_",
617
- },
618
- },
619
- ],
620
- });
621
- });
622
- }
623
- ```
624
-
625
- #### 5. Export Module (`src/slack/index.ts`)
626
-
627
- **File**: `src/slack/index.ts` (NEW)
628
-
629
- ```typescript
630
- export { initSlackApp, startSlackApp, stopSlackApp, getSlackApp } from "./app";
631
- export { routeMessage, extractTaskFromMessage } from "./router";
632
- export type { SlackMessageContext, AgentMatch, SlackConfig } from "./types";
633
- ```
634
-
635
- #### 6. Integrate with HTTP Server (`src/http.ts`)
636
-
637
- Add Slack startup/shutdown to the HTTP server lifecycle:
638
-
639
- ```typescript
640
- // Add import at top (after line 24)
641
- import { startSlackApp, stopSlackApp } from "./slack";
642
-
643
- // Modify shutdown function (around line 391)
644
- async function shutdown() {
645
- console.log("Shutting down...");
646
-
647
- // Stop Slack bot
648
- await stopSlackApp();
649
-
650
- // Close all active transports (SSE connections, etc.)
651
- for (const [id, transport] of Object.entries(transports)) {
652
- console.log(`[HTTP] Closing transport ${id}`);
653
- transport.close();
654
- delete transports[id];
655
- }
656
-
657
- // Close all active connections forcefully
658
- httpServer.closeAllConnections();
659
- httpServer.close(() => {
660
- closeDb();
661
- console.log("MCP HTTP server closed, and database connection closed");
662
- process.exit(0);
663
- });
664
- }
665
-
666
- // Add Slack startup after HTTP server starts (after line 418)
667
- httpServer
668
- .listen(port, async () => {
669
- console.log(`MCP HTTP server running on http://localhost:${port}/mcp`);
670
-
671
- // Start Slack bot (if configured)
672
- await startSlackApp();
673
- })
674
- ```
675
-
676
- ### Success Criteria:
677
-
678
- #### Automated Verification:
679
- - [x] TypeScript compiles: `bun run tsc:check`
680
- - [ ] Linting passes: `bun run lint`
681
- - [ ] Server starts: `bun run dev:http`
682
- - [ ] Console shows "[Slack] Bot connected via Socket Mode" (with valid tokens)
683
-
684
- #### Manual Verification:
685
- - [ ] Bot appears online in Slack workspace
686
- - [ ] `/agent-swarm-status` command works
687
- - [ ] Mentioning an agent name creates a task in the database
688
- - [ ] Task shows `source: "slack"` in database/dashboard
689
-
690
- **Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation that the Slack bot connects and basic commands work before proceeding to the next phase.
691
-
692
- ---
693
-
694
- ## Phase 4: Task Completion Responses
695
-
696
- ### Overview
697
- Send task completion/failure messages back to Slack with custom agent personas.
698
-
699
- ### Changes Required:
700
-
701
- #### 1. Response Sender (`src/slack/responses.ts`)
702
-
703
- **File**: `src/slack/responses.ts` (NEW)
704
-
705
- ```typescript
706
- import type { WebClient } from "@slack/web-api";
707
- import type { AgentTask, Agent } from "../types";
708
- import { getAgentById } from "../be/db";
709
- import { getSlackApp } from "./app";
710
-
711
- /**
712
- * Send a task completion message to Slack with the agent's persona.
713
- */
714
- export async function sendTaskResponse(task: AgentTask): Promise<boolean> {
715
- const app = getSlackApp();
716
- if (!app || !task.slackChannelId || !task.slackThreadTs) {
717
- return false;
718
- }
719
-
720
- const agent = getAgentById(task.agentId);
721
- if (!agent) {
722
- console.error(`[Slack] Agent not found for task ${task.id}`);
723
- return false;
724
- }
725
-
726
- const client = app.client;
727
-
728
- try {
729
- if (task.status === "completed") {
730
- await sendWithPersona(client, {
731
- channel: task.slackChannelId,
732
- thread_ts: task.slackThreadTs,
733
- text: task.output || "Task completed.",
734
- username: agent.name,
735
- icon_emoji: getAgentEmoji(agent),
736
- });
737
- } else if (task.status === "failed") {
738
- await sendWithPersona(client, {
739
- channel: task.slackChannelId,
740
- thread_ts: task.slackThreadTs,
741
- text: `:x: Task failed: ${task.failureReason || "Unknown error"}`,
742
- username: agent.name,
743
- icon_emoji: getAgentEmoji(agent),
744
- });
745
- }
746
-
747
- return true;
748
- } catch (error) {
749
- console.error(`[Slack] Failed to send response for task ${task.id}:`, error);
750
- return false;
751
- }
752
- }
753
-
754
- /**
755
- * Send a progress update to Slack.
756
- */
757
- export async function sendProgressUpdate(task: AgentTask, progress: string): Promise<boolean> {
758
- const app = getSlackApp();
759
- if (!app || !task.slackChannelId || !task.slackThreadTs) {
760
- return false;
761
- }
762
-
763
- const agent = getAgentById(task.agentId);
764
- if (!agent) return false;
765
-
766
- try {
767
- await sendWithPersona(app.client, {
768
- channel: task.slackChannelId,
769
- thread_ts: task.slackThreadTs,
770
- text: `:hourglass_flowing_sand: ${progress}`,
771
- username: agent.name,
772
- icon_emoji: getAgentEmoji(agent),
773
- });
774
- return true;
775
- } catch (error) {
776
- console.error(`[Slack] Failed to send progress update:`, error);
777
- return false;
778
- }
779
- }
780
-
781
- async function sendWithPersona(
782
- client: WebClient,
783
- options: {
784
- channel: string;
785
- thread_ts: string;
786
- text: string;
787
- username: string;
788
- icon_emoji: string;
789
- }
790
- ): Promise<void> {
791
- await client.chat.postMessage({
792
- channel: options.channel,
793
- thread_ts: options.thread_ts,
794
- text: options.text,
795
- username: options.username,
796
- icon_emoji: options.icon_emoji,
797
- });
798
- }
799
-
800
- function getAgentEmoji(agent: Agent): string {
801
- if (agent.isLead) return ":crown:";
802
-
803
- // Generate consistent emoji based on agent name hash
804
- const emojis = [":robot_face:", ":gear:", ":zap:", ":rocket:", ":star:", ":crystal_ball:", ":bulb:", ":wrench:"];
805
- const hash = agent.name.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
806
- return emojis[hash % emojis.length];
807
- }
808
- ```
809
-
810
- #### 2. Completion Watcher (`src/slack/watcher.ts`)
811
-
812
- **File**: `src/slack/watcher.ts` (NEW)
813
-
814
- ```typescript
815
- import { getCompletedSlackTasks, updateTaskSlackNotified } from "../be/db";
816
- import { sendTaskResponse } from "./responses";
817
- import { getSlackApp } from "./app";
818
-
819
- let watcherInterval: ReturnType<typeof setInterval> | null = null;
820
-
821
- /**
822
- * Start watching for completed Slack tasks and sending responses.
823
- */
824
- export function startTaskWatcher(intervalMs = 5000): void {
825
- if (watcherInterval) {
826
- console.log("[Slack] Task watcher already running");
827
- return;
828
- }
829
-
830
- watcherInterval = setInterval(async () => {
831
- if (!getSlackApp()) return;
832
-
833
- const tasks = getCompletedSlackTasks();
834
-
835
- for (const task of tasks) {
836
- const sent = await sendTaskResponse(task);
837
- if (sent) {
838
- // Mark task as Slack-notified to prevent re-sending
839
- markTaskNotified(task.id);
840
- }
841
- }
842
- }, intervalMs);
843
-
844
- console.log(`[Slack] Task watcher started (interval: ${intervalMs}ms)`);
845
- }
846
-
847
- export function stopTaskWatcher(): void {
848
- if (watcherInterval) {
849
- clearInterval(watcherInterval);
850
- watcherInterval = null;
851
- console.log("[Slack] Task watcher stopped");
852
- }
853
- }
854
-
855
- // Track notified tasks in memory (persists across watcher cycles)
856
- const notifiedTasks = new Set<string>();
857
-
858
- function markTaskNotified(taskId: string): void {
859
- notifiedTasks.add(taskId);
860
- }
861
-
862
- // Override getCompletedSlackTasks to filter already-notified
863
- export function getUnnotifiedCompletedSlackTasks() {
864
- const { getCompletedSlackTasks } = require("../be/db");
865
- return getCompletedSlackTasks().filter((t: { id: string }) => !notifiedTasks.has(t.id));
866
- }
867
- ```
868
-
869
- **Note:** We use an in-memory set for tracking notified tasks. For production, consider adding a `slackNotifiedAt` column to the database.
870
-
871
- #### 3. Update Slack App to Start Watcher (`src/slack/app.ts`)
872
-
873
- Add watcher startup:
874
-
875
- ```typescript
876
- // Add import at top
877
- import { startTaskWatcher, stopTaskWatcher } from "./watcher";
878
-
879
- // Update startSlackApp function
880
- export async function startSlackApp(): Promise<void> {
881
- if (!app) {
882
- await initSlackApp();
883
- }
884
-
885
- if (app) {
886
- await app.start();
887
- console.log("[Slack] Bot connected via Socket Mode");
888
-
889
- // Start watching for task completions
890
- startTaskWatcher();
891
- }
892
- }
893
-
894
- // Update stopSlackApp function
895
- export async function stopSlackApp(): Promise<void> {
896
- stopTaskWatcher();
897
-
898
- if (app) {
899
- await app.stop();
900
- app = null;
901
- console.log("[Slack] Bot disconnected");
902
- }
903
- }
904
- ```
905
-
906
- ### Success Criteria:
907
-
908
- #### Automated Verification:
909
- - [x] TypeScript compiles: `bun run tsc:check`
910
- - [ ] Linting passes: `bun run lint`
911
- - [ ] Server starts without errors: `bun run dev:http`
912
-
913
- #### Manual Verification:
914
- - [ ] Create a task from Slack message
915
- - [ ] Complete the task via MCP (or manually update DB)
916
- - [ ] Verify completion message appears in Slack thread
917
- - [ ] Message shows agent's name as the sender
918
- - [ ] Failed tasks show error message in Slack
919
-
920
- **Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation that task responses appear correctly in Slack before proceeding to the next phase.
921
-
922
- ---
923
-
924
- ## Phase 5: Polish and Edge Cases
925
-
926
- ### Overview
927
- Handle multi-agent mentions, broadcasts, and improve error handling.
928
-
929
- ### Changes Required:
930
-
931
- #### 1. Update Handlers for Better UX (`src/slack/handlers.ts`)
932
-
933
- Update message handler with better feedback:
934
-
935
- ```typescript
936
- // Replace the task creation loop in registerMessageHandler
937
-
938
- // Create tasks for each matched agent
939
- const results: { success: string[]; failed: string[] } = { success: [], failed: [] };
940
-
941
- for (const match of matches) {
942
- const agent = getAgentById(match.agent.id);
943
-
944
- if (!agent) {
945
- results.failed.push(`${match.agent.name} (not found)`);
946
- continue;
947
- }
948
-
949
- if (agent.status !== "idle") {
950
- results.failed.push(`${agent.name} (${agent.status})`);
951
- continue;
952
- }
953
-
954
- try {
955
- const task = createTask(agent.id, taskDescription, {
956
- source: "slack",
957
- slackChannelId: msg.channel,
958
- slackThreadTs: msg.thread_ts || msg.ts,
959
- slackUserId: msg.user,
960
- });
961
- results.success.push(`${agent.name}`);
962
- } catch (error) {
963
- results.failed.push(`${agent.name} (error)`);
964
- }
965
- }
966
-
967
- // Send summary
968
- const parts: string[] = [];
969
- if (results.success.length > 0) {
970
- parts.push(`:white_check_mark: Task assigned to: ${results.success.join(", ")}`);
971
- }
972
- if (results.failed.length > 0) {
973
- parts.push(`:warning: Could not assign to: ${results.failed.join(", ")}`);
974
- }
975
-
976
- if (parts.length > 0) {
977
- await say({
978
- text: parts.join("\n"),
979
- thread_ts: msg.thread_ts || msg.ts,
980
- });
981
- }
982
- ```
983
-
984
- #### 2. Add Help Command
985
-
986
- Add to `src/slack/commands.ts`:
987
-
988
- ```typescript
989
- app.command("/agent-swarm-help", async ({ command, ack, respond }) => {
990
- await ack();
991
-
992
- await respond({
993
- response_type: "ephemeral",
994
- blocks: [
995
- {
996
- type: "header",
997
- text: { type: "plain_text", text: "Agent Swarm Help" },
998
- },
999
- {
1000
- type: "section",
1001
- text: {
1002
- type: "mrkdwn",
1003
- text: `*How to assign tasks:*
1004
- • Mention an agent by name: \`Hey Alpha, can you review this code?\`
1005
- • Use explicit ID: \`swarm#<uuid> please analyze the logs\`
1006
- • Broadcast to all: \`swarm#all status report please\`
1007
- • Mention the bot: \`@agent-swarm help me\` (routes to lead agent)
1008
-
1009
- *Commands:*
1010
- • \`/agent-swarm-status\` - Show all agents and their current status
1011
- • \`/agent-swarm-help\` - Show this help message`,
1012
- },
1013
- },
1014
- ],
1015
- });
1016
- });
1017
- ```
1018
-
1019
- Update manifest if needed for the new command.
1020
-
1021
- #### 3. Rate Limiting Protection
1022
-
1023
- Add simple rate limiting to prevent spam:
1024
-
1025
- ```typescript
1026
- // Add to src/slack/handlers.ts
1027
-
1028
- const rateLimitMap = new Map<string, number>();
1029
- const RATE_LIMIT_WINDOW = 60_000; // 1 minute
1030
- const MAX_REQUESTS_PER_WINDOW = 10;
1031
-
1032
- function checkRateLimit(userId: string): boolean {
1033
- const now = Date.now();
1034
- const userRequests = rateLimitMap.get(userId) || 0;
1035
-
1036
- // Simple sliding window (resets after window)
1037
- if (userRequests >= MAX_REQUESTS_PER_WINDOW) {
1038
- return false;
1039
- }
1040
-
1041
- rateLimitMap.set(userId, userRequests + 1);
1042
-
1043
- // Clean up after window
1044
- setTimeout(() => {
1045
- const current = rateLimitMap.get(userId) || 0;
1046
- if (current > 0) {
1047
- rateLimitMap.set(userId, current - 1);
1048
- }
1049
- }, RATE_LIMIT_WINDOW);
1050
-
1051
- return true;
1052
- }
1053
-
1054
- // Use in message handler:
1055
- if (!checkRateLimit(msg.user)) {
1056
- await say({
1057
- text: "You're sending too many requests. Please slow down.",
1058
- thread_ts: msg.thread_ts || msg.ts,
1059
- });
1060
- return;
1061
- }
1062
- ```
1063
-
1064
- ### Success Criteria:
1065
-
1066
- #### Automated Verification:
1067
- - [x] TypeScript compiles: `bun run tsc:check`
1068
- - [x] Linting passes: `bun run lint`
1069
-
1070
- #### Manual Verification:
1071
- - [ ] Multi-agent mention creates separate tasks
1072
- - [ ] `swarm#all` creates tasks for all non-lead agents
1073
- - [ ] Rate limiting prevents spam
1074
- - [ ] Help command shows usage instructions
1075
-
1076
- **Implementation Note**: After completing this phase and all automated verification passes, the Slack integration should be fully functional for manual testing.
1077
-
1078
- ---
1079
-
1080
- ## Testing Strategy
1081
-
1082
- ### Unit Tests
1083
-
1084
- Create `src/slack/router.test.ts`:
1085
-
1086
- ```typescript
1087
- import { test, expect, describe, beforeEach, mock } from "bun:test";
1088
- import { routeMessage, extractTaskFromMessage } from "./router";
1089
-
1090
- // Mock the database
1091
- mock.module("../be/db", () => ({
1092
- getAllAgents: () => [
1093
- { id: "1", name: "Alpha Worker", isLead: false, status: "idle" },
1094
- { id: "2", name: "Beta Tester", isLead: false, status: "idle" },
1095
- { id: "3", name: "Lead Agent", isLead: true, status: "idle" },
1096
- ],
1097
- getAgentById: (id: string) => {
1098
- const agents: Record<string, any> = {
1099
- "1": { id: "1", name: "Alpha Worker", isLead: false, status: "idle" },
1100
- "2": { id: "2", name: "Beta Tester", isLead: false, status: "idle" },
1101
- "3": { id: "3", name: "Lead Agent", isLead: true, status: "idle" },
1102
- };
1103
- return agents[id] || null;
1104
- },
1105
- }));
1106
-
1107
- describe("routeMessage", () => {
1108
- test("matches agent by partial name", () => {
1109
- const matches = routeMessage("Hey Alpha, can you help?", "BOT123", false);
1110
- expect(matches).toHaveLength(1);
1111
- expect(matches[0].agent.name).toBe("Alpha Worker");
1112
- });
1113
-
1114
- test("routes to lead when only bot mentioned", () => {
1115
- const matches = routeMessage("<@BOT123> help", "BOT123", true);
1116
- expect(matches).toHaveLength(1);
1117
- expect(matches[0].agent.isLead).toBe(true);
1118
- });
1119
-
1120
- test("handles swarm#all broadcast", () => {
1121
- const matches = routeMessage("swarm#all status check", "BOT123", false);
1122
- expect(matches).toHaveLength(2); // All non-lead agents
1123
- });
1124
- });
1125
-
1126
- describe("extractTaskFromMessage", () => {
1127
- test("removes bot mention", () => {
1128
- const task = extractTaskFromMessage("<@BOT123> please review this", "BOT123");
1129
- expect(task).toBe("please review this");
1130
- });
1131
- });
1132
- ```
1133
-
1134
- ### Integration Tests
1135
-
1136
- Manual testing checklist:
1137
-
1138
- 1. **Bot Connection**
1139
- - Start server with valid Slack tokens
1140
- - Verify bot shows as online in Slack
1141
-
1142
- 2. **Task Creation**
1143
- - Send message: "Alpha, please check the logs"
1144
- - Verify task appears in dashboard with source: "slack"
1145
- - Verify confirmation message in Slack
1146
-
1147
- 3. **Task Completion**
1148
- - Complete task via MCP/dashboard
1149
- - Verify completion message appears in Slack thread
1150
- - Verify message shows agent's name
1151
-
1152
- 4. **Error Handling**
1153
- - Message busy agent - verify error response
1154
- - Invalid agent name - verify no task created
1155
- - Missing task description - verify error response
1156
-
1157
- 5. **Commands**
1158
- - `/agent-swarm-status` - verify agent list
1159
- - `/agent-swarm-help` - verify help text
1160
-
1161
- ## Performance Considerations
1162
-
1163
- 1. **Task Watcher Interval**: Default 5 seconds. Increase for high-volume deployments.
1164
- 2. **Rate Limiting**: 10 requests/minute per user. Adjust based on team size.
1165
- 3. **Memory**: Notified task tracking uses in-memory Set. For long-running instances, consider periodic cleanup or DB column.
1166
-
1167
- ## Migration Notes
1168
-
1169
- ### Database Migration
1170
-
1171
- The schema changes add new columns with defaults, so existing data is preserved:
1172
- - `source` defaults to `"mcp"` for existing tasks
1173
- - `slackChannelId`, `slackThreadTs`, `slackUserId` default to NULL
1174
-
1175
- No manual migration needed - SQLite `ALTER TABLE` is handled by the schema update.
1176
-
1177
- ### Environment Variables
1178
-
1179
- Add to deployment configuration:
1180
- ```
1181
- SLACK_BOT_TOKEN=xoxb-...
1182
- SLACK_APP_TOKEN=xapp-...
1183
- ```
1184
-
1185
- Bot will gracefully disable if tokens are missing.
1186
-
1187
- ## References
1188
-
1189
- - Research document: `thoughts/shared/research/2025-12-18-slack-integration.md`
1190
- - Slack manifest: `slack-manifest.json`
1191
- - HTTP server: `src/http.ts`
1192
- - Database layer: `src/be/db.ts`
1193
- - Types: `src/types.ts`
1194
- - Task creation: `src/tools/send-task.ts`
1195
- - Slack Bolt docs: https://slack.dev/bolt-js/