@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
@@ -0,0 +1,77 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import type { IncomingMessage, ServerResponse } from "node:http";
3
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
5
+ import { createServer } from "@/server";
6
+
7
+ export async function handleMcp(
8
+ req: IncomingMessage,
9
+ res: ServerResponse,
10
+ transports: Record<string, StreamableHTTPServerTransport>,
11
+ ): Promise<boolean> {
12
+ const sessionId = req.headers["mcp-session-id"] as string | undefined;
13
+
14
+ if (req.url !== "/mcp") {
15
+ return false;
16
+ }
17
+
18
+ if (req.method === "POST") {
19
+ const chunks: Buffer[] = [];
20
+ for await (const chunk of req) {
21
+ chunks.push(chunk);
22
+ }
23
+ const body = JSON.parse(Buffer.concat(chunks).toString());
24
+
25
+ let transport: StreamableHTTPServerTransport;
26
+
27
+ if (sessionId && transports[sessionId]) {
28
+ transport = transports[sessionId];
29
+ } else if (!sessionId && isInitializeRequest(body)) {
30
+ transport = new StreamableHTTPServerTransport({
31
+ sessionIdGenerator: () => randomUUID(),
32
+ onsessioninitialized: (id) => {
33
+ transports[id] = transport;
34
+ },
35
+ onsessionclosed: (id) => {
36
+ delete transports[id];
37
+ },
38
+ });
39
+
40
+ transport.onclose = () => {
41
+ if (transport.sessionId) {
42
+ delete transports[transport.sessionId];
43
+ }
44
+ };
45
+
46
+ const server = createServer();
47
+ await server.connect(transport);
48
+ } else {
49
+ res.writeHead(400, { "Content-Type": "application/json" });
50
+ res.end(
51
+ JSON.stringify({
52
+ jsonrpc: "2.0",
53
+ error: { code: -32000, message: "Invalid session" },
54
+ id: null,
55
+ }),
56
+ );
57
+ return true;
58
+ }
59
+
60
+ await transport.handleRequest(req, res, body);
61
+ return true;
62
+ }
63
+
64
+ if (req.method === "GET" || req.method === "DELETE") {
65
+ if (sessionId && transports[sessionId]) {
66
+ await transports[sessionId].handleRequest(req, res);
67
+ return true;
68
+ }
69
+ res.writeHead(400);
70
+ res.end("Invalid session");
71
+ return true;
72
+ }
73
+
74
+ res.writeHead(405);
75
+ res.end("Method not allowed");
76
+ return true;
77
+ }
@@ -0,0 +1,168 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import { z } from "zod";
3
+ import { chunkContent } from "../be/chunking";
4
+ import {
5
+ createMemory,
6
+ deleteMemoriesBySourcePath,
7
+ getDb,
8
+ searchMemoriesByVector,
9
+ updateMemoryEmbedding,
10
+ } from "../be/db";
11
+ import { getEmbedding, serializeEmbedding } from "../be/embedding";
12
+ import { AgentMemoryScopeSchema, AgentMemorySourceSchema } from "../types";
13
+ import { route } from "./route-def";
14
+ import { json, jsonError } from "./utils";
15
+
16
+ // ─── Route Definitions ───────────────────────────────────────────────────────
17
+
18
+ const indexMemory = route({
19
+ method: "post",
20
+ path: "/api/memory/index",
21
+ pattern: ["api", "memory", "index"],
22
+ summary: "Ingest content into memory system (async embedding)",
23
+ tags: ["Memory"],
24
+ body: z.object({
25
+ agentId: z.string().uuid().optional(),
26
+ content: z.string().min(1),
27
+ name: z.string().min(1),
28
+ scope: AgentMemoryScopeSchema,
29
+ source: AgentMemorySourceSchema,
30
+ sourceTaskId: z.string().uuid().optional(),
31
+ sourcePath: z.string().optional(),
32
+ tags: z.array(z.string()).optional(),
33
+ }),
34
+ responses: {
35
+ 202: { description: "Content queued for embedding" },
36
+ 400: { description: "Validation error" },
37
+ },
38
+ });
39
+
40
+ const searchMemory = route({
41
+ method: "post",
42
+ path: "/api/memory/search",
43
+ pattern: ["api", "memory", "search"],
44
+ summary: "Search memories by natural language query",
45
+ tags: ["Memory"],
46
+ auth: { apiKey: true, agentId: true },
47
+ body: z.object({
48
+ query: z.string().min(1),
49
+ limit: z.number().int().min(1).max(20).default(5),
50
+ }),
51
+ responses: {
52
+ 200: { description: "Search results" },
53
+ 400: { description: "Missing query or agent ID" },
54
+ },
55
+ });
56
+
57
+ // ─── Handler ─────────────────────────────────────────────────────────────────
58
+
59
+ export async function handleMemory(
60
+ req: IncomingMessage,
61
+ res: ServerResponse,
62
+ pathSegments: string[],
63
+ myAgentId: string | undefined,
64
+ ): Promise<boolean> {
65
+ if (indexMemory.match(req.method, pathSegments)) {
66
+ const parsed = await indexMemory.parse(req, res, pathSegments, new URLSearchParams());
67
+ if (!parsed) return true;
68
+
69
+ const { agentId, content, name, scope, source, sourceTaskId, sourcePath, tags } = parsed.body;
70
+
71
+ // Chunk content and create memories in a transaction (with dedup)
72
+ const contentChunks = chunkContent(content);
73
+ if (contentChunks.length === 0) {
74
+ // Content too small to chunk — create a single memory
75
+ contentChunks.push({
76
+ content: content.trim(),
77
+ chunkIndex: 0,
78
+ totalChunks: 1,
79
+ headings: [],
80
+ });
81
+ }
82
+
83
+ const memoryIds = getDb().transaction(() => {
84
+ // Delete old chunks if re-indexing same file
85
+ if (sourcePath && agentId) {
86
+ deleteMemoriesBySourcePath(sourcePath, agentId);
87
+ }
88
+
89
+ const ids: string[] = [];
90
+ for (const chunk of contentChunks) {
91
+ const memory = createMemory({
92
+ agentId: agentId || null,
93
+ content: chunk.content,
94
+ name,
95
+ scope,
96
+ source,
97
+ sourcePath: sourcePath || null,
98
+ sourceTaskId: sourceTaskId || null,
99
+ chunkIndex: chunk.chunkIndex,
100
+ totalChunks: chunk.totalChunks,
101
+ tags: tags || [],
102
+ });
103
+ ids.push(memory.id);
104
+ }
105
+ return ids;
106
+ })();
107
+
108
+ // Async embedding — fire and forget
109
+ (async () => {
110
+ for (let i = 0; i < contentChunks.length; i++) {
111
+ try {
112
+ const embedding = await getEmbedding(contentChunks[i]!.content);
113
+ if (embedding) {
114
+ updateMemoryEmbedding(memoryIds[i]!, serializeEmbedding(embedding));
115
+ }
116
+ } catch (err) {
117
+ console.error(`[memory] Failed to embed chunk ${memoryIds[i]}:`, (err as Error).message);
118
+ }
119
+ }
120
+ })();
121
+
122
+ json(res, { queued: true, memoryIds }, 202);
123
+ return true;
124
+ }
125
+
126
+ if (searchMemory.match(req.method, pathSegments)) {
127
+ if (!myAgentId) {
128
+ jsonError(res, "Missing required fields: query, X-Agent-ID header", 400);
129
+ return true;
130
+ }
131
+
132
+ const parsed = await searchMemory.parse(req, res, pathSegments, new URLSearchParams());
133
+ if (!parsed) return true;
134
+
135
+ const { query, limit } = parsed.body;
136
+
137
+ try {
138
+ const queryEmbedding = await getEmbedding(query);
139
+ if (!queryEmbedding) {
140
+ json(res, { results: [] });
141
+ return true;
142
+ }
143
+
144
+ const results = searchMemoriesByVector(queryEmbedding, myAgentId, {
145
+ scope: "all",
146
+ limit: Math.min(limit, 20),
147
+ isLead: false,
148
+ });
149
+
150
+ json(res, {
151
+ results: results.map((r) => ({
152
+ id: r.id,
153
+ name: r.name,
154
+ content: r.content,
155
+ similarity: r.similarity,
156
+ source: r.source,
157
+ scope: r.scope,
158
+ })),
159
+ });
160
+ } catch (err) {
161
+ console.error("[memory-search] Error:", (err as Error).message);
162
+ json(res, { results: [] });
163
+ }
164
+ return true;
165
+ }
166
+
167
+ return false;
168
+ }
@@ -0,0 +1,109 @@
1
+ import {
2
+ extendZodWithOpenApi,
3
+ OpenAPIRegistry,
4
+ OpenApiGeneratorV31,
5
+ } from "@asteasolutions/zod-to-openapi";
6
+ import { z } from "zod";
7
+ import { routeRegistry } from "./route-def";
8
+
9
+ extendZodWithOpenApi(z);
10
+
11
+ let cachedSpec: string | null = null;
12
+
13
+ interface OpenApiOptions {
14
+ version: string;
15
+ serverUrl?: string;
16
+ }
17
+
18
+ export function generateOpenApiSpec(opts: OpenApiOptions): string {
19
+ if (cachedSpec) return cachedSpec;
20
+
21
+ const registry = new OpenAPIRegistry();
22
+
23
+ // Register Bearer auth
24
+ registry.registerComponent("securitySchemes", "bearerAuth", {
25
+ type: "http",
26
+ scheme: "bearer",
27
+ description: "API key via Authorization: Bearer <API_KEY>",
28
+ });
29
+
30
+ // Register X-Agent-ID header
31
+ registry.registerComponent("securitySchemes", "agentId", {
32
+ type: "apiKey",
33
+ in: "header",
34
+ name: "X-Agent-ID",
35
+ description: "Agent UUID for agent-scoped operations",
36
+ });
37
+
38
+ // Convert route definitions to OpenAPI paths
39
+ for (const routeDef of routeRegistry) {
40
+ const responses: Record<
41
+ string,
42
+ { description: string; content?: Record<string, { schema: z.ZodType }> }
43
+ > = {};
44
+ for (const [code, resDef] of Object.entries(routeDef.responses)) {
45
+ responses[code] = {
46
+ description: resDef.description,
47
+ ...(resDef.schema && {
48
+ content: {
49
+ "application/json": { schema: resDef.schema },
50
+ },
51
+ }),
52
+ };
53
+ }
54
+
55
+ const request: Record<string, unknown> = {};
56
+ if (routeDef.params) request.params = routeDef.params;
57
+ if (routeDef.query) request.query = routeDef.query;
58
+ if (routeDef.body) {
59
+ request.body = {
60
+ content: { "application/json": { schema: routeDef.body } },
61
+ };
62
+ }
63
+
64
+ registry.registerPath({
65
+ method: routeDef.method,
66
+ path: routeDef.path,
67
+ summary: routeDef.summary,
68
+ description: routeDef.description,
69
+ tags: routeDef.tags,
70
+ request,
71
+ responses,
72
+ security: routeDef.auth?.apiKey !== false ? [{ bearerAuth: [] }] : undefined,
73
+ });
74
+ }
75
+
76
+ const serverUrl =
77
+ opts.serverUrl || process.env.MCP_BASE_URL || `http://localhost:${process.env.PORT || "3013"}`;
78
+
79
+ const generator = new OpenApiGeneratorV31(registry.definitions);
80
+ const doc = generator.generateDocument({
81
+ openapi: "3.1.0",
82
+ info: {
83
+ title: "Agent Swarm API",
84
+ version: opts.version,
85
+ description:
86
+ "Multi-agent orchestration API for Claude Code, Codex, and Gemini CLI. " +
87
+ "Enables task distribution, agent communication, and service discovery.\n\n" +
88
+ "MCP tools are documented separately in [MCP.md](./MCP.md).",
89
+ },
90
+ servers: [
91
+ {
92
+ url: serverUrl,
93
+ description: serverUrl.includes("localhost") ? "Local development" : "Production",
94
+ },
95
+ ],
96
+ });
97
+
98
+ cachedSpec = JSON.stringify(doc, null, 2);
99
+ return cachedSpec;
100
+ }
101
+
102
+ export const SCALAR_HTML = `<!DOCTYPE html>
103
+ <html>
104
+ <head><title>Agent Swarm API</title><meta charset="utf-8" /></head>
105
+ <body>
106
+ <script id="api-reference" data-url="/openapi.json"></script>
107
+ <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
108
+ </body>
109
+ </html>`;
@@ -0,0 +1,299 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import { z } from "zod";
3
+ import {
4
+ claimMentions,
5
+ claimOfferedTask,
6
+ claimTask,
7
+ getAgentById,
8
+ getAllChannelActivityCursors,
9
+ getDb,
10
+ getEpicsWithProgressUpdates,
11
+ getInboxSummary,
12
+ getOfferedTasksForAgent,
13
+ getPendingTaskForAgent,
14
+ getUnassignedTaskIds,
15
+ hasCapacity,
16
+ markEpicsProgressNotified,
17
+ startTask,
18
+ upsertChannelActivityCursor,
19
+ } from "../be/db";
20
+ import { fetchChannelActivity } from "../slack/channel-activity";
21
+ import { route } from "./route-def";
22
+ import { json, jsonError } from "./utils";
23
+
24
+ // ─── Route Definitions ───────────────────────────────────────────────────────
25
+
26
+ const pollTriggers = route({
27
+ method: "get",
28
+ path: "/api/poll",
29
+ pattern: ["api", "poll"],
30
+ summary: "Poll for triggers (tasks, mentions, epic updates)",
31
+ tags: ["Poll"],
32
+ auth: { apiKey: true, agentId: true },
33
+ responses: {
34
+ 200: { description: "Trigger data or null" },
35
+ 400: { description: "Missing X-Agent-ID" },
36
+ 404: { description: "Agent not found" },
37
+ },
38
+ });
39
+
40
+ // ─── Channel Activity Throttle ──────────────────────────────────────────────
41
+
42
+ const CHANNEL_ACTIVITY_INTERVAL_MS = 60_000; // Check at most once per 60s
43
+ let lastChannelActivityCheckAt = 0;
44
+
45
+ // ─── Cursor Commit Endpoint ─────────────────────────────────────────────────
46
+
47
+ const commitCursorsRoute = route({
48
+ method: "post",
49
+ path: "/api/channel-activity/commit-cursors",
50
+ pattern: ["api", "channel-activity", "commit-cursors"],
51
+ summary: "Commit channel activity cursors after successful processing",
52
+ tags: ["Poll"],
53
+ auth: { apiKey: true },
54
+ body: z.object({
55
+ cursorUpdates: z.array(
56
+ z.object({
57
+ channelId: z.string(),
58
+ ts: z.string(),
59
+ }),
60
+ ),
61
+ }),
62
+ responses: {
63
+ 200: { description: "Cursors committed" },
64
+ 400: { description: "Invalid request" },
65
+ },
66
+ });
67
+
68
+ // ─── Handler ─────────────────────────────────────────────────────────────────
69
+
70
+ export async function handlePoll(
71
+ req: IncomingMessage,
72
+ res: ServerResponse,
73
+ pathSegments: string[],
74
+ queryParams: URLSearchParams,
75
+ myAgentId: string | undefined,
76
+ ): Promise<boolean> {
77
+ // Handle cursor commit endpoint
78
+ if (commitCursorsRoute.match(req.method, pathSegments)) {
79
+ const parsed = await commitCursorsRoute.parse(req, res, pathSegments, queryParams);
80
+ if (!parsed) return true;
81
+ for (const { channelId, ts } of parsed.body.cursorUpdates) {
82
+ if (channelId && ts) {
83
+ upsertChannelActivityCursor(channelId, ts);
84
+ }
85
+ }
86
+ json(res, { success: true, committed: parsed.body.cursorUpdates.length });
87
+ return true;
88
+ }
89
+
90
+ if (pollTriggers.match(req.method, pathSegments)) {
91
+ if (!myAgentId) {
92
+ jsonError(res, "Missing X-Agent-ID header", 400);
93
+ return true;
94
+ }
95
+
96
+ // Use transaction for consistent reads across all trigger checks
97
+ let result:
98
+ | { error: string; status: number }
99
+ | { trigger: { type: string; [key: string]: unknown } | null };
100
+ try {
101
+ result = getDb().transaction(() => {
102
+ const agent = getAgentById(myAgentId);
103
+ if (!agent) {
104
+ return { error: "Agent not found", status: 404 };
105
+ }
106
+
107
+ // Check for offered tasks first (highest priority for both workers and leads)
108
+ // Atomically claim the task for review to prevent duplicate processing
109
+ const offeredTasks = getOfferedTasksForAgent(myAgentId);
110
+ const firstOfferedTask = offeredTasks[0];
111
+ if (firstOfferedTask) {
112
+ const claimedTask = claimOfferedTask(firstOfferedTask.id, myAgentId);
113
+ if (claimedTask) {
114
+ return {
115
+ trigger: {
116
+ type: "task_offered",
117
+ taskId: claimedTask.id,
118
+ task: claimedTask,
119
+ },
120
+ };
121
+ }
122
+ }
123
+
124
+ // Check for pending tasks (assigned directly to this agent)
125
+ // Only return a task if agent has capacity (server-side enforcement)
126
+ if (hasCapacity(myAgentId)) {
127
+ const pendingTask = getPendingTaskForAgent(myAgentId);
128
+ if (pendingTask) {
129
+ // Mark task as in_progress immediately to prevent duplicate polling
130
+ startTask(pendingTask.id);
131
+ return {
132
+ trigger: {
133
+ type: "task_assigned",
134
+ taskId: pendingTask.id,
135
+ task: { ...pendingTask, status: "in_progress" },
136
+ },
137
+ };
138
+ }
139
+ }
140
+
141
+ // Check for unread mentions (internal chat) - all agents can be woken by @mentions
142
+ // Uses atomic claiming via processing_since to prevent duplicate processing.
143
+ // Only idle agents poll, so busy workers won't be interrupted.
144
+ const claimedChannels = claimMentions(myAgentId);
145
+ if (claimedChannels.length > 0) {
146
+ // Recalculate inbox summary now that we've claimed
147
+ const inbox = getInboxSummary(myAgentId);
148
+ return {
149
+ trigger: {
150
+ type: "unread_mentions",
151
+ mentionsCount: inbox.mentionsCount,
152
+ claimedChannels: claimedChannels.map((c) => c.channelId), // Include for tracking
153
+ },
154
+ };
155
+ }
156
+
157
+ if (agent.isLead) {
158
+ // === LEAD-SPECIFIC TRIGGERS ===
159
+
160
+ // NOTE: tasks_finished trigger has been replaced by follow-up task creation
161
+ // in store-progress. When a worker completes/fails a task, a follow-up task
162
+ // is created and assigned to the lead, which is picked up via the normal
163
+ // task_assigned trigger above. This is more reliable and visible than the
164
+ // old poll-based notification approach.
165
+
166
+ // Check for epic progress updates (tasks completed/failed for active epics)
167
+ // This trigger helps lead plan next steps for epics - similar to ralph loop
168
+ const epicsWithUpdates = getEpicsWithProgressUpdates();
169
+ if (epicsWithUpdates.length > 0) {
170
+ // Atomically mark as notified within this transaction
171
+ const epicIds = epicsWithUpdates.map((e) => e.epic.id);
172
+ markEpicsProgressNotified(epicIds);
173
+
174
+ return {
175
+ trigger: {
176
+ type: "epic_progress_changed",
177
+ count: epicsWithUpdates.length,
178
+ epics: epicsWithUpdates,
179
+ },
180
+ };
181
+ }
182
+ } else {
183
+ // === WORKER-SPECIFIC TRIGGERS ===
184
+
185
+ // Auto-claim: atomically claim an unassigned task for this worker.
186
+ // claimTask() uses an atomic UPDATE WHERE status='unassigned', so only
187
+ // one worker wins if multiple poll simultaneously.
188
+ // This ensures session logs are correctly associated with the real task ID
189
+ // from the start (no reassociation needed).
190
+ if (hasCapacity(myAgentId)) {
191
+ const unassignedIds = getUnassignedTaskIds(5);
192
+ for (const candidateId of unassignedIds) {
193
+ const claimed = claimTask(candidateId, myAgentId);
194
+ if (claimed) {
195
+ return {
196
+ trigger: {
197
+ type: "task_assigned",
198
+ taskId: claimed.id,
199
+ task: claimed,
200
+ },
201
+ };
202
+ }
203
+ // Claim failed (another worker got it) — try next
204
+ }
205
+ }
206
+ }
207
+
208
+ // No trigger found
209
+ return { trigger: null };
210
+ })();
211
+ } catch (error) {
212
+ console.error("[/api/poll] Database error:", error);
213
+ jsonError(
214
+ res,
215
+ `Database error occurred while polling for triggers: ${error instanceof Error ? error.message : String(error)}`,
216
+ 500,
217
+ );
218
+ return true;
219
+ }
220
+
221
+ // Handle error case
222
+ if ("error" in result) {
223
+ jsonError(res, result.error, result.status ?? 500);
224
+ return true;
225
+ }
226
+
227
+ // If no trigger found and agent is lead, check for Slack channel activity.
228
+ // This is the lowest-priority trigger, checked AFTER all others.
229
+ // Runs outside the transaction because it requires async Slack API calls.
230
+ // Throttled to avoid Slack API rate limits (~50 calls/min).
231
+ if (
232
+ result.trigger === null &&
233
+ process.env.LEAD_MONITOR_CHANNELS === "true" &&
234
+ Date.now() - lastChannelActivityCheckAt >= CHANNEL_ACTIVITY_INTERVAL_MS
235
+ ) {
236
+ const agent = getAgentById(myAgentId);
237
+ if (agent?.isLead) {
238
+ lastChannelActivityCheckAt = Date.now();
239
+ try {
240
+ const cursors = getAllChannelActivityCursors();
241
+ const cursorMap = new Map(cursors.map((c) => [c.channelId, c.lastSeenTs]));
242
+
243
+ // Parse optional channel allowlist from env
244
+ const allowedIds = process.env.LEAD_MONITOR_CHANNEL_IDS
245
+ ? process.env.LEAD_MONITOR_CHANNEL_IDS.split(",")
246
+ .map((s) => s.trim())
247
+ .filter(Boolean)
248
+ : undefined;
249
+
250
+ const { messages, seedCursors } = await fetchChannelActivity(cursorMap, allowedIds);
251
+
252
+ // Commit seed cursors immediately (cold-start initialization, no trigger)
253
+ for (const [channelId, ts] of seedCursors) {
254
+ upsertChannelActivityCursor(channelId, ts);
255
+ }
256
+
257
+ if (messages.length > 0) {
258
+ // Compute cursor updates but DON'T commit them yet.
259
+ // They're included in the trigger payload so the runner can commit
260
+ // them after the lead successfully processes the messages.
261
+ const latestPerChannel = new Map<string, string>();
262
+ for (const msg of messages) {
263
+ const existing = latestPerChannel.get(msg.channelId);
264
+ if (!existing || Number.parseFloat(msg.ts) > Number.parseFloat(existing)) {
265
+ latestPerChannel.set(msg.channelId, msg.ts);
266
+ }
267
+ }
268
+
269
+ result = {
270
+ trigger: {
271
+ type: "channel_activity",
272
+ count: messages.length,
273
+ messages: messages.map((m) => ({
274
+ channelId: m.channelId,
275
+ channelName: m.channelName,
276
+ ts: m.ts,
277
+ user: m.user,
278
+ text: m.text.slice(0, 500),
279
+ })),
280
+ cursorUpdates: Array.from(latestPerChannel.entries()).map(([channelId, ts]) => ({
281
+ channelId,
282
+ ts,
283
+ })),
284
+ },
285
+ };
286
+ }
287
+ } catch (err) {
288
+ console.warn("[/api/poll] Channel activity check failed:", err);
289
+ // Don't fail the poll — just skip this trigger
290
+ }
291
+ }
292
+ }
293
+
294
+ json(res, result);
295
+ return true;
296
+ }
297
+
298
+ return false;
299
+ }