@desplega.ai/agent-swarm 1.20.0 → 1.49.0

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 (810) hide show
  1. package/.claude/settings.json +84 -0
  2. package/.claude/settings.local.json +3 -1
  3. package/.entire/settings.json +4 -0
  4. package/.env.docker.example +40 -23
  5. package/.env.example +39 -1
  6. package/.github/ISSUE_TEMPLATE/bug_report.yml +78 -0
  7. package/.github/ISSUE_TEMPLATE/community-template.yml +77 -0
  8. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  9. package/.github/ISSUE_TEMPLATE/feature_request.yml +60 -0
  10. package/.github/PULL_REQUEST_TEMPLATE/community-template.md +29 -0
  11. package/.github/workflows/ci.yml +3 -27
  12. package/.github/workflows/docker-and-deploy.yml +20 -5
  13. package/.github/workflows/merge-gate.yml +233 -0
  14. package/.opencode/plugins/entire.ts +133 -0
  15. package/.superset/config.json +6 -0
  16. package/.wts-setup.ts +103 -34
  17. package/CHANGELOG.md +447 -0
  18. package/CLAUDE.md +422 -5
  19. package/CONTRIBUTING.md +46 -1
  20. package/DEPLOYMENT.md +26 -9
  21. package/Dockerfile +15 -7
  22. package/Dockerfile.worker +99 -67
  23. package/MCP.md +452 -17
  24. package/README.md +271 -169
  25. package/api-entrypoint.sh +56 -0
  26. package/depot.json +1 -0
  27. package/docker-compose.example.yml +194 -3
  28. package/docker-compose.local.yml +119 -0
  29. package/docker-entrypoint.sh +393 -113
  30. package/docs-site/app/api/search/route.ts +4 -0
  31. package/docs-site/app/docs/[[...slug]]/page.tsx +87 -0
  32. package/docs-site/app/docs/layout.tsx +12 -0
  33. package/docs-site/app/globals.css +24 -0
  34. package/docs-site/app/layout.config.tsx +34 -0
  35. package/docs-site/app/layout.tsx +119 -0
  36. package/docs-site/app/llms-full.txt/route.ts +11 -0
  37. package/docs-site/app/llms.mdx/docs/[[...slug]]/route.ts +24 -0
  38. package/docs-site/app/llms.txt/route.ts +8 -0
  39. package/docs-site/app/page.tsx +5 -0
  40. package/docs-site/app/robots.ts +13 -0
  41. package/docs-site/app/sitemap.ts +37 -0
  42. package/docs-site/components/api-page.client.tsx +4 -0
  43. package/docs-site/components/api-page.tsx +7 -0
  44. package/docs-site/components/mdx/mermaid.tsx +55 -0
  45. package/docs-site/content/docs/(documentation)/architecture/agents.mdx +117 -0
  46. package/docs-site/content/docs/(documentation)/architecture/hooks.mdx +77 -0
  47. package/docs-site/content/docs/(documentation)/architecture/memory.mdx +96 -0
  48. package/docs-site/content/docs/(documentation)/architecture/meta.json +4 -0
  49. package/docs-site/content/docs/(documentation)/architecture/overview.mdx +172 -0
  50. package/docs-site/content/docs/(documentation)/concepts/epics.mdx +98 -0
  51. package/docs-site/content/docs/(documentation)/concepts/meta.json +4 -0
  52. package/docs-site/content/docs/(documentation)/concepts/scheduling.mdx +136 -0
  53. package/docs-site/content/docs/(documentation)/concepts/services.mdx +104 -0
  54. package/docs-site/content/docs/(documentation)/concepts/task-lifecycle.mdx +148 -0
  55. package/docs-site/content/docs/(documentation)/concepts/workflows.mdx +209 -0
  56. package/docs-site/content/docs/(documentation)/contributing.mdx +158 -0
  57. package/docs-site/content/docs/(documentation)/getting-started.mdx +157 -0
  58. package/docs-site/content/docs/(documentation)/guides/agentmail-integration.mdx +79 -0
  59. package/docs-site/content/docs/(documentation)/guides/deployment.mdx +171 -0
  60. package/docs-site/content/docs/(documentation)/guides/github-integration.mdx +81 -0
  61. package/docs-site/content/docs/(documentation)/guides/gitlab-integration.mdx +93 -0
  62. package/docs-site/content/docs/(documentation)/guides/linear-integration.mdx +98 -0
  63. package/docs-site/content/docs/(documentation)/guides/meta.json +13 -0
  64. package/docs-site/content/docs/(documentation)/guides/sentry-integration.mdx +52 -0
  65. package/docs-site/content/docs/(documentation)/guides/slack-integration.mdx +179 -0
  66. package/docs-site/content/docs/(documentation)/guides/x402-payments.mdx +154 -0
  67. package/docs-site/content/docs/(documentation)/index.mdx +65 -0
  68. package/docs-site/content/docs/(documentation)/meta.json +19 -0
  69. package/docs-site/content/docs/(documentation)/reference/cli.mdx +241 -0
  70. package/docs-site/content/docs/(documentation)/reference/environment-variables.mdx +205 -0
  71. package/docs-site/content/docs/(documentation)/reference/mcp-tools.mdx +449 -0
  72. package/docs-site/content/docs/(documentation)/reference/meta.json +4 -0
  73. package/docs-site/content/docs/api-reference/active-sessions.mdx +9 -0
  74. package/docs-site/content/docs/api-reference/agents.mdx +9 -0
  75. package/docs-site/content/docs/api-reference/channels.mdx +9 -0
  76. package/docs-site/content/docs/api-reference/config.mdx +9 -0
  77. package/docs-site/content/docs/api-reference/debug.mdx +9 -0
  78. package/docs-site/content/docs/api-reference/ecosystem.mdx +9 -0
  79. package/docs-site/content/docs/api-reference/epics.mdx +9 -0
  80. package/docs-site/content/docs/api-reference/index.mdx +32 -0
  81. package/docs-site/content/docs/api-reference/memory.mdx +9 -0
  82. package/docs-site/content/docs/api-reference/meta.json +25 -0
  83. package/docs-site/content/docs/api-reference/poll.mdx +9 -0
  84. package/docs-site/content/docs/api-reference/repos.mdx +9 -0
  85. package/docs-site/content/docs/api-reference/schedules.mdx +9 -0
  86. package/docs-site/content/docs/api-reference/session-data.mdx +9 -0
  87. package/docs-site/content/docs/api-reference/stats.mdx +9 -0
  88. package/docs-site/content/docs/api-reference/tasks.mdx +9 -0
  89. package/docs-site/content/docs/api-reference/trackers.mdx +9 -0
  90. package/docs-site/content/docs/api-reference/webhooks.mdx +9 -0
  91. package/docs-site/content/docs/api-reference/workflows.mdx +9 -0
  92. package/docs-site/content/docs/meta.json +3 -0
  93. package/docs-site/lib/get-llm-text.ts +10 -0
  94. package/docs-site/lib/openapi.ts +23 -0
  95. package/docs-site/lib/source.ts +8 -0
  96. package/docs-site/mdx-components.tsx +13 -0
  97. package/docs-site/next.config.mjs +29 -0
  98. package/docs-site/package.json +35 -0
  99. package/docs-site/pnpm-lock.yaml +5407 -0
  100. package/docs-site/postcss.config.mjs +8 -0
  101. package/docs-site/scripts/generate-docs.ts +171 -0
  102. package/docs-site/source.config.ts +17 -0
  103. package/docs-site/tsconfig.json +46 -0
  104. package/ecosystem.config.cjs +4 -4
  105. package/landing/next.config.ts +14 -0
  106. package/landing/package.json +31 -0
  107. package/landing/pnpm-lock.yaml +1091 -0
  108. package/landing/postcss.config.mjs +8 -0
  109. package/landing/public/apple-touch-icon.png +0 -0
  110. package/landing/public/favicon.ico +0 -0
  111. package/landing/public/logo.png +0 -0
  112. package/landing/public/og-image.png +0 -0
  113. package/landing/public/omghost-desplega.svg +30 -0
  114. package/landing/public/omghost-openfort.svg +9 -0
  115. package/landing/src/app/actions/waitlist.ts +25 -0
  116. package/landing/src/app/blog/openfort-hackathon/page.tsx +863 -0
  117. package/landing/src/app/blog/page.tsx +162 -0
  118. package/landing/src/app/blog/swarm-metrics/page.tsx +685 -0
  119. package/landing/src/app/examples/page.tsx +174 -0
  120. package/landing/src/app/examples/x402/page.tsx +456 -0
  121. package/landing/src/app/globals.css +122 -0
  122. package/landing/src/app/layout.tsx +134 -0
  123. package/landing/src/app/page.tsx +27 -0
  124. package/landing/src/app/robots.ts +13 -0
  125. package/landing/src/app/sitemap.ts +44 -0
  126. package/landing/src/components/architecture.tsx +163 -0
  127. package/landing/src/components/cta.tsx +52 -0
  128. package/landing/src/components/features.tsx +160 -0
  129. package/landing/src/components/footer.tsx +100 -0
  130. package/landing/src/components/hero.tsx +217 -0
  131. package/landing/src/components/how-it-works.tsx +165 -0
  132. package/landing/src/components/navbar.tsx +147 -0
  133. package/landing/src/components/waitlist.tsx +110 -0
  134. package/landing/src/components/why-choose.tsx +149 -0
  135. package/landing/src/components/workshops.tsx +328 -0
  136. package/landing/src/lib/utils.ts +6 -0
  137. package/landing/tsconfig.json +41 -0
  138. package/misc/transcripts/2026-03-09-pi-mono-e2e-verification.md +154 -0
  139. package/new-ui/CLAUDE.md +92 -0
  140. package/new-ui/README.md +73 -0
  141. package/new-ui/biome.json +42 -0
  142. package/new-ui/components.json +21 -0
  143. package/new-ui/index.html +25 -0
  144. package/new-ui/package.json +49 -0
  145. package/new-ui/pnpm-lock.yaml +4845 -0
  146. package/new-ui/public/logo.png +0 -0
  147. package/new-ui/src/api/client.ts +814 -0
  148. package/new-ui/src/api/hooks/index.ts +64 -0
  149. package/new-ui/src/api/hooks/use-agents.ts +58 -0
  150. package/new-ui/src/api/hooks/use-channels.ts +115 -0
  151. package/new-ui/src/api/hooks/use-config-api.ts +46 -0
  152. package/new-ui/src/api/hooks/use-costs.ts +122 -0
  153. package/new-ui/src/api/hooks/use-db-query.ts +29 -0
  154. package/new-ui/src/api/hooks/use-epics.ts +75 -0
  155. package/new-ui/src/api/hooks/use-repos.ts +61 -0
  156. package/new-ui/src/api/hooks/use-schedules.ts +81 -0
  157. package/new-ui/src/api/hooks/use-services.ts +16 -0
  158. package/new-ui/src/api/hooks/use-stats.ts +27 -0
  159. package/new-ui/src/api/hooks/use-tasks.ts +89 -0
  160. package/new-ui/src/api/hooks/use-workflows.ts +109 -0
  161. package/{ui/src/types/api.ts → new-ui/src/api/types.ts} +236 -10
  162. package/new-ui/src/app/App.tsx +13 -0
  163. package/new-ui/src/app/providers.tsx +32 -0
  164. package/new-ui/src/app/router.tsx +52 -0
  165. package/new-ui/src/components/layout/app-header.tsx +47 -0
  166. package/new-ui/src/components/layout/app-sidebar.tsx +128 -0
  167. package/new-ui/src/components/layout/breadcrumbs.tsx +57 -0
  168. package/new-ui/src/components/layout/config-guard.tsx +22 -0
  169. package/new-ui/src/components/layout/root-layout.tsx +40 -0
  170. package/new-ui/src/components/layout/swarm-switcher.tsx +85 -0
  171. package/new-ui/src/components/shared/command-menu.tsx +131 -0
  172. package/new-ui/src/components/shared/data-grid.tsx +141 -0
  173. package/new-ui/src/components/shared/empty-state.tsx +24 -0
  174. package/new-ui/src/components/shared/error-boundary.tsx +72 -0
  175. package/new-ui/src/components/shared/json-viewer.tsx +47 -0
  176. package/new-ui/src/components/shared/name-connection-modal.tsx +99 -0
  177. package/new-ui/src/components/shared/page-skeleton.tsx +16 -0
  178. package/new-ui/src/components/shared/session-log-viewer.tsx +364 -0
  179. package/new-ui/src/components/shared/stats-bar.tsx +132 -0
  180. package/new-ui/src/components/shared/status-badge.tsx +131 -0
  181. package/new-ui/src/components/shared/usage-summary.tsx +179 -0
  182. package/new-ui/src/components/ui/alert-dialog.tsx +176 -0
  183. package/new-ui/src/components/ui/alert.tsx +60 -0
  184. package/new-ui/src/components/ui/avatar.tsx +96 -0
  185. package/new-ui/src/components/ui/badge.tsx +46 -0
  186. package/new-ui/src/components/ui/button.tsx +62 -0
  187. package/new-ui/src/components/ui/card.tsx +75 -0
  188. package/new-ui/src/components/ui/command.tsx +160 -0
  189. package/new-ui/src/components/ui/dialog.tsx +143 -0
  190. package/new-ui/src/components/ui/dropdown-menu.tsx +226 -0
  191. package/new-ui/src/components/ui/input.tsx +21 -0
  192. package/new-ui/src/components/ui/label.tsx +19 -0
  193. package/new-ui/src/components/ui/progress.tsx +26 -0
  194. package/new-ui/src/components/ui/scroll-area.tsx +54 -0
  195. package/new-ui/src/components/ui/select.tsx +175 -0
  196. package/new-ui/src/components/ui/separator.tsx +28 -0
  197. package/new-ui/src/components/ui/sheet.tsx +132 -0
  198. package/new-ui/src/components/ui/sidebar.tsx +691 -0
  199. package/new-ui/src/components/ui/skeleton.tsx +13 -0
  200. package/new-ui/src/components/ui/sonner.tsx +35 -0
  201. package/new-ui/src/components/ui/switch.tsx +33 -0
  202. package/new-ui/src/components/ui/table.tsx +92 -0
  203. package/new-ui/src/components/ui/tabs.tsx +79 -0
  204. package/new-ui/src/components/ui/textarea.tsx +18 -0
  205. package/new-ui/src/components/ui/tooltip.tsx +51 -0
  206. package/new-ui/src/components/workflows/action-node.tsx +53 -0
  207. package/new-ui/src/components/workflows/condition-node.tsx +50 -0
  208. package/new-ui/src/components/workflows/graph-utils.ts +124 -0
  209. package/new-ui/src/components/workflows/json-tree.tsx +189 -0
  210. package/new-ui/src/components/workflows/node-styles.ts +10 -0
  211. package/new-ui/src/components/workflows/step-detail-sheet.tsx +87 -0
  212. package/new-ui/src/components/workflows/trigger-node.tsx +41 -0
  213. package/new-ui/src/components/workflows/workflow-graph.tsx +65 -0
  214. package/{ui/src/hooks/useAutoScroll.ts → new-ui/src/hooks/use-auto-scroll.ts} +0 -1
  215. package/new-ui/src/hooks/use-config.ts +203 -0
  216. package/new-ui/src/hooks/use-keyboard-shortcuts.ts +41 -0
  217. package/new-ui/src/hooks/use-mobile.ts +19 -0
  218. package/new-ui/src/hooks/use-theme.ts +60 -0
  219. package/new-ui/src/lib/config.ts +188 -0
  220. package/new-ui/src/lib/slugs.ts +71 -0
  221. package/{ui → new-ui}/src/lib/utils.ts +32 -0
  222. package/new-ui/src/main.tsx +11 -0
  223. package/new-ui/src/pages/agents/[id]/page.tsx +492 -0
  224. package/new-ui/src/pages/agents/page.tsx +134 -0
  225. package/new-ui/src/pages/chat/page.tsx +674 -0
  226. package/new-ui/src/pages/config/page.tsx +1109 -0
  227. package/new-ui/src/pages/dashboard/page.tsx +454 -0
  228. package/new-ui/src/pages/debug/page.tsx +275 -0
  229. package/new-ui/src/pages/epics/[id]/page.tsx +809 -0
  230. package/new-ui/src/pages/epics/page.tsx +321 -0
  231. package/new-ui/src/pages/not-found/page.tsx +18 -0
  232. package/new-ui/src/pages/repos/page.tsx +369 -0
  233. package/new-ui/src/pages/schedules/[id]/page.tsx +664 -0
  234. package/new-ui/src/pages/schedules/page.tsx +477 -0
  235. package/new-ui/src/pages/services/page.tsx +128 -0
  236. package/new-ui/src/pages/tasks/[id]/page.tsx +670 -0
  237. package/new-ui/src/pages/tasks/page.tsx +592 -0
  238. package/new-ui/src/pages/usage/page.tsx +195 -0
  239. package/new-ui/src/pages/workflow-runs/[id]/page.tsx +363 -0
  240. package/new-ui/src/pages/workflows/[id]/page.tsx +417 -0
  241. package/new-ui/src/pages/workflows/page.tsx +266 -0
  242. package/new-ui/src/styles/ag-grid.css +36 -0
  243. package/new-ui/src/styles/globals.css +213 -0
  244. package/new-ui/test-results/.last-run.json +4 -0
  245. package/{ui/tsconfig.json → new-ui/tsconfig.app.json} +7 -4
  246. package/new-ui/tsconfig.json +4 -0
  247. package/new-ui/tsconfig.node.json +26 -0
  248. package/new-ui/vercel.json +4 -0
  249. package/new-ui/vite.config.ts +28 -0
  250. package/openapi.json +4436 -0
  251. package/package.json +31 -7
  252. package/plugin/build-pi-skills.ts +233 -0
  253. package/plugin/commands/close-issue.md +7 -3
  254. package/plugin/commands/create-pr.md +18 -12
  255. package/plugin/commands/implement-issue.md +7 -3
  256. package/plugin/commands/respond-github.md +8 -4
  257. package/plugin/commands/review-pr.md +44 -10
  258. package/plugin/commands/start-leader.md +1 -3
  259. package/plugin/commands/start-worker.md +1 -3
  260. package/plugin/commands/work-on-task.md +22 -3
  261. package/plugin/pi-skills/close-issue/SKILL.md +90 -0
  262. package/plugin/pi-skills/create-pr/SKILL.md +99 -0
  263. package/plugin/pi-skills/implement-issue/SKILL.md +135 -0
  264. package/plugin/pi-skills/investigate-sentry-issue/SKILL.md +138 -0
  265. package/plugin/pi-skills/respond-github/SKILL.md +98 -0
  266. package/plugin/pi-skills/review-offered-task/SKILL.md +45 -0
  267. package/plugin/pi-skills/review-pr/SKILL.md +261 -0
  268. package/plugin/pi-skills/start-leader/SKILL.md +121 -0
  269. package/plugin/pi-skills/start-worker/SKILL.md +60 -0
  270. package/plugin/pi-skills/swarm-chat/SKILL.md +82 -0
  271. package/plugin/pi-skills/todos/SKILL.md +66 -0
  272. package/plugin/pi-skills/work-on-task/SKILL.md +65 -0
  273. package/plugin/skills/artifacts/examples/approval-flow.ts +34 -0
  274. package/plugin/skills/artifacts/examples/hono-dashboard.ts +31 -0
  275. package/plugin/skills/artifacts/examples/multi-artifact.ts +20 -0
  276. package/plugin/skills/artifacts/examples/static-report.sh +17 -0
  277. package/plugin/skills/artifacts/skill.md +71 -0
  278. package/prek.toml +75 -0
  279. package/scripts/check-db-boundary.sh +60 -0
  280. package/scripts/e2e-docker-provider.ts +820 -0
  281. package/scripts/e2e-io-schemas-test.ts +807 -0
  282. package/scripts/e2e-provider-test.ts +220 -0
  283. package/scripts/e2e-workflow-redesign.sh +229 -0
  284. package/scripts/e2e-workflow-test.sh +285 -0
  285. package/scripts/e2e-workflow-test.ts +857 -0
  286. package/scripts/generate-openapi.ts +26 -0
  287. package/scripts/measure-tool-tokens.ts +118 -0
  288. package/scripts/x402-e2e-test.ts +195 -0
  289. package/scripts/x402-test-server.ts +236 -0
  290. package/scripts/x402-testnet-e2e.ts +668 -0
  291. package/slack-manifest.json +17 -0
  292. package/src/agentmail/app.ts +65 -0
  293. package/src/agentmail/handlers.ts +207 -0
  294. package/src/agentmail/index.ts +9 -0
  295. package/src/agentmail/types.ts +51 -0
  296. package/src/artifact-sdk/browser-sdk.ts +30 -0
  297. package/src/artifact-sdk/index.ts +2 -0
  298. package/src/artifact-sdk/localtunnel.d.ts +20 -0
  299. package/src/artifact-sdk/port.ts +12 -0
  300. package/src/artifact-sdk/server.ts +156 -0
  301. package/src/artifact-sdk/tunnel.ts +19 -0
  302. package/src/be/chunking.ts +193 -0
  303. package/src/be/db-queries/oauth.ts +90 -0
  304. package/src/be/db-queries/tracker.ts +182 -0
  305. package/src/be/db.ts +2729 -770
  306. package/src/be/embedding.ts +80 -0
  307. package/src/be/migrations/001_initial.sql +409 -0
  308. package/src/be/migrations/002_one_time_schedules.sql +59 -0
  309. package/src/be/migrations/003_workflows.sql +51 -0
  310. package/src/be/migrations/004_workflow_source.sql +81 -0
  311. package/src/be/migrations/005_epic_next_steps.sql +2 -0
  312. package/src/be/migrations/006_vcs_provider.sql +94 -0
  313. package/src/be/migrations/007_task_dir.sql +2 -0
  314. package/src/be/migrations/008_workflow_redesign.sql +85 -0
  315. package/src/be/migrations/009_tracker_integration.sql +144 -0
  316. package/src/be/migrations/010_step_diagnostics.sql +1 -0
  317. package/src/be/migrations/011_step_next_port.sql +1 -0
  318. package/src/be/migrations/012_trigger_schema.sql +1 -0
  319. package/src/be/migrations/013_task_output_schema.sql +2 -0
  320. package/src/be/migrations/runner.ts +188 -0
  321. package/src/cli.tsx +231 -299
  322. package/src/commands/artifact.ts +241 -0
  323. package/src/commands/onboard/compose-generator.ts +169 -0
  324. package/src/commands/onboard/env-generator.ts +79 -0
  325. package/src/commands/onboard/manifest.ts +37 -0
  326. package/src/commands/onboard/presets.ts +85 -0
  327. package/src/commands/onboard/service-names.ts +47 -0
  328. package/src/commands/onboard/steps/core-credentials.tsx +111 -0
  329. package/src/commands/onboard/steps/custom-templates.tsx +168 -0
  330. package/src/commands/onboard/steps/generate.tsx +154 -0
  331. package/src/commands/onboard/steps/harness-credentials.tsx +195 -0
  332. package/src/commands/onboard/steps/harness.tsx +21 -0
  333. package/src/commands/onboard/steps/health-check.tsx +171 -0
  334. package/src/commands/onboard/steps/integration-github.tsx +105 -0
  335. package/src/commands/onboard/steps/integration-gitlab.tsx +79 -0
  336. package/src/commands/onboard/steps/integration-menu.tsx +58 -0
  337. package/src/commands/onboard/steps/integration-sentry.tsx +79 -0
  338. package/src/commands/onboard/steps/integration-slack.tsx +165 -0
  339. package/src/commands/onboard/steps/post-connect.tsx +145 -0
  340. package/src/commands/onboard/steps/post-dashboard.tsx +34 -0
  341. package/src/commands/onboard/steps/post-task.tsx +103 -0
  342. package/src/commands/onboard/steps/prereq-check.tsx +178 -0
  343. package/src/commands/onboard/steps/review.tsx +82 -0
  344. package/src/commands/onboard/steps/start.tsx +97 -0
  345. package/src/commands/onboard/templates.ts +34 -0
  346. package/src/commands/onboard/types.ts +259 -0
  347. package/src/commands/onboard.tsx +425 -0
  348. package/src/commands/runner.ts +1376 -559
  349. package/src/commands/setup.tsx +23 -38
  350. package/src/commands/shared/client-config.ts +41 -0
  351. package/src/github/app.ts +8 -0
  352. package/src/github/handlers.ts +93 -75
  353. package/src/github/index.ts +1 -0
  354. package/src/github/types.ts +1 -0
  355. package/src/gitlab/auth.ts +63 -0
  356. package/src/gitlab/handlers.ts +327 -0
  357. package/src/gitlab/index.ts +19 -0
  358. package/src/gitlab/reactions.ts +104 -0
  359. package/src/gitlab/types.ts +130 -0
  360. package/src/heartbeat/heartbeat.ts +425 -0
  361. package/src/heartbeat/index.ts +1 -0
  362. package/src/hooks/hook.ts +555 -4
  363. package/src/hooks/tool-loop-detection.test.ts +158 -0
  364. package/src/hooks/tool-loop-detection.ts +167 -0
  365. package/src/http/active-sessions.ts +172 -0
  366. package/src/http/agents.ts +328 -0
  367. package/src/http/config.ts +191 -0
  368. package/src/http/core.ts +309 -0
  369. package/src/http/db-query.ts +91 -0
  370. package/src/http/ecosystem.ts +63 -0
  371. package/src/http/epics.ts +460 -0
  372. package/src/http/index.ts +213 -0
  373. package/src/http/mcp.ts +77 -0
  374. package/src/http/memory.ts +168 -0
  375. package/src/http/openapi.ts +109 -0
  376. package/src/http/poll.ts +178 -0
  377. package/src/http/repos.ts +195 -0
  378. package/src/http/route-def.ts +123 -0
  379. package/src/http/schedules.ts +391 -0
  380. package/src/http/session-data.ts +241 -0
  381. package/src/http/stats.ts +174 -0
  382. package/src/http/tasks.ts +468 -0
  383. package/src/http/trackers/index.ts +10 -0
  384. package/src/http/trackers/linear.ts +187 -0
  385. package/src/http/types.ts +12 -0
  386. package/src/http/utils.ts +87 -0
  387. package/src/http/webhooks.ts +432 -0
  388. package/src/http/workflows.ts +522 -0
  389. package/src/http.ts +1 -1890
  390. package/src/linear/README.md +65 -0
  391. package/src/linear/app.ts +48 -0
  392. package/src/linear/client.ts +18 -0
  393. package/src/linear/index.ts +1 -0
  394. package/src/linear/oauth.ts +35 -0
  395. package/src/linear/outbound.ts +212 -0
  396. package/src/linear/sync.ts +543 -0
  397. package/src/linear/types.ts +7 -0
  398. package/src/linear/webhook.ts +104 -0
  399. package/src/oauth/README.md +66 -0
  400. package/src/oauth/index.ts +6 -0
  401. package/src/oauth/wrapper.ts +204 -0
  402. package/src/prompts/base-prompt.ts +366 -24
  403. package/src/prompts/defaults.ts +196 -0
  404. package/src/providers/claude-adapter.ts +429 -0
  405. package/src/providers/index.ts +24 -0
  406. package/src/providers/pi-mono-adapter.ts +442 -0
  407. package/src/providers/pi-mono-extension.ts +620 -0
  408. package/src/providers/pi-mono-mcp-client.ts +124 -0
  409. package/src/providers/types.ts +75 -0
  410. package/src/scheduler/scheduler.test.ts +2 -0
  411. package/src/scheduler/scheduler.ts +186 -29
  412. package/src/server.ts +82 -6
  413. package/src/slack/HEURISTICS.md +105 -0
  414. package/src/slack/actions.ts +133 -0
  415. package/src/slack/app.ts +7 -0
  416. package/src/slack/assistant.ts +115 -0
  417. package/src/slack/blocks.ts +233 -0
  418. package/src/slack/commands.ts +31 -17
  419. package/src/slack/files.ts +1 -1
  420. package/src/slack/handlers.test.ts +114 -1
  421. package/src/slack/handlers.ts +212 -52
  422. package/src/slack/responses.ts +120 -67
  423. package/src/slack/router.ts +17 -99
  424. package/src/slack/thread-buffer.ts +213 -0
  425. package/src/slack/watcher.ts +119 -4
  426. package/src/tests/agent-activity.test.ts +247 -0
  427. package/src/tests/agentmail-filters.test.ts +97 -0
  428. package/src/tests/artifact-sdk.test.ts +800 -0
  429. package/src/tests/base-prompt.test.ts +264 -0
  430. package/src/tests/build-pi-skills.test.ts +127 -0
  431. package/src/tests/claude-adapter.test.ts +126 -0
  432. package/src/tests/context-versioning.test.ts +425 -0
  433. package/src/tests/db-queries-oauth.test.ts +197 -0
  434. package/src/tests/db-queries-tracker.test.ts +230 -0
  435. package/src/tests/epics.test.ts +3 -3
  436. package/src/tests/error-tracker.test.ts +368 -0
  437. package/src/tests/fetch-resolved-env.test.ts +167 -0
  438. package/src/tests/generate-default-claude-md.test.ts +9 -1
  439. package/src/tests/generate-identity-templates.test.ts +124 -0
  440. package/src/tests/gitlab-auth.test.ts +109 -0
  441. package/src/tests/gitlab-handlers.test.ts +691 -0
  442. package/src/tests/gitlab-vcs-db.test.ts +177 -0
  443. package/src/tests/heartbeat.test.ts +363 -0
  444. package/src/tests/http-api-integration.test.ts +1698 -0
  445. package/src/tests/linear-outbound-sync.test.ts +200 -0
  446. package/src/tests/linear-webhook.test.ts +402 -0
  447. package/src/tests/match-route.test.ts +187 -0
  448. package/src/tests/memory.test.ts +737 -0
  449. package/src/tests/migration-runner-regressions.test.ts +86 -0
  450. package/src/tests/model-control.test.ts +338 -0
  451. package/src/tests/oauth-wrapper.test.ts +147 -0
  452. package/src/tests/onboard-compose.test.ts +138 -0
  453. package/src/tests/onboard-env.test.ts +174 -0
  454. package/src/tests/onboard-manifest.test.ts +137 -0
  455. package/src/tests/pi-mono-adapter.test.ts +234 -0
  456. package/src/tests/progress-dedup.test.ts +98 -0
  457. package/src/tests/provider-adapter.test.ts +122 -0
  458. package/src/tests/provider-command-format.test.ts +98 -0
  459. package/src/tests/reload-config.test.ts +170 -0
  460. package/src/tests/runner-polling-api.test.ts +25 -20
  461. package/src/tests/scheduled-tasks.test.ts +104 -0
  462. package/src/tests/scheduler-backoff.test.ts +166 -0
  463. package/src/tests/self-improvement.test.ts +540 -0
  464. package/src/tests/session-attach.test.ts +536 -0
  465. package/src/tests/session-costs.test.ts +267 -1
  466. package/src/tests/slack-actions.test.ts +133 -0
  467. package/src/tests/slack-assistant.test.ts +136 -0
  468. package/src/tests/slack-blocks.test.ts +246 -0
  469. package/src/tests/slack-metadata-inheritance.test.ts +243 -0
  470. package/src/tests/slack-queue-offline.test.ts +174 -0
  471. package/src/tests/slack-router.test.ts +181 -0
  472. package/src/tests/slack-thread-buffer.test.ts +305 -0
  473. package/src/tests/slack-thread-followups.test.ts +298 -0
  474. package/src/tests/slack-watcher.test.ts +101 -0
  475. package/src/tests/structured-output.test.ts +311 -0
  476. package/src/tests/swarm-repos.test.ts +198 -0
  477. package/src/tests/task-cancellation.test.ts +6 -4
  478. package/src/tests/task-working-dir.test.ts +176 -0
  479. package/src/tests/template-fetch.test.ts +490 -0
  480. package/src/tests/tool-annotations.test.ts +371 -0
  481. package/src/tests/tracker-tools.test.ts +184 -0
  482. package/src/tests/update-profile-api.test.ts +143 -3
  483. package/src/tests/validation-adapters.test.ts +86 -0
  484. package/src/tests/vcs-provider.test.ts +27 -0
  485. package/src/tests/workflow-agent-task.test.ts +196 -0
  486. package/src/tests/workflow-async-v2.test.ts +386 -0
  487. package/src/tests/workflow-convergence.test.ts +541 -0
  488. package/src/tests/workflow-definition-validation.test.ts +290 -0
  489. package/src/tests/workflow-engine-v2.test.ts +691 -0
  490. package/src/tests/workflow-executors.test.ts +736 -0
  491. package/src/tests/workflow-http-v2.test.ts +599 -0
  492. package/src/tests/workflow-integration-io.test.ts +902 -0
  493. package/src/tests/workflow-io-schemas.test.ts +624 -0
  494. package/src/tests/workflow-registry.test.ts +592 -0
  495. package/src/tests/workflow-retry-v2.test.ts +401 -0
  496. package/src/tests/workflow-retry-validation.test.ts +282 -0
  497. package/src/tests/workflow-template.test.ts +288 -0
  498. package/src/tests/workflow-trigger-schema.test.ts +359 -0
  499. package/src/tests/workflow-triggers-v2.test.ts +264 -0
  500. package/src/tests/workflow-versions.test.ts +208 -0
  501. package/src/tests/x402-client.test.ts +117 -0
  502. package/src/tests/x402-config.test.ts +182 -0
  503. package/src/tests/x402-spending-tracker.test.ts +185 -0
  504. package/src/tools/cancel-task.ts +2 -0
  505. package/src/tools/context-diff.ts +171 -0
  506. package/src/tools/context-history.ts +138 -0
  507. package/src/tools/create-channel.ts +1 -0
  508. package/src/tools/db-query.ts +78 -0
  509. package/src/tools/delete-channel.ts +132 -0
  510. package/src/tools/epics/assign-task-to-epic.ts +1 -0
  511. package/src/tools/epics/create-epic.ts +3 -2
  512. package/src/tools/epics/delete-epic.ts +2 -0
  513. package/src/tools/epics/get-epic-details.ts +2 -0
  514. package/src/tools/epics/list-epics.ts +2 -0
  515. package/src/tools/epics/unassign-task-from-epic.ts +1 -0
  516. package/src/tools/epics/update-epic.ts +7 -4
  517. package/src/tools/get-swarm.ts +2 -0
  518. package/src/tools/get-task-details.ts +2 -0
  519. package/src/tools/get-tasks.ts +27 -1
  520. package/src/tools/inject-learning.ts +106 -0
  521. package/src/tools/join-swarm.ts +17 -7
  522. package/src/tools/list-channels.ts +2 -0
  523. package/src/tools/list-services.ts +2 -0
  524. package/src/tools/memory-get.ts +56 -0
  525. package/src/tools/memory-search.ts +131 -0
  526. package/src/tools/my-agent-info.ts +2 -0
  527. package/src/tools/poll-task.ts +2 -20
  528. package/src/tools/post-message.ts +1 -0
  529. package/src/tools/read-messages.ts +2 -0
  530. package/src/tools/register-agentmail-inbox.ts +166 -0
  531. package/src/tools/register-service.ts +2 -0
  532. package/src/tools/schedules/create-schedule.ts +134 -24
  533. package/src/tools/schedules/delete-schedule.ts +2 -0
  534. package/src/tools/schedules/list-schedules.ts +20 -4
  535. package/src/tools/schedules/run-schedule-now.ts +1 -0
  536. package/src/tools/schedules/update-schedule.ts +49 -17
  537. package/src/tools/send-task.ts +132 -10
  538. package/src/tools/slack-download-file.ts +4 -2
  539. package/src/tools/slack-list-channels.ts +2 -0
  540. package/src/tools/slack-post.ts +2 -0
  541. package/src/tools/slack-read.ts +2 -0
  542. package/src/tools/slack-reply.ts +2 -0
  543. package/src/tools/slack-upload-file.ts +2 -0
  544. package/src/tools/store-progress.ts +187 -4
  545. package/src/tools/swarm-config/delete-config.ts +87 -0
  546. package/src/tools/swarm-config/get-config.ts +108 -0
  547. package/src/tools/swarm-config/index.ts +4 -0
  548. package/src/tools/swarm-config/list-config.ts +99 -0
  549. package/src/tools/swarm-config/set-config.ts +118 -0
  550. package/src/tools/task-action.ts +30 -5
  551. package/src/tools/task-dedup.ts +97 -0
  552. package/src/tools/tool-config.ts +117 -0
  553. package/src/tools/tracker/index.ts +6 -0
  554. package/src/tools/tracker/tracker-link-epic.ts +64 -0
  555. package/src/tools/tracker/tracker-link-task.ts +64 -0
  556. package/src/tools/tracker/tracker-map-agent.ts +57 -0
  557. package/src/tools/tracker/tracker-status.ts +56 -0
  558. package/src/tools/tracker/tracker-sync-status.ts +42 -0
  559. package/src/tools/tracker/tracker-unlink.ts +41 -0
  560. package/src/tools/unregister-service.ts +2 -0
  561. package/src/tools/update-profile.ts +93 -10
  562. package/src/tools/update-service-status.ts +2 -0
  563. package/src/tools/utils.ts +10 -1
  564. package/src/tools/workflows/create-workflow.ts +111 -0
  565. package/src/tools/workflows/delete-workflow.ts +42 -0
  566. package/src/tools/workflows/get-workflow-run.ts +59 -0
  567. package/src/tools/workflows/get-workflow.ts +53 -0
  568. package/src/tools/workflows/index.ts +9 -0
  569. package/src/tools/workflows/list-workflow-runs.ts +48 -0
  570. package/src/tools/workflows/list-workflows.ts +42 -0
  571. package/src/tools/workflows/retry-workflow-run.ts +40 -0
  572. package/src/tools/workflows/trigger-workflow.ts +96 -0
  573. package/src/tools/workflows/update-workflow.ts +118 -0
  574. package/src/tracker/types.ts +51 -0
  575. package/src/types.ts +477 -14
  576. package/src/utils/credentials.test.ts +156 -0
  577. package/src/utils/credentials.ts +50 -0
  578. package/src/utils/error-tracker.ts +190 -0
  579. package/src/vcs/index.ts +15 -0
  580. package/src/vcs/types.ts +5 -0
  581. package/src/workflows/checkpoint.ts +121 -0
  582. package/src/workflows/cooldown.ts +28 -0
  583. package/src/workflows/definition.ts +217 -0
  584. package/src/workflows/engine.ts +554 -0
  585. package/src/workflows/event-bus.ts +29 -0
  586. package/src/workflows/executors/agent-task.ts +92 -0
  587. package/src/workflows/executors/base.ts +86 -0
  588. package/src/workflows/executors/code-match.ts +88 -0
  589. package/src/workflows/executors/index.ts +16 -0
  590. package/src/workflows/executors/notify.ts +93 -0
  591. package/src/workflows/executors/property-match.ts +104 -0
  592. package/src/workflows/executors/raw-llm.ts +83 -0
  593. package/src/workflows/executors/registry.ts +76 -0
  594. package/src/workflows/executors/script.ts +103 -0
  595. package/src/workflows/executors/validate.ts +215 -0
  596. package/src/workflows/executors/vcs.ts +58 -0
  597. package/src/workflows/index.ts +61 -0
  598. package/src/workflows/input.ts +46 -0
  599. package/src/workflows/json-schema-validator.ts +118 -0
  600. package/src/workflows/recovery.ts +139 -0
  601. package/src/workflows/resume.ts +143 -0
  602. package/src/workflows/retry-poller.ts +216 -0
  603. package/src/workflows/template.ts +74 -0
  604. package/src/workflows/templates.ts +86 -0
  605. package/src/workflows/triggers.ts +95 -0
  606. package/src/workflows/validation.ts +104 -0
  607. package/src/workflows/version.ts +42 -0
  608. package/src/x402/cli.ts +140 -0
  609. package/src/x402/client.ts +192 -0
  610. package/src/x402/config.ts +131 -0
  611. package/src/x402/index.ts +37 -0
  612. package/src/x402/openfort-signer.ts +83 -0
  613. package/src/x402/spending-tracker.ts +109 -0
  614. package/templates/community/.gitkeep +0 -0
  615. package/templates/official/coder/CLAUDE.md +49 -0
  616. package/templates/official/coder/IDENTITY.md +28 -0
  617. package/templates/official/coder/SOUL.md +43 -0
  618. package/templates/official/coder/TOOLS.md +40 -0
  619. package/templates/official/coder/config.json +23 -0
  620. package/templates/official/coder/start-up.sh +23 -0
  621. package/templates/official/content-reviewer/CLAUDE.md +68 -0
  622. package/templates/official/content-reviewer/IDENTITY.md +28 -0
  623. package/templates/official/content-reviewer/SOUL.md +44 -0
  624. package/templates/official/content-reviewer/TOOLS.md +37 -0
  625. package/templates/official/content-reviewer/config.json +23 -0
  626. package/templates/official/content-reviewer/start-up.sh +23 -0
  627. package/templates/official/content-strategist/CLAUDE.md +63 -0
  628. package/templates/official/content-strategist/IDENTITY.md +33 -0
  629. package/templates/official/content-strategist/SOUL.md +48 -0
  630. package/templates/official/content-strategist/TOOLS.md +47 -0
  631. package/templates/official/content-strategist/config.json +23 -0
  632. package/templates/official/content-strategist/start-up.sh +23 -0
  633. package/templates/official/content-writer/CLAUDE.md +72 -0
  634. package/templates/official/content-writer/IDENTITY.md +30 -0
  635. package/templates/official/content-writer/SOUL.md +46 -0
  636. package/templates/official/content-writer/TOOLS.md +44 -0
  637. package/templates/official/content-writer/config.json +23 -0
  638. package/templates/official/content-writer/start-up.sh +23 -0
  639. package/templates/official/forward-deployed-engineer/CLAUDE.md +54 -0
  640. package/templates/official/forward-deployed-engineer/IDENTITY.md +37 -0
  641. package/templates/official/forward-deployed-engineer/SOUL.md +55 -0
  642. package/templates/official/forward-deployed-engineer/config.json +21 -0
  643. package/templates/official/lead/CLAUDE.md +33 -0
  644. package/templates/official/lead/IDENTITY.md +36 -0
  645. package/templates/official/lead/SOUL.md +51 -0
  646. package/templates/official/lead/config.json +22 -0
  647. package/templates/official/researcher/CLAUDE.md +46 -0
  648. package/templates/official/researcher/IDENTITY.md +28 -0
  649. package/templates/official/researcher/SOUL.md +43 -0
  650. package/templates/official/researcher/config.json +21 -0
  651. package/templates/official/reviewer/CLAUDE.md +63 -0
  652. package/templates/official/reviewer/IDENTITY.md +28 -0
  653. package/templates/official/reviewer/SOUL.md +45 -0
  654. package/templates/official/reviewer/config.json +21 -0
  655. package/templates/official/tester/CLAUDE.md +53 -0
  656. package/templates/official/tester/IDENTITY.md +28 -0
  657. package/templates/official/tester/SOUL.md +55 -0
  658. package/templates/official/tester/config.json +21 -0
  659. package/templates/schema.ts +35 -0
  660. package/templates-ui/README.md +46 -0
  661. package/templates-ui/components.json +17 -0
  662. package/templates-ui/eslint.config.mjs +18 -0
  663. package/templates-ui/next.config.ts +7 -0
  664. package/templates-ui/package.json +35 -0
  665. package/templates-ui/pnpm-lock.yaml +4571 -0
  666. package/templates-ui/postcss.config.mjs +7 -0
  667. package/templates-ui/public/file.svg +1 -0
  668. package/templates-ui/public/globe.svg +1 -0
  669. package/templates-ui/public/logo.png +0 -0
  670. package/templates-ui/public/next.svg +1 -0
  671. package/templates-ui/public/vercel.svg +1 -0
  672. package/templates-ui/public/window.svg +1 -0
  673. package/templates-ui/src/app/[category]/[name]/page.tsx +89 -0
  674. package/templates-ui/src/app/api/templates/[...slug]/route.ts +52 -0
  675. package/templates-ui/src/app/api/templates/route.ts +18 -0
  676. package/templates-ui/src/app/builder/page.tsx +37 -0
  677. package/templates-ui/src/app/globals.css +94 -0
  678. package/templates-ui/src/app/layout.tsx +79 -0
  679. package/templates-ui/src/app/page.tsx +38 -0
  680. package/templates-ui/src/app/robots.ts +11 -0
  681. package/templates-ui/src/app/sitemap.ts +31 -0
  682. package/templates-ui/src/components/compose-builder.tsx +442 -0
  683. package/templates-ui/src/components/compose-preview.tsx +117 -0
  684. package/templates-ui/src/components/file-preview.tsx +77 -0
  685. package/templates-ui/src/components/footer.tsx +40 -0
  686. package/templates-ui/src/components/header.tsx +41 -0
  687. package/templates-ui/src/components/template-card.tsx +87 -0
  688. package/templates-ui/src/components/template-detail.tsx +125 -0
  689. package/templates-ui/src/components/template-gallery.tsx +263 -0
  690. package/templates-ui/src/components/ui/badge.tsx +36 -0
  691. package/templates-ui/src/components/ui/button.tsx +57 -0
  692. package/templates-ui/src/components/ui/card.tsx +76 -0
  693. package/templates-ui/src/components/ui/separator.tsx +31 -0
  694. package/templates-ui/src/components/ui/tooltip.tsx +32 -0
  695. package/templates-ui/src/lib/compose-generator.ts +241 -0
  696. package/templates-ui/src/lib/templates.ts +137 -0
  697. package/templates-ui/src/lib/utils.ts +6 -0
  698. package/templates-ui/tsconfig.json +34 -0
  699. package/thoughts/research/2026-02-28-openfort-viem-x402-research.md +679 -0
  700. package/thoughts/research/2026-02-28-x402-payments-research.md +686 -0
  701. package/thoughts/researcher/plans/2026-02-20-agent-self-improvement-plan.md +282 -0
  702. package/thoughts/researcher/research/2026-02-20-agent-self-improvement.md +492 -0
  703. package/thoughts/shared/plans/2026-02-26-mcp-tool-context-reduction.md +282 -0
  704. package/thoughts/shared/plans/2026-03-02-claude-context-mode-integration.md +328 -0
  705. package/thoughts/shared/plans/2026-03-02-code-level-heartbeat.md +224 -0
  706. package/thoughts/shared/research/2026-02-26-cliffy-mcp-tools.md +159 -0
  707. package/thoughts/shared/research/2026-03-03-database-migration-system-refactor.md +337 -0
  708. package/thoughts/swarm-researcher/plans/2026-02-23-openclaw-improvements-plan.md +778 -0
  709. package/thoughts/swarm-researcher/plans/2026-02-26-artifacts-localtunnel-plan.md +1269 -0
  710. package/thoughts/swarm-researcher/research/2026-02-23-openclaw-vs-agent-swarm-comparison.md +411 -0
  711. package/thoughts/swarm-researcher/research/2026-02-26-artifacts-localtunnel.md +724 -0
  712. package/thoughts/taras/brainstorms/2026-03-20-prompt-template-registry.md +443 -0
  713. package/thoughts/taras/brainstorms/2026-03-20-setup-cli-onboarding.md +307 -0
  714. package/thoughts/taras/plans/2026-02-20-auto-improvement.md +803 -0
  715. package/thoughts/taras/plans/2026-02-20-env-management.md +538 -0
  716. package/thoughts/taras/plans/2026-02-20-memory-system.md +882 -0
  717. package/thoughts/taras/plans/2026-02-20-repos-knowledge.md +806 -0
  718. package/thoughts/taras/plans/2026-02-20-session-attach.md +647 -0
  719. package/thoughts/taras/plans/2026-02-20-worker-identity.md +820 -0
  720. package/thoughts/taras/plans/2026-02-25-feat-new-ui-visual-redesign-plan.md +768 -0
  721. package/thoughts/taras/plans/2026-03-04-fix-buildSystemPrompt-missing-fields.md +77 -0
  722. package/thoughts/taras/plans/2026-03-04-new-ui-missing-actions.md +543 -0
  723. package/thoughts/taras/plans/2026-03-06-one-time-scheduled-tasks.md +373 -0
  724. package/thoughts/taras/plans/2026-03-08-memory-self-improvement-enhancements.md +512 -0
  725. package/thoughts/taras/plans/2026-03-08-pi-mono-provider-implementation.md +919 -0
  726. package/thoughts/taras/plans/2026-03-09-templates-registry.md +723 -0
  727. package/thoughts/taras/plans/2026-03-10-task-working-directory.md +371 -0
  728. package/thoughts/taras/plans/2026-03-11-archil-per-agent-write-strategy.md +621 -0
  729. package/thoughts/taras/plans/2026-03-12-eliminate-inbox-route-to-tasks.md +61 -0
  730. package/thoughts/taras/plans/2026-03-12-slack-thread-followup-additive.md +488 -0
  731. package/thoughts/taras/plans/2026-03-13-slack-ai-improvements.md +644 -0
  732. package/thoughts/taras/plans/2026-03-16-route-wrapper-openapi.md +636 -0
  733. package/thoughts/taras/plans/2026-03-17-multi-api-config.md +444 -0
  734. package/thoughts/taras/plans/2026-03-18-agent-fs-integration.md +591 -0
  735. package/thoughts/taras/plans/2026-03-18-debug-db-explorer.md +446 -0
  736. package/thoughts/taras/plans/2026-03-18-workflow-redesign.md +987 -0
  737. package/thoughts/taras/plans/2026-03-19-compound-learnings.md +403 -0
  738. package/thoughts/taras/plans/2026-03-19-ticket-tracker-linear-integration.md +860 -0
  739. package/thoughts/taras/plans/2026-03-19-workflow-io-schemas-and-bugs.md +899 -0
  740. package/thoughts/taras/plans/2026-03-20-setup-cli-onboarding.md +874 -0
  741. package/thoughts/taras/plans/2026-03-20-workflow-structured-output-validation-workspace.md +723 -0
  742. package/thoughts/taras/research/2026-02-19-agent-native-swarm-architecture.md +390 -0
  743. package/thoughts/taras/research/2026-02-19-swarm-gaps-implementation.md +594 -0
  744. package/thoughts/taras/research/2026-02-25-dashboard-ui-design-best-practices.md +825 -0
  745. package/thoughts/taras/research/2026-02-26-task-detail-page-redesign.md +393 -0
  746. package/thoughts/taras/research/2026-03-03-new-ui-missing-actions.md +168 -0
  747. package/thoughts/taras/research/2026-03-05-pi-mono-provider-research.md +230 -0
  748. package/thoughts/taras/research/2026-03-06-workflow-engine-design.md +445 -0
  749. package/thoughts/taras/research/2026-03-08-drive-loop-concept.md +375 -0
  750. package/thoughts/taras/research/2026-03-08-pi-mono-deep-dive.md +869 -0
  751. package/thoughts/taras/research/2026-03-09-templates-registry.md +373 -0
  752. package/thoughts/taras/research/2026-03-10-agent-working-directory.md +223 -0
  753. package/thoughts/taras/research/2026-03-10-configurable-event-prompts.md +339 -0
  754. package/thoughts/taras/research/2026-03-11-archil-production-setup.md +181 -0
  755. package/thoughts/taras/research/2026-03-11-archil-shared-disk-write-strategies.md +437 -0
  756. package/thoughts/taras/research/2026-03-13-slack-ai-features.md +258 -0
  757. package/thoughts/taras/research/2026-03-16-openapi-docs-generation.md +335 -0
  758. package/thoughts/taras/research/2026-03-16-route-wrapper-openapi.md +670 -0
  759. package/thoughts/taras/research/2026-03-16-slack-thread-followups-e2e.md +54 -0
  760. package/thoughts/taras/research/2026-03-18-agent-fs-integration.md +558 -0
  761. package/thoughts/taras/research/2026-03-18-linear-integration-finalization.md +526 -0
  762. package/thoughts/taras/research/2026-03-18-workflow-redesign.md +797 -0
  763. package/thoughts/taras/research/2026-03-19-workflow-node-io-schemas-and-bugs.md +563 -0
  764. package/thoughts/taras/research/2026-03-19-workflow-structured-output-validation-workspace.md +486 -0
  765. package/thoughts/taras/research/2026-03-20-prompt-template-registry.md +469 -0
  766. package/tsconfig.json +1 -1
  767. package/FAQ.md +0 -19
  768. package/src/tests/get-inbox-message.test.ts +0 -145
  769. package/src/tools/get-inbox-message.ts +0 -89
  770. package/src/tools/inbox-delegate.ts +0 -113
  771. package/ui/CLAUDE.md +0 -49
  772. package/ui/bun.lock +0 -771
  773. package/ui/index.html +0 -22
  774. package/ui/package-lock.json +0 -5290
  775. package/ui/package.json +0 -33
  776. package/ui/pnpm-lock.yaml +0 -3341
  777. package/ui/postcss.config.js +0 -6
  778. package/ui/src/App.tsx +0 -63
  779. package/ui/src/components/ActivityFeed.tsx +0 -440
  780. package/ui/src/components/AgentDetailPanel.tsx +0 -733
  781. package/ui/src/components/AgentsPanel.tsx +0 -815
  782. package/ui/src/components/ChatPanel.tsx +0 -1920
  783. package/ui/src/components/ConfigModal.tsx +0 -253
  784. package/ui/src/components/Dashboard.tsx +0 -832
  785. package/ui/src/components/EditAgentProfileModal.tsx +0 -433
  786. package/ui/src/components/EpicDetailPage.tsx +0 -741
  787. package/ui/src/components/EpicsPanel.tsx +0 -566
  788. package/ui/src/components/Header.tsx +0 -160
  789. package/ui/src/components/JsonViewer.tsx +0 -171
  790. package/ui/src/components/ScheduledTaskDetailPanel.tsx +0 -517
  791. package/ui/src/components/ScheduledTasksPanel.tsx +0 -639
  792. package/ui/src/components/ServicesPanel.tsx +0 -622
  793. package/ui/src/components/SessionLogPanel.tsx +0 -1219
  794. package/ui/src/components/StatsBar.tsx +0 -321
  795. package/ui/src/components/StatusBadge.tsx +0 -168
  796. package/ui/src/components/TaskDetailPanel.tsx +0 -903
  797. package/ui/src/components/TasksPanel.tsx +0 -614
  798. package/ui/src/components/UsageCharts.tsx +0 -216
  799. package/ui/src/components/UsageTab.tsx +0 -394
  800. package/ui/src/hooks/queries.ts +0 -353
  801. package/ui/src/index.css +0 -257
  802. package/ui/src/lib/api.ts +0 -268
  803. package/ui/src/lib/config.ts +0 -35
  804. package/ui/src/lib/contentPreview.ts +0 -208
  805. package/ui/src/lib/theme.ts +0 -214
  806. package/ui/src/main.tsx +0 -28
  807. package/ui/src/vite-env.d.ts +0 -1
  808. package/ui/tailwind.config.js +0 -37
  809. package/ui/vite.config.ts +0 -35
  810. /package/{ui → docs-site}/public/logo.png +0 -0
@@ -0,0 +1,820 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Docker-based E2E Provider Test
4
+ *
5
+ * Builds the Docker worker image, starts a local API server with a clean DB,
6
+ * registers agents, creates tasks, and runs Docker workers against them.
7
+ * Tests both Claude and pi-mono providers with the same image.
8
+ *
9
+ * Test scenarios (each can be run independently via --test flag):
10
+ * 1. basic — Worker picks up task, completes it, cost + session recorded
11
+ * 2. cancel — Cancel a task mid-execution, verify tool_call is blocked
12
+ * 3. resume — Pause a task, restart worker, verify it resumes
13
+ * 4. tool-loop — Feed a task that triggers tool loop detection
14
+ * 5. summarize — Verify session summarization produces a memory entry
15
+ *
16
+ * Usage:
17
+ * bun scripts/e2e-docker-provider.ts --provider claude
18
+ * bun scripts/e2e-docker-provider.ts --provider pi
19
+ * bun scripts/e2e-docker-provider.ts --provider both
20
+ * bun scripts/e2e-docker-provider.ts --provider claude --test basic
21
+ * bun scripts/e2e-docker-provider.ts --provider claude --test basic,cancel
22
+ * E2E_PORT=13099 bun scripts/e2e-docker-provider.ts --provider both --test basic
23
+ */
24
+ import { $, type Subprocess } from "bun";
25
+
26
+ // ─── Arg Parsing ─────────────────────────────────────────────────────────────
27
+ function parseArg(name: string, fallback: string): string {
28
+ const flag = process.argv.find((a) => a.startsWith(`--${name}=`));
29
+ if (flag) return flag.split("=")[1]!;
30
+ const idx = process.argv.indexOf(`--${name}`);
31
+ if (idx >= 0 && process.argv[idx + 1]) return process.argv[idx + 1]!;
32
+ return fallback;
33
+ }
34
+
35
+ // ─── Config ──────────────────────────────────────────────────────────────────
36
+ const PORT = process.env.E2E_PORT || "13099";
37
+ const API_KEY = process.env.API_KEY || "123123";
38
+ const BASE_URL = `http://localhost:${PORT}`;
39
+ const DB_PATH = `/tmp/e2e-docker-${Date.now()}.sqlite`;
40
+ const DOCKER_IMAGE = "agent-swarm-worker:e2e";
41
+ const CONTAINER_PREFIX = "e2e-docker";
42
+ const WORKER_PORT_BASE = 13200; // Docker host port for workers
43
+
44
+ const PROVIDER_ARG = parseArg("provider", "claude");
45
+ const TEST_ARG = parseArg("test", "all");
46
+ const SKIP_BUILD = process.argv.includes("--skip-build");
47
+ const TIMEOUT_MS = Number.parseInt(process.env.E2E_TIMEOUT || "120000", 10); // 2 min per test
48
+
49
+ if (!["claude", "pi", "both"].includes(PROVIDER_ARG)) {
50
+ console.error(`Invalid --provider: ${PROVIDER_ARG}. Supported: claude, pi, both`);
51
+ process.exit(1);
52
+ }
53
+
54
+ const PROVIDERS = PROVIDER_ARG === "both" ? ["claude", "pi"] : [PROVIDER_ARG];
55
+ const ALL_TESTS = ["basic", "cancel", "resume", "tool-loop", "summarize"];
56
+ const TESTS_TO_RUN = TEST_ARG === "all" ? ALL_TESTS : TEST_ARG.split(",");
57
+
58
+ for (const t of TESTS_TO_RUN) {
59
+ if (!ALL_TESTS.includes(t)) {
60
+ console.error(`Unknown test: ${t}. Available: ${ALL_TESTS.join(", ")}`);
61
+ process.exit(1);
62
+ }
63
+ }
64
+
65
+ // ─── State ───────────────────────────────────────────────────────────────────
66
+ let apiProc: Subprocess | null = null;
67
+ const activeContainers: string[] = [];
68
+ let pass = 0;
69
+ let fail = 0;
70
+ let skip = 0;
71
+ let cleaningUp = false;
72
+
73
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
74
+ function log(msg: string) {
75
+ console.log(`\x1b[1;36m[E2E-Docker]\x1b[0m ${msg}`);
76
+ }
77
+ function logPass(name: string) {
78
+ console.log(`\x1b[1;32m ✓ PASS\x1b[0m ${name}`);
79
+ pass++;
80
+ }
81
+ function logFail(name: string, detail: string) {
82
+ console.log(`\x1b[1;31m ✗ FAIL\x1b[0m ${name}: ${detail}`);
83
+ fail++;
84
+ }
85
+ function logSkip(name: string, reason: string) {
86
+ console.log(`\x1b[1;33m ○ SKIP\x1b[0m ${name}: ${reason}`);
87
+ skip++;
88
+ }
89
+ function logSection(title: string) {
90
+ console.log(`\n\x1b[1;35m═══ ${title} ═══\x1b[0m`);
91
+ }
92
+
93
+ async function api(method: string, path: string, body?: unknown, agentId?: string) {
94
+ const headers: Record<string, string> = {
95
+ Authorization: `Bearer ${API_KEY}`,
96
+ "Content-Type": "application/json",
97
+ };
98
+ if (agentId) headers["X-Agent-ID"] = agentId;
99
+
100
+ const resp = await fetch(`${BASE_URL}${path}`, {
101
+ method,
102
+ headers,
103
+ body: body ? JSON.stringify(body) : undefined,
104
+ });
105
+ if (!resp.ok) {
106
+ const text = await resp.text();
107
+ throw new Error(`API ${method} ${path} returned ${resp.status}: ${text}`);
108
+ }
109
+ return resp.json();
110
+ }
111
+
112
+ async function apiSafe(method: string, path: string, body?: unknown, agentId?: string) {
113
+ try {
114
+ return await api(method, path, body, agentId);
115
+ } catch (err) {
116
+ return { error: String(err) };
117
+ }
118
+ }
119
+
120
+ async function waitForApi(maxWaitMs = 15000): Promise<boolean> {
121
+ const start = Date.now();
122
+ while (Date.now() - start < maxWaitMs) {
123
+ try {
124
+ const resp = await fetch(`${BASE_URL}/health`);
125
+ if (resp.ok) return true;
126
+ } catch {}
127
+ await Bun.sleep(500);
128
+ }
129
+ return false;
130
+ }
131
+
132
+ /** Poll a condition until it's true or timeout */
133
+ async function pollUntil(
134
+ fn: () => Promise<boolean>,
135
+ timeoutMs: number,
136
+ intervalMs = 2000,
137
+ ): Promise<boolean> {
138
+ const start = Date.now();
139
+ while (Date.now() - start < timeoutMs) {
140
+ if (await fn()) return true;
141
+ await Bun.sleep(intervalMs);
142
+ }
143
+ return false;
144
+ }
145
+
146
+ /** Start a Docker worker container for the given provider */
147
+ async function startWorker(
148
+ provider: string,
149
+ agentId: string,
150
+ hostPort: number,
151
+ extraEnv: Record<string, string> = {},
152
+ ): Promise<string> {
153
+ const containerName = `${CONTAINER_PREFIX}-${provider}-${Date.now()}`;
154
+
155
+ const envFlags: string[] = [
156
+ "-e",
157
+ `HARNESS_PROVIDER=${provider}`,
158
+ "-e",
159
+ `API_KEY=${API_KEY}`,
160
+ "-e",
161
+ `AGENT_ID=${agentId}`,
162
+ "-e",
163
+ `MCP_BASE_URL=http://host.docker.internal:${PORT}`,
164
+ "-e",
165
+ "AGENT_ROLE=worker",
166
+ "-e",
167
+ "YOLO=true",
168
+ "-e",
169
+ "STARTUP_SCRIPT_STRICT=false",
170
+ "-e",
171
+ "SLACK_DISABLE=true",
172
+ "-e",
173
+ "GITHUB_DISABLE=true",
174
+ "-e",
175
+ "MAX_CONCURRENT_TASKS=1",
176
+ ];
177
+
178
+ // Provider-specific auth
179
+ if (provider === "claude") {
180
+ const token = process.env.CLAUDE_CODE_OAUTH_TOKEN;
181
+ if (!token) throw new Error("CLAUDE_CODE_OAUTH_TOKEN required for claude provider");
182
+ envFlags.push("-e", `CLAUDE_CODE_OAUTH_TOKEN=${token}`);
183
+ } else if (provider === "pi") {
184
+ // pi-mono can use OPENROUTER_API_KEY or ANTHROPIC_API_KEY
185
+ const orKey = process.env.OPENROUTER_API_KEY;
186
+ const antKey = process.env.ANTHROPIC_API_KEY;
187
+ if (orKey) envFlags.push("-e", `OPENROUTER_API_KEY=${orKey}`);
188
+ if (antKey) envFlags.push("-e", `ANTHROPIC_API_KEY=${antKey}`);
189
+ if (!orKey && !antKey)
190
+ throw new Error("OPENROUTER_API_KEY or ANTHROPIC_API_KEY required for pi provider");
191
+ }
192
+
193
+ for (const [k, v] of Object.entries(extraEnv)) {
194
+ envFlags.push("-e", `${k}=${v}`);
195
+ }
196
+
197
+ const args = [
198
+ "docker",
199
+ "run",
200
+ "--rm",
201
+ "-d",
202
+ "--name",
203
+ containerName,
204
+ ...envFlags,
205
+ "-p",
206
+ `${hostPort}:3000`,
207
+ DOCKER_IMAGE,
208
+ ];
209
+
210
+ const result = await $`${args}`.text();
211
+ const containerId = result.trim().slice(0, 12);
212
+ activeContainers.push(containerName);
213
+ log(`Started container ${containerName} (${containerId}) on port ${hostPort}`);
214
+ return containerName;
215
+ }
216
+
217
+ /** Get Docker container logs */
218
+ async function containerLogs(name: string, tail = 50): Promise<string> {
219
+ try {
220
+ return await $`docker logs --tail ${tail} ${name} 2>&1`.text();
221
+ } catch {
222
+ return "<no logs available>";
223
+ }
224
+ }
225
+
226
+ /** Stop and remove a Docker container */
227
+ async function stopContainer(name: string): Promise<void> {
228
+ try {
229
+ await $`docker stop -t 5 ${name} 2>/dev/null`.quiet();
230
+ } catch {}
231
+ const idx = activeContainers.indexOf(name);
232
+ if (idx >= 0) activeContainers.splice(idx, 1);
233
+ }
234
+
235
+ async function cleanup() {
236
+ if (cleaningUp) return;
237
+ cleaningUp = true;
238
+ log("Cleaning up...");
239
+
240
+ // Stop all containers
241
+ for (const name of [...activeContainers]) {
242
+ await stopContainer(name);
243
+ }
244
+
245
+ // Stop API server
246
+ if (apiProc) {
247
+ apiProc.kill("SIGTERM");
248
+ await apiProc.exited.catch(() => {});
249
+ }
250
+
251
+ // Remove temp DB files
252
+ for (const suffix of ["", "-wal", "-shm"]) {
253
+ try {
254
+ await $`rm -f ${DB_PATH}${suffix}`.quiet();
255
+ } catch {}
256
+ }
257
+ }
258
+
259
+ process.on("SIGINT", async () => {
260
+ await cleanup();
261
+ process.exit(1);
262
+ });
263
+ process.on("SIGTERM", async () => {
264
+ await cleanup();
265
+ process.exit(1);
266
+ });
267
+
268
+ // ─── Test Implementations ────────────────────────────────────────────────────
269
+
270
+ /** Test: basic — Worker picks up task, completes, cost + session recorded */
271
+ async function testBasic(provider: string, portOffset: number) {
272
+ const testName = `[${provider}] basic`;
273
+ logSection(`${testName}: Task completion + cost recording`);
274
+
275
+ // Register agent
276
+ const joinResult = await api("POST", "/api/agents", {
277
+ name: `e2e-${provider}-basic`,
278
+ role: "worker",
279
+ status: "online",
280
+ harnessProvider: provider,
281
+ });
282
+ const agentId = joinResult?.agent?.id || joinResult?.id || joinResult?.id;
283
+ if (!agentId) {
284
+ logFail(testName, `Agent registration failed: ${JSON.stringify(joinResult)}`);
285
+ return;
286
+ }
287
+ logPass(`${testName}: Agent registered ${agentId.slice(0, 8)}`);
288
+
289
+ // Create task
290
+ const taskResult = await api("POST", "/api/tasks", {
291
+ task: "Say 'hello world' and nothing else. Do not use any tools. Just output text.",
292
+ agentId,
293
+ source: "api",
294
+ });
295
+ const taskId = taskResult?.task?.id || taskResult?.id || taskResult?.id;
296
+ if (!taskId) {
297
+ logFail(testName, `Task creation failed: ${JSON.stringify(taskResult)}`);
298
+ return;
299
+ }
300
+ logPass(`${testName}: Task created ${taskId.slice(0, 8)}`);
301
+
302
+ // Start worker container
303
+ const hostPort = WORKER_PORT_BASE + portOffset;
304
+ let containerName: string;
305
+ try {
306
+ containerName = await startWorker(provider, agentId, hostPort);
307
+ } catch (err) {
308
+ logFail(testName, `Container start failed: ${err}`);
309
+ return;
310
+ }
311
+
312
+ // Poll until task completes or timeout
313
+ const completed = await pollUntil(async () => {
314
+ const details = await apiSafe("GET", `/api/tasks/${taskId}`);
315
+ const status = details?.task?.status || details?.status;
316
+ if (status === "completed" || status === "failed") return true;
317
+ return false;
318
+ }, TIMEOUT_MS);
319
+
320
+ // Check results
321
+ const finalTask = await apiSafe("GET", `/api/tasks/${taskId}`);
322
+ const status = finalTask?.task?.status || finalTask?.status;
323
+
324
+ if (!completed) {
325
+ const logs = await containerLogs(containerName);
326
+ logFail(
327
+ testName,
328
+ `Timed out after ${TIMEOUT_MS / 1000}s. Status: ${status}\nLast logs:\n${logs}`,
329
+ );
330
+ await stopContainer(containerName);
331
+ return;
332
+ }
333
+
334
+ if (status === "completed") {
335
+ logPass(`${testName}: Task completed`);
336
+ } else {
337
+ logFail(testName, `Task status: ${status} (expected completed)`);
338
+ }
339
+
340
+ // Check session ID
341
+ const sessionId = finalTask?.task?.claudeSessionId || finalTask?.claudeSessionId;
342
+ if (sessionId) {
343
+ logPass(`${testName}: Session ID recorded (${sessionId.slice(0, 8)})`);
344
+ } else {
345
+ logFail(testName, "No session ID on task");
346
+ }
347
+
348
+ // Wait for runner to finish post-completion work (cost save, log flush, etc.)
349
+ // The task may be marked "completed" by hooks before the runner's
350
+ // waitForCompletion() resolves and saves cost data.
351
+ await Bun.sleep(10000);
352
+
353
+ // Grab container logs before checking cost (for diagnostics)
354
+ const workerLogs = await containerLogs(containerName, 200);
355
+ const diagLines = workerLogs
356
+ .split("\n")
357
+ .filter(
358
+ (l) =>
359
+ l.includes("cost") ||
360
+ l.includes("Cost") ||
361
+ l.includes("result") ||
362
+ l.includes("save") ||
363
+ l.includes("[runner]") ||
364
+ l.includes("exit"),
365
+ );
366
+ if (diagLines.length > 0) {
367
+ log(`${testName}: Diagnostic log lines:\n${diagLines.join("\n")}`);
368
+ } else {
369
+ log(`${testName}: DIAG: No diagnostic lines found. Total log lines: ${workerLogs.split("\n").length}`);
370
+ // Show last 20 lines for debugging
371
+ const lastLines = workerLogs.split("\n").slice(-20).join("\n");
372
+ log(`${testName}: Last 20 lines:\n${lastLines}`);
373
+ }
374
+
375
+ // Poll for cost data (may take time if Stop hook runs summarization)
376
+ let costEntries: Array<{ totalCostUsd: number }> = [];
377
+ const costFound = await pollUntil(async () => {
378
+ const costs = await apiSafe("GET", `/api/session-costs?agentId=${agentId}`);
379
+ costEntries = costs?.costs || [];
380
+ if (costEntries.length > 0) return true;
381
+ // Also check unfiltered
382
+ const allCosts = await apiSafe("GET", "/api/session-costs");
383
+ const allEntries = allCosts?.costs || [];
384
+ if (allEntries.length > 0) {
385
+ log(
386
+ `${testName}: DIAG: No costs for agentId=${agentId}, but ${allEntries.length} total. Sample: ${JSON.stringify(allEntries[0])}`,
387
+ );
388
+ return true; // Cost exists, just wrong agentId filter
389
+ }
390
+ return false;
391
+ }, 30000, 3000);
392
+
393
+ if (costEntries.length > 0) {
394
+ const total = costEntries.reduce(
395
+ (sum: number, c: { totalCostUsd: number }) => sum + c.totalCostUsd,
396
+ 0,
397
+ );
398
+ logPass(`${testName}: Cost recorded ($${total.toFixed(4)}, ${costEntries.length} entries)`);
399
+ } else if (costFound) {
400
+ logSkip(`${testName}: Cost data`, "Costs exist but not linked to this agentId");
401
+ } else {
402
+ logFail(testName, "No cost data recorded (polled for 30s)");
403
+ }
404
+
405
+ // Check session logs
406
+ const logs = await apiSafe("GET", `/api/session-logs?taskId=${taskId}`);
407
+ const logEntries = logs?.logs || [];
408
+ if (logEntries.length > 0) {
409
+ logPass(`${testName}: Session logs recorded (${logEntries.length} entries)`);
410
+ } else {
411
+ logSkip(`${testName}: Session logs`, "No log entries (may be expected for trivial tasks)");
412
+ }
413
+
414
+ await stopContainer(containerName);
415
+ }
416
+
417
+ /** Test: cancel — Cancel a task mid-execution, verify blocking */
418
+ async function testCancel(provider: string, portOffset: number) {
419
+ const testName = `[${provider}] cancel`;
420
+ logSection(`${testName}: Task cancellation mid-execution`);
421
+
422
+ // Register agent
423
+ const joinResult = await api("POST", "/api/agents", {
424
+ name: `e2e-${provider}-cancel`,
425
+ role: "worker",
426
+ status: "online",
427
+ harnessProvider: provider,
428
+ });
429
+ const agentId = joinResult?.agent?.id || joinResult?.id;
430
+ if (!agentId) {
431
+ logFail(testName, `Agent registration failed`);
432
+ return;
433
+ }
434
+
435
+ // Create a task that takes a while (uses tools, so cancellation hook fires)
436
+ const taskResult = await api("POST", "/api/tasks", {
437
+ task: "List all files in the current directory recursively. Then create a file called 'test-output.txt' with the listing.",
438
+ agentId,
439
+ source: "api",
440
+ });
441
+ const taskId = taskResult?.task?.id || taskResult?.id;
442
+ if (!taskId) {
443
+ logFail(testName, `Task creation failed`);
444
+ return;
445
+ }
446
+ logPass(`${testName}: Task created ${taskId.slice(0, 8)}`);
447
+
448
+ // Start worker
449
+ const hostPort = WORKER_PORT_BASE + portOffset;
450
+ let containerName: string;
451
+ try {
452
+ containerName = await startWorker(provider, agentId, hostPort);
453
+ } catch (err) {
454
+ logFail(testName, `Container start failed: ${err}`);
455
+ return;
456
+ }
457
+
458
+ // Wait for task to start (status changes from pending/assigned to in_progress)
459
+ const started = await pollUntil(async () => {
460
+ const details = await apiSafe("GET", `/api/tasks/${taskId}`);
461
+ return details?.task?.status || details?.status === "in_progress";
462
+ }, 30000);
463
+
464
+ if (!started) {
465
+ // Even if not in_progress, try cancelling anyway after a delay
466
+ await Bun.sleep(10000);
467
+ }
468
+
469
+ // Cancel the task
470
+ log(`${testName}: Cancelling task...`);
471
+ await apiSafe("POST", `/api/tasks/${taskId}/cancel`, { reason: "E2E cancellation test" });
472
+
473
+ // Poll until task reaches cancelled/completed/failed state
474
+ const finished = await pollUntil(async () => {
475
+ const details = await apiSafe("GET", `/api/tasks/${taskId}`);
476
+ const s = details?.task?.status || details?.status;
477
+ return s === "cancelled" || s === "completed" || s === "failed";
478
+ }, TIMEOUT_MS);
479
+
480
+ const finalTask = await apiSafe("GET", `/api/tasks/${taskId}`);
481
+ const finalStatus = finalTask?.task?.status || finalTask?.status;
482
+
483
+ if (finalStatus === "cancelled") {
484
+ logPass(`${testName}: Task cancelled successfully`);
485
+ } else if (finished) {
486
+ logFail(testName, `Task ended with status: ${finalStatus} (expected cancelled)`);
487
+ } else {
488
+ logFail(testName, `Timed out waiting for cancellation. Status: ${finalStatus}`);
489
+ }
490
+
491
+ await stopContainer(containerName);
492
+ }
493
+
494
+ /** Test: resume — Pause a task, restart worker, verify it resumes */
495
+ async function testResume(provider: string, portOffset: number) {
496
+ const testName = `[${provider}] resume`;
497
+ logSection(`${testName}: Session resume after restart`);
498
+
499
+ // Register agent
500
+ const joinResult = await api("POST", "/api/agents", {
501
+ name: `e2e-${provider}-resume`,
502
+ role: "worker",
503
+ status: "online",
504
+ harnessProvider: provider,
505
+ });
506
+ const agentId = joinResult?.agent?.id || joinResult?.id;
507
+ if (!agentId) {
508
+ logFail(testName, `Agent registration failed`);
509
+ return;
510
+ }
511
+
512
+ // Create a multi-step task
513
+ const taskResult = await api("POST", "/api/tasks", {
514
+ task: "Step 1: Create a file /workspace/resume-test-1.txt with content 'step1'. Step 2: Create a file /workspace/resume-test-2.txt with content 'step2'. Step 3: Create a file /workspace/resume-test-3.txt with content 'step3'.",
515
+ agentId,
516
+ source: "api",
517
+ });
518
+ const taskId = taskResult?.task?.id || taskResult?.id;
519
+ if (!taskId) {
520
+ logFail(testName, `Task creation failed`);
521
+ return;
522
+ }
523
+ logPass(`${testName}: Task created ${taskId.slice(0, 8)}`);
524
+
525
+ // Start worker
526
+ const hostPort = WORKER_PORT_BASE + portOffset;
527
+ let containerName: string;
528
+ try {
529
+ containerName = await startWorker(provider, agentId, hostPort);
530
+ } catch (err) {
531
+ logFail(testName, `Container start failed: ${err}`);
532
+ return;
533
+ }
534
+
535
+ // Wait for task to start
536
+ const started = await pollUntil(async () => {
537
+ const details = await apiSafe("GET", `/api/tasks/${taskId}`);
538
+ return details?.task?.status || details?.status === "in_progress";
539
+ }, 30000);
540
+
541
+ if (!started) {
542
+ logFail(testName, "Task never reached in_progress");
543
+ await stopContainer(containerName);
544
+ return;
545
+ }
546
+
547
+ // Wait a bit, then kill the container abruptly
548
+ await Bun.sleep(5000);
549
+ const taskBefore = await apiSafe("GET", `/api/tasks/${taskId}`);
550
+ const sessionIdBefore = taskBefore?.task?.claudeSessionId || taskBefore?.claudeSessionId;
551
+ log(`${testName}: Session ID before restart: ${sessionIdBefore?.slice(0, 8) || "none"}`);
552
+
553
+ // Kill container (simulates crash)
554
+ await stopContainer(containerName);
555
+ log(`${testName}: Container killed. Restarting...`);
556
+ await Bun.sleep(2000);
557
+
558
+ // Reset task to assigned so worker picks it up again
559
+ // The runner should detect the existing sessionId and --resume
560
+ await apiSafe("POST", `/api/tasks/${taskId}/reassign`, { assigneeId: agentId });
561
+
562
+ // Start new worker
563
+ try {
564
+ containerName = await startWorker(provider, agentId, hostPort);
565
+ } catch (err) {
566
+ logFail(testName, `Container restart failed: ${err}`);
567
+ return;
568
+ }
569
+
570
+ // Wait for task to complete
571
+ const completed = await pollUntil(async () => {
572
+ const details = await apiSafe("GET", `/api/tasks/${taskId}`);
573
+ const s = details?.task?.status || details?.status;
574
+ return s === "completed" || s === "failed";
575
+ }, TIMEOUT_MS);
576
+
577
+ const finalTask = await apiSafe("GET", `/api/tasks/${taskId}`);
578
+ const finalStatus = finalTask?.task?.status || finalTask?.status;
579
+ const sessionIdAfter = finalTask?.task?.claudeSessionId || finalTask?.claudeSessionId;
580
+
581
+ if (completed && finalStatus === "completed") {
582
+ logPass(`${testName}: Task completed after resume`);
583
+ } else {
584
+ logFail(testName, `Task status: ${finalStatus}`);
585
+ }
586
+
587
+ // Check if session ID changed (it may or may not, depending on resume support)
588
+ if (sessionIdBefore && sessionIdAfter) {
589
+ if (sessionIdBefore === sessionIdAfter) {
590
+ logPass(`${testName}: Same session ID — resume worked`);
591
+ } else {
592
+ logSkip(
593
+ `${testName}: Session ID changed`,
594
+ "Provider may not support resume or session was stale",
595
+ );
596
+ }
597
+ }
598
+
599
+ await stopContainer(containerName);
600
+ }
601
+
602
+ /** Test: tool-loop — Trigger tool loop detection */
603
+ async function testToolLoop(provider: string, portOffset: number) {
604
+ const testName = `[${provider}] tool-loop`;
605
+ logSection(`${testName}: Tool loop detection`);
606
+
607
+ // Register agent
608
+ const joinResult = await api("POST", "/api/agents", {
609
+ name: `e2e-${provider}-loop`,
610
+ role: "worker",
611
+ status: "online",
612
+ harnessProvider: provider,
613
+ });
614
+ const agentId = joinResult?.agent?.id || joinResult?.id;
615
+ if (!agentId) {
616
+ logFail(testName, `Agent registration failed`);
617
+ return;
618
+ }
619
+
620
+ // Create a task likely to trigger tool loops
621
+ const taskResult = await api("POST", "/api/tasks", {
622
+ task: "Read the file /workspace/nonexistent-file.txt and tell me its contents. Keep trying until you succeed. Do not give up.",
623
+ agentId,
624
+ source: "api",
625
+ });
626
+ const taskId = taskResult?.task?.id || taskResult?.id;
627
+ if (!taskId) {
628
+ logFail(testName, `Task creation failed`);
629
+ return;
630
+ }
631
+ logPass(`${testName}: Task created ${taskId.slice(0, 8)}`);
632
+
633
+ // Start worker
634
+ const hostPort = WORKER_PORT_BASE + portOffset;
635
+ let containerName: string;
636
+ try {
637
+ containerName = await startWorker(provider, agentId, hostPort);
638
+ } catch (err) {
639
+ logFail(testName, `Container start failed: ${err}`);
640
+ return;
641
+ }
642
+
643
+ // Wait for task to complete/fail (loop detection should stop it)
644
+ const completed = await pollUntil(async () => {
645
+ const details = await apiSafe("GET", `/api/tasks/${taskId}`);
646
+ const s = details?.task?.status || details?.status;
647
+ return s === "completed" || s === "failed";
648
+ }, TIMEOUT_MS);
649
+
650
+ // Check container logs for loop detection markers
651
+ const logs = await containerLogs(containerName, 200);
652
+ const loopDetected =
653
+ logs.includes("LOOP DETECTED") || logs.includes("loop") || logs.includes("blocked");
654
+
655
+ if (loopDetected) {
656
+ logPass(`${testName}: Tool loop detection triggered`);
657
+ } else if (completed) {
658
+ logSkip(`${testName}: Loop detection`, "Task completed without triggering loop detection");
659
+ } else {
660
+ logFail(testName, "Task timed out without loop detection");
661
+ }
662
+
663
+ await stopContainer(containerName);
664
+ }
665
+
666
+ /** Test: summarize — Verify session summarization produces memory entry */
667
+ async function testSummarize(provider: string, portOffset: number) {
668
+ const testName = `[${provider}] summarize`;
669
+ logSection(`${testName}: Session summarization`);
670
+
671
+ // Register agent
672
+ const joinResult = await api("POST", "/api/agents", {
673
+ name: `e2e-${provider}-summarize`,
674
+ role: "worker",
675
+ status: "online",
676
+ harnessProvider: provider,
677
+ });
678
+ const agentId = joinResult?.agent?.id || joinResult?.id;
679
+ if (!agentId) {
680
+ logFail(testName, `Agent registration failed`);
681
+ return;
682
+ }
683
+
684
+ // Create a task with enough substance to summarize
685
+ const taskResult = await api("POST", "/api/tasks", {
686
+ task: "Create a file /workspace/summary-test.txt explaining what 2+2 equals and why. Use the Write tool.",
687
+ agentId,
688
+ source: "api",
689
+ });
690
+ const taskId = taskResult?.task?.id || taskResult?.id;
691
+ if (!taskId) {
692
+ logFail(testName, `Task creation failed`);
693
+ return;
694
+ }
695
+ logPass(`${testName}: Task created ${taskId.slice(0, 8)}`);
696
+
697
+ // Start worker
698
+ const hostPort = WORKER_PORT_BASE + portOffset;
699
+ let containerName: string;
700
+ try {
701
+ containerName = await startWorker(provider, agentId, hostPort);
702
+ } catch (err) {
703
+ logFail(testName, `Container start failed: ${err}`);
704
+ return;
705
+ }
706
+
707
+ // Wait for task to complete
708
+ const completed = await pollUntil(async () => {
709
+ const details = await apiSafe("GET", `/api/tasks/${taskId}`);
710
+ const s = details?.task?.status || details?.status;
711
+ return s === "completed" || s === "failed";
712
+ }, TIMEOUT_MS);
713
+
714
+ if (!completed) {
715
+ logFail(testName, "Task timed out");
716
+ await stopContainer(containerName);
717
+ return;
718
+ }
719
+
720
+ // Wait a bit for summarization to complete (runs in Stop hook)
721
+ await Bun.sleep(10000);
722
+
723
+ // Check for memory entries from this agent
724
+ const memories = await apiSafe("GET", `/api/memory?agentId=${agentId}`);
725
+ const memEntries = memories?.memories || memories?.entries || [];
726
+
727
+ if (memEntries.length > 0) {
728
+ logPass(`${testName}: Memory entry created (${memEntries.length} entries)`);
729
+ } else {
730
+ logSkip(
731
+ `${testName}: Memory entry`,
732
+ "No memory entries found (summarization may have failed silently)",
733
+ );
734
+ }
735
+
736
+ await stopContainer(containerName);
737
+ }
738
+
739
+ // ─── Test Router ─────────────────────────────────────────────────────────────
740
+ const TEST_FUNCTIONS: Record<string, (provider: string, portOffset: number) => Promise<void>> = {
741
+ basic: testBasic,
742
+ cancel: testCancel,
743
+ resume: testResume,
744
+ "tool-loop": testToolLoop,
745
+ summarize: testSummarize,
746
+ };
747
+
748
+ // ─── Main ────────────────────────────────────────────────────────────────────
749
+ async function main() {
750
+ log(`Providers: ${PROVIDERS.join(", ")}`);
751
+ log(`Tests: ${TESTS_TO_RUN.join(", ")}`);
752
+ log(`API port: ${PORT}, DB: ${DB_PATH}`);
753
+ log(`Timeout per test: ${TIMEOUT_MS / 1000}s`);
754
+
755
+ // 1. Build Docker image (unless --skip-build)
756
+ if (!SKIP_BUILD) {
757
+ logSection("Building Docker image");
758
+ try {
759
+ await $`docker build -f Dockerfile.worker -t ${DOCKER_IMAGE} .`.quiet();
760
+ logPass("Docker image built");
761
+ } catch (err) {
762
+ logFail("Docker build", String(err));
763
+ process.exit(1);
764
+ }
765
+ } else {
766
+ log("Skipping Docker build (--skip-build)");
767
+ }
768
+
769
+ // 2. Start API server
770
+ logSection("Starting API server");
771
+ apiProc = Bun.spawn(["bun", "run", "src/http.ts"], {
772
+ env: {
773
+ ...process.env,
774
+ PORT,
775
+ API_KEY,
776
+ DATABASE_PATH: DB_PATH,
777
+ SLACK_DISABLE: "true",
778
+ GITHUB_DISABLE: "true",
779
+ },
780
+ stdout: "pipe",
781
+ stderr: "pipe",
782
+ });
783
+
784
+ if (!(await waitForApi())) {
785
+ logFail("API startup", "Server did not become healthy within 15s");
786
+ await cleanup();
787
+ process.exit(1);
788
+ }
789
+ logPass("API server started");
790
+
791
+ // 3. Run tests for each provider
792
+ let portOffset = 0;
793
+ for (const provider of PROVIDERS) {
794
+ logSection(`Provider: ${provider}`);
795
+ for (const testName of TESTS_TO_RUN) {
796
+ const testFn = TEST_FUNCTIONS[testName];
797
+ if (!testFn) continue;
798
+ try {
799
+ await testFn(provider, portOffset++);
800
+ } catch (err) {
801
+ logFail(`[${provider}] ${testName}`, `Unhandled error: ${err}`);
802
+ }
803
+ }
804
+ }
805
+
806
+ // 4. Summary
807
+ logSection("Results");
808
+ log(
809
+ `\x1b[1;32m${pass} passed\x1b[0m, \x1b[1;31m${fail} failed\x1b[0m, \x1b[1;33m${skip} skipped\x1b[0m`,
810
+ );
811
+
812
+ await cleanup();
813
+ process.exit(fail > 0 ? 1 : 0);
814
+ }
815
+
816
+ main().catch(async (err) => {
817
+ console.error("E2E test error:", err);
818
+ await cleanup();
819
+ process.exit(1);
820
+ });