@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,468 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import { z } from "zod";
3
+ import {
4
+ cancelTask,
5
+ completeTask,
6
+ createTaskExtended,
7
+ failTask,
8
+ getAllTasks,
9
+ getDb,
10
+ getLogsByTaskId,
11
+ getPausedTasksForAgent,
12
+ getTaskById,
13
+ getTasksCount,
14
+ pauseTask,
15
+ resumeTask,
16
+ updateAgentStatusFromCapacity,
17
+ updateTaskClaudeSessionId,
18
+ updateTaskProgress,
19
+ } from "../be/db";
20
+ import { route } from "./route-def";
21
+ import { json, jsonError } from "./utils";
22
+
23
+ // ─── Route Definitions ───────────────────────────────────────────────────────
24
+
25
+ const listTasks = route({
26
+ method: "get",
27
+ path: "/api/tasks",
28
+ pattern: ["api", "tasks"],
29
+ summary: "List tasks with filters",
30
+ tags: ["Tasks"],
31
+ query: z.object({
32
+ status: z.string().optional(),
33
+ agentId: z.string().optional(),
34
+ epicId: z.string().optional(),
35
+ scheduleId: z.string().optional(),
36
+ search: z.string().optional(),
37
+ includeHeartbeat: z.enum(["true", "false"]).optional(),
38
+ limit: z.coerce.number().int().optional(),
39
+ offset: z.coerce.number().int().optional(),
40
+ }),
41
+ responses: {
42
+ 200: { description: "Paginated task list" },
43
+ },
44
+ });
45
+
46
+ const createTask = route({
47
+ method: "post",
48
+ path: "/api/tasks",
49
+ pattern: ["api", "tasks"],
50
+ summary: "Create a new task",
51
+ tags: ["Tasks"],
52
+ body: z.object({
53
+ task: z.string().min(1),
54
+ agentId: z.string().optional(),
55
+ taskType: z.string().optional(),
56
+ tags: z.array(z.string()).optional(),
57
+ priority: z.number().int().optional(),
58
+ dependsOn: z.array(z.string()).optional(),
59
+ offeredTo: z.string().optional(),
60
+ dir: z.string().optional(),
61
+ parentTaskId: z.string().optional(),
62
+ source: z.string().optional(),
63
+ outputSchema: z.record(z.string(), z.unknown()).optional(),
64
+ }),
65
+ responses: {
66
+ 201: { description: "Task created" },
67
+ 400: { description: "Validation error" },
68
+ },
69
+ });
70
+
71
+ const updateClaudeSession = route({
72
+ method: "put",
73
+ path: "/api/tasks/{id}/claude-session",
74
+ pattern: ["api", "tasks", null, "claude-session"],
75
+ summary: "Update Claude session ID for a task",
76
+ tags: ["Tasks"],
77
+ params: z.object({ id: z.string() }),
78
+ body: z.object({ claudeSessionId: z.string().min(1) }),
79
+ responses: {
80
+ 200: { description: "Session ID updated" },
81
+ 404: { description: "Task not found" },
82
+ },
83
+ });
84
+
85
+ const cancelTaskRoute = route({
86
+ method: "post",
87
+ path: "/api/tasks/{id}/cancel",
88
+ pattern: ["api", "tasks", null, "cancel"],
89
+ summary: "Cancel a pending or in-progress task",
90
+ tags: ["Tasks"],
91
+ params: z.object({ id: z.string() }),
92
+ responses: {
93
+ 200: { description: "Task cancelled" },
94
+ 400: { description: "Cannot cancel terminal task" },
95
+ 404: { description: "Task not found" },
96
+ },
97
+ });
98
+
99
+ const getTask = route({
100
+ method: "get",
101
+ path: "/api/tasks/{id}",
102
+ pattern: ["api", "tasks", null],
103
+ summary: "Get task details with logs",
104
+ tags: ["Tasks"],
105
+ params: z.object({ id: z.string() }),
106
+ responses: {
107
+ 200: { description: "Task with logs" },
108
+ 404: { description: "Task not found" },
109
+ },
110
+ });
111
+
112
+ const updateTaskProgressRoute = route({
113
+ method: "post",
114
+ path: "/api/tasks/{id}/progress",
115
+ pattern: ["api", "tasks", null, "progress"],
116
+ summary: "Update task progress text",
117
+ tags: ["Tasks"],
118
+ params: z.object({ id: z.string() }),
119
+ body: z.object({ progress: z.string().min(1) }),
120
+ responses: {
121
+ 200: { description: "Progress updated" },
122
+ 404: { description: "Task not found" },
123
+ },
124
+ });
125
+
126
+ const finishTask = route({
127
+ method: "post",
128
+ path: "/api/tasks/{id}/finish",
129
+ pattern: ["api", "tasks", null, "finish"],
130
+ summary: "Mark task as completed or failed (runner endpoint)",
131
+ tags: ["Tasks"],
132
+ params: z.object({ id: z.string() }),
133
+ body: z.object({
134
+ status: z.enum(["completed", "failed"]),
135
+ output: z.string().optional(),
136
+ failureReason: z.string().optional(),
137
+ }),
138
+ auth: { apiKey: true, agentId: true },
139
+ responses: {
140
+ 200: { description: "Task finished" },
141
+ 400: { description: "Invalid status" },
142
+ 403: { description: "Not assigned to this agent" },
143
+ 404: { description: "Task not found" },
144
+ },
145
+ });
146
+
147
+ const listPausedTasks = route({
148
+ method: "get",
149
+ path: "/api/paused-tasks",
150
+ pattern: ["api", "paused-tasks"],
151
+ summary: "Get paused tasks for this agent",
152
+ tags: ["Tasks"],
153
+ auth: { apiKey: true, agentId: true },
154
+ responses: {
155
+ 200: { description: "Paused task list" },
156
+ },
157
+ });
158
+
159
+ const pauseTaskRoute = route({
160
+ method: "post",
161
+ path: "/api/tasks/{id}/pause",
162
+ pattern: ["api", "tasks", null, "pause"],
163
+ summary: "Pause an in-progress task",
164
+ tags: ["Tasks"],
165
+ params: z.object({ id: z.string() }),
166
+ responses: {
167
+ 200: { description: "Task paused" },
168
+ 400: { description: "Task not in_progress" },
169
+ 403: { description: "Task belongs to another agent" },
170
+ 404: { description: "Task not found" },
171
+ },
172
+ });
173
+
174
+ const resumeTaskRoute = route({
175
+ method: "post",
176
+ path: "/api/tasks/{id}/resume",
177
+ pattern: ["api", "tasks", null, "resume"],
178
+ summary: "Resume a paused task",
179
+ tags: ["Tasks"],
180
+ params: z.object({ id: z.string() }),
181
+ responses: {
182
+ 200: { description: "Task resumed" },
183
+ 400: { description: "Task not paused" },
184
+ 403: { description: "Task belongs to another agent" },
185
+ 404: { description: "Task not found" },
186
+ },
187
+ });
188
+
189
+ // ─── Handler ─────────────────────────────────────────────────────────────────
190
+
191
+ export async function handleTasks(
192
+ req: IncomingMessage,
193
+ res: ServerResponse,
194
+ pathSegments: string[],
195
+ queryParams: URLSearchParams,
196
+ myAgentId: string | undefined,
197
+ ): Promise<boolean> {
198
+ if (listTasks.match(req.method, pathSegments)) {
199
+ const parsed = await listTasks.parse(req, res, pathSegments, queryParams);
200
+ if (!parsed) return true;
201
+ const filters = {
202
+ status: (parsed.query.status as import("../types").AgentTaskStatus) || undefined,
203
+ agentId: parsed.query.agentId || undefined,
204
+ epicId: parsed.query.epicId || undefined,
205
+ scheduleId: parsed.query.scheduleId || undefined,
206
+ search: parsed.query.search || undefined,
207
+ includeHeartbeat: parsed.query.includeHeartbeat === "true" || undefined,
208
+ limit: parsed.query.limit,
209
+ offset: parsed.query.offset,
210
+ };
211
+ const tasks = getAllTasks(filters);
212
+ const total = getTasksCount(filters);
213
+ json(res, { tasks, total });
214
+ return true;
215
+ }
216
+
217
+ if (createTask.match(req.method, pathSegments)) {
218
+ const parsed = await createTask.parse(req, res, pathSegments, queryParams);
219
+ if (!parsed) return true;
220
+
221
+ try {
222
+ const task = createTaskExtended(parsed.body.task, {
223
+ agentId: parsed.body.agentId || undefined,
224
+ creatorAgentId: myAgentId || undefined,
225
+ taskType: parsed.body.taskType || undefined,
226
+ tags: parsed.body.tags || undefined,
227
+ priority: parsed.body.priority || 50,
228
+ dependsOn: parsed.body.dependsOn || undefined,
229
+ offeredTo: parsed.body.offeredTo || undefined,
230
+ dir: parsed.body.dir || undefined,
231
+ parentTaskId: parsed.body.parentTaskId || undefined,
232
+ source: (parsed.body.source as import("../types").AgentTaskSource) || "api",
233
+ outputSchema: parsed.body.outputSchema || undefined,
234
+ });
235
+ json(res, task, 201);
236
+ } catch (error) {
237
+ console.error("[HTTP] Failed to create task:", error);
238
+ jsonError(res, "Failed to create task", 500);
239
+ }
240
+ return true;
241
+ }
242
+
243
+ if (updateClaudeSession.match(req.method, pathSegments)) {
244
+ const parsed = await updateClaudeSession.parse(req, res, pathSegments, queryParams);
245
+ if (!parsed) return true;
246
+ const task = updateTaskClaudeSessionId(parsed.params.id, parsed.body.claudeSessionId);
247
+ if (!task) {
248
+ jsonError(res, "Task not found", 404);
249
+ return true;
250
+ }
251
+ json(res, task);
252
+ return true;
253
+ }
254
+
255
+ if (cancelTaskRoute.match(req.method, pathSegments)) {
256
+ const parsed = await cancelTaskRoute.parse(req, res, pathSegments, queryParams);
257
+ if (!parsed) return true;
258
+ const task = getTaskById(parsed.params.id);
259
+
260
+ if (!task) {
261
+ jsonError(res, "Task not found", 404);
262
+ return true;
263
+ }
264
+
265
+ const terminalStatuses = ["completed", "failed", "cancelled"];
266
+ if (terminalStatuses.includes(task.status)) {
267
+ jsonError(res, `Cannot cancel task with status '${task.status}'`, 400);
268
+ return true;
269
+ }
270
+
271
+ // Parse optional reason from body (already consumed by parse if body schema exists,
272
+ // but cancel has no body schema — read raw)
273
+ let reason: string | undefined;
274
+ const chunks: Buffer[] = [];
275
+ for await (const chunk of req) {
276
+ chunks.push(chunk);
277
+ }
278
+ const raw = Buffer.concat(chunks).toString();
279
+ if (raw) {
280
+ try {
281
+ const body = JSON.parse(raw);
282
+ reason = body.reason;
283
+ } catch {
284
+ // No body or invalid JSON — proceed without reason
285
+ }
286
+ }
287
+
288
+ const cancelledTask = cancelTask(parsed.params.id, reason);
289
+ if (!cancelledTask) {
290
+ jsonError(res, "Failed to cancel task", 500);
291
+ return true;
292
+ }
293
+
294
+ if (task.agentId) {
295
+ updateAgentStatusFromCapacity(task.agentId);
296
+ }
297
+
298
+ json(res, { success: true, task: cancelledTask });
299
+ return true;
300
+ }
301
+
302
+ if (getTask.match(req.method, pathSegments)) {
303
+ const parsed = await getTask.parse(req, res, pathSegments, queryParams);
304
+ if (!parsed) return true;
305
+ const task = getTaskById(parsed.params.id);
306
+
307
+ if (!task) {
308
+ jsonError(res, "Task not found", 404);
309
+ return true;
310
+ }
311
+
312
+ const logs = getLogsByTaskId(parsed.params.id);
313
+ json(res, { ...task, logs });
314
+ return true;
315
+ }
316
+
317
+ if (updateTaskProgressRoute.match(req.method, pathSegments)) {
318
+ const parsed = await updateTaskProgressRoute.parse(req, res, pathSegments, queryParams);
319
+ if (!parsed) return true;
320
+ const task = getTaskById(parsed.params.id);
321
+
322
+ if (!task) {
323
+ jsonError(res, "Task not found", 404);
324
+ return true;
325
+ }
326
+
327
+ updateTaskProgress(parsed.params.id, parsed.body.progress);
328
+ json(res, { success: true });
329
+ return true;
330
+ }
331
+
332
+ if (finishTask.match(req.method, pathSegments)) {
333
+ if (!myAgentId) {
334
+ jsonError(res, "Missing X-Agent-ID header", 400);
335
+ return true;
336
+ }
337
+
338
+ const parsed = await finishTask.parse(req, res, pathSegments, queryParams);
339
+ if (!parsed) return true;
340
+
341
+ const result = getDb().transaction(() => {
342
+ const task = getTaskById(parsed.params.id);
343
+
344
+ if (!task) {
345
+ return { error: "Task not found", status: 404 };
346
+ }
347
+
348
+ if (task.agentId && task.agentId !== myAgentId) {
349
+ return { error: "Task is assigned to another agent", status: 403 };
350
+ }
351
+
352
+ if (task.status !== "in_progress") {
353
+ return { task, alreadyFinished: true };
354
+ }
355
+
356
+ let updatedTask: typeof task;
357
+ if (parsed.body.status === "completed") {
358
+ const result = completeTask(
359
+ parsed.params.id,
360
+ parsed.body.output || "Completed by runner wrapper (no explicit output)",
361
+ );
362
+ if (!result) {
363
+ return { error: "Failed to complete task", status: 500 };
364
+ }
365
+ updatedTask = result;
366
+ } else {
367
+ const result = failTask(
368
+ parsed.params.id,
369
+ parsed.body.failureReason || "Process exited without explicit completion",
370
+ );
371
+ if (!result) {
372
+ return { error: "Failed to mark task as failed", status: 500 };
373
+ }
374
+ updatedTask = result;
375
+ }
376
+
377
+ if (task.agentId) {
378
+ updateAgentStatusFromCapacity(task.agentId);
379
+ }
380
+
381
+ return { task: updatedTask };
382
+ })();
383
+
384
+ if ("error" in result && result.error) {
385
+ jsonError(res, result.error, (result as { status?: number }).status ?? 500);
386
+ return true;
387
+ }
388
+
389
+ json(res, {
390
+ success: true,
391
+ alreadyFinished: "alreadyFinished" in result ? result.alreadyFinished : false,
392
+ task: result.task,
393
+ });
394
+ return true;
395
+ }
396
+
397
+ if (listPausedTasks.match(req.method, pathSegments)) {
398
+ if (!myAgentId) {
399
+ jsonError(res, "Missing X-Agent-ID header", 400);
400
+ return true;
401
+ }
402
+ const pausedTasks = getPausedTasksForAgent(myAgentId);
403
+ json(res, { tasks: pausedTasks });
404
+ return true;
405
+ }
406
+
407
+ if (pauseTaskRoute.match(req.method, pathSegments)) {
408
+ const parsed = await pauseTaskRoute.parse(req, res, pathSegments, queryParams);
409
+ if (!parsed) return true;
410
+ const task = getTaskById(parsed.params.id);
411
+
412
+ if (!task) {
413
+ jsonError(res, "Task not found", 404);
414
+ return true;
415
+ }
416
+
417
+ if (myAgentId && task.agentId !== myAgentId) {
418
+ jsonError(res, "Task belongs to another agent", 403);
419
+ return true;
420
+ }
421
+
422
+ if (task.status !== "in_progress") {
423
+ jsonError(res, `Task status is '${task.status}', not 'in_progress'`, 400);
424
+ return true;
425
+ }
426
+
427
+ const pausedTask = pauseTask(parsed.params.id);
428
+ if (!pausedTask) {
429
+ jsonError(res, "Failed to pause task", 500);
430
+ return true;
431
+ }
432
+
433
+ json(res, { success: true, task: pausedTask });
434
+ return true;
435
+ }
436
+
437
+ if (resumeTaskRoute.match(req.method, pathSegments)) {
438
+ const parsed = await resumeTaskRoute.parse(req, res, pathSegments, queryParams);
439
+ if (!parsed) return true;
440
+ const task = getTaskById(parsed.params.id);
441
+
442
+ if (!task) {
443
+ jsonError(res, "Task not found", 404);
444
+ return true;
445
+ }
446
+
447
+ if (myAgentId && task.agentId !== myAgentId) {
448
+ jsonError(res, "Task belongs to another agent", 403);
449
+ return true;
450
+ }
451
+
452
+ if (task.status !== "paused") {
453
+ jsonError(res, `Task status is '${task.status}', not 'paused'`, 400);
454
+ return true;
455
+ }
456
+
457
+ const resumedTask = resumeTask(parsed.params.id);
458
+ if (!resumedTask) {
459
+ jsonError(res, "Failed to resume task", 500);
460
+ return true;
461
+ }
462
+
463
+ json(res, { success: true, task: resumedTask });
464
+ return true;
465
+ }
466
+
467
+ return false;
468
+ }
@@ -0,0 +1,10 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import { handleLinearTracker } from "./linear";
3
+
4
+ export async function handleTrackers(
5
+ req: IncomingMessage,
6
+ res: ServerResponse,
7
+ pathSegments: string[],
8
+ ): Promise<boolean> {
9
+ return await handleLinearTracker(req, res, pathSegments);
10
+ }
@@ -0,0 +1,187 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import { z } from "zod";
3
+ import { getOAuthTokens } from "../../be/db-queries/oauth";
4
+ import { isLinearEnabled } from "../../linear/app";
5
+ import { getLinearAuthorizationUrl, handleLinearCallback } from "../../linear/oauth";
6
+ import { handleLinearWebhook } from "../../linear/webhook";
7
+ import { route } from "../route-def";
8
+ import { parseQueryParams } from "../utils";
9
+
10
+ // ─── Route Definitions ───────────────────────────────────────────────────────
11
+
12
+ const linearAuthorize = route({
13
+ method: "get",
14
+ path: "/api/trackers/linear/authorize",
15
+ pattern: ["api", "trackers", "linear", "authorize"],
16
+ summary: "Redirect to Linear OAuth consent screen",
17
+ tags: ["Trackers"],
18
+ auth: { apiKey: false },
19
+ responses: {
20
+ 302: { description: "Redirect to Linear OAuth" },
21
+ 500: { description: "Failed to generate authorization URL" },
22
+ 503: { description: "Linear integration not configured" },
23
+ },
24
+ });
25
+
26
+ const linearCallback = route({
27
+ method: "get",
28
+ path: "/api/trackers/linear/callback",
29
+ pattern: ["api", "trackers", "linear", "callback"],
30
+ summary: "Handle Linear OAuth callback",
31
+ tags: ["Trackers"],
32
+ auth: { apiKey: false },
33
+ query: z.object({
34
+ code: z.string(),
35
+ state: z.string(),
36
+ }),
37
+ responses: {
38
+ 200: { description: "OAuth complete" },
39
+ 400: { description: "Invalid state or code" },
40
+ 500: { description: "Token exchange failed" },
41
+ },
42
+ });
43
+
44
+ const linearStatus = route({
45
+ method: "get",
46
+ path: "/api/trackers/linear/status",
47
+ pattern: ["api", "trackers", "linear", "status"],
48
+ summary: "Linear connection status, token expiry, workspace info, expected webhook URL",
49
+ tags: ["Trackers"],
50
+ responses: {
51
+ 200: { description: "Connection status" },
52
+ 503: { description: "Linear integration not configured" },
53
+ },
54
+ });
55
+
56
+ const linearWebhook = route({
57
+ method: "post",
58
+ path: "/api/trackers/linear/webhook",
59
+ pattern: ["api", "trackers", "linear", "webhook"],
60
+ summary: "Handle Linear webhook events (signature-verified)",
61
+ tags: ["Trackers"],
62
+ auth: { apiKey: false },
63
+ responses: {
64
+ 200: { description: "Event accepted" },
65
+ 401: { description: "Invalid signature" },
66
+ 503: { description: "Linear integration not configured" },
67
+ },
68
+ });
69
+
70
+ // ─── Handler ─────────────────────────────────────────────────────────────────
71
+
72
+ export async function handleLinearTracker(
73
+ req: IncomingMessage,
74
+ res: ServerResponse,
75
+ pathSegments: string[],
76
+ ): Promise<boolean> {
77
+ // GET /api/trackers/linear/authorize — redirect to Linear OAuth
78
+ if (linearAuthorize.match(req.method, pathSegments)) {
79
+ if (!isLinearEnabled()) {
80
+ res.writeHead(503, { "Content-Type": "application/json" });
81
+ res.end(JSON.stringify({ error: "Linear integration not configured" }));
82
+ return true;
83
+ }
84
+
85
+ try {
86
+ const url = await getLinearAuthorizationUrl();
87
+ if (!url) {
88
+ res.writeHead(500, { "Content-Type": "application/json" });
89
+ res.end(JSON.stringify({ error: "Failed to generate authorization URL" }));
90
+ return true;
91
+ }
92
+
93
+ res.writeHead(302, { Location: url });
94
+ res.end();
95
+ } catch (err) {
96
+ const message = err instanceof Error ? err.message : String(err);
97
+ console.error("[Linear] Failed to generate authorization URL:", message);
98
+ res.writeHead(500, { "Content-Type": "application/json" });
99
+ res.end(JSON.stringify({ error: "Failed to generate authorization URL" }));
100
+ }
101
+ return true;
102
+ }
103
+
104
+ // GET /api/trackers/linear/callback — handle OAuth callback from Linear
105
+ if (linearCallback.match(req.method, pathSegments)) {
106
+ const queryParams = parseQueryParams(req.url || "");
107
+ const parsed = await linearCallback.parse(req, res, pathSegments, queryParams);
108
+ if (!parsed) return true; // parse() already sent 400
109
+
110
+ const { code, state } = parsed.query;
111
+
112
+ try {
113
+ await handleLinearCallback(code, state);
114
+ res.writeHead(200, { "Content-Type": "text/html" });
115
+ res.end(`<!DOCTYPE html>
116
+ <html>
117
+ <head><title>Linear Connected</title></head>
118
+ <body style="font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0;">
119
+ <div style="text-align: center;">
120
+ <h1>Linear Connected</h1>
121
+ <p>OAuth authorization complete. You can close this window.</p>
122
+ </div>
123
+ </body>
124
+ </html>`);
125
+ } catch (err) {
126
+ const message = err instanceof Error ? err.message : String(err);
127
+ console.error("[Linear] OAuth callback failed:", message);
128
+
129
+ if (message.includes("Invalid or expired OAuth state")) {
130
+ res.writeHead(400, { "Content-Type": "application/json" });
131
+ res.end(JSON.stringify({ error: "Invalid or expired OAuth state" }));
132
+ } else {
133
+ res.writeHead(500, { "Content-Type": "application/json" });
134
+ res.end(JSON.stringify({ error: "Token exchange failed", details: message }));
135
+ }
136
+ }
137
+ return true;
138
+ }
139
+
140
+ // GET /api/trackers/linear/status — connection status
141
+ if (linearStatus.match(req.method, pathSegments)) {
142
+ if (!isLinearEnabled()) {
143
+ res.writeHead(503, { "Content-Type": "application/json" });
144
+ res.end(JSON.stringify({ error: "Linear integration not configured" }));
145
+ return true;
146
+ }
147
+
148
+ const tokens = getOAuthTokens("linear");
149
+ const baseUrl = process.env.MCP_BASE_URL || `http://localhost:${process.env.PORT || "3013"}`;
150
+
151
+ const status = {
152
+ provider: "linear",
153
+ connected: !!tokens,
154
+ tokenExpiry: tokens?.expiresAt ?? null,
155
+ scope: tokens?.scope ?? null,
156
+ webhookUrl: `${baseUrl}/api/trackers/linear/webhook`,
157
+ };
158
+
159
+ res.writeHead(200, { "Content-Type": "application/json" });
160
+ res.end(JSON.stringify(status));
161
+ return true;
162
+ }
163
+
164
+ // POST /api/trackers/linear/webhook — handle Linear webhook events
165
+ if (linearWebhook.match(req.method, pathSegments)) {
166
+ // Read raw body for HMAC signature verification (same pattern as GitHub/GitLab webhooks)
167
+ const chunks: Buffer[] = [];
168
+ for await (const chunk of req) {
169
+ chunks.push(chunk);
170
+ }
171
+ const rawBody = Buffer.concat(chunks).toString();
172
+
173
+ // Collect headers into a plain object for the webhook handler
174
+ const headers: Record<string, string | undefined> = {
175
+ "linear-signature": req.headers["linear-signature"] as string | undefined,
176
+ "x-linear-signature": req.headers["x-linear-signature"] as string | undefined,
177
+ "linear-delivery": req.headers["linear-delivery"] as string | undefined,
178
+ };
179
+
180
+ const result = await handleLinearWebhook(rawBody, headers);
181
+ res.writeHead(result.status, { "Content-Type": "application/json" });
182
+ res.end(JSON.stringify(result.body));
183
+ return true;
184
+ }
185
+
186
+ return false;
187
+ }
@@ -0,0 +1,12 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+
3
+ export type RouteContext = {
4
+ req: IncomingMessage;
5
+ res: ServerResponse;
6
+ pathSegments: string[];
7
+ queryParams: URLSearchParams;
8
+ myAgentId: string | undefined;
9
+ };
10
+
11
+ /** A route handler returns true if it handled the request, false otherwise */
12
+ export type RouteHandler = (ctx: RouteContext) => Promise<boolean>;