@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,736 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import { unlink } from "node:fs/promises";
3
+ import { closeDb, initDb } from "../be/db";
4
+ import type { ExecutorMeta } from "../types";
5
+ import type { ExecutorDependencies, ExecutorInput } from "../workflows/executors/base";
6
+ import { CodeMatchExecutor, CodeMatchOutputSchema } from "../workflows/executors/code-match";
7
+ import { NotifyExecutor } from "../workflows/executors/notify";
8
+ import {
9
+ PropertyMatchExecutor,
10
+ PropertyMatchOutputSchema,
11
+ } from "../workflows/executors/property-match";
12
+ import { RawLlmExecutor } from "../workflows/executors/raw-llm";
13
+ import { createExecutorRegistry } from "../workflows/executors/registry";
14
+ import { ScriptExecutor, ScriptOutputSchema } from "../workflows/executors/script";
15
+ import { ValidateExecutor, ValidateOutputSchema } from "../workflows/executors/validate";
16
+ import { VcsExecutor, VcsOutputSchema } from "../workflows/executors/vcs";
17
+
18
+ const TEST_DB_PATH = "./test-workflow-executors.sqlite";
19
+
20
+ // ─── Mock Dependencies ───────────────────────────────────────
21
+
22
+ const postedMessages: { channelId: string; content: string }[] = [];
23
+
24
+ const mockDeps: ExecutorDependencies = {
25
+ db: {
26
+ postMessage: (channelId: string, _agentId: string | null, content: string) => {
27
+ const msg = { id: `msg-${Date.now()}`, channelId, content };
28
+ postedMessages.push({ channelId, content });
29
+ return msg;
30
+ },
31
+ } as unknown as typeof import("../be/db"),
32
+ eventBus: { emit: () => {}, on: () => {}, off: () => {} },
33
+ interpolate: (template: string, ctx: Record<string, unknown>) => {
34
+ return template.replace(/\{\{([^}]+)\}\}/g, (_match, path: string) => {
35
+ const keys = path.trim().split(".");
36
+ let value: unknown = ctx;
37
+ for (const key of keys) {
38
+ if (value == null || typeof value !== "object") return "";
39
+ value = (value as Record<string, unknown>)[key];
40
+ }
41
+ if (value == null) return "";
42
+ return typeof value === "object" ? JSON.stringify(value) : String(value);
43
+ });
44
+ },
45
+ };
46
+
47
+ const mockMeta: ExecutorMeta = {
48
+ runId: "00000000-0000-0000-0000-000000000001",
49
+ stepId: "00000000-0000-0000-0000-000000000002",
50
+ nodeId: "test-node",
51
+ workflowId: "00000000-0000-0000-0000-000000000003",
52
+ dryRun: false,
53
+ };
54
+
55
+ function input(
56
+ config: Record<string, unknown>,
57
+ context: Record<string, unknown> = {},
58
+ ): ExecutorInput {
59
+ return { config, context, meta: mockMeta };
60
+ }
61
+
62
+ // ─── Setup / Teardown ────────────────────────────────────────
63
+
64
+ beforeAll(async () => {
65
+ try {
66
+ await unlink(TEST_DB_PATH);
67
+ } catch {
68
+ // File doesn't exist
69
+ }
70
+ initDb(TEST_DB_PATH);
71
+ });
72
+
73
+ afterAll(async () => {
74
+ closeDb();
75
+ for (const suffix of ["", "-wal", "-shm"]) {
76
+ try {
77
+ await unlink(`${TEST_DB_PATH}${suffix}`);
78
+ } catch {
79
+ // File may not exist
80
+ }
81
+ }
82
+ });
83
+
84
+ // ─── PropertyMatch Executor ──────────────────────────────────
85
+
86
+ describe("PropertyMatchExecutor", () => {
87
+ const executor = new PropertyMatchExecutor(mockDeps);
88
+
89
+ test("config schema rejects empty conditions", () => {
90
+ const result = executor.configSchema.safeParse({ conditions: [] });
91
+ expect(result.success).toBe(false);
92
+ });
93
+
94
+ test("config schema rejects missing conditions", () => {
95
+ const result = executor.configSchema.safeParse({});
96
+ expect(result.success).toBe(false);
97
+ });
98
+
99
+ test("config schema accepts valid config", () => {
100
+ const result = executor.configSchema.safeParse({
101
+ conditions: [{ field: "data.value", op: "eq", value: 42 }],
102
+ });
103
+ expect(result.success).toBe(true);
104
+ });
105
+
106
+ test("config schema defaults mode to all", () => {
107
+ const result = executor.configSchema.parse({
108
+ conditions: [{ field: "x", op: "eq", value: 1 }],
109
+ });
110
+ expect(result.mode).toBe("all");
111
+ });
112
+
113
+ test("returns true port when all conditions pass (mode=all)", async () => {
114
+ const result = await executor.run(
115
+ input(
116
+ {
117
+ conditions: [
118
+ { field: "a", op: "eq", value: 1 },
119
+ { field: "b", op: "gt", value: 0 },
120
+ ],
121
+ },
122
+ { a: 1, b: 5 },
123
+ ),
124
+ );
125
+ expect(result.status).toBe("success");
126
+ expect(result.nextPort).toBe("true");
127
+ const out = result.output as { passed: boolean };
128
+ expect(out.passed).toBe(true);
129
+ });
130
+
131
+ test("returns false port when one condition fails (mode=all)", async () => {
132
+ const result = await executor.run(
133
+ input(
134
+ {
135
+ conditions: [
136
+ { field: "a", op: "eq", value: 1 },
137
+ { field: "b", op: "eq", value: 999 },
138
+ ],
139
+ },
140
+ { a: 1, b: 5 },
141
+ ),
142
+ );
143
+ expect(result.nextPort).toBe("false");
144
+ });
145
+
146
+ test("returns true port when one condition passes (mode=any)", async () => {
147
+ const result = await executor.run(
148
+ input(
149
+ {
150
+ conditions: [
151
+ { field: "a", op: "eq", value: 999 },
152
+ { field: "b", op: "eq", value: 5 },
153
+ ],
154
+ mode: "any",
155
+ },
156
+ { a: 1, b: 5 },
157
+ ),
158
+ );
159
+ expect(result.nextPort).toBe("true");
160
+ });
161
+
162
+ test("resolves nested dot-path fields", async () => {
163
+ const result = await executor.run(
164
+ input(
165
+ { conditions: [{ field: "data.nested.value", op: "eq", value: "hello" }] },
166
+ { data: { nested: { value: "hello" } } },
167
+ ),
168
+ );
169
+ expect(result.nextPort).toBe("true");
170
+ });
171
+
172
+ test("contains operator works on strings", async () => {
173
+ const result = await executor.run(
174
+ input(
175
+ { conditions: [{ field: "msg", op: "contains", value: "world" }] },
176
+ { msg: "hello world" },
177
+ ),
178
+ );
179
+ expect(result.nextPort).toBe("true");
180
+ });
181
+
182
+ test("exists operator returns true for defined value", async () => {
183
+ const result = await executor.run(
184
+ input({ conditions: [{ field: "x", op: "exists" }] }, { x: 0 }),
185
+ );
186
+ expect(result.nextPort).toBe("true");
187
+ });
188
+
189
+ test("exists operator returns false for undefined value", async () => {
190
+ const result = await executor.run(
191
+ input({ conditions: [{ field: "missing", op: "exists" }] }, {}),
192
+ );
193
+ expect(result.nextPort).toBe("false");
194
+ });
195
+
196
+ test("output schema validates correctly", () => {
197
+ const valid = PropertyMatchOutputSchema.safeParse({
198
+ passed: true,
199
+ results: [{ field: "x", op: "eq", expected: 1, actual: 1, passed: true }],
200
+ });
201
+ expect(valid.success).toBe(true);
202
+ });
203
+ });
204
+
205
+ // ─── CodeMatch Executor ──────────────────────────────────────
206
+
207
+ describe("CodeMatchExecutor", () => {
208
+ const executor = new CodeMatchExecutor(mockDeps);
209
+
210
+ test("config schema rejects empty outputPorts", () => {
211
+ const result = executor.configSchema.safeParse({ code: "return true", outputPorts: [] });
212
+ expect(result.success).toBe(false);
213
+ });
214
+
215
+ test("config schema rejects missing code", () => {
216
+ const result = executor.configSchema.safeParse({ outputPorts: ["a"] });
217
+ expect(result.success).toBe(false);
218
+ });
219
+
220
+ test("config schema accepts valid config", () => {
221
+ const result = executor.configSchema.safeParse({
222
+ code: "(input) => true",
223
+ outputPorts: ["true", "false"],
224
+ });
225
+ expect(result.success).toBe(true);
226
+ });
227
+
228
+ test("executes code and returns port", async () => {
229
+ const result = await executor.run(
230
+ input(
231
+ { code: "(input) => input.value > 10 ? 'high' : 'low'", outputPorts: ["high", "low"] },
232
+ { value: 42 },
233
+ ),
234
+ );
235
+ expect(result.status).toBe("success");
236
+ expect(result.nextPort).toBe("high");
237
+ const out = result.output as { port: string };
238
+ expect(out.port).toBe("high");
239
+ });
240
+
241
+ test("maps boolean result to true/false port", async () => {
242
+ const result = await executor.run(
243
+ input({ code: "(input) => input.x === 1", outputPorts: ["true", "false"] }, { x: 1 }),
244
+ );
245
+ expect(result.nextPort).toBe("true");
246
+ });
247
+
248
+ test("fails when returned port not in outputPorts", async () => {
249
+ const result = await executor.run(
250
+ input({ code: "(input) => 'unknown'", outputPorts: ["a", "b"] }, {}),
251
+ );
252
+ expect(result.status).toBe("failed");
253
+ expect(result.error).toContain("not in outputPorts");
254
+ });
255
+
256
+ test("fails on code execution error", async () => {
257
+ const result = await executor.run(
258
+ input({ code: "(input) => { throw new Error('boom') }", outputPorts: ["a"] }, {}),
259
+ );
260
+ expect(result.status).toBe("failed");
261
+ expect(result.error).toContain("boom");
262
+ });
263
+
264
+ test("sandboxes dangerous globals: process is undefined", async () => {
265
+ const result = await executor.run(
266
+ input(
267
+ {
268
+ code: "(input) => typeof process === 'undefined' ? 'safe' : 'unsafe'",
269
+ outputPorts: ["safe", "unsafe"],
270
+ },
271
+ {},
272
+ ),
273
+ );
274
+ expect(result.nextPort).toBe("safe");
275
+ });
276
+
277
+ test("sandboxes dangerous globals: Bun is undefined", async () => {
278
+ const result = await executor.run(
279
+ input(
280
+ {
281
+ code: "(input) => typeof Bun === 'undefined' ? 'safe' : 'unsafe'",
282
+ outputPorts: ["safe", "unsafe"],
283
+ },
284
+ {},
285
+ ),
286
+ );
287
+ expect(result.nextPort).toBe("safe");
288
+ });
289
+
290
+ test("sandboxes dangerous globals: require is undefined", async () => {
291
+ const result = await executor.run(
292
+ input(
293
+ {
294
+ code: "(input) => typeof require === 'undefined' ? 'safe' : 'unsafe'",
295
+ outputPorts: ["safe", "unsafe"],
296
+ },
297
+ {},
298
+ ),
299
+ );
300
+ expect(result.nextPort).toBe("safe");
301
+ });
302
+
303
+ test("sandboxes dangerous globals: fetch is undefined", async () => {
304
+ const result = await executor.run(
305
+ input(
306
+ {
307
+ code: "(input) => typeof fetch === 'undefined' ? 'safe' : 'unsafe'",
308
+ outputPorts: ["safe", "unsafe"],
309
+ },
310
+ {},
311
+ ),
312
+ );
313
+ expect(result.nextPort).toBe("safe");
314
+ });
315
+
316
+ test("output schema validates correctly", () => {
317
+ const valid = CodeMatchOutputSchema.safeParse({ port: "high", rawResult: "high" });
318
+ expect(valid.success).toBe(true);
319
+ });
320
+ });
321
+
322
+ // ─── Notify Executor ─────────────────────────────────────────
323
+
324
+ describe("NotifyExecutor", () => {
325
+ const executor = new NotifyExecutor(mockDeps);
326
+
327
+ test("config schema rejects invalid channel", () => {
328
+ const result = executor.configSchema.safeParse({
329
+ channel: "invalid",
330
+ template: "hi",
331
+ });
332
+ expect(result.success).toBe(false);
333
+ });
334
+
335
+ test("config schema accepts valid config", () => {
336
+ const result = executor.configSchema.safeParse({
337
+ channel: "swarm",
338
+ template: "hello {{name}}",
339
+ });
340
+ expect(result.success).toBe(true);
341
+ });
342
+
343
+ test("swarm channel posts message when target is set", async () => {
344
+ postedMessages.length = 0;
345
+ const result = await executor.run(
346
+ input({ channel: "swarm", target: "channel-1", template: "Hello {{who}}" }, { who: "world" }),
347
+ );
348
+ expect(result.status).toBe("success");
349
+ const out = result.output as { sent: boolean; message: string };
350
+ expect(out.sent).toBe(true);
351
+ expect(out.message).toBe("Hello world");
352
+ expect(postedMessages).toHaveLength(1);
353
+ expect(postedMessages[0].channelId).toBe("channel-1");
354
+ });
355
+
356
+ test("swarm channel returns sent=false when no target", async () => {
357
+ const result = await executor.run(input({ channel: "swarm", template: "no target" }, {}));
358
+ const out = result.output as { sent: boolean };
359
+ expect(out.sent).toBe(false);
360
+ });
361
+
362
+ test("slack stub returns sent=false", async () => {
363
+ const result = await executor.run(
364
+ input({ channel: "slack", target: "#general", template: "hi" }, {}),
365
+ );
366
+ const out = result.output as { sent: boolean };
367
+ expect(out.sent).toBe(false);
368
+ });
369
+
370
+ test("email stub returns sent=false", async () => {
371
+ const result = await executor.run(
372
+ input({ channel: "email", target: "user@test.com", template: "hi" }, {}),
373
+ );
374
+ const out = result.output as { sent: boolean };
375
+ expect(out.sent).toBe(false);
376
+ });
377
+ });
378
+
379
+ // ─── RawLlm Executor ────────────────────────────────────────
380
+
381
+ describe("RawLlmExecutor", () => {
382
+ const executor = new RawLlmExecutor(mockDeps);
383
+
384
+ test("config schema rejects missing prompt", () => {
385
+ const result = executor.configSchema.safeParse({});
386
+ expect(result.success).toBe(false);
387
+ });
388
+
389
+ test("config schema accepts valid config with prompt only", () => {
390
+ const result = executor.configSchema.safeParse({ prompt: "classify this" });
391
+ expect(result.success).toBe(true);
392
+ });
393
+
394
+ test("config schema accepts full config", () => {
395
+ const result = executor.configSchema.safeParse({
396
+ prompt: "classify",
397
+ model: "openai/gpt-4o",
398
+ schema: { type: "object", properties: { category: { type: "string" } } },
399
+ fallbackPort: "error",
400
+ });
401
+ expect(result.success).toBe(true);
402
+ });
403
+
404
+ // LLM integration tests — behavior depends on whether OPENROUTER_API_KEY is set
405
+ test("handles LLM call (success or fallback)", async () => {
406
+ const result = await executor.run(input({ prompt: "Say hello", fallbackPort: "error" }, {}));
407
+ // Either the LLM call succeeds or falls back
408
+ expect(result.status).toBe("success");
409
+ if (result.nextPort === "error") {
410
+ // Fallback path — LLM call failed (no API key or network error)
411
+ expect(result.error).toContain("fallback");
412
+ } else {
413
+ // Success path — LLM returned a result
414
+ const out = result.output as { result: unknown; model: string };
415
+ expect(out.model).toBeDefined();
416
+ expect(out.result).toBeDefined();
417
+ }
418
+ });
419
+
420
+ test("handles LLM call without fallback (success or failure)", async () => {
421
+ const result = await executor.run(input({ prompt: "Say hello" }, {}));
422
+ // Either the LLM call succeeds or fails
423
+ if (result.status === "failed") {
424
+ expect(result.error).toContain("LLM call failed");
425
+ } else {
426
+ expect(result.status).toBe("success");
427
+ const out = result.output as { result: unknown; model: string };
428
+ expect(out.result).toBeDefined();
429
+ }
430
+ });
431
+ });
432
+
433
+ // ─── Script Executor ─────────────────────────────────────────
434
+
435
+ describe("ScriptExecutor", () => {
436
+ const executor = new ScriptExecutor(mockDeps);
437
+
438
+ test("config schema rejects missing runtime", () => {
439
+ const result = executor.configSchema.safeParse({ script: "echo hi" });
440
+ expect(result.success).toBe(false);
441
+ });
442
+
443
+ test("config schema rejects invalid runtime", () => {
444
+ const result = executor.configSchema.safeParse({ runtime: "ruby", script: "puts 'hi'" });
445
+ expect(result.success).toBe(false);
446
+ });
447
+
448
+ test("config schema accepts valid config", () => {
449
+ const result = executor.configSchema.safeParse({
450
+ runtime: "bash",
451
+ script: "echo hello",
452
+ });
453
+ expect(result.success).toBe(true);
454
+ });
455
+
456
+ test("config schema defaults timeout to 30000", () => {
457
+ const result = executor.configSchema.parse({ runtime: "bash", script: "echo hi" });
458
+ expect(result.timeout).toBe(30_000);
459
+ });
460
+
461
+ test("runs bash script and captures stdout", async () => {
462
+ const result = await executor.run(input({ runtime: "bash", script: "echo 'hello world'" }, {}));
463
+ expect(result.status).toBe("success");
464
+ const out = result.output as { exitCode: number; stdout: string; stderr: string };
465
+ expect(out.exitCode).toBe(0);
466
+ expect(out.stdout).toBe("hello world");
467
+ expect(out.stderr).toBe("");
468
+ expect(result.nextPort).toBe("success");
469
+ });
470
+
471
+ test("captures stderr on failure", async () => {
472
+ const result = await executor.run(
473
+ input({ runtime: "bash", script: "echo err >&2; exit 1" }, {}),
474
+ );
475
+ expect(result.status).toBe("success"); // executor succeeds, script fails
476
+ const out = result.output as { exitCode: number; stdout: string; stderr: string };
477
+ expect(out.exitCode).toBe(1);
478
+ expect(out.stderr).toBe("err");
479
+ expect(result.nextPort).toBe("failure");
480
+ });
481
+
482
+ test("returns failure port on non-zero exit code", async () => {
483
+ const result = await executor.run(input({ runtime: "bash", script: "exit 42" }, {}));
484
+ expect(result.nextPort).toBe("failure");
485
+ const out = result.output as { exitCode: number };
486
+ expect(out.exitCode).toBe(42);
487
+ });
488
+
489
+ test("runs TypeScript script via bun", async () => {
490
+ const result = await executor.run(
491
+ input({ runtime: "ts", script: "console.log('ts works')" }, {}),
492
+ );
493
+ expect(result.status).toBe("success");
494
+ const out = result.output as { exitCode: number; stdout: string };
495
+ expect(out.exitCode).toBe(0);
496
+ expect(out.stdout).toBe("ts works");
497
+ });
498
+
499
+ test("output schema validates correctly", () => {
500
+ const valid = ScriptOutputSchema.safeParse({ exitCode: 0, stdout: "hi", stderr: "" });
501
+ expect(valid.success).toBe(true);
502
+ });
503
+ });
504
+
505
+ // ─── VCS Executor ────────────────────────────────────────────
506
+
507
+ describe("VcsExecutor", () => {
508
+ const executor = new VcsExecutor(mockDeps);
509
+
510
+ test("config schema rejects invalid action", () => {
511
+ const result = executor.configSchema.safeParse({
512
+ action: "delete-repo",
513
+ provider: "github",
514
+ repo: "owner/repo",
515
+ });
516
+ expect(result.success).toBe(false);
517
+ });
518
+
519
+ test("config schema rejects invalid provider", () => {
520
+ const result = executor.configSchema.safeParse({
521
+ action: "create-issue",
522
+ provider: "bitbucket",
523
+ repo: "owner/repo",
524
+ });
525
+ expect(result.success).toBe(false);
526
+ });
527
+
528
+ test("config schema accepts valid config", () => {
529
+ const result = executor.configSchema.safeParse({
530
+ action: "create-issue",
531
+ provider: "github",
532
+ repo: "owner/repo",
533
+ title: "Bug report",
534
+ body: "Something broke",
535
+ });
536
+ expect(result.success).toBe(true);
537
+ });
538
+
539
+ test("returns stub output with url and id", async () => {
540
+ const result = await executor.run(
541
+ input({ action: "create-issue", provider: "github", repo: "org/repo", title: "Test" }, {}),
542
+ );
543
+ expect(result.status).toBe("success");
544
+ const out = result.output as { url: string; id: string };
545
+ expect(out.url).toContain("github.com");
546
+ expect(out.url).toContain("org/repo");
547
+ expect(out.id).toContain("stub-");
548
+ });
549
+
550
+ test("interpolates title and body from context", async () => {
551
+ const result = await executor.run(
552
+ input(
553
+ {
554
+ action: "create-pr",
555
+ provider: "github",
556
+ repo: "org/repo",
557
+ title: "PR: {{task}}",
558
+ body: "Details: {{details}}",
559
+ },
560
+ { task: "fix bug", details: "memory leak" },
561
+ ),
562
+ );
563
+ expect(result.status).toBe("success");
564
+ });
565
+
566
+ test("output schema validates correctly", () => {
567
+ const valid = VcsOutputSchema.safeParse({ url: "https://github.com/x/y/1", id: "123" });
568
+ expect(valid.success).toBe(true);
569
+
570
+ const validNumeric = VcsOutputSchema.safeParse({ url: "https://github.com/x/y/1", id: 123 });
571
+ expect(validNumeric.success).toBe(true);
572
+ });
573
+ });
574
+
575
+ // ─── Validate Executor ───────────────────────────────────────
576
+
577
+ describe("ValidateExecutor", () => {
578
+ const executor = new ValidateExecutor(mockDeps);
579
+
580
+ test("config schema rejects missing targetNodeId", () => {
581
+ const result = executor.configSchema.safeParse({});
582
+ expect(result.success).toBe(false);
583
+ });
584
+
585
+ test("config schema accepts targetNodeId only", () => {
586
+ const result = executor.configSchema.safeParse({ targetNodeId: "step1" });
587
+ expect(result.success).toBe(true);
588
+ });
589
+
590
+ test("config schema accepts full config", () => {
591
+ const result = executor.configSchema.safeParse({
592
+ targetNodeId: "step1",
593
+ prompt: "Is the output valid?",
594
+ schema: { type: "object", properties: { name: { type: "string" } } },
595
+ });
596
+ expect(result.success).toBe(true);
597
+ });
598
+
599
+ test("fails when target node has no output", async () => {
600
+ const result = await executor.run(input({ targetNodeId: "missing" }, {}));
601
+ expect(result.status).toBe("success");
602
+ expect(result.nextPort).toBe("fail");
603
+ const out = result.output as { pass: boolean; reasoning: string };
604
+ expect(out.pass).toBe(false);
605
+ expect(out.reasoning).toContain("no output");
606
+ });
607
+
608
+ test("passes when target node exists and no criteria given", async () => {
609
+ const result = await executor.run(
610
+ input({ targetNodeId: "step1" }, { step1: { data: "something" } }),
611
+ );
612
+ expect(result.status).toBe("success");
613
+ expect(result.nextPort).toBe("pass");
614
+ });
615
+
616
+ test("schema validation passes for matching data", async () => {
617
+ const result = await executor.run(
618
+ input(
619
+ {
620
+ targetNodeId: "step1",
621
+ schema: {
622
+ type: "object",
623
+ properties: { stdout: { const: "good-data" } },
624
+ },
625
+ },
626
+ { step1: { stdout: "good-data" } },
627
+ ),
628
+ );
629
+ expect(result.nextPort).toBe("pass");
630
+ });
631
+
632
+ test("schema validation fails for non-matching data", async () => {
633
+ const result = await executor.run(
634
+ input(
635
+ {
636
+ targetNodeId: "step1",
637
+ schema: {
638
+ type: "object",
639
+ properties: { stdout: { const: "good-data" } },
640
+ },
641
+ },
642
+ { step1: { stdout: "bad-data" } },
643
+ ),
644
+ );
645
+ expect(result.nextPort).toBe("fail");
646
+ const out = result.output as { pass: boolean; reasoning: string };
647
+ expect(out.pass).toBe(false);
648
+ expect(out.reasoning).toContain("Schema validation failed");
649
+ });
650
+
651
+ test("schema validation checks type", async () => {
652
+ const result = await executor.run(
653
+ input({ targetNodeId: "step1", schema: { type: "string" } }, { step1: 42 }),
654
+ );
655
+ expect(result.nextPort).toBe("fail");
656
+ });
657
+
658
+ test("schema validation checks required properties", async () => {
659
+ const result = await executor.run(
660
+ input(
661
+ {
662
+ targetNodeId: "step1",
663
+ schema: { type: "object", required: ["name", "age"] },
664
+ },
665
+ { step1: { name: "Alice" } },
666
+ ),
667
+ );
668
+ expect(result.nextPort).toBe("fail");
669
+ const out = result.output as { reasoning: string };
670
+ expect(out.reasoning).toContain("age");
671
+ });
672
+
673
+ test("output schema validates correctly", () => {
674
+ const valid = ValidateOutputSchema.safeParse({
675
+ pass: true,
676
+ reasoning: "Looks good",
677
+ confidence: 0.95,
678
+ });
679
+ expect(valid.success).toBe(true);
680
+
681
+ const invalid = ValidateOutputSchema.safeParse({
682
+ pass: true,
683
+ reasoning: "Looks good",
684
+ confidence: 1.5, // Out of range
685
+ });
686
+ expect(invalid.success).toBe(false);
687
+ });
688
+ });
689
+
690
+ // ─── Registry Wiring ─────────────────────────────────────────
691
+
692
+ describe("createExecutorRegistry", () => {
693
+ test("registers all 8 executors (7 instant + 1 async)", () => {
694
+ const registry = createExecutorRegistry(mockDeps);
695
+ const types = registry.types();
696
+
697
+ expect(types).toContain("property-match");
698
+ expect(types).toContain("code-match");
699
+ expect(types).toContain("notify");
700
+ expect(types).toContain("raw-llm");
701
+ expect(types).toContain("script");
702
+ expect(types).toContain("vcs");
703
+ expect(types).toContain("validate");
704
+ expect(types).toContain("agent-task");
705
+ expect(types).toHaveLength(8);
706
+ });
707
+
708
+ test("instant executors have mode instant, async executors have mode async", () => {
709
+ const registry = createExecutorRegistry(mockDeps);
710
+ const instantTypes = [
711
+ "property-match",
712
+ "code-match",
713
+ "notify",
714
+ "raw-llm",
715
+ "script",
716
+ "vcs",
717
+ "validate",
718
+ ];
719
+ for (const type of instantTypes) {
720
+ expect(registry.get(type).mode).toBe("instant");
721
+ }
722
+ expect(registry.get("agent-task").mode).toBe("async");
723
+ });
724
+
725
+ test("get() retrieves the correct executor by type", () => {
726
+ const registry = createExecutorRegistry(mockDeps);
727
+ const pm = registry.get("property-match");
728
+ expect(pm).toBeInstanceOf(PropertyMatchExecutor);
729
+
730
+ const cm = registry.get("code-match");
731
+ expect(cm).toBeInstanceOf(CodeMatchExecutor);
732
+
733
+ const sc = registry.get("script");
734
+ expect(sc).toBeInstanceOf(ScriptExecutor);
735
+ });
736
+ });