@desplega.ai/agent-swarm 1.20.0 → 1.51.2

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 (561) hide show
  1. package/README.md +271 -169
  2. package/openapi.json +5015 -0
  3. package/package.json +40 -7
  4. package/plugin/commands/close-issue.md +7 -3
  5. package/plugin/commands/create-pr.md +18 -12
  6. package/plugin/commands/implement-issue.md +7 -3
  7. package/plugin/commands/respond-github.md +8 -4
  8. package/plugin/commands/review-pr.md +44 -10
  9. package/plugin/commands/start-leader.md +1 -3
  10. package/plugin/commands/start-worker.md +1 -3
  11. package/plugin/commands/work-on-task.md +22 -3
  12. package/plugin/pi-skills/close-issue/SKILL.md +90 -0
  13. package/plugin/pi-skills/create-pr/SKILL.md +99 -0
  14. package/plugin/pi-skills/implement-issue/SKILL.md +135 -0
  15. package/plugin/pi-skills/investigate-sentry-issue/SKILL.md +138 -0
  16. package/plugin/pi-skills/respond-github/SKILL.md +98 -0
  17. package/plugin/pi-skills/review-offered-task/SKILL.md +45 -0
  18. package/plugin/pi-skills/review-pr/SKILL.md +261 -0
  19. package/plugin/pi-skills/start-leader/SKILL.md +121 -0
  20. package/plugin/pi-skills/start-worker/SKILL.md +60 -0
  21. package/plugin/pi-skills/swarm-chat/SKILL.md +82 -0
  22. package/plugin/pi-skills/todos/SKILL.md +66 -0
  23. package/plugin/pi-skills/work-on-task/SKILL.md +65 -0
  24. package/plugin/skills/artifacts/examples/approval-flow.ts +34 -0
  25. package/plugin/skills/artifacts/examples/hono-dashboard.ts +31 -0
  26. package/plugin/skills/artifacts/examples/multi-artifact.ts +20 -0
  27. package/plugin/skills/artifacts/examples/static-report.sh +17 -0
  28. package/plugin/skills/artifacts/skill.md +71 -0
  29. package/src/agentmail/app.ts +65 -0
  30. package/src/agentmail/handlers.ts +262 -0
  31. package/src/agentmail/index.ts +9 -0
  32. package/src/agentmail/templates.ts +111 -0
  33. package/src/agentmail/types.ts +51 -0
  34. package/src/artifact-sdk/browser-sdk.ts +30 -0
  35. package/src/artifact-sdk/index.ts +2 -0
  36. package/src/artifact-sdk/localtunnel.d.ts +20 -0
  37. package/src/artifact-sdk/port.ts +12 -0
  38. package/src/artifact-sdk/server.ts +156 -0
  39. package/src/artifact-sdk/tunnel.ts +19 -0
  40. package/src/be/chunking.ts +193 -0
  41. package/src/be/db-queries/oauth.ts +90 -0
  42. package/src/be/db-queries/tracker.ts +182 -0
  43. package/src/be/db.ts +3327 -784
  44. package/src/be/embedding.ts +80 -0
  45. package/src/be/migrations/001_initial.sql +409 -0
  46. package/src/be/migrations/002_one_time_schedules.sql +59 -0
  47. package/src/be/migrations/003_workflows.sql +51 -0
  48. package/src/be/migrations/004_workflow_source.sql +81 -0
  49. package/src/be/migrations/005_epic_next_steps.sql +2 -0
  50. package/src/be/migrations/006_vcs_provider.sql +94 -0
  51. package/src/be/migrations/007_task_dir.sql +2 -0
  52. package/src/be/migrations/008_workflow_redesign.sql +85 -0
  53. package/src/be/migrations/009_tracker_integration.sql +144 -0
  54. package/src/be/migrations/010_step_diagnostics.sql +1 -0
  55. package/src/be/migrations/011_step_next_port.sql +1 -0
  56. package/src/be/migrations/012_trigger_schema.sql +1 -0
  57. package/src/be/migrations/013_task_output_schema.sql +2 -0
  58. package/src/be/migrations/014_prompt_templates.sql +33 -0
  59. package/src/be/migrations/015_workflow_workspace.sql +3 -0
  60. package/src/be/migrations/016_active_session_runner_session.sql +4 -0
  61. package/src/be/migrations/017_channel_activity_cursors.sql +6 -0
  62. package/src/be/migrations/018_fix_seed_double_version.sql +30 -0
  63. package/src/be/migrations/runner.ts +188 -0
  64. package/src/be/seed.ts +62 -0
  65. package/src/cli.tsx +231 -299
  66. package/src/commands/artifact.ts +241 -0
  67. package/src/commands/onboard/compose-generator.ts +169 -0
  68. package/src/commands/onboard/env-generator.ts +79 -0
  69. package/src/commands/onboard/manifest.ts +37 -0
  70. package/src/commands/onboard/presets.ts +85 -0
  71. package/src/commands/onboard/service-names.ts +47 -0
  72. package/src/commands/onboard/steps/core-credentials.tsx +111 -0
  73. package/src/commands/onboard/steps/custom-templates.tsx +168 -0
  74. package/src/commands/onboard/steps/generate.tsx +154 -0
  75. package/src/commands/onboard/steps/harness-credentials.tsx +195 -0
  76. package/src/commands/onboard/steps/harness.tsx +21 -0
  77. package/src/commands/onboard/steps/health-check.tsx +171 -0
  78. package/src/commands/onboard/steps/integration-github.tsx +105 -0
  79. package/src/commands/onboard/steps/integration-gitlab.tsx +79 -0
  80. package/src/commands/onboard/steps/integration-menu.tsx +58 -0
  81. package/src/commands/onboard/steps/integration-sentry.tsx +79 -0
  82. package/src/commands/onboard/steps/integration-slack.tsx +165 -0
  83. package/src/commands/onboard/steps/post-connect.tsx +145 -0
  84. package/src/commands/onboard/steps/post-dashboard.tsx +34 -0
  85. package/src/commands/onboard/steps/post-task.tsx +103 -0
  86. package/src/commands/onboard/steps/prereq-check.tsx +178 -0
  87. package/src/commands/onboard/steps/review.tsx +82 -0
  88. package/src/commands/onboard/steps/start.tsx +97 -0
  89. package/src/commands/onboard/templates.ts +34 -0
  90. package/src/commands/onboard/types.ts +259 -0
  91. package/src/commands/onboard.tsx +425 -0
  92. package/src/commands/runner.ts +1540 -630
  93. package/src/commands/setup.tsx +23 -38
  94. package/src/commands/shared/client-config.ts +41 -0
  95. package/src/commands/templates.ts +172 -0
  96. package/src/github/app.ts +8 -0
  97. package/src/github/handlers.ts +384 -151
  98. package/src/github/index.ts +1 -0
  99. package/src/github/mentions-aliases.test.ts +73 -0
  100. package/src/github/mentions.test.ts +3 -3
  101. package/src/github/mentions.ts +32 -6
  102. package/src/github/templates.ts +398 -0
  103. package/src/github/types.ts +1 -0
  104. package/src/gitlab/auth.ts +63 -0
  105. package/src/gitlab/handlers.ts +368 -0
  106. package/src/gitlab/index.ts +19 -0
  107. package/src/gitlab/reactions.ts +104 -0
  108. package/src/gitlab/templates.ts +140 -0
  109. package/src/gitlab/types.ts +130 -0
  110. package/src/heartbeat/heartbeat.ts +434 -0
  111. package/src/heartbeat/index.ts +1 -0
  112. package/src/heartbeat/templates.ts +30 -0
  113. package/src/hooks/hook.ts +555 -4
  114. package/src/hooks/tool-loop-detection.test.ts +158 -0
  115. package/src/hooks/tool-loop-detection.ts +167 -0
  116. package/src/http/active-sessions.ts +199 -0
  117. package/src/http/agents.ts +328 -0
  118. package/src/http/config.ts +191 -0
  119. package/src/http/core.ts +309 -0
  120. package/src/http/db-query.ts +91 -0
  121. package/src/http/ecosystem.ts +63 -0
  122. package/src/http/epics.ts +460 -0
  123. package/src/http/index.ts +216 -0
  124. package/src/http/mcp.ts +77 -0
  125. package/src/http/memory.ts +168 -0
  126. package/src/http/openapi.ts +109 -0
  127. package/src/http/poll.ts +299 -0
  128. package/src/http/prompt-templates.ts +412 -0
  129. package/src/http/repos.ts +195 -0
  130. package/src/http/route-def.ts +123 -0
  131. package/src/http/schedules.ts +426 -0
  132. package/src/http/session-data.ts +241 -0
  133. package/src/http/stats.ts +174 -0
  134. package/src/http/tasks.ts +468 -0
  135. package/src/http/trackers/index.ts +10 -0
  136. package/src/http/trackers/linear.ts +187 -0
  137. package/src/http/types.ts +12 -0
  138. package/src/http/utils.ts +87 -0
  139. package/src/http/webhooks.ts +432 -0
  140. package/src/http/workflows.ts +530 -0
  141. package/src/http.ts +1 -1890
  142. package/src/linear/README.md +65 -0
  143. package/src/linear/app.ts +48 -0
  144. package/src/linear/client.ts +18 -0
  145. package/src/linear/index.ts +1 -0
  146. package/src/linear/oauth.ts +35 -0
  147. package/src/linear/outbound.ts +212 -0
  148. package/src/linear/sync.ts +567 -0
  149. package/src/linear/templates.ts +47 -0
  150. package/src/linear/types.ts +7 -0
  151. package/src/linear/webhook.ts +104 -0
  152. package/src/oauth/README.md +66 -0
  153. package/src/oauth/index.ts +6 -0
  154. package/src/oauth/wrapper.ts +204 -0
  155. package/src/prompts/base-prompt.ts +150 -265
  156. package/src/prompts/defaults.ts +196 -0
  157. package/src/prompts/registry.ts +57 -0
  158. package/src/prompts/resolver.ts +296 -0
  159. package/src/prompts/session-templates.ts +604 -0
  160. package/src/providers/claude-adapter.ts +442 -0
  161. package/src/providers/index.ts +24 -0
  162. package/src/providers/pi-mono-adapter.ts +442 -0
  163. package/src/providers/pi-mono-extension.ts +624 -0
  164. package/src/providers/pi-mono-mcp-client.ts +124 -0
  165. package/src/providers/types.ts +75 -0
  166. package/src/scheduler/scheduler.test.ts +2 -0
  167. package/src/scheduler/scheduler.ts +231 -40
  168. package/src/server.ts +97 -6
  169. package/src/slack/HEURISTICS.md +105 -0
  170. package/src/slack/actions.ts +133 -0
  171. package/src/slack/app.ts +7 -0
  172. package/src/slack/assistant.ts +118 -0
  173. package/src/slack/blocks.ts +233 -0
  174. package/src/slack/channel-activity.ts +177 -0
  175. package/src/slack/commands.ts +31 -17
  176. package/src/slack/files.ts +1 -1
  177. package/src/slack/handlers.test.ts +114 -1
  178. package/src/slack/handlers.ts +230 -55
  179. package/src/slack/responses.ts +120 -67
  180. package/src/slack/router.ts +17 -99
  181. package/src/slack/templates.ts +55 -0
  182. package/src/slack/thread-buffer.ts +213 -0
  183. package/src/slack/watcher.ts +119 -4
  184. package/src/tests/agent-activity.test.ts +247 -0
  185. package/src/tests/agentmail-filters.test.ts +97 -0
  186. package/src/tests/artifact-sdk.test.ts +800 -0
  187. package/src/tests/base-prompt.test.ts +264 -0
  188. package/src/tests/build-pi-skills.test.ts +127 -0
  189. package/src/tests/channel-activity.test.ts +363 -0
  190. package/src/tests/claude-adapter.test.ts +126 -0
  191. package/src/tests/context-versioning.test.ts +425 -0
  192. package/src/tests/db-queries-oauth.test.ts +197 -0
  193. package/src/tests/db-queries-tracker.test.ts +230 -0
  194. package/src/tests/epics.test.ts +3 -3
  195. package/src/tests/error-tracker.test.ts +368 -0
  196. package/src/tests/fetch-resolved-env.test.ts +167 -0
  197. package/src/tests/generate-default-claude-md.test.ts +9 -1
  198. package/src/tests/generate-identity-templates.test.ts +124 -0
  199. package/src/tests/gitlab-auth.test.ts +109 -0
  200. package/src/tests/gitlab-handlers.test.ts +691 -0
  201. package/src/tests/gitlab-vcs-db.test.ts +177 -0
  202. package/src/tests/heartbeat.test.ts +364 -0
  203. package/src/tests/http-api-integration.test.ts +1698 -0
  204. package/src/tests/linear-outbound-sync.test.ts +200 -0
  205. package/src/tests/linear-webhook.test.ts +406 -0
  206. package/src/tests/match-route.test.ts +187 -0
  207. package/src/tests/memory.test.ts +737 -0
  208. package/src/tests/migration-runner-regressions.test.ts +86 -0
  209. package/src/tests/model-control.test.ts +338 -0
  210. package/src/tests/oauth-wrapper.test.ts +147 -0
  211. package/src/tests/onboard-compose.test.ts +138 -0
  212. package/src/tests/onboard-env.test.ts +174 -0
  213. package/src/tests/onboard-manifest.test.ts +137 -0
  214. package/src/tests/pi-mono-adapter.test.ts +234 -0
  215. package/src/tests/pool-session-logs.test.ts +199 -0
  216. package/src/tests/progress-dedup.test.ts +98 -0
  217. package/src/tests/prompt-template-github.test.ts +682 -0
  218. package/src/tests/prompt-template-remaining.test.ts +504 -0
  219. package/src/tests/prompt-template-resolver.test.ts +621 -0
  220. package/src/tests/prompt-template-session.test.ts +363 -0
  221. package/src/tests/prompt-templates-db.test.ts +616 -0
  222. package/src/tests/provider-adapter.test.ts +122 -0
  223. package/src/tests/provider-command-format.test.ts +98 -0
  224. package/src/tests/reload-config.test.ts +170 -0
  225. package/src/tests/runner-polling-api.test.ts +25 -20
  226. package/src/tests/scheduled-tasks.test.ts +104 -0
  227. package/src/tests/scheduler-backoff.test.ts +166 -0
  228. package/src/tests/self-improvement.test.ts +541 -0
  229. package/src/tests/session-attach.test.ts +536 -0
  230. package/src/tests/session-costs.test.ts +267 -1
  231. package/src/tests/slack-actions.test.ts +133 -0
  232. package/src/tests/slack-assistant.test.ts +136 -0
  233. package/src/tests/slack-blocks.test.ts +246 -0
  234. package/src/tests/slack-metadata-inheritance.test.ts +243 -0
  235. package/src/tests/slack-queue-offline.test.ts +174 -0
  236. package/src/tests/slack-router.test.ts +181 -0
  237. package/src/tests/slack-thread-buffer.test.ts +305 -0
  238. package/src/tests/slack-thread-followups.test.ts +298 -0
  239. package/src/tests/slack-watcher.test.ts +101 -0
  240. package/src/tests/structured-output.test.ts +307 -0
  241. package/src/tests/swarm-repos.test.ts +198 -0
  242. package/src/tests/task-cancellation.test.ts +6 -4
  243. package/src/tests/task-working-dir.test.ts +176 -0
  244. package/src/tests/template-fetch.test.ts +490 -0
  245. package/src/tests/tool-annotations.test.ts +371 -0
  246. package/src/tests/tracker-tools.test.ts +184 -0
  247. package/src/tests/update-profile-agentid.test.ts +248 -0
  248. package/src/tests/update-profile-api.test.ts +143 -3
  249. package/src/tests/update-profile-auth.test.ts +195 -0
  250. package/src/tests/validation-adapters.test.ts +86 -0
  251. package/src/tests/vcs-provider.test.ts +27 -0
  252. package/src/tests/workflow-agent-task.test.ts +196 -0
  253. package/src/tests/workflow-async-v2.test.ts +508 -0
  254. package/src/tests/workflow-convergence.test.ts +541 -0
  255. package/src/tests/workflow-definition-validation.test.ts +366 -0
  256. package/src/tests/workflow-engine-v2.test.ts +691 -0
  257. package/src/tests/workflow-executors.test.ts +736 -0
  258. package/src/tests/workflow-http-v2.test.ts +599 -0
  259. package/src/tests/workflow-integration-io.test.ts +902 -0
  260. package/src/tests/workflow-io-schemas.test.ts +624 -0
  261. package/src/tests/workflow-registry.test.ts +592 -0
  262. package/src/tests/workflow-retry-v2.test.ts +401 -0
  263. package/src/tests/workflow-retry-validation.test.ts +282 -0
  264. package/src/tests/workflow-schedule-trigger.test.ts +104 -0
  265. package/src/tests/workflow-template.test.ts +288 -0
  266. package/src/tests/workflow-trigger-schema.test.ts +359 -0
  267. package/src/tests/workflow-triggers-v2.test.ts +264 -0
  268. package/src/tests/workflow-versions.test.ts +208 -0
  269. package/src/tests/workflow-workspace.test.ts +272 -0
  270. package/src/tests/x402-client.test.ts +117 -0
  271. package/src/tests/x402-config.test.ts +182 -0
  272. package/src/tests/x402-spending-tracker.test.ts +185 -0
  273. package/src/tools/cancel-task.ts +2 -0
  274. package/src/tools/context-diff.ts +171 -0
  275. package/src/tools/context-history.ts +138 -0
  276. package/src/tools/create-channel.ts +1 -0
  277. package/src/tools/db-query.ts +78 -0
  278. package/src/tools/delete-channel.ts +132 -0
  279. package/src/tools/epics/assign-task-to-epic.ts +1 -0
  280. package/src/tools/epics/create-epic.ts +3 -2
  281. package/src/tools/epics/delete-epic.ts +2 -0
  282. package/src/tools/epics/get-epic-details.ts +2 -0
  283. package/src/tools/epics/list-epics.ts +2 -0
  284. package/src/tools/epics/unassign-task-from-epic.ts +1 -0
  285. package/src/tools/epics/update-epic.ts +7 -4
  286. package/src/tools/get-swarm.ts +2 -0
  287. package/src/tools/get-task-details.ts +2 -0
  288. package/src/tools/get-tasks.ts +27 -1
  289. package/src/tools/inject-learning.ts +106 -0
  290. package/src/tools/join-swarm.ts +17 -7
  291. package/src/tools/list-channels.ts +2 -0
  292. package/src/tools/list-services.ts +2 -0
  293. package/src/tools/memory-get.ts +56 -0
  294. package/src/tools/memory-search.ts +131 -0
  295. package/src/tools/my-agent-info.ts +2 -0
  296. package/src/tools/poll-task.ts +2 -20
  297. package/src/tools/post-message.ts +1 -0
  298. package/src/tools/prompt-templates/delete.ts +86 -0
  299. package/src/tools/prompt-templates/get.ts +89 -0
  300. package/src/tools/prompt-templates/index.ts +5 -0
  301. package/src/tools/prompt-templates/list.ts +95 -0
  302. package/src/tools/prompt-templates/preview.ts +84 -0
  303. package/src/tools/prompt-templates/set.ts +117 -0
  304. package/src/tools/read-messages.ts +2 -0
  305. package/src/tools/register-agentmail-inbox.ts +166 -0
  306. package/src/tools/register-service.ts +2 -0
  307. package/src/tools/schedules/create-schedule.ts +134 -24
  308. package/src/tools/schedules/delete-schedule.ts +2 -0
  309. package/src/tools/schedules/list-schedules.ts +20 -4
  310. package/src/tools/schedules/run-schedule-now.ts +1 -0
  311. package/src/tools/schedules/update-schedule.ts +49 -17
  312. package/src/tools/send-task.ts +132 -10
  313. package/src/tools/slack-download-file.ts +4 -2
  314. package/src/tools/slack-list-channels.ts +2 -0
  315. package/src/tools/slack-post.ts +2 -0
  316. package/src/tools/slack-read.ts +2 -0
  317. package/src/tools/slack-reply.ts +2 -0
  318. package/src/tools/slack-upload-file.ts +2 -0
  319. package/src/tools/store-progress.ts +205 -4
  320. package/src/tools/swarm-config/delete-config.ts +87 -0
  321. package/src/tools/swarm-config/get-config.ts +108 -0
  322. package/src/tools/swarm-config/index.ts +4 -0
  323. package/src/tools/swarm-config/list-config.ts +99 -0
  324. package/src/tools/swarm-config/set-config.ts +118 -0
  325. package/src/tools/task-action.ts +50 -5
  326. package/src/tools/task-dedup.ts +97 -0
  327. package/src/tools/templates.ts +53 -0
  328. package/src/tools/tool-config.ts +124 -0
  329. package/src/tools/tracker/index.ts +6 -0
  330. package/src/tools/tracker/tracker-link-epic.ts +64 -0
  331. package/src/tools/tracker/tracker-link-task.ts +64 -0
  332. package/src/tools/tracker/tracker-map-agent.ts +57 -0
  333. package/src/tools/tracker/tracker-status.ts +56 -0
  334. package/src/tools/tracker/tracker-sync-status.ts +42 -0
  335. package/src/tools/tracker/tracker-unlink.ts +41 -0
  336. package/src/tools/unregister-service.ts +2 -0
  337. package/src/tools/update-profile.ts +172 -17
  338. package/src/tools/update-service-status.ts +2 -0
  339. package/src/tools/utils.ts +10 -1
  340. package/src/tools/workflows/create-workflow.ts +129 -0
  341. package/src/tools/workflows/delete-workflow.ts +42 -0
  342. package/src/tools/workflows/get-workflow-run.ts +59 -0
  343. package/src/tools/workflows/get-workflow.ts +53 -0
  344. package/src/tools/workflows/index.ts +9 -0
  345. package/src/tools/workflows/list-workflow-runs.ts +48 -0
  346. package/src/tools/workflows/list-workflows.ts +42 -0
  347. package/src/tools/workflows/retry-workflow-run.ts +40 -0
  348. package/src/tools/workflows/trigger-workflow.ts +96 -0
  349. package/src/tools/workflows/update-workflow.ts +133 -0
  350. package/src/tracker/types.ts +51 -0
  351. package/src/types.ts +530 -14
  352. package/src/utils/credentials.test.ts +156 -0
  353. package/src/utils/credentials.ts +50 -0
  354. package/src/utils/error-tracker.ts +190 -0
  355. package/src/vcs/index.ts +15 -0
  356. package/src/vcs/types.ts +5 -0
  357. package/src/workflows/checkpoint.ts +121 -0
  358. package/src/workflows/cooldown.ts +28 -0
  359. package/src/workflows/definition.ts +235 -0
  360. package/src/workflows/engine.ts +580 -0
  361. package/src/workflows/event-bus.ts +29 -0
  362. package/src/workflows/executors/agent-task.ts +103 -0
  363. package/src/workflows/executors/base.ts +86 -0
  364. package/src/workflows/executors/code-match.ts +88 -0
  365. package/src/workflows/executors/index.ts +16 -0
  366. package/src/workflows/executors/notify.ts +93 -0
  367. package/src/workflows/executors/property-match.ts +104 -0
  368. package/src/workflows/executors/raw-llm.ts +83 -0
  369. package/src/workflows/executors/registry.ts +76 -0
  370. package/src/workflows/executors/script.ts +103 -0
  371. package/src/workflows/executors/validate.ts +215 -0
  372. package/src/workflows/executors/vcs.ts +58 -0
  373. package/src/workflows/index.ts +61 -0
  374. package/src/workflows/input.ts +46 -0
  375. package/src/workflows/json-schema-validator.ts +118 -0
  376. package/src/workflows/recovery.ts +139 -0
  377. package/src/workflows/resume.ts +229 -0
  378. package/src/workflows/retry-poller.ts +216 -0
  379. package/src/workflows/template.ts +74 -0
  380. package/src/workflows/templates.ts +86 -0
  381. package/src/workflows/triggers.ts +124 -0
  382. package/src/workflows/validation.ts +104 -0
  383. package/src/workflows/version.ts +44 -0
  384. package/src/x402/cli.ts +140 -0
  385. package/src/x402/client.ts +192 -0
  386. package/src/x402/config.ts +131 -0
  387. package/src/x402/index.ts +37 -0
  388. package/src/x402/openfort-signer.ts +83 -0
  389. package/src/x402/spending-tracker.ts +109 -0
  390. package/templates/official/coder/CLAUDE.md +49 -0
  391. package/templates/official/coder/IDENTITY.md +28 -0
  392. package/templates/official/coder/SOUL.md +43 -0
  393. package/templates/official/coder/TOOLS.md +40 -0
  394. package/templates/official/coder/config.json +23 -0
  395. package/templates/official/coder/start-up.sh +23 -0
  396. package/templates/official/content-reviewer/CLAUDE.md +68 -0
  397. package/templates/official/content-reviewer/IDENTITY.md +28 -0
  398. package/templates/official/content-reviewer/SOUL.md +44 -0
  399. package/templates/official/content-reviewer/TOOLS.md +37 -0
  400. package/templates/official/content-reviewer/config.json +23 -0
  401. package/templates/official/content-reviewer/start-up.sh +23 -0
  402. package/templates/official/content-strategist/CLAUDE.md +63 -0
  403. package/templates/official/content-strategist/IDENTITY.md +33 -0
  404. package/templates/official/content-strategist/SOUL.md +48 -0
  405. package/templates/official/content-strategist/TOOLS.md +47 -0
  406. package/templates/official/content-strategist/config.json +23 -0
  407. package/templates/official/content-strategist/start-up.sh +23 -0
  408. package/templates/official/content-writer/CLAUDE.md +72 -0
  409. package/templates/official/content-writer/IDENTITY.md +30 -0
  410. package/templates/official/content-writer/SOUL.md +46 -0
  411. package/templates/official/content-writer/TOOLS.md +44 -0
  412. package/templates/official/content-writer/config.json +23 -0
  413. package/templates/official/content-writer/start-up.sh +23 -0
  414. package/templates/official/forward-deployed-engineer/CLAUDE.md +54 -0
  415. package/templates/official/forward-deployed-engineer/IDENTITY.md +37 -0
  416. package/templates/official/forward-deployed-engineer/SOUL.md +55 -0
  417. package/templates/official/forward-deployed-engineer/config.json +21 -0
  418. package/templates/official/lead/CLAUDE.md +33 -0
  419. package/templates/official/lead/IDENTITY.md +36 -0
  420. package/templates/official/lead/SOUL.md +51 -0
  421. package/templates/official/lead/config.json +22 -0
  422. package/templates/official/researcher/CLAUDE.md +46 -0
  423. package/templates/official/researcher/IDENTITY.md +28 -0
  424. package/templates/official/researcher/SOUL.md +43 -0
  425. package/templates/official/researcher/config.json +21 -0
  426. package/templates/official/reviewer/CLAUDE.md +63 -0
  427. package/templates/official/reviewer/IDENTITY.md +28 -0
  428. package/templates/official/reviewer/SOUL.md +45 -0
  429. package/templates/official/reviewer/config.json +21 -0
  430. package/templates/official/tester/CLAUDE.md +53 -0
  431. package/templates/official/tester/IDENTITY.md +28 -0
  432. package/templates/official/tester/SOUL.md +55 -0
  433. package/templates/official/tester/config.json +21 -0
  434. package/templates/schema.ts +35 -0
  435. package/.claude/settings.local.json +0 -115
  436. package/.dockerignore +0 -61
  437. package/.editorconfig +0 -15
  438. package/.env.docker.example +0 -39
  439. package/.env.example +0 -40
  440. package/.github/workflows/ci.yml +0 -76
  441. package/.github/workflows/docker-and-deploy.yml +0 -117
  442. package/.wts-config.json +0 -4
  443. package/.wts-setup.ts +0 -102
  444. package/CLAUDE.md +0 -104
  445. package/CONTRIBUTING.md +0 -270
  446. package/DEPLOYMENT.md +0 -605
  447. package/Dockerfile +0 -57
  448. package/Dockerfile.worker +0 -157
  449. package/FAQ.md +0 -19
  450. package/MCP.md +0 -406
  451. package/UI.md +0 -40
  452. package/assets/agent-swarm-logo-orange.png +0 -0
  453. package/assets/agent-swarm-logo.png +0 -0
  454. package/assets/agent-swarm.mp4 +0 -0
  455. package/assets/agent-swarm.png +0 -0
  456. package/biome.json +0 -39
  457. package/deploy/DEPLOY.md +0 -60
  458. package/deploy/agent-swarm.service +0 -17
  459. package/deploy/docker-push.ts +0 -30
  460. package/deploy/install.ts +0 -85
  461. package/deploy/prod-db.ts +0 -42
  462. package/deploy/uninstall.ts +0 -12
  463. package/deploy/update.ts +0 -21
  464. package/docker-compose.example.yml +0 -159
  465. package/docker-entrypoint.sh +0 -352
  466. package/ecosystem.config.cjs +0 -66
  467. package/plugin/README.md +0 -1
  468. package/plugin/hooks/hooks.json +0 -71
  469. package/pyproject.toml +0 -9
  470. package/scripts/generate-mcp-docs.ts +0 -415
  471. package/slack-manifest.json +0 -71
  472. package/src/tests/get-inbox-message.test.ts +0 -145
  473. package/src/tools/get-inbox-message.ts +0 -89
  474. package/src/tools/inbox-delegate.ts +0 -113
  475. package/thoughts/shared/plans/2025-12-18-slack-integration.md +0 -1195
  476. package/thoughts/shared/plans/2025-12-19-agent-log-streaming.md +0 -732
  477. package/thoughts/shared/plans/2025-12-19-role-based-swarm-plugin.md +0 -361
  478. package/thoughts/shared/plans/2025-12-20-mobile-responsive-ui.md +0 -501
  479. package/thoughts/shared/plans/2025-12-20-startup-team-swarm.md +0 -560
  480. package/thoughts/shared/plans/2025-12-23-runner-level-polling.md +0 -934
  481. package/thoughts/shared/plans/2025-12-23-runner-session-logs.md +0 -1000
  482. package/thoughts/shared/plans/2025-12-23-worker-lead-spawn-triggers.md +0 -568
  483. package/thoughts/shared/plans/2026-01-09-inverse-teleport.md +0 -1516
  484. package/thoughts/shared/plans/2026-01-12-agent-rename-pm2-control.md +0 -1133
  485. package/thoughts/shared/plans/2026-01-12-github-app-integration.md +0 -380
  486. package/thoughts/shared/plans/2026-01-12-lead-inbox-model.md +0 -876
  487. package/thoughts/shared/plans/2026-01-12-ralph-wiggum-integration.md +0 -463
  488. package/thoughts/shared/plans/2026-01-13-agent-concurrency.md +0 -691
  489. package/thoughts/shared/plans/2026-01-13-github-assignment-handling.md +0 -690
  490. package/thoughts/shared/plans/2026-01-13-prevent-duplicate-trigger-processing.md +0 -1071
  491. package/thoughts/shared/plans/2026-01-14-fix-slack-thread-context.md +0 -507
  492. package/thoughts/shared/plans/2026-01-15-scheduled-tasks-implementation.md +0 -565
  493. package/thoughts/shared/plans/2026-01-15-usage-cost-tracking-ui.md +0 -1479
  494. package/thoughts/shared/plans/2026-01-16-epics-feature-implementation.md +0 -1230
  495. package/thoughts/shared/research/.gitkeep +0 -0
  496. package/thoughts/shared/research/2025-01-09-inverse-teleport-plan-review.md +0 -420
  497. package/thoughts/shared/research/2025-12-18-slack-integration.md +0 -442
  498. package/thoughts/shared/research/2025-12-19-agent-log-streaming.md +0 -339
  499. package/thoughts/shared/research/2025-12-19-agent-secrets-cli-research.md +0 -390
  500. package/thoughts/shared/research/2025-12-21-gemini-cli-integration.md +0 -376
  501. package/thoughts/shared/research/2025-12-22-runner-loop-architecture.md +0 -582
  502. package/thoughts/shared/research/2025-12-22-setup-experience-improvements.md +0 -264
  503. package/thoughts/shared/research/2026-01-13-lead-duplicate-trigger-processing.md +0 -223
  504. package/thoughts/shared/research/2026-01-14-lead-slack-thread-context.md +0 -277
  505. package/thoughts/shared/research/2026-01-15-ai-tracker-agent-swarm-integration.md +0 -376
  506. package/thoughts/shared/research/2026-01-15-auto-starting-processes-in-worker-containers.md +0 -787
  507. package/thoughts/shared/research/2026-01-15-scheduled-tasks.md +0 -390
  508. package/thoughts/shared/research/2026-01-16-epics-feature-research.md +0 -437
  509. package/thoughts/taras/plans/2026-01-22-agent-swarm-schemas.md +0 -98
  510. package/thoughts/taras/plans/2026-01-28-per-worker-claude-md.md +0 -617
  511. package/thoughts/taras/plans/2026-01-28-sentry-cli-integration.md +0 -214
  512. package/thoughts/taras/research/2026-01-22-vercel-cli-integration.md +0 -287
  513. package/thoughts/taras/research/2026-01-27-excessive-polling-issue.md +0 -311
  514. package/thoughts/taras/research/2026-01-28-per-worker-claude-md.md +0 -383
  515. package/thoughts/taras/research/2026-01-28-sentry-cli-integration.md +0 -240
  516. package/tsconfig.json +0 -37
  517. package/ui/CLAUDE.md +0 -49
  518. package/ui/bun.lock +0 -771
  519. package/ui/index.html +0 -22
  520. package/ui/package-lock.json +0 -5290
  521. package/ui/package.json +0 -33
  522. package/ui/pnpm-lock.yaml +0 -3341
  523. package/ui/postcss.config.js +0 -6
  524. package/ui/public/logo.png +0 -0
  525. package/ui/src/App.tsx +0 -63
  526. package/ui/src/components/ActivityFeed.tsx +0 -440
  527. package/ui/src/components/AgentDetailPanel.tsx +0 -733
  528. package/ui/src/components/AgentsPanel.tsx +0 -815
  529. package/ui/src/components/ChatPanel.tsx +0 -1920
  530. package/ui/src/components/ConfigModal.tsx +0 -253
  531. package/ui/src/components/Dashboard.tsx +0 -832
  532. package/ui/src/components/EditAgentProfileModal.tsx +0 -433
  533. package/ui/src/components/EpicDetailPage.tsx +0 -741
  534. package/ui/src/components/EpicsPanel.tsx +0 -566
  535. package/ui/src/components/Header.tsx +0 -160
  536. package/ui/src/components/JsonViewer.tsx +0 -171
  537. package/ui/src/components/ScheduledTaskDetailPanel.tsx +0 -517
  538. package/ui/src/components/ScheduledTasksPanel.tsx +0 -639
  539. package/ui/src/components/ServicesPanel.tsx +0 -622
  540. package/ui/src/components/SessionLogPanel.tsx +0 -1219
  541. package/ui/src/components/StatsBar.tsx +0 -321
  542. package/ui/src/components/StatusBadge.tsx +0 -168
  543. package/ui/src/components/TaskDetailPanel.tsx +0 -903
  544. package/ui/src/components/TasksPanel.tsx +0 -614
  545. package/ui/src/components/UsageCharts.tsx +0 -216
  546. package/ui/src/components/UsageTab.tsx +0 -394
  547. package/ui/src/hooks/queries.ts +0 -353
  548. package/ui/src/hooks/useAutoScroll.ts +0 -83
  549. package/ui/src/index.css +0 -257
  550. package/ui/src/lib/api.ts +0 -268
  551. package/ui/src/lib/config.ts +0 -35
  552. package/ui/src/lib/contentPreview.ts +0 -208
  553. package/ui/src/lib/theme.ts +0 -214
  554. package/ui/src/lib/utils.ts +0 -88
  555. package/ui/src/main.tsx +0 -28
  556. package/ui/src/types/api.ts +0 -323
  557. package/ui/src/vite-env.d.ts +0 -1
  558. package/ui/tailwind.config.js +0 -37
  559. package/ui/tsconfig.json +0 -31
  560. package/ui/vite.config.ts +0 -35
  561. /package/{thoughts/shared/plans → templates/community}/.gitkeep +0 -0
@@ -1,7 +1,23 @@
1
1
  import type { App } from "@slack/bolt";
2
2
  import type { WebClient } from "@slack/web-api";
3
- import { createInboxMessage, createTask, getAgentById, getTasksByAgentId } from "../be/db";
3
+ import {
4
+ createTask,
5
+ createTaskExtended,
6
+ getAgentById,
7
+ getAgentWorkingOnThread,
8
+ getLeadAgent,
9
+ getMostRecentTaskInThread,
10
+ getTasksByAgentId,
11
+ } from "../be/db";
12
+ import { resolveTemplate } from "../prompts/resolver";
13
+ import { workflowEventBus } from "../workflows/event-bus";
14
+ import { buildAssignmentSummaryBlocks } from "./blocks";
15
+ import type { SlackFile } from "./files";
4
16
  import { extractTaskFromMessage, routeMessage } from "./router";
17
+ // Side-effect import: registers all Slack event templates in the in-memory registry
18
+ import "./templates";
19
+ import { bufferThreadMessage, getBufferMessageCount, instantFlush } from "./thread-buffer";
20
+ import { registerTaskMessage } from "./watcher";
5
21
 
6
22
  // User filtering configuration from environment variables
7
23
  const allowedEmailDomains = (process.env.SLACK_ALLOWED_EMAIL_DOMAINS || "")
@@ -132,6 +148,7 @@ interface MessageEvent {
132
148
  channel: string;
133
149
  ts: string;
134
150
  thread_ts?: string;
151
+ files?: SlackFile[];
135
152
  }
136
153
 
137
154
  interface ThreadMessage {
@@ -210,17 +227,43 @@ async function getThreadContext(
210
227
  }
211
228
  }
212
229
 
213
- const appUrl = process.env.APP_URL || "";
230
+ /**
231
+ * Format a file size in bytes to a human-readable string.
232
+ */
233
+ export function formatFileSize(bytes: number): string {
234
+ if (bytes < 1024) return `${bytes} B`;
235
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
236
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
237
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
238
+ }
239
+
240
+ /**
241
+ * Build a text representation of file attachments for inclusion in messages.
242
+ * Each file is formatted as: [File: filename.ext (mimetype, size) id=FILE_ID]
243
+ */
244
+ export function buildAttachmentText(files: SlackFile[]): string {
245
+ return files
246
+ .map((f) => `[File: ${f.name} (${f.mimetype}, ${formatFileSize(f.size)}) id=${f.id}]`)
247
+ .join("\n");
248
+ }
214
249
 
215
250
  /**
216
- * Get a link to the task in the dashboard, or just the task ID if no APP_URL.
251
+ * Build the effective message text from the original text and any file attachments.
252
+ * - Text only: returns the text as-is
253
+ * - Files only: returns the attachment metadata
254
+ * - Both: returns text followed by attachment metadata
217
255
  */
218
- function getTaskLink(taskId: string): string {
219
- const shortId = taskId.slice(0, 8);
220
- if (appUrl) {
221
- return `<${appUrl}?tab=tasks&task=${taskId}&expand=true|\`${shortId}\`>`;
256
+ export function buildEffectiveText(text: string | undefined, files?: SlackFile[]): string {
257
+ const hasText = !!text?.trim();
258
+ const hasFiles = files && files.length > 0;
259
+
260
+ if (hasText && hasFiles) {
261
+ return `${text}\n\n${buildAttachmentText(files)}`;
222
262
  }
223
- return `\`${shortId}\``;
263
+ if (hasFiles) {
264
+ return buildAttachmentText(files);
265
+ }
266
+ return text || "";
224
267
  }
225
268
 
226
269
  // Message deduplication (prevents duplicate event processing)
@@ -275,7 +318,11 @@ export function registerMessageHandler(app: App): void {
275
318
  }
276
319
 
277
320
  const msg = event as MessageEvent;
278
- if (!msg.text || !msg.user) return;
321
+ const hasText = !!msg.text?.trim();
322
+ const hasFiles = !!(msg.files && msg.files.length > 0);
323
+
324
+ // Require either text or files, and always require a user
325
+ if ((!hasText && !hasFiles) || !msg.user) return;
279
326
 
280
327
  // Deduplicate events (Slack can send same event twice)
281
328
  const messageKey = `${msg.channel}:${msg.ts}`;
@@ -289,29 +336,141 @@ export function registerMessageHandler(app: App): void {
289
336
  return;
290
337
  }
291
338
 
339
+ // Emit workflow trigger event for Slack messages
340
+ workflowEventBus.emit("slack.message", {
341
+ channel: msg.channel,
342
+ text: msg.text,
343
+ user: msg.user,
344
+ ts: msg.ts,
345
+ threadTs: msg.thread_ts,
346
+ });
347
+
348
+ // Build effective text that includes attachment metadata
349
+ const effectiveText = buildEffectiveText(msg.text, msg.files);
350
+
292
351
  // Get bot's user ID
293
352
  const authResult = await client.auth.test();
294
353
  const botUserId = authResult.user_id as string;
295
354
 
296
- // Check if bot was mentioned
297
- const botMentioned = msg.text.includes(`<@${botUserId}>`);
355
+ // Check if bot was mentioned (in original text only)
356
+ const botMentioned = !!msg.text?.includes(`<@${botUserId}>`);
357
+
358
+ // ADDITIVE_SLACK: Check for !now command in threads
359
+ const additiveSlack = process.env.ADDITIVE_SLACK === "true";
360
+ if (additiveSlack && msg.thread_ts) {
361
+ const stripped = effectiveText.replace(/<@[A-Z0-9]+>/g, "").trim();
362
+ if (stripped.startsWith("!now")) {
363
+ const nowMessage = stripped.replace(/^!now\s*/, "").trim();
364
+ const threadKey = `${msg.channel}:${msg.thread_ts}`;
365
+
366
+ console.log(
367
+ `[Slack] !now command detected in thread ${threadKey}${nowMessage ? ` with message: "${nowMessage}"` : ""}`,
368
+ );
298
369
 
299
- // Build thread context for routing (if we're in a thread)
370
+ if (nowMessage) {
371
+ bufferThreadMessage(msg.channel, msg.thread_ts, nowMessage, msg.user, msg.ts);
372
+ }
373
+
374
+ // Instant flush — no dependency
375
+ await instantFlush(threadKey);
376
+
377
+ try {
378
+ await client.reactions.add({ channel: msg.channel, name: "zap", timestamp: msg.ts });
379
+ } catch (e) {
380
+ console.log(`[Slack] Reaction failed: ${e instanceof Error ? e.message : e}`);
381
+ }
382
+
383
+ return;
384
+ }
385
+ }
386
+
387
+ // ADDITIVE_SLACK: Buffer non-mention thread messages
388
+ if (additiveSlack && !botMentioned && msg.thread_ts) {
389
+ // Check if this thread has any swarm activity (existing tasks)
390
+ const hasSwarmActivity = getAgentWorkingOnThread(msg.channel, msg.thread_ts) !== null;
391
+
392
+ if (hasSwarmActivity) {
393
+ const threadKey = `${msg.channel}:${msg.thread_ts}`;
394
+ bufferThreadMessage(msg.channel, msg.thread_ts, effectiveText, msg.user, msg.ts);
395
+
396
+ // Slack feedback: react with :eyes: on first buffer, :heavy_plus_sign: on appends
397
+ const count = getBufferMessageCount(threadKey);
398
+ console.log(
399
+ `[Slack] Additive buffer: ${threadKey} (message #${count}, reaction: ${count === 1 ? "eyes" : "heavy_plus_sign"})`,
400
+ );
401
+ try {
402
+ await client.reactions.add({
403
+ channel: msg.channel,
404
+ name: count === 1 ? "eyes" : "heavy_plus_sign",
405
+ timestamp: msg.ts,
406
+ });
407
+ } catch (e) {
408
+ console.log(`[Slack] Reaction failed: ${e instanceof Error ? e.message : e}`);
409
+ }
410
+
411
+ return; // Don't process further — buffer will flush
412
+ }
413
+ }
414
+
415
+ // Route message to agents (use original text for routing to preserve mention/name matching)
416
+ const routingText = msg.text || effectiveText;
300
417
  const routingThreadContext = msg.thread_ts
301
418
  ? { channelId: msg.channel, threadTs: msg.thread_ts }
302
419
  : undefined;
303
-
304
- // Route message to agents
305
- const matches = routeMessage(msg.text, botUserId, botMentioned, routingThreadContext);
420
+ const matches = routeMessage(routingText, botUserId, botMentioned, routingThreadContext);
306
421
 
307
422
  if (matches.length === 0) {
308
- // No agents matched - ignore message unless bot was directly mentioned
309
- if (botMentioned) {
423
+ if (!botMentioned) return;
424
+
425
+ // Bot was mentioned but no online agents matched — queue the request
426
+ if (!checkRateLimit(msg.user)) {
310
427
  await say({
311
- text: ":satellite: _No agents are currently available. Use `/agent-swarm-status` to check the swarm._",
428
+ text: ":satellite: _You're sending too many requests. Please slow down._",
312
429
  thread_ts: msg.thread_ts || msg.ts,
313
430
  });
431
+ return;
314
432
  }
433
+
434
+ const taskDescription = extractTaskFromMessage(effectiveText, botUserId);
435
+ if (!taskDescription) {
436
+ await say({
437
+ text: ":satellite: _Please provide a task description after mentioning an agent._",
438
+ thread_ts: msg.thread_ts || msg.ts,
439
+ });
440
+ return;
441
+ }
442
+
443
+ const threadTs = msg.thread_ts || msg.ts;
444
+ const threadContext = await getThreadContext(
445
+ client,
446
+ msg.channel,
447
+ msg.thread_ts,
448
+ msg.ts,
449
+ botUserId,
450
+ );
451
+ let fullTaskDescription: string;
452
+ if (threadContext) {
453
+ const ctxResult = resolveTemplate("slack.message.thread_context", {
454
+ thread_messages: threadContext,
455
+ });
456
+ fullTaskDescription = `${ctxResult.text}\n\n${taskDescription}`;
457
+ } else {
458
+ fullTaskDescription = taskDescription;
459
+ }
460
+
461
+ const lead = getLeadAgent();
462
+ createTaskExtended(fullTaskDescription, {
463
+ agentId: lead?.id,
464
+ source: "slack",
465
+ slackChannelId: msg.channel,
466
+ slackThreadTs: threadTs,
467
+ slackUserId: msg.user,
468
+ });
469
+
470
+ await say({
471
+ text: ":satellite: _No agents are online right now. Your request has been queued and will be processed when agents come back up._",
472
+ thread_ts: threadTs,
473
+ });
315
474
  return;
316
475
  }
317
476
 
@@ -324,8 +483,8 @@ export function registerMessageHandler(app: App): void {
324
483
  return;
325
484
  }
326
485
 
327
- // Extract task description
328
- const taskDescription = extractTaskFromMessage(msg.text, botUserId);
486
+ // Extract task description (using effective text which includes attachment metadata)
487
+ const taskDescription = extractTaskFromMessage(effectiveText, botUserId);
329
488
  if (!taskDescription) {
330
489
  await say({
331
490
  text: ":satellite: _Please provide a task description after mentioning an agent._",
@@ -345,39 +504,41 @@ export function registerMessageHandler(app: App): void {
345
504
  msg.ts,
346
505
  botUserId,
347
506
  );
348
- // Structure content with clear separation for inbox messages
349
- const structuredContent = threadContext
350
- ? `<new_message>\n${taskDescription}\n</new_message>\n\n<thread_history>\n${threadContext}\n</thread_history>`
351
- : taskDescription;
352
- // For workers (tasks), keep using the old format for backwards compatibility
353
- const fullTaskDescription = threadContext
354
- ? `<thread_context>\n${threadContext}\n</thread_context>\n\n${taskDescription}`
355
- : taskDescription;
356
- const results: { assigned: string[]; queued: string[]; failed: string[] } = {
357
- assigned: [],
358
- queued: [],
359
- failed: [],
360
- };
507
+ let fullTaskDescription: string;
508
+ if (threadContext) {
509
+ const ctxResult = resolveTemplate("slack.message.thread_context", {
510
+ thread_messages: threadContext,
511
+ });
512
+ fullTaskDescription = `${ctxResult.text}\n\n${taskDescription}`;
513
+ } else {
514
+ fullTaskDescription = taskDescription;
515
+ }
516
+ const results: {
517
+ assigned: Array<{ agentName: string; taskId: string }>;
518
+ queued: Array<{ agentName: string; taskId: string }>;
519
+ failed: Array<{ agentName: string; reason: string }>;
520
+ } = { assigned: [], queued: [], failed: [] };
361
521
 
362
522
  for (const match of matches) {
363
523
  const agent = getAgentById(match.agent.id);
364
524
 
365
525
  if (!agent) {
366
- results.failed.push(`\`${match.agent.name}\` (not found)`);
526
+ results.failed.push({ agentName: match.agent.name, reason: "not found" });
367
527
  continue;
368
528
  }
369
529
 
370
530
  try {
371
- // Lead agents receive inbox messages, not tasks
531
+ const latestTask = getMostRecentTaskInThread(msg.channel, threadTs);
372
532
  if (agent.isLead) {
373
- createInboxMessage(agent.id, structuredContent, {
533
+ const task = createTaskExtended(fullTaskDescription, {
534
+ agentId: agent.id,
374
535
  source: "slack",
375
536
  slackChannelId: msg.channel,
376
537
  slackThreadTs: threadTs,
377
538
  slackUserId: msg.user,
378
- matchedText: match.matchedText,
539
+ parentTaskId: latestTask?.id,
379
540
  });
380
- results.assigned.push(`*${agent.name}* (inbox)`);
541
+ results.assigned.push({ agentName: agent.name, taskId: task.id });
381
542
  continue;
382
543
  }
383
544
 
@@ -396,32 +557,46 @@ export function registerMessageHandler(app: App): void {
396
557
  );
397
558
 
398
559
  if (inProgressInThread) {
399
- results.queued.push(`*${agent.name}* (${getTaskLink(task.id)})`);
560
+ results.queued.push({ agentName: agent.name, taskId: task.id });
400
561
  } else {
401
- results.assigned.push(`*${agent.name}* (${getTaskLink(task.id)})`);
562
+ results.assigned.push({ agentName: agent.name, taskId: task.id });
402
563
  }
403
564
  } catch {
404
- results.failed.push(`\`${agent.name}\` (error)`);
565
+ results.failed.push({ agentName: agent.name, reason: "error" });
405
566
  }
406
567
  }
407
568
 
408
- // Send consolidated summary
409
- const parts: string[] = [];
410
- if (results.assigned.length > 0) {
411
- parts.push(`:satellite: _Task assigned to: ${results.assigned.join(", ")}_`);
412
- }
413
- if (results.queued.length > 0) {
414
- parts.push(`:satellite: _Task queued for: ${results.queued.join(", ")}_`);
415
- }
416
- if (results.failed.length > 0) {
417
- parts.push(`:satellite: _Could not assign to: ${results.failed.join(", ")}_`);
418
- }
569
+ // Send consolidated summary with Block Kit
570
+ const totalResults = results.assigned.length + results.queued.length + results.failed.length;
571
+ if (totalResults > 0) {
572
+ // Build plain-text fallback
573
+ const parts: string[] = [];
574
+ if (results.assigned.length > 0) {
575
+ const names = results.assigned.map((a) => `${a.agentName}`).join(", ");
576
+ parts.push(`Task assigned to: ${names}`);
577
+ }
578
+ if (results.queued.length > 0) {
579
+ const names = results.queued.map((q) => `${q.agentName}`).join(", ");
580
+ parts.push(`Task queued for: ${names}`);
581
+ }
582
+ if (results.failed.length > 0) {
583
+ const names = results.failed.map((f) => `${f.agentName}`).join(", ");
584
+ parts.push(`Could not assign to: ${names}`);
585
+ }
419
586
 
420
- if (parts.length > 0) {
421
- await say({
422
- text: parts.join("\n"),
587
+ const resp = await say({
588
+ text: parts.join(". "),
589
+ blocks: buildAssignmentSummaryBlocks(results),
423
590
  thread_ts: msg.thread_ts || msg.ts,
424
591
  });
592
+
593
+ // Register the assignment message so the watcher can update it in-place
594
+ // (assignment → progress → completion all in one evolving message)
595
+ if (resp?.ts) {
596
+ for (const { taskId } of results.assigned) {
597
+ registerTaskMessage(taskId, msg.channel, threadTs, resp.ts);
598
+ }
599
+ }
425
600
  }
426
601
  });
427
602
 
@@ -2,36 +2,18 @@ import type { WebClient } from "@slack/web-api";
2
2
  import { getAgentById } from "../be/db";
3
3
  import type { Agent, AgentTask } from "../types";
4
4
  import { getSlackApp } from "./app";
5
+ import {
6
+ buildCancelledBlocks,
7
+ buildCompletedBlocks,
8
+ buildFailedBlocks,
9
+ buildProgressBlocks,
10
+ markdownToSlack,
11
+ } from "./blocks";
5
12
 
6
- const isDev = process.env.ENV === "development";
7
- const appUrl = process.env.APP_URL || "";
13
+ // Re-export for backward compatibility
14
+ export { markdownToSlack } from "./blocks";
8
15
 
9
- /**
10
- * Convert GitHub-flavored markdown to Slack mrkdwn format.
11
- *
12
- * Key differences:
13
- * - GitHub: **bold**, *italic*, ~~strike~~, [text](url)
14
- * - Slack: *bold*, _italic_, ~strike~, <url|text>
15
- */
16
- export function markdownToSlack(text: string): string {
17
- return (
18
- text
19
- // Headers to bold (# Header -> *Header*)
20
- .replace(/^#{1,6}\s+(.+)$/gm, "*$1*")
21
- // Bold **text** -> *text* (must be before italic)
22
- .replace(/\*\*(.+?)\*\*/g, "*$1*")
23
- // Italic *text* -> _text_ (single asterisks, after bold is converted)
24
- .replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "_$1_")
25
- // Strikethrough ~~text~~ -> ~text~
26
- .replace(/~~(.+?)~~/g, "~$1~")
27
- // Links [text](url) -> <url|text>
28
- .replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<$2|$1>")
29
- // Inline code already works the same
30
- // Bullet points already work the same
31
- // Remove excessive blank lines
32
- .replace(/\n{3,}/g, "\n\n")
33
- );
34
- }
16
+ const isDev = process.env.ENV === "development";
35
17
 
36
18
  /**
37
19
  * Get the display name for an agent, with (dev) prefix if in development mode.
@@ -40,17 +22,6 @@ function getAgentDisplayName(agent: Agent): string {
40
22
  return isDev ? `(dev) ${agent.name}` : agent.name;
41
23
  }
42
24
 
43
- /**
44
- * Get a link to the task in the dashboard, or just the task ID if no APP_URL.
45
- */
46
- function getTaskLink(taskId: string): string {
47
- const shortId = taskId.slice(0, 8);
48
- if (appUrl) {
49
- return `<${appUrl}?tab=tasks&task=${taskId}&expand=true|\`${shortId}\`>`;
50
- }
51
- return `\`${shortId}\``;
52
- }
53
-
54
25
  /**
55
26
  * Send a task completion message to Slack with the agent's persona.
56
27
  */
@@ -72,28 +43,31 @@ export async function sendTaskResponse(task: AgentTask): Promise<boolean> {
72
43
  }
73
44
 
74
45
  const client = app.client;
75
- const taskLink = getTaskLink(task.id);
76
- const footer = `_Check the full logs at ${taskLink}_`;
46
+ const agentName = agent.name;
77
47
 
78
48
  try {
79
49
  if (task.status === "completed") {
80
- const output = task.output || "_Task completed._";
50
+ const output = task.output || "Task completed.";
81
51
  const slackOutput = markdownToSlack(output);
52
+ const blocks = buildCompletedBlocks({ agentName, taskId: task.id, body: slackOutput });
82
53
  await sendWithPersona(client, {
83
54
  channel: task.slackChannelId,
84
55
  thread_ts: task.slackThreadTs,
85
- text: `${slackOutput}\n\n${footer}`,
56
+ text: slackOutput,
86
57
  username: getAgentDisplayName(agent),
87
58
  icon_emoji: getAgentEmoji(agent),
59
+ blocks,
88
60
  });
89
61
  } else if (task.status === "failed") {
90
62
  const reason = task.failureReason || "Unknown error";
63
+ const blocks = buildFailedBlocks({ agentName, taskId: task.id, reason });
91
64
  await sendWithPersona(client, {
92
65
  channel: task.slackChannelId,
93
66
  thread_ts: task.slackThreadTs,
94
- text: `:x: *Task failed*\n\`\`\`${reason}\`\`\`\n${footer}`,
67
+ text: `Task failed: ${reason}`,
95
68
  username: getAgentDisplayName(agent),
96
69
  icon_emoji: getAgentEmoji(agent),
70
+ blocks,
97
71
  });
98
72
  }
99
73
 
@@ -105,33 +79,110 @@ export async function sendTaskResponse(task: AgentTask): Promise<boolean> {
105
79
  }
106
80
 
107
81
  /**
108
- * Send a progress update to Slack.
82
+ * Send a progress update to Slack. Returns the message ts for chat.update tracking.
109
83
  */
110
- export async function sendProgressUpdate(task: AgentTask, progress: string): Promise<boolean> {
84
+ export async function sendProgressUpdate(
85
+ task: AgentTask,
86
+ progress: string,
87
+ ): Promise<string | undefined> {
111
88
  const app = getSlackApp();
112
89
  if (!app || !task.slackChannelId || !task.slackThreadTs) {
113
- return false;
90
+ return undefined;
114
91
  }
115
92
 
116
- if (!task.agentId) return false;
93
+ if (!task.agentId) return undefined;
117
94
 
118
95
  const agent = getAgentById(task.agentId);
119
- if (!agent) return false;
96
+ if (!agent) return undefined;
120
97
 
121
- const taskLink = getTaskLink(task.id);
122
- const footer = `_Check progress at ${taskLink}_`;
98
+ const blocks = buildProgressBlocks({ agentName: agent.name, taskId: task.id, progress });
123
99
 
124
100
  try {
125
- await sendWithPersona(app.client, {
101
+ return await sendWithPersona(app.client, {
126
102
  channel: task.slackChannelId,
127
103
  thread_ts: task.slackThreadTs,
128
- text: `:hourglass_flowing_sand: _${progress}_\n\n${footer}`,
104
+ text: progress,
129
105
  username: getAgentDisplayName(agent),
130
106
  icon_emoji: getAgentEmoji(agent),
107
+ blocks,
131
108
  });
132
- return true;
133
109
  } catch (error) {
134
110
  console.error(`[Slack] Failed to send progress update:`, error);
111
+ return undefined;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Update an existing progress message in-place via chat.update.
117
+ */
118
+ export async function updateProgressInPlace(
119
+ task: AgentTask,
120
+ progress: string,
121
+ messageTs: string,
122
+ ): Promise<boolean> {
123
+ const app = getSlackApp();
124
+ if (!app || !task.slackChannelId || !task.agentId) return false;
125
+
126
+ const agent = getAgentById(task.agentId);
127
+ if (!agent) return false;
128
+
129
+ const blocks = buildProgressBlocks({ agentName: agent.name, taskId: task.id, progress });
130
+
131
+ try {
132
+ await app.client.chat.update({
133
+ channel: task.slackChannelId,
134
+ ts: messageTs,
135
+ text: progress,
136
+ // biome-ignore lint/suspicious/noExplicitAny: Block Kit objects
137
+ blocks: blocks as any,
138
+ });
139
+ return true;
140
+ } catch (error) {
141
+ console.error(`[Slack] Failed to update progress in-place:`, error);
142
+ return false;
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Update the task message to its final state (completed/failed) via chat.update.
148
+ * Uses full blocks (not compact) since this is the only message for the task.
149
+ */
150
+ export async function updateToFinal(task: AgentTask, messageTs: string): Promise<boolean> {
151
+ const app = getSlackApp();
152
+ if (!app || !task.slackChannelId || !task.agentId) return false;
153
+
154
+ const agent = getAgentById(task.agentId);
155
+ if (!agent) return false;
156
+
157
+ const agentName = agent.name;
158
+ let blocks: unknown[];
159
+ let text: string;
160
+
161
+ if (task.status === "completed") {
162
+ const output = task.output || "Task completed.";
163
+ const slackOutput = markdownToSlack(output);
164
+ blocks = buildCompletedBlocks({ agentName, taskId: task.id, body: slackOutput });
165
+ text = slackOutput;
166
+ } else if (task.status === "cancelled") {
167
+ blocks = buildCancelledBlocks({ agentName, taskId: task.id });
168
+ text = "Task cancelled";
169
+ } else {
170
+ const reason = task.failureReason || "Unknown error";
171
+ blocks = buildFailedBlocks({ agentName, taskId: task.id, reason });
172
+ text = `Task failed: ${reason}`;
173
+ }
174
+
175
+ try {
176
+ await app.client.chat.update({
177
+ channel: task.slackChannelId,
178
+ ts: messageTs,
179
+ text,
180
+ // biome-ignore lint/suspicious/noExplicitAny: Block Kit objects
181
+ blocks: blocks as any,
182
+ });
183
+ return true;
184
+ } catch (error) {
185
+ console.error(`[Slack] Failed to update task message to final state:`, error);
135
186
  return false;
136
187
  }
137
188
  }
@@ -144,24 +195,26 @@ async function sendWithPersona(
144
195
  text: string;
145
196
  username: string;
146
197
  icon_emoji: string;
198
+ blocks?: unknown[];
147
199
  },
148
- ): Promise<void> {
149
- await client.chat.postMessage({
200
+ ): Promise<string | undefined> {
201
+ const blocks = options.blocks ?? [
202
+ { type: "section", text: { type: "mrkdwn", text: options.text } },
203
+ ];
204
+
205
+ // Skip persona overrides in DM channels (assistant threads use the app's own identity)
206
+ const isDM = options.channel.startsWith("D");
207
+
208
+ const result = await client.chat.postMessage({
150
209
  channel: options.channel,
151
210
  thread_ts: options.thread_ts,
152
211
  text: options.text, // Fallback for notifications
153
- username: options.username,
154
- icon_emoji: options.icon_emoji,
155
- blocks: [
156
- {
157
- type: "section",
158
- text: {
159
- type: "mrkdwn",
160
- text: options.text,
161
- },
162
- },
163
- ],
212
+ ...(isDM ? {} : { username: options.username, icon_emoji: options.icon_emoji }),
213
+ // biome-ignore lint/suspicious/noExplicitAny: Block Kit objects are typed as plain JSON
214
+ blocks: blocks as any,
164
215
  });
216
+
217
+ return result.ts;
165
218
  }
166
219
 
167
220
  function getAgentEmoji(agent: Agent): string {