@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
@@ -7,8 +7,11 @@ import {
7
7
  createSessionCost,
8
8
  createTaskExtended,
9
9
  getAllSessionCosts,
10
+ getDashboardCostSummary,
11
+ getSessionCostSummary,
10
12
  getSessionCostsByAgentId,
11
13
  getSessionCostsByTaskId,
14
+ getSessionCostsFiltered,
12
15
  initDb,
13
16
  } from "../be/db";
14
17
  import type { SessionCost } from "../types";
@@ -77,6 +80,43 @@ async function handleRequest(
77
80
  }
78
81
  }
79
82
 
83
+ // GET /api/session-costs/summary - Aggregated usage summary
84
+ if (
85
+ req.method === "GET" &&
86
+ pathSegments[0] === "api" &&
87
+ pathSegments[1] === "session-costs" &&
88
+ pathSegments[2] === "summary"
89
+ ) {
90
+ const rawGroupBy = queryParams.get("groupBy");
91
+ const validGroupBy = ["day", "agent", "both"] as const;
92
+ if (rawGroupBy && !validGroupBy.includes(rawGroupBy as (typeof validGroupBy)[number])) {
93
+ return {
94
+ status: 400,
95
+ body: {
96
+ error: `Invalid groupBy value '${rawGroupBy}'. Must be one of: ${validGroupBy.join(", ")}`,
97
+ },
98
+ };
99
+ }
100
+ const summary = getSessionCostSummary({
101
+ startDate: queryParams.get("startDate") || undefined,
102
+ endDate: queryParams.get("endDate") || undefined,
103
+ agentId: queryParams.get("agentId") || undefined,
104
+ groupBy: (rawGroupBy as "day" | "agent" | "both") || "both",
105
+ });
106
+ return { status: 200, body: summary };
107
+ }
108
+
109
+ // GET /api/session-costs/dashboard - Cost today and MTD
110
+ if (
111
+ req.method === "GET" &&
112
+ pathSegments[0] === "api" &&
113
+ pathSegments[1] === "session-costs" &&
114
+ pathSegments[2] === "dashboard"
115
+ ) {
116
+ const dashboardCosts = getDashboardCostSummary();
117
+ return { status: 200, body: dashboardCosts };
118
+ }
119
+
80
120
  // GET /api/session-costs - Query session costs with filters
81
121
  if (
82
122
  req.method === "GET" &&
@@ -86,12 +126,21 @@ async function handleRequest(
86
126
  ) {
87
127
  const agentId = queryParams.get("agentId");
88
128
  const taskId = queryParams.get("taskId");
129
+ const startDate = queryParams.get("startDate");
130
+ const endDate = queryParams.get("endDate");
89
131
  const limitParam = queryParams.get("limit");
90
132
  const limit = limitParam ? parseInt(limitParam, 10) : 100;
91
133
 
92
134
  let costs: SessionCost[];
93
135
  if (taskId) {
94
- costs = getSessionCostsByTaskId(taskId);
136
+ costs = getSessionCostsByTaskId(taskId, limit);
137
+ } else if (startDate || endDate) {
138
+ costs = getSessionCostsFiltered({
139
+ agentId: agentId || undefined,
140
+ startDate: startDate || undefined,
141
+ endDate: endDate || undefined,
142
+ limit,
143
+ });
95
144
  } else if (agentId) {
96
145
  costs = getSessionCostsByAgentId(agentId, limit);
97
146
  } else {
@@ -722,4 +771,221 @@ describe("Session Costs API", () => {
722
771
  expect(data.cost.outputTokens).toBe(50000);
723
772
  });
724
773
  });
774
+
775
+ describe("Database: getSessionCostsFiltered", () => {
776
+ test("should filter by date range", () => {
777
+ const agent = createAgent({ name: "Filter DB Agent", isLead: false, status: "idle" });
778
+
779
+ createSessionCost({
780
+ sessionId: "filtered-db-1",
781
+ agentId: agent.id,
782
+ totalCostUsd: 0.1,
783
+ durationMs: 1000,
784
+ numTurns: 1,
785
+ model: "opus",
786
+ });
787
+
788
+ // All records created today, so filtering with today's date should return them
789
+ const today = new Date().toISOString().slice(0, 10);
790
+ const results = getSessionCostsFiltered({
791
+ agentId: agent.id,
792
+ startDate: today,
793
+ });
794
+
795
+ expect(results.length).toBeGreaterThanOrEqual(1);
796
+ expect(results.every((r) => r.agentId === agent.id)).toBe(true);
797
+ });
798
+
799
+ test("should return empty for future date range", () => {
800
+ const results = getSessionCostsFiltered({
801
+ startDate: "2099-01-01",
802
+ });
803
+
804
+ expect(results.length).toBe(0);
805
+ });
806
+
807
+ test("should respect limit parameter", () => {
808
+ const agent = createAgent({ name: "Filter Limit Agent", isLead: false, status: "idle" });
809
+
810
+ for (let i = 0; i < 5; i++) {
811
+ createSessionCost({
812
+ sessionId: `filter-limit-${i}`,
813
+ agentId: agent.id,
814
+ totalCostUsd: 0.01,
815
+ durationMs: 1000,
816
+ numTurns: 1,
817
+ model: "opus",
818
+ });
819
+ }
820
+
821
+ const results = getSessionCostsFiltered({ agentId: agent.id, limit: 2 });
822
+ expect(results.length).toBe(2);
823
+ });
824
+ });
825
+
826
+ describe("Database: getSessionCostSummary", () => {
827
+ test("should return totals, daily, and byAgent", () => {
828
+ const agent = createAgent({ name: "Summary DB Agent", isLead: false, status: "idle" });
829
+
830
+ createSessionCost({
831
+ sessionId: "summary-db-1",
832
+ agentId: agent.id,
833
+ totalCostUsd: 0.5,
834
+ inputTokens: 1000,
835
+ outputTokens: 500,
836
+ cacheReadTokens: 100,
837
+ cacheWriteTokens: 50,
838
+ durationMs: 5000,
839
+ numTurns: 3,
840
+ model: "opus",
841
+ });
842
+
843
+ const today = new Date().toISOString().slice(0, 10);
844
+ const summary = getSessionCostSummary({
845
+ agentId: agent.id,
846
+ startDate: today,
847
+ groupBy: "both",
848
+ });
849
+
850
+ expect(summary.totals.totalCostUsd).toBeGreaterThanOrEqual(0.5);
851
+ expect(summary.totals.totalSessions).toBeGreaterThanOrEqual(1);
852
+ expect(summary.totals.totalInputTokens).toBeGreaterThanOrEqual(1000);
853
+ expect(summary.totals.avgCostPerSession).toBeGreaterThan(0);
854
+ expect(summary.daily.length).toBeGreaterThanOrEqual(1);
855
+ expect(summary.byAgent.length).toBeGreaterThanOrEqual(1);
856
+ });
857
+
858
+ test("should return only daily when groupBy=day", () => {
859
+ const summary = getSessionCostSummary({ groupBy: "day" });
860
+
861
+ expect(summary.totals).toBeDefined();
862
+ expect(summary.daily.length).toBeGreaterThanOrEqual(1);
863
+ expect(summary.byAgent.length).toBe(0);
864
+ });
865
+
866
+ test("should return only byAgent when groupBy=agent", () => {
867
+ const summary = getSessionCostSummary({ groupBy: "agent" });
868
+
869
+ expect(summary.totals).toBeDefined();
870
+ expect(summary.daily.length).toBe(0);
871
+ expect(summary.byAgent.length).toBeGreaterThanOrEqual(1);
872
+ });
873
+
874
+ test("should return empty results for future date range", () => {
875
+ const summary = getSessionCostSummary({
876
+ startDate: "2099-01-01",
877
+ groupBy: "both",
878
+ });
879
+
880
+ expect(summary.totals.totalSessions).toBe(0);
881
+ expect(summary.totals.totalCostUsd).toBe(0);
882
+ expect(summary.daily.length).toBe(0);
883
+ expect(summary.byAgent.length).toBe(0);
884
+ });
885
+ });
886
+
887
+ describe("Database: getDashboardCostSummary", () => {
888
+ test("should return costToday and costMtd", () => {
889
+ const result = getDashboardCostSummary();
890
+
891
+ expect(typeof result.costToday).toBe("number");
892
+ expect(typeof result.costMtd).toBe("number");
893
+ // costMtd should be >= costToday since MTD includes today
894
+ expect(result.costMtd).toBeGreaterThanOrEqual(result.costToday);
895
+ });
896
+ });
897
+
898
+ describe("GET /api/session-costs with date filtering", () => {
899
+ test("should filter by startDate", async () => {
900
+ const agent = createAgent({ name: "Date Filter Agent", isLead: false, status: "idle" });
901
+
902
+ createSessionCost({
903
+ sessionId: "date-filter-1",
904
+ agentId: agent.id,
905
+ totalCostUsd: 0.05,
906
+ durationMs: 1000,
907
+ numTurns: 1,
908
+ model: "opus",
909
+ });
910
+
911
+ const today = new Date().toISOString().slice(0, 10);
912
+ const response = await fetch(
913
+ `${baseUrl}/api/session-costs?agentId=${agent.id}&startDate=${today}`,
914
+ );
915
+
916
+ expect(response.status).toBe(200);
917
+ const data = (await response.json()) as { costs: SessionCost[] };
918
+ expect(data.costs.length).toBeGreaterThanOrEqual(1);
919
+ });
920
+
921
+ test("should return empty for future startDate", async () => {
922
+ const response = await fetch(`${baseUrl}/api/session-costs?startDate=2099-01-01`);
923
+
924
+ expect(response.status).toBe(200);
925
+ const data = (await response.json()) as { costs: SessionCost[] };
926
+ expect(data.costs.length).toBe(0);
927
+ });
928
+ });
929
+
930
+ describe("GET /api/session-costs/summary", () => {
931
+ test("should return aggregated summary", async () => {
932
+ const response = await fetch(`${baseUrl}/api/session-costs/summary`);
933
+
934
+ expect(response.status).toBe(200);
935
+ const data = (await response.json()) as {
936
+ totals: { totalCostUsd: number; totalSessions: number };
937
+ daily: unknown[];
938
+ byAgent: unknown[];
939
+ };
940
+ expect(data.totals).toBeDefined();
941
+ expect(data.totals.totalSessions).toBeGreaterThan(0);
942
+ expect(data.daily.length).toBeGreaterThan(0);
943
+ expect(data.byAgent.length).toBeGreaterThan(0);
944
+ });
945
+
946
+ test("should filter by startDate and endDate", async () => {
947
+ const today = new Date().toISOString().slice(0, 10);
948
+ const response = await fetch(
949
+ `${baseUrl}/api/session-costs/summary?startDate=${today}&endDate=${today}`,
950
+ );
951
+
952
+ expect(response.status).toBe(200);
953
+ const data = (await response.json()) as {
954
+ totals: { totalSessions: number };
955
+ };
956
+ expect(data.totals.totalSessions).toBeGreaterThanOrEqual(0);
957
+ });
958
+
959
+ test("should respect groupBy=day", async () => {
960
+ const response = await fetch(`${baseUrl}/api/session-costs/summary?groupBy=day`);
961
+
962
+ expect(response.status).toBe(200);
963
+ const data = (await response.json()) as {
964
+ daily: unknown[];
965
+ byAgent: unknown[];
966
+ };
967
+ expect(data.daily.length).toBeGreaterThan(0);
968
+ expect(data.byAgent.length).toBe(0);
969
+ });
970
+
971
+ test("should reject invalid groupBy", async () => {
972
+ const response = await fetch(`${baseUrl}/api/session-costs/summary?groupBy=invalid`);
973
+
974
+ expect(response.status).toBe(400);
975
+ const data = (await response.json()) as { error: string };
976
+ expect(data.error).toContain("Invalid groupBy");
977
+ });
978
+ });
979
+
980
+ describe("GET /api/session-costs/dashboard", () => {
981
+ test("should return costToday and costMtd", async () => {
982
+ const response = await fetch(`${baseUrl}/api/session-costs/dashboard`);
983
+
984
+ expect(response.status).toBe(200);
985
+ const data = (await response.json()) as { costToday: number; costMtd: number };
986
+ expect(typeof data.costToday).toBe("number");
987
+ expect(typeof data.costMtd).toBe("number");
988
+ expect(data.costMtd).toBeGreaterThanOrEqual(data.costToday);
989
+ });
990
+ });
725
991
  });
@@ -0,0 +1,133 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import { unlinkSync } from "node:fs";
3
+ import {
4
+ cancelTask,
5
+ closeDb,
6
+ createAgent,
7
+ createTaskExtended,
8
+ getLeadAgent,
9
+ getTaskById,
10
+ initDb,
11
+ } from "../be/db";
12
+ import { buildCancelledBlocks, getTaskLink } from "../slack/blocks";
13
+
14
+ const TEST_DB_PATH = "./test-slack-actions.sqlite";
15
+
16
+ let leadAgent: ReturnType<typeof createAgent>;
17
+ let slackTask: ReturnType<typeof createTaskExtended>;
18
+
19
+ beforeAll(() => {
20
+ initDb(TEST_DB_PATH);
21
+ leadAgent = createAgent({ name: "ActionLead", isLead: true, status: "idle" });
22
+ slackTask = createTaskExtended("original task for actions test", {
23
+ agentId: leadAgent.id,
24
+ source: "slack",
25
+ slackChannelId: "C_ACTIONS",
26
+ slackThreadTs: "3333333333.000001",
27
+ slackUserId: "U_ACTIONS",
28
+ });
29
+ });
30
+
31
+ afterAll(() => {
32
+ closeDb();
33
+ try {
34
+ unlinkSync(TEST_DB_PATH);
35
+ unlinkSync(`${TEST_DB_PATH}-wal`);
36
+ unlinkSync(`${TEST_DB_PATH}-shm`);
37
+ } catch {
38
+ // ignore
39
+ }
40
+ });
41
+
42
+ describe("follow_up_task action logic", () => {
43
+ test("getTaskById retrieves the original task", () => {
44
+ const task = getTaskById(slackTask.id);
45
+ expect(task).toBeDefined();
46
+ expect(task!.task).toBe("original task for actions test");
47
+ expect(task!.slackChannelId).toBe("C_ACTIONS");
48
+ });
49
+
50
+ test("getLeadAgent returns the lead", () => {
51
+ const lead = getLeadAgent();
52
+ expect(lead).toBeDefined();
53
+ expect(lead!.name).toBe("ActionLead");
54
+ });
55
+
56
+ test("creates follow-up task with parentTaskId and custom description", () => {
57
+ const lead = getLeadAgent()!;
58
+ const followUpTask = createTaskExtended("Please also check the logs", {
59
+ agentId: lead.id,
60
+ source: "slack",
61
+ parentTaskId: slackTask.id,
62
+ slackChannelId: "C_ACTIONS",
63
+ slackThreadTs: "3333333333.000001",
64
+ slackUserId: "U_ACTIONS",
65
+ });
66
+
67
+ expect(followUpTask).toBeDefined();
68
+ expect(followUpTask.task).toBe("Please also check the logs");
69
+ expect(followUpTask.parentTaskId).toBe(slackTask.id);
70
+
71
+ const fetched = getTaskById(followUpTask.id);
72
+ expect(fetched).toBeDefined();
73
+ expect(fetched!.parentTaskId).toBe(slackTask.id);
74
+ expect(fetched!.slackChannelId).toBe("C_ACTIONS");
75
+ });
76
+
77
+ test("getTaskLink produces a link for the task", () => {
78
+ const link = getTaskLink(slackTask.id);
79
+ expect(link).toBeTruthy();
80
+ expect(link).toContain(slackTask.id.slice(0, 8));
81
+ });
82
+ });
83
+
84
+ describe("cancel_task action logic", () => {
85
+ test("cancelTask returns task object for a pending task", () => {
86
+ const agent = createAgent({ name: "CancelAgent", isLead: false, status: "idle" });
87
+ const task = createTaskExtended("task to cancel", {
88
+ agentId: agent.id,
89
+ source: "slack",
90
+ slackChannelId: "C_CANCEL",
91
+ slackThreadTs: "4444444444.000001",
92
+ slackUserId: "U_CANCEL",
93
+ });
94
+
95
+ const result = cancelTask(task.id, "Cancelled via Slack");
96
+ expect(result).toBeTruthy();
97
+
98
+ // Verify task is now cancelled
99
+ const fetched = getTaskById(task.id);
100
+ expect(fetched).toBeDefined();
101
+ expect(fetched!.status).toBe("cancelled");
102
+ });
103
+
104
+ test("cancelTask returns null for already-cancelled task", () => {
105
+ const agent = createAgent({ name: "CompletedAgent", isLead: false, status: "idle" });
106
+ const task = createTaskExtended("task to double-cancel", {
107
+ agentId: agent.id,
108
+ source: "slack",
109
+ slackChannelId: "C_DONE",
110
+ slackThreadTs: "5555555555.000001",
111
+ slackUserId: "U_DONE",
112
+ });
113
+
114
+ // First cancel succeeds
115
+ const first = cancelTask(task.id, "First cancel");
116
+ expect(first).toBeTruthy();
117
+
118
+ // Second cancel returns null — already in terminal state
119
+ const second = cancelTask(task.id, "Second cancel");
120
+ expect(second).toBeNull();
121
+ });
122
+
123
+ test("buildCancelledBlocks produces correct blocks for cancelled task", () => {
124
+ const blocks = buildCancelledBlocks({
125
+ agentName: "Alpha",
126
+ taskId: slackTask.id,
127
+ });
128
+
129
+ expect(blocks.length).toBe(1);
130
+ expect(blocks[0].type).toBe("section");
131
+ expect(blocks[0].text.text).toContain("Cancelled");
132
+ });
133
+ });
@@ -0,0 +1,136 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import { unlinkSync } from "node:fs";
3
+ import {
4
+ closeDb,
5
+ createAgent,
6
+ createTaskExtended,
7
+ getAgentWorkingOnThread,
8
+ getLeadAgent,
9
+ getTaskById,
10
+ initDb,
11
+ } from "../be/db";
12
+
13
+ const TEST_DB_PATH = "./test-slack-assistant.sqlite";
14
+
15
+ let _leadAgent: ReturnType<typeof createAgent>;
16
+
17
+ beforeAll(() => {
18
+ initDb(TEST_DB_PATH);
19
+ _leadAgent = createAgent({ name: "AssistantLead", isLead: true, status: "idle" });
20
+ });
21
+
22
+ afterAll(() => {
23
+ closeDb();
24
+ try {
25
+ unlinkSync(TEST_DB_PATH);
26
+ unlinkSync(`${TEST_DB_PATH}-wal`);
27
+ unlinkSync(`${TEST_DB_PATH}-shm`);
28
+ } catch {
29
+ // ignore
30
+ }
31
+ });
32
+
33
+ describe("assistant userMessage routing — new thread (no working agent)", () => {
34
+ test("getAgentWorkingOnThread returns null when no tasks exist for thread", () => {
35
+ const result = getAgentWorkingOnThread("D_ASSISTANT", "6666666666.000001");
36
+ expect(result).toBeNull();
37
+ });
38
+
39
+ test("getLeadAgent returns the lead agent for task assignment", () => {
40
+ const lead = getLeadAgent();
41
+ expect(lead).toBeDefined();
42
+ expect(lead!.name).toBe("AssistantLead");
43
+ expect(lead!.isLead).toBe(true);
44
+ });
45
+
46
+ test("creates task with slack context for new assistant thread message", () => {
47
+ const lead = getLeadAgent()!;
48
+ const task = createTaskExtended("What's the status of all agents?", {
49
+ agentId: lead.id,
50
+ source: "slack",
51
+ slackChannelId: "D_ASSISTANT",
52
+ slackThreadTs: "7777777777.000001",
53
+ slackUserId: "U_ASSISTANT",
54
+ });
55
+
56
+ expect(task).toBeDefined();
57
+ expect(task.task).toBe("What's the status of all agents?");
58
+ expect(task.source).toBe("slack");
59
+
60
+ const fetched = getTaskById(task.id);
61
+ expect(fetched).toBeDefined();
62
+ expect(fetched!.slackChannelId).toBe("D_ASSISTANT");
63
+ expect(fetched!.slackThreadTs).toBe("7777777777.000001");
64
+ expect(fetched!.slackUserId).toBe("U_ASSISTANT");
65
+ });
66
+
67
+ test("queues task without agentId when no lead is available", () => {
68
+ // Create a task without agentId (simulates no lead scenario)
69
+ const task = createTaskExtended("queued task, no lead", {
70
+ source: "slack",
71
+ slackChannelId: "D_NOHEAD",
72
+ slackThreadTs: "8888888888.000001",
73
+ slackUserId: "U_NOHEAD",
74
+ });
75
+
76
+ expect(task).toBeDefined();
77
+ const fetched = getTaskById(task.id);
78
+ expect(fetched).toBeDefined();
79
+ // Task should be created but unassigned (pending or similar status)
80
+ expect(fetched!.agentId).toBeNull();
81
+ });
82
+ });
83
+
84
+ describe("assistant userMessage routing — follow-up (working agent exists)", () => {
85
+ test("getAgentWorkingOnThread returns the agent working on an active thread task", () => {
86
+ const worker = createAgent({ name: "ThreadWorker", isLead: false, status: "idle" });
87
+ createTaskExtended("initial task in thread", {
88
+ agentId: worker.id,
89
+ source: "slack",
90
+ slackChannelId: "D_FOLLOWUP",
91
+ slackThreadTs: "9999999999.000001",
92
+ slackUserId: "U_FOLLOWUP",
93
+ });
94
+
95
+ const result = getAgentWorkingOnThread("D_FOLLOWUP", "9999999999.000001");
96
+ expect(result).toBeDefined();
97
+ expect(result!.name).toBe("ThreadWorker");
98
+ });
99
+
100
+ test("creates follow-up task assigned to the working agent", () => {
101
+ const workingAgent = getAgentWorkingOnThread("D_FOLLOWUP", "9999999999.000001");
102
+ expect(workingAgent).toBeDefined();
103
+
104
+ const followUp = createTaskExtended("follow-up message in assistant thread", {
105
+ agentId: workingAgent!.id,
106
+ source: "slack",
107
+ slackChannelId: "D_FOLLOWUP",
108
+ slackThreadTs: "9999999999.000001",
109
+ slackUserId: "U_FOLLOWUP",
110
+ });
111
+
112
+ expect(followUp).toBeDefined();
113
+ expect(followUp.agentId).toBe(workingAgent!.id);
114
+
115
+ const fetched = getTaskById(followUp.id);
116
+ expect(fetched).toBeDefined();
117
+ expect(fetched!.slackChannelId).toBe("D_FOLLOWUP");
118
+ expect(fetched!.slackThreadTs).toBe("9999999999.000001");
119
+ });
120
+ });
121
+
122
+ describe("assistant thread title truncation", () => {
123
+ test("short messages stay as-is", () => {
124
+ const text = "Check agent status";
125
+ const title = text.length > 50 ? `${text.slice(0, 47)}...` : text;
126
+ expect(title).toBe("Check agent status");
127
+ });
128
+
129
+ test("long messages get truncated to 50 chars", () => {
130
+ const text =
131
+ "This is a very long message that definitely exceeds the fifty character limit for thread titles";
132
+ const title = text.length > 50 ? `${text.slice(0, 47)}...` : text;
133
+ expect(title).toBe("This is a very long message that definitely exc...");
134
+ expect(title.length).toBe(50);
135
+ });
136
+ });