@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,599 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import crypto from "node:crypto";
3
+ import { unlink } from "node:fs/promises";
4
+ import {
5
+ createServer as createHttpServer,
6
+ type IncomingMessage,
7
+ type Server,
8
+ type ServerResponse,
9
+ } from "node:http";
10
+ import { closeDb, getWorkflowVersions, initDb } from "../be/db";
11
+ import { getPathSegments, parseQueryParams } from "../http/utils";
12
+ import { handleWorkflows } from "../http/workflows";
13
+ import type {
14
+ Workflow,
15
+ WorkflowDefinition,
16
+ WorkflowEdge,
17
+ WorkflowRun,
18
+ WorkflowRunStep,
19
+ WorkflowVersion,
20
+ } from "../types";
21
+ import { initWorkflows, stopRetryPoller } from "../workflows";
22
+
23
+ const TEST_DB_PATH = "./test-workflow-http-v2.sqlite";
24
+ const TEST_PORT = 13030;
25
+
26
+ // ─── Test Server ─────────────────────────────────────────────
27
+
28
+ function createTestServer(): Server {
29
+ return createHttpServer(async (req: IncomingMessage, res: ServerResponse) => {
30
+ res.setHeader("Content-Type", "application/json");
31
+ const pathSegments = getPathSegments(req.url || "");
32
+ const queryParams = parseQueryParams(req.url || "");
33
+ const myAgentId = req.headers["x-agent-id"] as string | undefined;
34
+
35
+ const handled = await handleWorkflows(req, res, pathSegments, queryParams, myAgentId);
36
+ if (!handled) {
37
+ res.writeHead(404);
38
+ res.end(JSON.stringify({ error: "Not found" }));
39
+ }
40
+ });
41
+ }
42
+
43
+ // ─── Helpers ────────────────────────────────────────────────
44
+
45
+ const baseUrl = `http://localhost:${TEST_PORT}`;
46
+ const headers = {
47
+ "Content-Type": "application/json",
48
+ "X-Agent-ID": crypto.randomUUID(),
49
+ };
50
+
51
+ function simpleDefinition(): WorkflowDefinition {
52
+ return {
53
+ nodes: [
54
+ {
55
+ id: "n1",
56
+ type: "notify",
57
+ config: { channel: "swarm", template: "test" },
58
+ },
59
+ ],
60
+ };
61
+ }
62
+
63
+ function branchDefinition(): WorkflowDefinition {
64
+ return {
65
+ nodes: [
66
+ {
67
+ id: "start",
68
+ type: "property-match",
69
+ config: { conditions: [{ field: "trigger.ok", op: "eq", value: true }] },
70
+ next: { true: "yes", false: "no" },
71
+ },
72
+ { id: "yes", type: "notify", config: { channel: "swarm", template: "yes" } },
73
+ { id: "no", type: "notify", config: { channel: "swarm", template: "no" } },
74
+ ],
75
+ };
76
+ }
77
+
78
+ async function createTestWorkflow(overrides?: Record<string, unknown>): Promise<Workflow> {
79
+ const res = await fetch(`${baseUrl}/api/workflows`, {
80
+ method: "POST",
81
+ headers,
82
+ body: JSON.stringify({
83
+ name: `test-wf-${crypto.randomUUID().slice(0, 8)}`,
84
+ definition: simpleDefinition(),
85
+ ...overrides,
86
+ }),
87
+ });
88
+ return (await res.json()) as Workflow;
89
+ }
90
+
91
+ // ─── Setup / Teardown ────────────────────────────────────────
92
+
93
+ describe("Workflow HTTP API v2", () => {
94
+ let server: Server;
95
+
96
+ beforeAll(async () => {
97
+ try {
98
+ await unlink(TEST_DB_PATH);
99
+ } catch {
100
+ // ignore
101
+ }
102
+ initDb(TEST_DB_PATH);
103
+ initWorkflows();
104
+
105
+ server = createTestServer();
106
+ await new Promise<void>((resolve) => {
107
+ server.listen(TEST_PORT, () => resolve());
108
+ });
109
+ });
110
+
111
+ afterAll(async () => {
112
+ stopRetryPoller();
113
+ await new Promise<void>((resolve) => server.close(() => resolve()));
114
+ closeDb();
115
+ for (const suffix of ["", "-wal", "-shm"]) {
116
+ try {
117
+ await unlink(`${TEST_DB_PATH}${suffix}`);
118
+ } catch {
119
+ // ignore
120
+ }
121
+ }
122
+ });
123
+
124
+ // ─── CREATE ────────────────────────────────────────────────
125
+
126
+ describe("POST /api/workflows (create)", () => {
127
+ test("creates workflow with new schema (triggers, cooldown, input)", async () => {
128
+ const res = await fetch(`${baseUrl}/api/workflows`, {
129
+ method: "POST",
130
+ headers,
131
+ body: JSON.stringify({
132
+ name: "full-schema-workflow",
133
+ description: "test",
134
+ definition: simpleDefinition(),
135
+ triggers: [{ type: "webhook", hmacSecret: "secret-123" }],
136
+ cooldown: { minutes: 30 },
137
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: intentional — this is the input resolution syntax
138
+ input: { apiKey: "${API_KEY}", secret: "secret.MY_SECRET", literal: "hello" },
139
+ }),
140
+ });
141
+
142
+ expect(res.status).toBe(201);
143
+ const body = (await res.json()) as Workflow;
144
+ expect(body.name).toBe("full-schema-workflow");
145
+ expect(body.enabled).toBe(true);
146
+ expect(body.triggers).toHaveLength(1);
147
+ expect(body.triggers[0]!.type).toBe("webhook");
148
+ expect(body.cooldown).toEqual({ minutes: 30 });
149
+ expect(body.input).toBeDefined();
150
+ expect(body.input!.literal).toBe("hello");
151
+ });
152
+
153
+ test("creates workflow with minimal schema (definition only)", async () => {
154
+ const res = await fetch(`${baseUrl}/api/workflows`, {
155
+ method: "POST",
156
+ headers,
157
+ body: JSON.stringify({
158
+ name: "minimal-workflow",
159
+ definition: simpleDefinition(),
160
+ }),
161
+ });
162
+
163
+ expect(res.status).toBe(201);
164
+ const body = (await res.json()) as Workflow;
165
+ expect(body.triggers).toEqual([]);
166
+ expect(body.cooldown).toBeUndefined();
167
+ expect(body.input).toBeUndefined();
168
+ });
169
+
170
+ test("rejects invalid definition (dangling next reference)", async () => {
171
+ const res = await fetch(`${baseUrl}/api/workflows`, {
172
+ method: "POST",
173
+ headers,
174
+ body: JSON.stringify({
175
+ name: "bad-definition",
176
+ definition: {
177
+ nodes: [
178
+ {
179
+ id: "n1",
180
+ type: "notify",
181
+ config: {},
182
+ next: "nonexistent",
183
+ },
184
+ ],
185
+ },
186
+ }),
187
+ });
188
+
189
+ expect(res.status).toBe(400);
190
+ const body = (await res.json()) as { error: string };
191
+ expect(body.error).toContain("nonexistent");
192
+ });
193
+
194
+ test("rejects invalid definition (multiple entry nodes)", async () => {
195
+ const res = await fetch(`${baseUrl}/api/workflows`, {
196
+ method: "POST",
197
+ headers,
198
+ body: JSON.stringify({
199
+ name: "multi-entry",
200
+ definition: {
201
+ nodes: [
202
+ { id: "a", type: "notify", config: {} },
203
+ { id: "b", type: "notify", config: {} },
204
+ ],
205
+ },
206
+ }),
207
+ });
208
+
209
+ expect(res.status).toBe(400);
210
+ const body = (await res.json()) as { error: string };
211
+ expect(body.error).toContain("Multiple entry nodes");
212
+ });
213
+ });
214
+
215
+ // ─── GET (single) ──────────────────────────────────────────
216
+
217
+ describe("GET /api/workflows/:id", () => {
218
+ test("returns workflow with triggers, cooldown, and auto-generated edges", async () => {
219
+ const workflow = await createTestWorkflow({
220
+ definition: branchDefinition(),
221
+ triggers: [{ type: "webhook" }],
222
+ cooldown: { hours: 1 },
223
+ });
224
+
225
+ const res = await fetch(`${baseUrl}/api/workflows/${workflow.id}`, { headers });
226
+ expect(res.status).toBe(200);
227
+
228
+ const body = (await res.json()) as Workflow & { edges: WorkflowEdge[] };
229
+ expect(body.id).toBe(workflow.id);
230
+ expect(body.triggers).toHaveLength(1);
231
+ expect(body.cooldown).toEqual({ hours: 1 });
232
+ // Auto-generated edges from branch definition
233
+ expect(body.edges).toBeDefined();
234
+ expect(body.edges.length).toBeGreaterThanOrEqual(2);
235
+ // Verify edge structure
236
+ const edgeToYes = body.edges.find((e) => e.target === "yes");
237
+ expect(edgeToYes).toBeDefined();
238
+ expect(edgeToYes!.source).toBe("start");
239
+ expect(edgeToYes!.sourcePort).toBe("true");
240
+ });
241
+
242
+ test("returns 404 for missing workflow", async () => {
243
+ const res = await fetch(`${baseUrl}/api/workflows/${crypto.randomUUID()}`, { headers });
244
+ expect(res.status).toBe(404);
245
+ });
246
+ });
247
+
248
+ // ─── LIST ────────────────────────────────────────────────
249
+
250
+ describe("GET /api/workflows (list)", () => {
251
+ test("returns all workflows with new fields", async () => {
252
+ const res = await fetch(`${baseUrl}/api/workflows`, { headers });
253
+ expect(res.status).toBe(200);
254
+ const body = (await res.json()) as Workflow[];
255
+ expect(Array.isArray(body)).toBe(true);
256
+ expect(body.length).toBeGreaterThan(0);
257
+ // Each workflow should have the new fields
258
+ for (const wf of body) {
259
+ expect(Array.isArray(wf.triggers)).toBe(true);
260
+ expect(wf.definition).toBeDefined();
261
+ expect(wf.definition.nodes).toBeDefined();
262
+ }
263
+ });
264
+ });
265
+
266
+ // ─── UPDATE ──────────────────────────────────────────────
267
+
268
+ describe("PUT /api/workflows/:id (update)", () => {
269
+ test("creates version snapshot on update", async () => {
270
+ const workflow = await createTestWorkflow();
271
+
272
+ // First update
273
+ const res1 = await fetch(`${baseUrl}/api/workflows/${workflow.id}`, {
274
+ method: "PUT",
275
+ headers,
276
+ body: JSON.stringify({ description: "updated once" }),
277
+ });
278
+ expect(res1.status).toBe(200);
279
+
280
+ // Second update
281
+ const res2 = await fetch(`${baseUrl}/api/workflows/${workflow.id}`, {
282
+ method: "PUT",
283
+ headers,
284
+ body: JSON.stringify({ description: "updated twice" }),
285
+ });
286
+ expect(res2.status).toBe(200);
287
+
288
+ // Verify versions were created
289
+ const versions = getWorkflowVersions(workflow.id);
290
+ expect(versions.length).toBe(2);
291
+ // Version 1 should have original description (undefined)
292
+ expect(versions.find((v) => v.version === 1)?.snapshot.description).toBeUndefined();
293
+ // Version 2 should have "updated once"
294
+ expect(versions.find((v) => v.version === 2)?.snapshot.description).toBe("updated once");
295
+ });
296
+
297
+ test("accepts new fields (triggers, cooldown, input)", async () => {
298
+ const workflow = await createTestWorkflow();
299
+
300
+ const res = await fetch(`${baseUrl}/api/workflows/${workflow.id}`, {
301
+ method: "PUT",
302
+ headers,
303
+ body: JSON.stringify({
304
+ triggers: [{ type: "webhook", hmacSecret: "new-secret" }],
305
+ cooldown: { seconds: 30 },
306
+ input: { key: "value" },
307
+ }),
308
+ });
309
+ expect(res.status).toBe(200);
310
+ const body = (await res.json()) as Workflow;
311
+ expect(body.triggers).toHaveLength(1);
312
+ expect(body.triggers[0]!.type).toBe("webhook");
313
+ expect(body.cooldown).toEqual({ seconds: 30 });
314
+ expect(body.input).toEqual({ key: "value" });
315
+ });
316
+
317
+ test("rejects invalid definition on update", async () => {
318
+ const workflow = await createTestWorkflow();
319
+
320
+ const res = await fetch(`${baseUrl}/api/workflows/${workflow.id}`, {
321
+ method: "PUT",
322
+ headers,
323
+ body: JSON.stringify({
324
+ definition: {
325
+ nodes: [{ id: "a", type: "x", config: {}, next: "nowhere" }],
326
+ },
327
+ }),
328
+ });
329
+ expect(res.status).toBe(400);
330
+ });
331
+
332
+ test("returns 404 for missing workflow", async () => {
333
+ const res = await fetch(`${baseUrl}/api/workflows/${crypto.randomUUID()}`, {
334
+ method: "PUT",
335
+ headers,
336
+ body: JSON.stringify({ description: "nope" }),
337
+ });
338
+ expect(res.status).toBe(404);
339
+ });
340
+ });
341
+
342
+ // ─── DELETE ──────────────────────────────────────────────
343
+
344
+ describe("DELETE /api/workflows/:id", () => {
345
+ test("deletes workflow and returns 204", async () => {
346
+ const workflow = await createTestWorkflow();
347
+ const res = await fetch(`${baseUrl}/api/workflows/${workflow.id}`, {
348
+ method: "DELETE",
349
+ headers,
350
+ });
351
+ expect(res.status).toBe(204);
352
+
353
+ // Verify it's gone
354
+ const getRes = await fetch(`${baseUrl}/api/workflows/${workflow.id}`, { headers });
355
+ expect(getRes.status).toBe(404);
356
+ });
357
+
358
+ test("returns 404 for missing workflow", async () => {
359
+ const res = await fetch(`${baseUrl}/api/workflows/${crypto.randomUUID()}`, {
360
+ method: "DELETE",
361
+ headers,
362
+ });
363
+ expect(res.status).toBe(404);
364
+ });
365
+ });
366
+
367
+ // ─── TRIGGER ─────────────────────────────────────────────
368
+
369
+ describe("POST /api/workflows/:id/trigger", () => {
370
+ test("triggers workflow and returns runId", async () => {
371
+ const workflow = await createTestWorkflow();
372
+
373
+ const res = await fetch(`${baseUrl}/api/workflows/${workflow.id}/trigger`, {
374
+ method: "POST",
375
+ headers,
376
+ body: JSON.stringify({}),
377
+ });
378
+ expect(res.status).toBe(201);
379
+ const body = (await res.json()) as { runId: string; skipped: boolean };
380
+ expect(body.runId).toBeDefined();
381
+ expect(body.skipped).toBe(false);
382
+ });
383
+
384
+ test("succeeds without agent ID (manual trigger)", async () => {
385
+ const workflow = await createTestWorkflow();
386
+
387
+ const res = await fetch(`${baseUrl}/api/workflows/${workflow.id}/trigger`, {
388
+ method: "POST",
389
+ headers: { "Content-Type": "application/json" }, // No X-Agent-ID needed
390
+ body: JSON.stringify({}),
391
+ });
392
+ expect(res.status).toBe(201);
393
+ });
394
+
395
+ test("returns 400 for disabled workflow", async () => {
396
+ const workflow = await createTestWorkflow();
397
+ // Disable it
398
+ await fetch(`${baseUrl}/api/workflows/${workflow.id}`, {
399
+ method: "PUT",
400
+ headers,
401
+ body: JSON.stringify({ enabled: false }),
402
+ });
403
+
404
+ const res = await fetch(`${baseUrl}/api/workflows/${workflow.id}/trigger`, {
405
+ method: "POST",
406
+ headers,
407
+ body: JSON.stringify({}),
408
+ });
409
+ expect(res.status).toBe(400);
410
+ });
411
+
412
+ test("returns 404 for missing workflow", async () => {
413
+ const res = await fetch(`${baseUrl}/api/workflows/${crypto.randomUUID()}/trigger`, {
414
+ method: "POST",
415
+ headers,
416
+ body: JSON.stringify({}),
417
+ });
418
+ expect(res.status).toBe(404);
419
+ });
420
+ });
421
+
422
+ // ─── LIST RUNS ────────────────────────────────────────────
423
+
424
+ describe("GET /api/workflows/:id/runs", () => {
425
+ test("lists runs and supports status filter", async () => {
426
+ const workflow = await createTestWorkflow();
427
+
428
+ // Trigger a run
429
+ await fetch(`${baseUrl}/api/workflows/${workflow.id}/trigger`, {
430
+ method: "POST",
431
+ headers,
432
+ body: JSON.stringify({}),
433
+ });
434
+
435
+ // List all runs
436
+ const res1 = await fetch(`${baseUrl}/api/workflows/${workflow.id}/runs`, { headers });
437
+ expect(res1.status).toBe(200);
438
+ const allRuns = (await res1.json()) as WorkflowRun[];
439
+ expect(allRuns.length).toBeGreaterThanOrEqual(1);
440
+
441
+ // Filter by status — use a status that likely doesn't match
442
+ const res2 = await fetch(`${baseUrl}/api/workflows/${workflow.id}/runs?status=waiting`, {
443
+ headers,
444
+ });
445
+ expect(res2.status).toBe(200);
446
+ const filteredRuns = (await res2.json()) as WorkflowRun[];
447
+ // All returned runs should have the requested status
448
+ for (const run of filteredRuns) {
449
+ expect(run.status).toBe("waiting");
450
+ }
451
+ });
452
+ });
453
+
454
+ // ─── GET RUN DETAIL ────────────────────────────────────────
455
+
456
+ describe("GET /api/workflow-runs/:id", () => {
457
+ test("returns run with steps including retry columns", async () => {
458
+ const workflow = await createTestWorkflow();
459
+
460
+ // Trigger
461
+ const triggerRes = await fetch(`${baseUrl}/api/workflows/${workflow.id}/trigger`, {
462
+ method: "POST",
463
+ headers,
464
+ body: JSON.stringify({}),
465
+ });
466
+ const { runId } = (await triggerRes.json()) as { runId: string };
467
+
468
+ const res = await fetch(`${baseUrl}/api/workflow-runs/${runId}`, { headers });
469
+ expect(res.status).toBe(200);
470
+ const body = (await res.json()) as { run: WorkflowRun; steps: WorkflowRunStep[] };
471
+ expect(body.run).toBeDefined();
472
+ expect(body.run.id).toBe(runId);
473
+ expect(body.steps).toBeDefined();
474
+ expect(Array.isArray(body.steps)).toBe(true);
475
+
476
+ // Steps should have retry columns
477
+ for (const step of body.steps) {
478
+ expect(typeof step.retryCount).toBe("number");
479
+ expect(typeof step.maxRetries).toBe("number");
480
+ // nextRetryAt may be undefined for non-retried steps
481
+ }
482
+ });
483
+
484
+ test("returns 404 for missing run", async () => {
485
+ const res = await fetch(`${baseUrl}/api/workflow-runs/${crypto.randomUUID()}`, {
486
+ headers,
487
+ });
488
+ expect(res.status).toBe(404);
489
+ });
490
+ });
491
+
492
+ // ─── WEBHOOK ──────────────────────────────────────────────
493
+
494
+ describe("POST /api/webhooks/:workflowId", () => {
495
+ test("valid HMAC returns 201", async () => {
496
+ const workflow = await createTestWorkflow({
497
+ triggers: [{ type: "webhook", hmacSecret: "test-secret" }],
498
+ });
499
+
500
+ const body = '{"event":"test"}';
501
+ const hmac = crypto.createHmac("sha256", "test-secret");
502
+ hmac.update(body);
503
+ const sig = `sha256=${hmac.digest("hex")}`;
504
+
505
+ const res = await fetch(`${baseUrl}/api/webhooks/${workflow.id}`, {
506
+ method: "POST",
507
+ headers: {
508
+ "Content-Type": "application/json",
509
+ "X-Hub-Signature-256": sig,
510
+ },
511
+ body,
512
+ });
513
+ expect(res.status).toBe(201);
514
+ const result = (await res.json()) as { runId: string };
515
+ expect(result.runId).toBeDefined();
516
+ });
517
+
518
+ test("invalid HMAC returns 401", async () => {
519
+ const workflow = await createTestWorkflow({
520
+ triggers: [{ type: "webhook", hmacSecret: "test-secret" }],
521
+ });
522
+
523
+ const res = await fetch(`${baseUrl}/api/webhooks/${workflow.id}`, {
524
+ method: "POST",
525
+ headers: {
526
+ "Content-Type": "application/json",
527
+ "X-Hub-Signature-256": "sha256=invalid",
528
+ },
529
+ body: '{"event":"test"}',
530
+ });
531
+ expect(res.status).toBe(401);
532
+ });
533
+ });
534
+
535
+ // ─── VERSION HISTORY ──────────────────────────────────────
536
+
537
+ describe("Version History Endpoints", () => {
538
+ test("GET /api/workflows/:id/versions lists versions", async () => {
539
+ const workflow = await createTestWorkflow();
540
+
541
+ // Make two updates to create two versions
542
+ await fetch(`${baseUrl}/api/workflows/${workflow.id}`, {
543
+ method: "PUT",
544
+ headers,
545
+ body: JSON.stringify({ description: "v1 update" }),
546
+ });
547
+ await fetch(`${baseUrl}/api/workflows/${workflow.id}`, {
548
+ method: "PUT",
549
+ headers,
550
+ body: JSON.stringify({ description: "v2 update" }),
551
+ });
552
+
553
+ const res = await fetch(`${baseUrl}/api/workflows/${workflow.id}/versions`, {
554
+ headers,
555
+ });
556
+ expect(res.status).toBe(200);
557
+ const body = (await res.json()) as { versions: WorkflowVersion[] };
558
+ expect(body.versions).toHaveLength(2);
559
+ // Newest first
560
+ expect(body.versions[0]!.version).toBe(2);
561
+ expect(body.versions[1]!.version).toBe(1);
562
+ });
563
+
564
+ test("GET /api/workflows/:id/versions/:version returns specific version", async () => {
565
+ const workflow = await createTestWorkflow();
566
+
567
+ await fetch(`${baseUrl}/api/workflows/${workflow.id}`, {
568
+ method: "PUT",
569
+ headers,
570
+ body: JSON.stringify({ description: "first update" }),
571
+ });
572
+
573
+ const res = await fetch(`${baseUrl}/api/workflows/${workflow.id}/versions/1`, {
574
+ headers,
575
+ });
576
+ expect(res.status).toBe(200);
577
+ const body = (await res.json()) as WorkflowVersion;
578
+ expect(body.version).toBe(1);
579
+ expect(body.snapshot).toBeDefined();
580
+ expect(body.snapshot.definition).toBeDefined();
581
+ });
582
+
583
+ test("GET /api/workflows/:id/versions/:version returns 404 for nonexistent version", async () => {
584
+ const workflow = await createTestWorkflow();
585
+
586
+ const res = await fetch(`${baseUrl}/api/workflows/${workflow.id}/versions/999`, {
587
+ headers,
588
+ });
589
+ expect(res.status).toBe(404);
590
+ });
591
+
592
+ test("GET /api/workflows/:id/versions returns 404 for nonexistent workflow", async () => {
593
+ const res = await fetch(`${baseUrl}/api/workflows/${crypto.randomUUID()}/versions`, {
594
+ headers,
595
+ });
596
+ expect(res.status).toBe(404);
597
+ });
598
+ });
599
+ });