@desplega.ai/agent-swarm 1.49.0 → 1.52.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 (547) hide show
  1. package/README.md +1 -1
  2. package/openapi.json +2070 -728
  3. package/package.json +10 -1
  4. package/src/agentmail/handlers.ts +65 -10
  5. package/src/agentmail/templates.ts +111 -0
  6. package/src/be/db.ts +1233 -7
  7. package/src/be/migrations/014_prompt_templates.sql +33 -0
  8. package/src/be/migrations/015_workflow_workspace.sql +3 -0
  9. package/src/be/migrations/016_active_session_runner_session.sql +4 -0
  10. package/src/be/migrations/017_channel_activity_cursors.sql +6 -0
  11. package/src/be/migrations/018_fix_seed_double_version.sql +30 -0
  12. package/src/be/migrations/019_skills.sql +65 -0
  13. package/src/be/migrations/020_approval_requests.sql +41 -0
  14. package/src/be/seed.ts +62 -0
  15. package/src/be/skill-parser.ts +70 -0
  16. package/src/be/skill-sync.ts +106 -0
  17. package/src/commands/runner.ts +320 -132
  18. package/src/commands/templates.ts +172 -0
  19. package/src/github/handlers.ts +292 -77
  20. package/src/github/mentions-aliases.test.ts +73 -0
  21. package/src/github/mentions.test.ts +3 -3
  22. package/src/github/mentions.ts +32 -6
  23. package/src/github/templates.ts +398 -0
  24. package/src/gitlab/handlers.ts +63 -22
  25. package/src/gitlab/templates.ts +140 -0
  26. package/src/heartbeat/heartbeat.ts +19 -10
  27. package/src/heartbeat/templates.ts +30 -0
  28. package/src/http/active-sessions.ts +27 -0
  29. package/src/http/approval-requests.ts +247 -0
  30. package/src/http/config.ts +3 -3
  31. package/src/http/index.ts +9 -2
  32. package/src/http/poll.ts +135 -14
  33. package/src/http/prompt-templates.ts +412 -0
  34. package/src/http/schedules.ts +35 -0
  35. package/src/http/skills.ts +479 -0
  36. package/src/http/workflows.ts +8 -0
  37. package/src/linear/sync.ts +28 -4
  38. package/src/linear/templates.ts +47 -0
  39. package/src/prompts/base-prompt.ts +41 -490
  40. package/src/prompts/registry.ts +57 -0
  41. package/src/prompts/resolver.ts +296 -0
  42. package/src/prompts/session-templates.ts +604 -0
  43. package/src/providers/claude-adapter.ts +15 -2
  44. package/src/providers/pi-mono-extension.ts +5 -1
  45. package/src/scheduler/scheduler.ts +125 -91
  46. package/src/server.ts +44 -0
  47. package/src/slack/assistant.ts +7 -4
  48. package/src/slack/channel-activity.ts +177 -0
  49. package/src/slack/handlers.ts +21 -6
  50. package/src/slack/templates.ts +55 -0
  51. package/src/tests/approval-requests.test.ts +735 -0
  52. package/src/tests/artifact-sdk.test.ts +12 -12
  53. package/src/tests/base-prompt.test.ts +49 -49
  54. package/src/tests/channel-activity.test.ts +363 -0
  55. package/src/tests/heartbeat.test.ts +1 -0
  56. package/src/tests/linear-webhook.test.ts +7 -3
  57. package/src/tests/pool-session-logs.test.ts +199 -0
  58. package/src/tests/prompt-template-github.test.ts +682 -0
  59. package/src/tests/prompt-template-remaining.test.ts +504 -0
  60. package/src/tests/prompt-template-resolver.test.ts +621 -0
  61. package/src/tests/prompt-template-session.test.ts +363 -0
  62. package/src/tests/prompt-templates-db.test.ts +616 -0
  63. package/src/tests/self-improvement.test.ts +8 -7
  64. package/src/tests/skill-parser.test.ts +178 -0
  65. package/src/tests/skill-sync.test.ts +171 -0
  66. package/src/tests/slack-metadata-inheritance.test.ts +1 -1
  67. package/src/tests/slack-thread-followups.test.ts +1 -1
  68. package/src/tests/structured-output.test.ts +0 -4
  69. package/src/tests/tool-annotations.test.ts +2 -1
  70. package/src/tests/update-profile-agentid.test.ts +248 -0
  71. package/src/tests/update-profile-auth.test.ts +195 -0
  72. package/src/tests/workflow-async-v2.test.ts +126 -4
  73. package/src/tests/workflow-definition-validation.test.ts +76 -0
  74. package/src/tests/workflow-executors.test.ts +4 -2
  75. package/src/tests/workflow-retry-v2.test.ts +1 -1
  76. package/src/tests/workflow-schedule-trigger.test.ts +104 -0
  77. package/src/tests/workflow-workspace.test.ts +272 -0
  78. package/src/tools/prompt-templates/delete.ts +86 -0
  79. package/src/tools/prompt-templates/get.ts +89 -0
  80. package/src/tools/prompt-templates/index.ts +5 -0
  81. package/src/tools/prompt-templates/list.ts +95 -0
  82. package/src/tools/prompt-templates/preview.ts +84 -0
  83. package/src/tools/prompt-templates/set.ts +117 -0
  84. package/src/tools/request-human-input.ts +106 -0
  85. package/src/tools/skills/index.ts +11 -0
  86. package/src/tools/skills/skill-create.ts +105 -0
  87. package/src/tools/skills/skill-delete.ts +67 -0
  88. package/src/tools/skills/skill-get.ts +75 -0
  89. package/src/tools/skills/skill-install-remote.ts +152 -0
  90. package/src/tools/skills/skill-install.ts +101 -0
  91. package/src/tools/skills/skill-list.ts +77 -0
  92. package/src/tools/skills/skill-publish.ts +123 -0
  93. package/src/tools/skills/skill-search.ts +43 -0
  94. package/src/tools/skills/skill-sync-remote.ts +128 -0
  95. package/src/tools/skills/skill-uninstall.ts +60 -0
  96. package/src/tools/skills/skill-update.ts +128 -0
  97. package/src/tools/store-progress.ts +22 -4
  98. package/src/tools/task-action.ts +20 -0
  99. package/src/tools/templates.ts +53 -0
  100. package/src/tools/tool-config.ts +23 -0
  101. package/src/tools/update-profile.ts +106 -34
  102. package/src/tools/workflows/create-workflow.ts +19 -1
  103. package/src/tools/workflows/update-workflow.ts +16 -1
  104. package/src/types.ts +109 -2
  105. package/src/workflows/definition.ts +30 -12
  106. package/src/workflows/engine.ts +40 -14
  107. package/src/workflows/executors/agent-task.ts +14 -3
  108. package/src/workflows/executors/human-in-the-loop.ts +160 -0
  109. package/src/workflows/executors/registry.ts +2 -0
  110. package/src/workflows/index.ts +1 -1
  111. package/src/workflows/recovery.ts +72 -0
  112. package/src/workflows/resume.ts +162 -12
  113. package/src/workflows/triggers.ts +31 -2
  114. package/src/workflows/version.ts +2 -0
  115. package/.claude/settings.json +0 -84
  116. package/.claude/settings.local.json +0 -117
  117. package/.dockerignore +0 -61
  118. package/.editorconfig +0 -15
  119. package/.entire/settings.json +0 -4
  120. package/.env.docker.example +0 -56
  121. package/.env.example +0 -78
  122. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -78
  123. package/.github/ISSUE_TEMPLATE/community-template.yml +0 -77
  124. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  125. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -60
  126. package/.github/PULL_REQUEST_TEMPLATE/community-template.md +0 -29
  127. package/.github/workflows/ci.yml +0 -52
  128. package/.github/workflows/docker-and-deploy.yml +0 -132
  129. package/.github/workflows/merge-gate.yml +0 -233
  130. package/.opencode/plugins/entire.ts +0 -133
  131. package/.superset/config.json +0 -6
  132. package/.wts-config.json +0 -4
  133. package/.wts-setup.ts +0 -171
  134. package/CHANGELOG.md +0 -447
  135. package/CLAUDE.md +0 -521
  136. package/CONTRIBUTING.md +0 -315
  137. package/DEPLOYMENT.md +0 -622
  138. package/Dockerfile +0 -65
  139. package/Dockerfile.worker +0 -189
  140. package/MCP.md +0 -841
  141. package/UI.md +0 -40
  142. package/api-entrypoint.sh +0 -56
  143. package/assets/agent-swarm-logo-orange.png +0 -0
  144. package/assets/agent-swarm-logo.png +0 -0
  145. package/assets/agent-swarm.mp4 +0 -0
  146. package/assets/agent-swarm.png +0 -0
  147. package/biome.json +0 -39
  148. package/deploy/DEPLOY.md +0 -60
  149. package/deploy/agent-swarm.service +0 -17
  150. package/deploy/docker-push.ts +0 -30
  151. package/deploy/install.ts +0 -85
  152. package/deploy/prod-db.ts +0 -42
  153. package/deploy/uninstall.ts +0 -12
  154. package/deploy/update.ts +0 -21
  155. package/depot.json +0 -1
  156. package/docker-compose.example.yml +0 -350
  157. package/docker-compose.local.yml +0 -119
  158. package/docker-entrypoint.sh +0 -632
  159. package/docs-site/app/api/search/route.ts +0 -4
  160. package/docs-site/app/docs/[[...slug]]/page.tsx +0 -87
  161. package/docs-site/app/docs/layout.tsx +0 -12
  162. package/docs-site/app/globals.css +0 -24
  163. package/docs-site/app/layout.config.tsx +0 -34
  164. package/docs-site/app/layout.tsx +0 -119
  165. package/docs-site/app/llms-full.txt/route.ts +0 -11
  166. package/docs-site/app/llms.mdx/docs/[[...slug]]/route.ts +0 -24
  167. package/docs-site/app/llms.txt/route.ts +0 -8
  168. package/docs-site/app/page.tsx +0 -5
  169. package/docs-site/app/robots.ts +0 -13
  170. package/docs-site/app/sitemap.ts +0 -37
  171. package/docs-site/components/api-page.client.tsx +0 -4
  172. package/docs-site/components/api-page.tsx +0 -7
  173. package/docs-site/components/mdx/mermaid.tsx +0 -55
  174. package/docs-site/content/docs/(documentation)/architecture/agents.mdx +0 -117
  175. package/docs-site/content/docs/(documentation)/architecture/hooks.mdx +0 -77
  176. package/docs-site/content/docs/(documentation)/architecture/memory.mdx +0 -96
  177. package/docs-site/content/docs/(documentation)/architecture/meta.json +0 -4
  178. package/docs-site/content/docs/(documentation)/architecture/overview.mdx +0 -172
  179. package/docs-site/content/docs/(documentation)/concepts/epics.mdx +0 -98
  180. package/docs-site/content/docs/(documentation)/concepts/meta.json +0 -4
  181. package/docs-site/content/docs/(documentation)/concepts/scheduling.mdx +0 -136
  182. package/docs-site/content/docs/(documentation)/concepts/services.mdx +0 -104
  183. package/docs-site/content/docs/(documentation)/concepts/task-lifecycle.mdx +0 -148
  184. package/docs-site/content/docs/(documentation)/concepts/workflows.mdx +0 -209
  185. package/docs-site/content/docs/(documentation)/contributing.mdx +0 -158
  186. package/docs-site/content/docs/(documentation)/getting-started.mdx +0 -157
  187. package/docs-site/content/docs/(documentation)/guides/agentmail-integration.mdx +0 -79
  188. package/docs-site/content/docs/(documentation)/guides/deployment.mdx +0 -171
  189. package/docs-site/content/docs/(documentation)/guides/github-integration.mdx +0 -81
  190. package/docs-site/content/docs/(documentation)/guides/gitlab-integration.mdx +0 -93
  191. package/docs-site/content/docs/(documentation)/guides/linear-integration.mdx +0 -98
  192. package/docs-site/content/docs/(documentation)/guides/meta.json +0 -13
  193. package/docs-site/content/docs/(documentation)/guides/sentry-integration.mdx +0 -52
  194. package/docs-site/content/docs/(documentation)/guides/slack-integration.mdx +0 -179
  195. package/docs-site/content/docs/(documentation)/guides/x402-payments.mdx +0 -154
  196. package/docs-site/content/docs/(documentation)/index.mdx +0 -65
  197. package/docs-site/content/docs/(documentation)/meta.json +0 -19
  198. package/docs-site/content/docs/(documentation)/reference/cli.mdx +0 -241
  199. package/docs-site/content/docs/(documentation)/reference/environment-variables.mdx +0 -205
  200. package/docs-site/content/docs/(documentation)/reference/mcp-tools.mdx +0 -449
  201. package/docs-site/content/docs/(documentation)/reference/meta.json +0 -4
  202. package/docs-site/content/docs/api-reference/active-sessions.mdx +0 -9
  203. package/docs-site/content/docs/api-reference/agents.mdx +0 -9
  204. package/docs-site/content/docs/api-reference/channels.mdx +0 -9
  205. package/docs-site/content/docs/api-reference/config.mdx +0 -9
  206. package/docs-site/content/docs/api-reference/debug.mdx +0 -9
  207. package/docs-site/content/docs/api-reference/ecosystem.mdx +0 -9
  208. package/docs-site/content/docs/api-reference/epics.mdx +0 -9
  209. package/docs-site/content/docs/api-reference/index.mdx +0 -32
  210. package/docs-site/content/docs/api-reference/memory.mdx +0 -9
  211. package/docs-site/content/docs/api-reference/meta.json +0 -25
  212. package/docs-site/content/docs/api-reference/poll.mdx +0 -9
  213. package/docs-site/content/docs/api-reference/repos.mdx +0 -9
  214. package/docs-site/content/docs/api-reference/schedules.mdx +0 -9
  215. package/docs-site/content/docs/api-reference/session-data.mdx +0 -9
  216. package/docs-site/content/docs/api-reference/stats.mdx +0 -9
  217. package/docs-site/content/docs/api-reference/tasks.mdx +0 -9
  218. package/docs-site/content/docs/api-reference/trackers.mdx +0 -9
  219. package/docs-site/content/docs/api-reference/webhooks.mdx +0 -9
  220. package/docs-site/content/docs/api-reference/workflows.mdx +0 -9
  221. package/docs-site/content/docs/meta.json +0 -3
  222. package/docs-site/lib/get-llm-text.ts +0 -10
  223. package/docs-site/lib/openapi.ts +0 -23
  224. package/docs-site/lib/source.ts +0 -8
  225. package/docs-site/mdx-components.tsx +0 -13
  226. package/docs-site/next.config.mjs +0 -29
  227. package/docs-site/package.json +0 -35
  228. package/docs-site/pnpm-lock.yaml +0 -5407
  229. package/docs-site/postcss.config.mjs +0 -8
  230. package/docs-site/public/logo.png +0 -0
  231. package/docs-site/scripts/generate-docs.ts +0 -171
  232. package/docs-site/source.config.ts +0 -17
  233. package/docs-site/tsconfig.json +0 -46
  234. package/ecosystem.config.cjs +0 -66
  235. package/landing/next.config.ts +0 -14
  236. package/landing/package.json +0 -31
  237. package/landing/pnpm-lock.yaml +0 -1091
  238. package/landing/postcss.config.mjs +0 -8
  239. package/landing/public/apple-touch-icon.png +0 -0
  240. package/landing/public/favicon.ico +0 -0
  241. package/landing/public/logo.png +0 -0
  242. package/landing/public/og-image.png +0 -0
  243. package/landing/public/omghost-desplega.svg +0 -30
  244. package/landing/public/omghost-openfort.svg +0 -9
  245. package/landing/src/app/actions/waitlist.ts +0 -25
  246. package/landing/src/app/blog/openfort-hackathon/page.tsx +0 -863
  247. package/landing/src/app/blog/page.tsx +0 -162
  248. package/landing/src/app/blog/swarm-metrics/page.tsx +0 -685
  249. package/landing/src/app/examples/page.tsx +0 -174
  250. package/landing/src/app/examples/x402/page.tsx +0 -456
  251. package/landing/src/app/globals.css +0 -122
  252. package/landing/src/app/layout.tsx +0 -134
  253. package/landing/src/app/page.tsx +0 -27
  254. package/landing/src/app/robots.ts +0 -13
  255. package/landing/src/app/sitemap.ts +0 -44
  256. package/landing/src/components/architecture.tsx +0 -163
  257. package/landing/src/components/cta.tsx +0 -52
  258. package/landing/src/components/features.tsx +0 -160
  259. package/landing/src/components/footer.tsx +0 -100
  260. package/landing/src/components/hero.tsx +0 -217
  261. package/landing/src/components/how-it-works.tsx +0 -165
  262. package/landing/src/components/navbar.tsx +0 -147
  263. package/landing/src/components/waitlist.tsx +0 -110
  264. package/landing/src/components/why-choose.tsx +0 -149
  265. package/landing/src/components/workshops.tsx +0 -328
  266. package/landing/src/lib/utils.ts +0 -6
  267. package/landing/tsconfig.json +0 -41
  268. package/misc/transcripts/2026-03-09-pi-mono-e2e-verification.md +0 -154
  269. package/new-ui/CLAUDE.md +0 -92
  270. package/new-ui/README.md +0 -73
  271. package/new-ui/biome.json +0 -42
  272. package/new-ui/components.json +0 -21
  273. package/new-ui/index.html +0 -25
  274. package/new-ui/package.json +0 -49
  275. package/new-ui/pnpm-lock.yaml +0 -4845
  276. package/new-ui/public/logo.png +0 -0
  277. package/new-ui/src/api/client.ts +0 -814
  278. package/new-ui/src/api/hooks/index.ts +0 -64
  279. package/new-ui/src/api/hooks/use-agents.ts +0 -58
  280. package/new-ui/src/api/hooks/use-channels.ts +0 -115
  281. package/new-ui/src/api/hooks/use-config-api.ts +0 -46
  282. package/new-ui/src/api/hooks/use-costs.ts +0 -122
  283. package/new-ui/src/api/hooks/use-db-query.ts +0 -29
  284. package/new-ui/src/api/hooks/use-epics.ts +0 -75
  285. package/new-ui/src/api/hooks/use-repos.ts +0 -61
  286. package/new-ui/src/api/hooks/use-schedules.ts +0 -81
  287. package/new-ui/src/api/hooks/use-services.ts +0 -16
  288. package/new-ui/src/api/hooks/use-stats.ts +0 -27
  289. package/new-ui/src/api/hooks/use-tasks.ts +0 -89
  290. package/new-ui/src/api/hooks/use-workflows.ts +0 -109
  291. package/new-ui/src/api/types.ts +0 -549
  292. package/new-ui/src/app/App.tsx +0 -13
  293. package/new-ui/src/app/providers.tsx +0 -32
  294. package/new-ui/src/app/router.tsx +0 -52
  295. package/new-ui/src/components/layout/app-header.tsx +0 -47
  296. package/new-ui/src/components/layout/app-sidebar.tsx +0 -128
  297. package/new-ui/src/components/layout/breadcrumbs.tsx +0 -57
  298. package/new-ui/src/components/layout/config-guard.tsx +0 -22
  299. package/new-ui/src/components/layout/root-layout.tsx +0 -40
  300. package/new-ui/src/components/layout/swarm-switcher.tsx +0 -85
  301. package/new-ui/src/components/shared/command-menu.tsx +0 -131
  302. package/new-ui/src/components/shared/data-grid.tsx +0 -141
  303. package/new-ui/src/components/shared/empty-state.tsx +0 -24
  304. package/new-ui/src/components/shared/error-boundary.tsx +0 -72
  305. package/new-ui/src/components/shared/json-viewer.tsx +0 -47
  306. package/new-ui/src/components/shared/name-connection-modal.tsx +0 -99
  307. package/new-ui/src/components/shared/page-skeleton.tsx +0 -16
  308. package/new-ui/src/components/shared/session-log-viewer.tsx +0 -364
  309. package/new-ui/src/components/shared/stats-bar.tsx +0 -132
  310. package/new-ui/src/components/shared/status-badge.tsx +0 -131
  311. package/new-ui/src/components/shared/usage-summary.tsx +0 -179
  312. package/new-ui/src/components/ui/alert-dialog.tsx +0 -176
  313. package/new-ui/src/components/ui/alert.tsx +0 -60
  314. package/new-ui/src/components/ui/avatar.tsx +0 -96
  315. package/new-ui/src/components/ui/badge.tsx +0 -46
  316. package/new-ui/src/components/ui/button.tsx +0 -62
  317. package/new-ui/src/components/ui/card.tsx +0 -75
  318. package/new-ui/src/components/ui/command.tsx +0 -160
  319. package/new-ui/src/components/ui/dialog.tsx +0 -143
  320. package/new-ui/src/components/ui/dropdown-menu.tsx +0 -226
  321. package/new-ui/src/components/ui/input.tsx +0 -21
  322. package/new-ui/src/components/ui/label.tsx +0 -19
  323. package/new-ui/src/components/ui/progress.tsx +0 -26
  324. package/new-ui/src/components/ui/scroll-area.tsx +0 -54
  325. package/new-ui/src/components/ui/select.tsx +0 -175
  326. package/new-ui/src/components/ui/separator.tsx +0 -28
  327. package/new-ui/src/components/ui/sheet.tsx +0 -132
  328. package/new-ui/src/components/ui/sidebar.tsx +0 -691
  329. package/new-ui/src/components/ui/skeleton.tsx +0 -13
  330. package/new-ui/src/components/ui/sonner.tsx +0 -35
  331. package/new-ui/src/components/ui/switch.tsx +0 -33
  332. package/new-ui/src/components/ui/table.tsx +0 -92
  333. package/new-ui/src/components/ui/tabs.tsx +0 -79
  334. package/new-ui/src/components/ui/textarea.tsx +0 -18
  335. package/new-ui/src/components/ui/tooltip.tsx +0 -51
  336. package/new-ui/src/components/workflows/action-node.tsx +0 -53
  337. package/new-ui/src/components/workflows/condition-node.tsx +0 -50
  338. package/new-ui/src/components/workflows/graph-utils.ts +0 -124
  339. package/new-ui/src/components/workflows/json-tree.tsx +0 -189
  340. package/new-ui/src/components/workflows/node-styles.ts +0 -10
  341. package/new-ui/src/components/workflows/step-detail-sheet.tsx +0 -87
  342. package/new-ui/src/components/workflows/trigger-node.tsx +0 -41
  343. package/new-ui/src/components/workflows/workflow-graph.tsx +0 -65
  344. package/new-ui/src/hooks/use-auto-scroll.ts +0 -82
  345. package/new-ui/src/hooks/use-config.ts +0 -203
  346. package/new-ui/src/hooks/use-keyboard-shortcuts.ts +0 -41
  347. package/new-ui/src/hooks/use-mobile.ts +0 -19
  348. package/new-ui/src/hooks/use-theme.ts +0 -60
  349. package/new-ui/src/lib/config.ts +0 -188
  350. package/new-ui/src/lib/slugs.ts +0 -71
  351. package/new-ui/src/lib/utils.ts +0 -120
  352. package/new-ui/src/main.tsx +0 -11
  353. package/new-ui/src/pages/agents/[id]/page.tsx +0 -492
  354. package/new-ui/src/pages/agents/page.tsx +0 -134
  355. package/new-ui/src/pages/chat/page.tsx +0 -674
  356. package/new-ui/src/pages/config/page.tsx +0 -1109
  357. package/new-ui/src/pages/dashboard/page.tsx +0 -454
  358. package/new-ui/src/pages/debug/page.tsx +0 -275
  359. package/new-ui/src/pages/epics/[id]/page.tsx +0 -809
  360. package/new-ui/src/pages/epics/page.tsx +0 -321
  361. package/new-ui/src/pages/not-found/page.tsx +0 -18
  362. package/new-ui/src/pages/repos/page.tsx +0 -369
  363. package/new-ui/src/pages/schedules/[id]/page.tsx +0 -664
  364. package/new-ui/src/pages/schedules/page.tsx +0 -477
  365. package/new-ui/src/pages/services/page.tsx +0 -128
  366. package/new-ui/src/pages/tasks/[id]/page.tsx +0 -670
  367. package/new-ui/src/pages/tasks/page.tsx +0 -592
  368. package/new-ui/src/pages/usage/page.tsx +0 -195
  369. package/new-ui/src/pages/workflow-runs/[id]/page.tsx +0 -363
  370. package/new-ui/src/pages/workflows/[id]/page.tsx +0 -417
  371. package/new-ui/src/pages/workflows/page.tsx +0 -266
  372. package/new-ui/src/styles/ag-grid.css +0 -36
  373. package/new-ui/src/styles/globals.css +0 -213
  374. package/new-ui/test-results/.last-run.json +0 -4
  375. package/new-ui/tsconfig.app.json +0 -34
  376. package/new-ui/tsconfig.json +0 -4
  377. package/new-ui/tsconfig.node.json +0 -26
  378. package/new-ui/vercel.json +0 -4
  379. package/new-ui/vite.config.ts +0 -28
  380. package/plugin/README.md +0 -1
  381. package/plugin/build-pi-skills.ts +0 -233
  382. package/plugin/hooks/hooks.json +0 -71
  383. package/prek.toml +0 -75
  384. package/pyproject.toml +0 -9
  385. package/scripts/check-db-boundary.sh +0 -60
  386. package/scripts/e2e-docker-provider.ts +0 -820
  387. package/scripts/e2e-io-schemas-test.ts +0 -807
  388. package/scripts/e2e-provider-test.ts +0 -220
  389. package/scripts/e2e-workflow-redesign.sh +0 -229
  390. package/scripts/e2e-workflow-test.sh +0 -285
  391. package/scripts/e2e-workflow-test.ts +0 -857
  392. package/scripts/generate-mcp-docs.ts +0 -415
  393. package/scripts/generate-openapi.ts +0 -26
  394. package/scripts/measure-tool-tokens.ts +0 -118
  395. package/scripts/x402-e2e-test.ts +0 -195
  396. package/scripts/x402-test-server.ts +0 -236
  397. package/scripts/x402-testnet-e2e.ts +0 -668
  398. package/slack-manifest.json +0 -88
  399. package/templates-ui/README.md +0 -46
  400. package/templates-ui/components.json +0 -17
  401. package/templates-ui/eslint.config.mjs +0 -18
  402. package/templates-ui/next.config.ts +0 -7
  403. package/templates-ui/package.json +0 -35
  404. package/templates-ui/pnpm-lock.yaml +0 -4571
  405. package/templates-ui/postcss.config.mjs +0 -7
  406. package/templates-ui/public/file.svg +0 -1
  407. package/templates-ui/public/globe.svg +0 -1
  408. package/templates-ui/public/logo.png +0 -0
  409. package/templates-ui/public/next.svg +0 -1
  410. package/templates-ui/public/vercel.svg +0 -1
  411. package/templates-ui/public/window.svg +0 -1
  412. package/templates-ui/src/app/[category]/[name]/page.tsx +0 -89
  413. package/templates-ui/src/app/api/templates/[...slug]/route.ts +0 -52
  414. package/templates-ui/src/app/api/templates/route.ts +0 -18
  415. package/templates-ui/src/app/builder/page.tsx +0 -37
  416. package/templates-ui/src/app/globals.css +0 -94
  417. package/templates-ui/src/app/layout.tsx +0 -79
  418. package/templates-ui/src/app/page.tsx +0 -38
  419. package/templates-ui/src/app/robots.ts +0 -11
  420. package/templates-ui/src/app/sitemap.ts +0 -31
  421. package/templates-ui/src/components/compose-builder.tsx +0 -442
  422. package/templates-ui/src/components/compose-preview.tsx +0 -117
  423. package/templates-ui/src/components/file-preview.tsx +0 -77
  424. package/templates-ui/src/components/footer.tsx +0 -40
  425. package/templates-ui/src/components/header.tsx +0 -41
  426. package/templates-ui/src/components/template-card.tsx +0 -87
  427. package/templates-ui/src/components/template-detail.tsx +0 -125
  428. package/templates-ui/src/components/template-gallery.tsx +0 -263
  429. package/templates-ui/src/components/ui/badge.tsx +0 -36
  430. package/templates-ui/src/components/ui/button.tsx +0 -57
  431. package/templates-ui/src/components/ui/card.tsx +0 -76
  432. package/templates-ui/src/components/ui/separator.tsx +0 -31
  433. package/templates-ui/src/components/ui/tooltip.tsx +0 -32
  434. package/templates-ui/src/lib/compose-generator.ts +0 -241
  435. package/templates-ui/src/lib/templates.ts +0 -137
  436. package/templates-ui/src/lib/utils.ts +0 -6
  437. package/templates-ui/tsconfig.json +0 -34
  438. package/thoughts/research/2026-02-28-openfort-viem-x402-research.md +0 -679
  439. package/thoughts/research/2026-02-28-x402-payments-research.md +0 -686
  440. package/thoughts/researcher/plans/2026-02-20-agent-self-improvement-plan.md +0 -282
  441. package/thoughts/researcher/research/2026-02-20-agent-self-improvement.md +0 -492
  442. package/thoughts/shared/plans/.gitkeep +0 -0
  443. package/thoughts/shared/plans/2025-12-18-slack-integration.md +0 -1195
  444. package/thoughts/shared/plans/2025-12-19-agent-log-streaming.md +0 -732
  445. package/thoughts/shared/plans/2025-12-19-role-based-swarm-plugin.md +0 -361
  446. package/thoughts/shared/plans/2025-12-20-mobile-responsive-ui.md +0 -501
  447. package/thoughts/shared/plans/2025-12-20-startup-team-swarm.md +0 -560
  448. package/thoughts/shared/plans/2025-12-23-runner-level-polling.md +0 -934
  449. package/thoughts/shared/plans/2025-12-23-runner-session-logs.md +0 -1000
  450. package/thoughts/shared/plans/2025-12-23-worker-lead-spawn-triggers.md +0 -568
  451. package/thoughts/shared/plans/2026-01-09-inverse-teleport.md +0 -1516
  452. package/thoughts/shared/plans/2026-01-12-agent-rename-pm2-control.md +0 -1133
  453. package/thoughts/shared/plans/2026-01-12-github-app-integration.md +0 -380
  454. package/thoughts/shared/plans/2026-01-12-lead-inbox-model.md +0 -876
  455. package/thoughts/shared/plans/2026-01-12-ralph-wiggum-integration.md +0 -463
  456. package/thoughts/shared/plans/2026-01-13-agent-concurrency.md +0 -691
  457. package/thoughts/shared/plans/2026-01-13-github-assignment-handling.md +0 -690
  458. package/thoughts/shared/plans/2026-01-13-prevent-duplicate-trigger-processing.md +0 -1071
  459. package/thoughts/shared/plans/2026-01-14-fix-slack-thread-context.md +0 -507
  460. package/thoughts/shared/plans/2026-01-15-scheduled-tasks-implementation.md +0 -565
  461. package/thoughts/shared/plans/2026-01-15-usage-cost-tracking-ui.md +0 -1479
  462. package/thoughts/shared/plans/2026-01-16-epics-feature-implementation.md +0 -1230
  463. package/thoughts/shared/plans/2026-02-26-mcp-tool-context-reduction.md +0 -282
  464. package/thoughts/shared/plans/2026-03-02-claude-context-mode-integration.md +0 -328
  465. package/thoughts/shared/plans/2026-03-02-code-level-heartbeat.md +0 -224
  466. package/thoughts/shared/research/.gitkeep +0 -0
  467. package/thoughts/shared/research/2025-01-09-inverse-teleport-plan-review.md +0 -420
  468. package/thoughts/shared/research/2025-12-18-slack-integration.md +0 -442
  469. package/thoughts/shared/research/2025-12-19-agent-log-streaming.md +0 -339
  470. package/thoughts/shared/research/2025-12-19-agent-secrets-cli-research.md +0 -390
  471. package/thoughts/shared/research/2025-12-21-gemini-cli-integration.md +0 -376
  472. package/thoughts/shared/research/2025-12-22-runner-loop-architecture.md +0 -582
  473. package/thoughts/shared/research/2025-12-22-setup-experience-improvements.md +0 -264
  474. package/thoughts/shared/research/2026-01-13-lead-duplicate-trigger-processing.md +0 -223
  475. package/thoughts/shared/research/2026-01-14-lead-slack-thread-context.md +0 -277
  476. package/thoughts/shared/research/2026-01-15-ai-tracker-agent-swarm-integration.md +0 -376
  477. package/thoughts/shared/research/2026-01-15-auto-starting-processes-in-worker-containers.md +0 -787
  478. package/thoughts/shared/research/2026-01-15-scheduled-tasks.md +0 -390
  479. package/thoughts/shared/research/2026-01-16-epics-feature-research.md +0 -437
  480. package/thoughts/shared/research/2026-02-26-cliffy-mcp-tools.md +0 -159
  481. package/thoughts/shared/research/2026-03-03-database-migration-system-refactor.md +0 -337
  482. package/thoughts/swarm-researcher/plans/2026-02-23-openclaw-improvements-plan.md +0 -778
  483. package/thoughts/swarm-researcher/plans/2026-02-26-artifacts-localtunnel-plan.md +0 -1269
  484. package/thoughts/swarm-researcher/research/2026-02-23-openclaw-vs-agent-swarm-comparison.md +0 -411
  485. package/thoughts/swarm-researcher/research/2026-02-26-artifacts-localtunnel.md +0 -724
  486. package/thoughts/taras/brainstorms/2026-03-20-prompt-template-registry.md +0 -443
  487. package/thoughts/taras/brainstorms/2026-03-20-setup-cli-onboarding.md +0 -307
  488. package/thoughts/taras/plans/2026-01-22-agent-swarm-schemas.md +0 -98
  489. package/thoughts/taras/plans/2026-01-28-per-worker-claude-md.md +0 -617
  490. package/thoughts/taras/plans/2026-01-28-sentry-cli-integration.md +0 -214
  491. package/thoughts/taras/plans/2026-02-20-auto-improvement.md +0 -803
  492. package/thoughts/taras/plans/2026-02-20-env-management.md +0 -538
  493. package/thoughts/taras/plans/2026-02-20-memory-system.md +0 -882
  494. package/thoughts/taras/plans/2026-02-20-repos-knowledge.md +0 -806
  495. package/thoughts/taras/plans/2026-02-20-session-attach.md +0 -647
  496. package/thoughts/taras/plans/2026-02-20-worker-identity.md +0 -820
  497. package/thoughts/taras/plans/2026-02-25-feat-new-ui-visual-redesign-plan.md +0 -768
  498. package/thoughts/taras/plans/2026-03-04-fix-buildSystemPrompt-missing-fields.md +0 -77
  499. package/thoughts/taras/plans/2026-03-04-new-ui-missing-actions.md +0 -543
  500. package/thoughts/taras/plans/2026-03-06-one-time-scheduled-tasks.md +0 -373
  501. package/thoughts/taras/plans/2026-03-08-memory-self-improvement-enhancements.md +0 -512
  502. package/thoughts/taras/plans/2026-03-08-pi-mono-provider-implementation.md +0 -919
  503. package/thoughts/taras/plans/2026-03-09-templates-registry.md +0 -723
  504. package/thoughts/taras/plans/2026-03-10-task-working-directory.md +0 -371
  505. package/thoughts/taras/plans/2026-03-11-archil-per-agent-write-strategy.md +0 -621
  506. package/thoughts/taras/plans/2026-03-12-eliminate-inbox-route-to-tasks.md +0 -61
  507. package/thoughts/taras/plans/2026-03-12-slack-thread-followup-additive.md +0 -488
  508. package/thoughts/taras/plans/2026-03-13-slack-ai-improvements.md +0 -644
  509. package/thoughts/taras/plans/2026-03-16-route-wrapper-openapi.md +0 -636
  510. package/thoughts/taras/plans/2026-03-17-multi-api-config.md +0 -444
  511. package/thoughts/taras/plans/2026-03-18-agent-fs-integration.md +0 -591
  512. package/thoughts/taras/plans/2026-03-18-debug-db-explorer.md +0 -446
  513. package/thoughts/taras/plans/2026-03-18-workflow-redesign.md +0 -987
  514. package/thoughts/taras/plans/2026-03-19-compound-learnings.md +0 -403
  515. package/thoughts/taras/plans/2026-03-19-ticket-tracker-linear-integration.md +0 -860
  516. package/thoughts/taras/plans/2026-03-19-workflow-io-schemas-and-bugs.md +0 -899
  517. package/thoughts/taras/plans/2026-03-20-setup-cli-onboarding.md +0 -874
  518. package/thoughts/taras/plans/2026-03-20-workflow-structured-output-validation-workspace.md +0 -723
  519. package/thoughts/taras/research/2026-01-22-vercel-cli-integration.md +0 -287
  520. package/thoughts/taras/research/2026-01-27-excessive-polling-issue.md +0 -311
  521. package/thoughts/taras/research/2026-01-28-per-worker-claude-md.md +0 -383
  522. package/thoughts/taras/research/2026-01-28-sentry-cli-integration.md +0 -240
  523. package/thoughts/taras/research/2026-02-19-agent-native-swarm-architecture.md +0 -390
  524. package/thoughts/taras/research/2026-02-19-swarm-gaps-implementation.md +0 -594
  525. package/thoughts/taras/research/2026-02-25-dashboard-ui-design-best-practices.md +0 -825
  526. package/thoughts/taras/research/2026-02-26-task-detail-page-redesign.md +0 -393
  527. package/thoughts/taras/research/2026-03-03-new-ui-missing-actions.md +0 -168
  528. package/thoughts/taras/research/2026-03-05-pi-mono-provider-research.md +0 -230
  529. package/thoughts/taras/research/2026-03-06-workflow-engine-design.md +0 -445
  530. package/thoughts/taras/research/2026-03-08-drive-loop-concept.md +0 -375
  531. package/thoughts/taras/research/2026-03-08-pi-mono-deep-dive.md +0 -869
  532. package/thoughts/taras/research/2026-03-09-templates-registry.md +0 -373
  533. package/thoughts/taras/research/2026-03-10-agent-working-directory.md +0 -223
  534. package/thoughts/taras/research/2026-03-10-configurable-event-prompts.md +0 -339
  535. package/thoughts/taras/research/2026-03-11-archil-production-setup.md +0 -181
  536. package/thoughts/taras/research/2026-03-11-archil-shared-disk-write-strategies.md +0 -437
  537. package/thoughts/taras/research/2026-03-13-slack-ai-features.md +0 -258
  538. package/thoughts/taras/research/2026-03-16-openapi-docs-generation.md +0 -335
  539. package/thoughts/taras/research/2026-03-16-route-wrapper-openapi.md +0 -670
  540. package/thoughts/taras/research/2026-03-16-slack-thread-followups-e2e.md +0 -54
  541. package/thoughts/taras/research/2026-03-18-agent-fs-integration.md +0 -558
  542. package/thoughts/taras/research/2026-03-18-linear-integration-finalization.md +0 -526
  543. package/thoughts/taras/research/2026-03-18-workflow-redesign.md +0 -797
  544. package/thoughts/taras/research/2026-03-19-workflow-node-io-schemas-and-bugs.md +0 -563
  545. package/thoughts/taras/research/2026-03-19-workflow-structured-output-validation-workspace.md +0 -486
  546. package/thoughts/taras/research/2026-03-20-prompt-template-registry.md +0 -469
  547. package/tsconfig.json +0 -37
@@ -1,778 +0,0 @@
1
- ---
2
- date: 2026-02-23
3
- author: Researcher (16990304-76e4-4017-b991-f3e37b34cf73)
4
- topic: "Implementation Plan: P1 + P2 Openclaw Improvements"
5
- status: ready
6
- related_research: thoughts/swarm-researcher/research/2026-02-23-openclaw-vs-agent-swarm-comparison.md
7
- ---
8
-
9
- # Implementation Plan: P1 + P2 Openclaw Improvements
10
-
11
- ## Overview
12
-
13
- This plan covers all 6 improvements from the openclaw comparison research (3 P1, 3 P2). Items are ordered by implementation dependency and difficulty. Each item includes exact file paths, code insertion points, pseudocode, and testing strategy.
14
-
15
- **Implementation order**: 2 → 6 → 3 → 4 → 1 → 5
16
-
17
- Rationale: Start with the simplest items that have no dependencies, then build up to more complex features.
18
-
19
- ---
20
-
21
- ## Item 2 (P1): Active Session Heartbeat — Wire Existing Endpoint
22
-
23
- **Goal**: Call the already-existing `heartbeatActiveSession()` endpoint from the PostToolUse hook so that `cleanupStaleSessions()` can actually detect stale sessions.
24
-
25
- **Current state**: The endpoint `PUT /api/active-sessions/heartbeat/:taskId` and DB function `heartbeatActiveSession()` (db.ts:5567) exist but are never called. Sessions only get a heartbeat timestamp at creation time.
26
-
27
- ### Files to modify
28
-
29
- **`src/hooks/hook.ts`** — PostToolUse handler (line ~771-847)
30
-
31
- ### Implementation
32
-
33
- Add a heartbeat call inside the `PostToolUse` case, after the ping and agent info are available. The heartbeat should fire on every tool use, which naturally provides liveness data without extra overhead.
34
-
35
- ```typescript
36
- // Inside case "PostToolUse": (after line 771)
37
- // Before existing logic, fire session heartbeat
38
- if (agentInfo && !agentInfo.isLead) {
39
- const taskFileData = await readTaskFile();
40
- if (taskFileData?.taskId) {
41
- try {
42
- await fetch(`${getBaseUrl()}/api/active-sessions/heartbeat/${taskFileData.taskId}`, {
43
- method: "PUT",
44
- headers: mcpConfig!.headers,
45
- });
46
- } catch {
47
- // Non-blocking — heartbeat failure should not interrupt agent work
48
- }
49
- }
50
- }
51
- ```
52
-
53
- Insert this block at the very beginning of the `case "PostToolUse":` block (line 772, before the `if (agentInfo)` check). The heartbeat is fire-and-forget.
54
-
55
- ### Why at PostToolUse (not PreToolUse)
56
-
57
- PreToolUse already does cancellation checks which add latency. Adding heartbeat there would compound. PostToolUse runs after the tool completes, so the latency doesn't block the agent.
58
-
59
- ### Testing
60
-
61
- 1. Start a worker, assign a task
62
- 2. Run `SELECT lastHeartbeatAt FROM active_sessions WHERE taskId = ?` — verify it updates on each tool call
63
- 3. Kill a worker mid-task, wait 30+ minutes, run `POST /api/active-sessions/cleanup` — verify stale session is deleted
64
-
65
- ### Estimated changes: ~15 lines in hook.ts
66
-
67
- ---
68
-
69
- ## Item 6 (P2): Task Progress Deduplication
70
-
71
- **Goal**: Skip duplicate `store-progress` calls where progress text is identical to the last update within a time window.
72
-
73
- **Current state**: `updateTaskProgress()` (db.ts:1837) stores every progress update unconditionally and creates a `task_progress` log entry each time. Agents sometimes send repeated progress messages.
74
-
75
- ### Files to modify
76
-
77
- 1. **`src/be/db.ts`** — Migration + new column + helper
78
- 2. **`src/tools/store-progress.ts`** — Dedup check before calling updateTaskProgress
79
-
80
- ### Implementation
81
-
82
- #### Step 1: DB Migration (db.ts)
83
-
84
- Add a new migration in the migrations block (after the existing `ALTER TABLE` migrations, around line ~800):
85
-
86
- ```typescript
87
- // Migration: Add lastProgressText for deduplication
88
- try {
89
- database.run("ALTER TABLE agent_tasks ADD COLUMN lastProgressText TEXT");
90
- } catch {}
91
- try {
92
- database.run("ALTER TABLE agent_tasks ADD COLUMN lastProgressAt TEXT");
93
- } catch {}
94
- ```
95
-
96
- #### Step 2: Update `updateTaskProgress` (db.ts:1837)
97
-
98
- Modify the function to also update `lastProgressText` and `lastProgressAt`:
99
-
100
- ```typescript
101
- export function updateTaskProgress(id: string, progress: string): AgentTask | null {
102
- const row = getDb()
103
- .prepare<AgentTaskRow, [string, string, string]>(
104
- `UPDATE agent_tasks SET progress = ?, lastProgressText = ?, lastProgressAt = ?, lastUpdatedAt = ?
105
- WHERE id = ? RETURNING *`
106
- )
107
- .get(progress, progress, new Date().toISOString(), new Date().toISOString(), id);
108
- // ... rest unchanged
109
- }
110
- ```
111
-
112
- Wait — actually the current `taskQueries.setProgress()` prepared statement handles this. We need to modify that query. Let me look at the actual query.
113
-
114
- The simplest approach: modify `store-progress.ts` to check before calling `updateTaskProgress`.
115
-
116
- #### Step 2 (revised): Dedup in store-progress.ts (line ~103)
117
-
118
- Before calling `updateTaskProgress`, check if the progress text matches the existing task's progress:
119
-
120
- ```typescript
121
- // Inside the transaction, before line 103:
122
- if (progress) {
123
- // Dedup: skip if same progress text was set within the last 5 minutes
124
- const isDuplicate =
125
- existingTask.progress === progress &&
126
- existingTask.lastUpdatedAt &&
127
- Date.now() - new Date(existingTask.lastUpdatedAt).getTime() < 5 * 60 * 1000;
128
-
129
- if (!isDuplicate) {
130
- const result = updateTaskProgress(taskId, progress);
131
- if (result) updatedTask = result;
132
- }
133
- }
134
- ```
135
-
136
- This avoids any DB schema changes — we just compare against the existing `progress` field and `lastUpdatedAt` timestamp on the task.
137
-
138
- ### Testing
139
-
140
- 1. Call `store-progress` with progress "Working on X" twice in rapid succession
141
- 2. Verify only one `task_progress` log entry is created
142
- 3. Call again after 5+ minutes — verify it stores
143
- 4. Verify status changes (completed/failed) always go through regardless of dedup
144
-
145
- ### Estimated changes: ~10 lines in store-progress.ts (no DB migration needed)
146
-
147
- ---
148
-
149
- ## Item 3 (P1): Missed Schedule Recovery on Startup
150
-
151
- **Goal**: When the scheduler starts, check for past-due scheduled tasks and execute them immediately instead of waiting for the next scheduled run.
152
-
153
- **Current state**: `startScheduler()` (scheduler.ts:77) calls `processSchedules()` once on startup. `getDueScheduledTasks()` (db.ts:4242) already returns tasks where `nextRunAt <= now`, so past-due tasks ARE picked up on the first tick. However, if a task's `nextRunAt` is far in the past, `calculateNextRun()` will schedule the NEXT run from `now`, which is correct.
154
-
155
- Wait — re-reading the code, the current `processSchedules()` ALREADY handles this because `getDueScheduledTasks()` returns `WHERE enabled = 1 AND nextRunAt IS NOT NULL AND nextRunAt <= ?` with `?` = now. So any past-due tasks are already picked up on the first tick.
156
-
157
- **Actually needed**: The real gap is that `executeSchedule()` calls `calculateNextRun(schedule, new Date())` which calculates next run from NOW. For cron expressions, this works correctly. For intervals, this also works (next = now + interval). So the schedule recovery is already implicit.
158
-
159
- **Remaining gap**: What if the scheduler was down and a schedule was due MULTIPLE times? E.g., an hourly schedule was down for 3 hours. Currently only one execution happens. Openclaw runs all missed past-due executions.
160
-
161
- **Revised approach**: Add a `missedRunRecovery` option to `startScheduler()` that checks how many runs were missed and optionally catches up.
162
-
163
- ### Files to modify
164
-
165
- **`src/scheduler/scheduler.ts`** — `startScheduler()` and new `recoverMissedSchedules()` function
166
-
167
- ### Implementation
168
-
169
- Add a recovery function before the main poll loop:
170
-
171
- ```typescript
172
- /**
173
- * Recover missed scheduled task runs from downtime.
174
- * Only fires ONE catch-up run per schedule (not N missed runs).
175
- * Tags the task with "recovered" so it's distinguishable.
176
- */
177
- async function recoverMissedSchedules(): Promise<void> {
178
- const now = new Date();
179
- const dueSchedules = getDueScheduledTasks();
180
-
181
- for (const schedule of dueSchedules) {
182
- // Only recover if nextRunAt is significantly in the past (> 1 tick)
183
- if (!schedule.nextRunAt) continue;
184
- const missedBy = now.getTime() - new Date(schedule.nextRunAt).getTime();
185
- if (missedBy < 15000) continue; // Less than 15s — normal timing jitter
186
-
187
- console.log(
188
- `[Scheduler] Recovering missed schedule "${schedule.name}" ` +
189
- `(was due ${Math.round(missedBy / 1000)}s ago)`
190
- );
191
-
192
- try {
193
- // Use transaction for atomicity
194
- const tx = getDb().transaction(() => {
195
- createTaskExtended(schedule.taskTemplate, {
196
- creatorAgentId: schedule.createdByAgentId,
197
- taskType: schedule.taskType,
198
- tags: [...schedule.tags, "scheduled", `schedule:${schedule.name}`, "recovered"],
199
- priority: schedule.priority,
200
- agentId: schedule.targetAgentId,
201
- });
202
-
203
- // Update nextRunAt to the next FUTURE run
204
- const nextRun = calculateNextRun(schedule, now);
205
- updateScheduledTask(schedule.id, {
206
- lastRunAt: now.toISOString(),
207
- nextRunAt: nextRun,
208
- lastUpdatedAt: now.toISOString(),
209
- });
210
- });
211
- tx();
212
- } catch (err) {
213
- console.error(`[Scheduler] Error recovering "${schedule.name}":`, err);
214
- }
215
- }
216
- }
217
- ```
218
-
219
- Then call it in `startScheduler()` before the first `processSchedules()`:
220
-
221
- ```typescript
222
- export function startScheduler(intervalMs = 10000): void {
223
- if (schedulerInterval) {
224
- console.log("[Scheduler] Already running");
225
- return;
226
- }
227
-
228
- console.log(`[Scheduler] Starting with ${intervalMs}ms polling interval`);
229
-
230
- // Recover missed schedules from downtime (before normal processing)
231
- void recoverMissedSchedules();
232
-
233
- // Run immediately once, then start interval
234
- void processSchedules();
235
- // ...
236
- }
237
- ```
238
-
239
- **Note**: `recoverMissedSchedules()` and `processSchedules()` both call `getDueScheduledTasks()`, but `recoverMissedSchedules` updates `nextRunAt` for recovered schedules, so `processSchedules()` won't double-execute them.
240
-
241
- ### Testing
242
-
243
- 1. Create a schedule with `intervalMs: 60000` (1 minute)
244
- 2. Stop the scheduler, wait 5 minutes
245
- 3. Restart — verify exactly one "recovered" task is created
246
- 4. Verify the nextRunAt is set to a future time (not to the missed time)
247
-
248
- ### Estimated changes: ~40 lines in scheduler.ts
249
-
250
- ---
251
-
252
- ## Item 4 (P2): Exponential Backoff for Failed Scheduled Tasks
253
-
254
- **Goal**: When a scheduled task's execution fails repeatedly, apply exponential backoff to prevent flooding and auto-disable after too many consecutive failures.
255
-
256
- **Current state**: `executeSchedule()` (scheduler.ts:44) catches errors and logs them but takes no recovery action. The schedule fires again at the normal interval.
257
-
258
- ### Files to modify
259
-
260
- 1. **`src/be/db.ts`** — Migration for new columns + update helper
261
- 2. **`src/scheduler/scheduler.ts`** — Error tracking in `executeSchedule()`, backoff in `processSchedules()`
262
-
263
- ### Implementation
264
-
265
- #### Step 1: DB Migration (db.ts)
266
-
267
- Add after existing migrations:
268
-
269
- ```typescript
270
- // Migration: Add error tracking columns to scheduled_tasks
271
- try {
272
- database.run("ALTER TABLE scheduled_tasks ADD COLUMN consecutiveErrors INTEGER DEFAULT 0");
273
- } catch {}
274
- try {
275
- database.run("ALTER TABLE scheduled_tasks ADD COLUMN lastErrorAt TEXT");
276
- } catch {}
277
- try {
278
- database.run("ALTER TABLE scheduled_tasks ADD COLUMN lastErrorMessage TEXT");
279
- } catch {}
280
- ```
281
-
282
- #### Step 2: Update `updateScheduledTask` signature
283
-
284
- The existing `updateScheduledTask()` (db.ts:4155) already accepts a generic update object with dynamic keys. The new columns (`consecutiveErrors`, `lastErrorAt`, `lastErrorMessage`) need to be added to its field allowlist.
285
-
286
- In db.ts, find the `updateScheduledTask` function and add the new fields to the allowed fields list:
287
-
288
- ```typescript
289
- // In updateScheduledTask(), add to the allowed fields:
290
- const allowedFields = [
291
- // ... existing fields ...
292
- "consecutiveErrors",
293
- "lastErrorAt",
294
- "lastErrorMessage",
295
- ];
296
- ```
297
-
298
- Also update the `ScheduledTask` type in `types.ts` and the `rowToScheduledTask` mapper if it exists.
299
-
300
- #### Step 3: Backoff logic in scheduler.ts
301
-
302
- Define the backoff schedule:
303
-
304
- ```typescript
305
- const ERROR_BACKOFF_MS = [
306
- 60_000, // 1 minute
307
- 300_000, // 5 minutes
308
- 900_000, // 15 minutes
309
- 1_800_000, // 30 minutes
310
- 3_600_000, // 1 hour (cap)
311
- ];
312
-
313
- const MAX_CONSECUTIVE_ERRORS = 5; // Auto-disable after this many
314
-
315
- function getBackoffMs(consecutiveErrors: number): number {
316
- const idx = Math.min(consecutiveErrors - 1, ERROR_BACKOFF_MS.length - 1);
317
- return ERROR_BACKOFF_MS[Math.max(0, idx)];
318
- }
319
- ```
320
-
321
- #### Step 4: Modify `executeSchedule()` to track errors
322
-
323
- Wrap the existing `executeSchedule()` in error handling that updates the schedule on failure:
324
-
325
- ```typescript
326
- async function executeSchedule(schedule: ScheduledTask): Promise<void> {
327
- try {
328
- const tx = getDb().transaction(() => {
329
- const now = new Date().toISOString();
330
- createTaskExtended(schedule.taskTemplate, {
331
- creatorAgentId: schedule.createdByAgentId,
332
- taskType: schedule.taskType,
333
- tags: [...schedule.tags, "scheduled", `schedule:${schedule.name}`],
334
- priority: schedule.priority,
335
- agentId: schedule.targetAgentId,
336
- });
337
-
338
- const nextRun = calculateNextRun(schedule, new Date());
339
- updateScheduledTask(schedule.id, {
340
- lastRunAt: now,
341
- nextRunAt: nextRun,
342
- lastUpdatedAt: now,
343
- // Reset error tracking on success
344
- consecutiveErrors: 0,
345
- lastErrorAt: null,
346
- lastErrorMessage: null,
347
- });
348
-
349
- return nextRun;
350
- });
351
-
352
- const nextRun = tx();
353
- console.log(`[Scheduler] Executed schedule "${schedule.name}", next run: ${nextRun}`);
354
- } catch (err) {
355
- const errorCount = (schedule.consecutiveErrors ?? 0) + 1;
356
- const now = new Date();
357
- const errorMsg = err instanceof Error ? err.message : String(err);
358
-
359
- console.error(
360
- `[Scheduler] Error executing "${schedule.name}" (${errorCount} consecutive):`,
361
- errorMsg
362
- );
363
-
364
- const updates: Record<string, unknown> = {
365
- consecutiveErrors: errorCount,
366
- lastErrorAt: now.toISOString(),
367
- lastErrorMessage: errorMsg.slice(0, 500),
368
- lastUpdatedAt: now.toISOString(),
369
- };
370
-
371
- if (errorCount >= MAX_CONSECUTIVE_ERRORS) {
372
- // Auto-disable after too many consecutive failures
373
- updates.enabled = 0;
374
- console.warn(
375
- `[Scheduler] Auto-disabled "${schedule.name}" after ${errorCount} consecutive errors`
376
- );
377
- } else {
378
- // Apply backoff to nextRunAt
379
- const backoff = getBackoffMs(errorCount);
380
- updates.nextRunAt = new Date(now.getTime() + backoff).toISOString();
381
- console.log(
382
- `[Scheduler] Backing off "${schedule.name}" for ${backoff / 1000}s`
383
- );
384
- }
385
-
386
- updateScheduledTask(schedule.id, updates);
387
- }
388
- }
389
- ```
390
-
391
- ### Testing
392
-
393
- 1. Create a schedule with an invalid `taskTemplate` (or target a non-existent agent) to trigger failures
394
- 2. Observe `consecutiveErrors` incrementing and `nextRunAt` being pushed forward
395
- 3. After 5 failures, verify the schedule is auto-disabled (`enabled = 0`)
396
- 4. Fix the issue, re-enable the schedule, verify `consecutiveErrors` resets to 0 on success
397
-
398
- ### Estimated changes: ~60 lines in scheduler.ts, ~10 lines in db.ts
399
-
400
- ---
401
-
402
- ## Item 1 (P1): Tool Loop Detection
403
-
404
- **Goal**: Detect when an agent is stuck in a repetitive tool call loop and intervene by blocking the tool call with an explanatory message.
405
-
406
- **Current state**: No loop detection exists. Agents may repeat the same tool calls indefinitely (especially `Bash`, `Read`, or `Grep` with identical arguments) until context compaction or max-turns limits.
407
-
408
- ### Files to modify
409
-
410
- 1. **`src/hooks/tool-loop-detection.ts`** — New file with detection logic
411
- 2. **`src/hooks/hook.ts`** — PreToolUse handler integration
412
-
413
- ### Implementation
414
-
415
- #### Step 1: New file `src/hooks/tool-loop-detection.ts`
416
-
417
- ```typescript
418
- /**
419
- * Tool loop detection for agent-swarm hooks.
420
- *
421
- * Tracks recent tool calls per session and detects repetitive patterns.
422
- * Uses a file-based history to persist across hook invocations (hooks are
423
- * separate Bun processes, not long-running).
424
- */
425
-
426
- interface ToolCallRecord {
427
- toolName: string;
428
- argsHash: string;
429
- timestamp: number;
430
- }
431
-
432
- interface LoopDetectionResult {
433
- blocked: boolean;
434
- reason?: string;
435
- severity?: "warning" | "critical";
436
- }
437
-
438
- const HISTORY_DIR = "/tmp/agent-swarm-tool-history";
439
- const MAX_HISTORY = 30; // Sliding window size
440
- const REPEAT_WARNING_THRESHOLD = 8;
441
- const REPEAT_CRITICAL_THRESHOLD = 15;
442
- const PINGPONG_WARNING_THRESHOLD = 6;
443
- const PINGPONG_CRITICAL_THRESHOLD = 12;
444
-
445
- /**
446
- * Simple hash of tool arguments for comparison.
447
- * Uses JSON.stringify + a basic hash to avoid storing full args.
448
- */
449
- function hashArgs(args: Record<string, unknown>): string {
450
- const str = JSON.stringify(args, Object.keys(args).sort());
451
- let hash = 0;
452
- for (let i = 0; i < str.length; i++) {
453
- const char = str.charCodeAt(i);
454
- hash = ((hash << 5) - hash) + char;
455
- hash |= 0; // Convert to 32bit integer
456
- }
457
- return hash.toString(36);
458
- }
459
-
460
- /**
461
- * Load tool call history for a session.
462
- */
463
- async function loadHistory(sessionKey: string): Promise<ToolCallRecord[]> {
464
- try {
465
- const file = Bun.file(`${HISTORY_DIR}/${sessionKey}.json`);
466
- if (await file.exists()) {
467
- return await file.json();
468
- }
469
- } catch {}
470
- return [];
471
- }
472
-
473
- /**
474
- * Save tool call history for a session.
475
- */
476
- async function saveHistory(sessionKey: string, history: ToolCallRecord[]): Promise<void> {
477
- try {
478
- await Bun.$`mkdir -p ${HISTORY_DIR}`.quiet();
479
- await Bun.write(
480
- `${HISTORY_DIR}/${sessionKey}.json`,
481
- JSON.stringify(history.slice(-MAX_HISTORY))
482
- );
483
- } catch {}
484
- }
485
-
486
- /**
487
- * Detect if the current tool call is part of a repetitive loop.
488
- */
489
- export async function checkToolLoop(
490
- sessionKey: string,
491
- toolName: string,
492
- toolInput: Record<string, unknown>
493
- ): Promise<LoopDetectionResult> {
494
- const argsHash = hashArgs(toolInput);
495
- const history = await loadHistory(sessionKey);
496
-
497
- // Add current call to history
498
- history.push({ toolName, argsHash, timestamp: Date.now() });
499
- await saveHistory(sessionKey, history);
500
-
501
- // Only check if we have enough history
502
- if (history.length < REPEAT_WARNING_THRESHOLD) {
503
- return { blocked: false };
504
- }
505
-
506
- // Strategy 1: Same tool + same args repeated
507
- const key = `${toolName}:${argsHash}`;
508
- let repeatCount = 0;
509
- for (const record of history) {
510
- if (`${record.toolName}:${record.argsHash}` === key) {
511
- repeatCount++;
512
- }
513
- }
514
-
515
- if (repeatCount >= REPEAT_CRITICAL_THRESHOLD) {
516
- return {
517
- blocked: true,
518
- severity: "critical",
519
- reason: `Tool "${toolName}" has been called ${repeatCount} times with identical arguments in the last ${MAX_HISTORY} calls. You are stuck in a loop. Try a completely different approach.`,
520
- };
521
- }
522
-
523
- if (repeatCount >= REPEAT_WARNING_THRESHOLD) {
524
- return {
525
- blocked: false, // Warning only — don't block yet
526
- severity: "warning",
527
- reason: `Tool "${toolName}" has been called ${repeatCount} times with identical arguments. Consider trying a different approach.`,
528
- };
529
- }
530
-
531
- // Strategy 2: Ping-pong between two tool call patterns
532
- if (history.length >= PINGPONG_WARNING_THRESHOLD) {
533
- const recent = history.slice(-PINGPONG_CRITICAL_THRESHOLD);
534
- const patterns = new Map<string, number>();
535
- for (const r of recent) {
536
- const p = `${r.toolName}:${r.argsHash}`;
537
- patterns.set(p, (patterns.get(p) || 0) + 1);
538
- }
539
-
540
- // Check if exactly 2 patterns dominate
541
- const sorted = [...patterns.entries()].sort((a, b) => b[1] - a[1]);
542
- if (sorted.length >= 2) {
543
- const [first, second] = sorted;
544
- const dominance = first[1] + second[1];
545
- if (dominance >= recent.length * 0.8) {
546
- // Two patterns account for 80%+ of recent calls
547
- const totalPingPong = first[1] + second[1];
548
- if (totalPingPong >= PINGPONG_CRITICAL_THRESHOLD) {
549
- return {
550
- blocked: true,
551
- severity: "critical",
552
- reason: `Detected ping-pong loop: alternating between "${first[0].split(":")[0]}" and "${second[0].split(":")[0]}" for ${totalPingPong} calls. Break out of this pattern.`,
553
- };
554
- }
555
- if (totalPingPong >= PINGPONG_WARNING_THRESHOLD) {
556
- return {
557
- blocked: false,
558
- severity: "warning",
559
- reason: `Possible ping-pong pattern detected between two tool calls. Consider a different approach.`,
560
- };
561
- }
562
- }
563
- }
564
- }
565
-
566
- return { blocked: false };
567
- }
568
-
569
- /**
570
- * Clear tool call history for a session (call on session end).
571
- */
572
- export async function clearToolHistory(sessionKey: string): Promise<void> {
573
- try {
574
- await Bun.$`rm -f ${HISTORY_DIR}/${sessionKey}.json`.quiet();
575
- } catch {}
576
- }
577
- ```
578
-
579
- #### Step 2: Integrate in hook.ts PreToolUse handler
580
-
581
- In hook.ts, add import at top:
582
-
583
- ```typescript
584
- import { checkToolLoop, clearToolHistory } from "./tool-loop-detection";
585
- ```
586
-
587
- In the `PreToolUse` case (after cancellation check, around line 755), add:
588
-
589
- ```typescript
590
- // Tool loop detection (workers only, when processing a task)
591
- if (agentInfo && !agentInfo.isLead && agentInfo.status === "busy") {
592
- const taskFileData = await readTaskFile();
593
- if (taskFileData?.taskId && msg.tool_name && msg.tool_input) {
594
- const result = await checkToolLoop(
595
- taskFileData.taskId,
596
- msg.tool_name,
597
- msg.tool_input as Record<string, unknown>
598
- );
599
-
600
- if (result.blocked) {
601
- outputBlockResponse(
602
- `🔄 LOOP DETECTED: ${result.reason} ` +
603
- `Stop repeating this action and try a fundamentally different approach. ` +
604
- `If you're truly stuck, use store-progress to report the blocker.`
605
- );
606
- return;
607
- }
608
-
609
- if (result.severity === "warning" && result.reason) {
610
- // Output as a non-blocking warning (just console.log, not a block response)
611
- console.log(`⚠️ ${result.reason}`);
612
- }
613
- }
614
- }
615
- ```
616
-
617
- Also clear history on `SessionStart`:
618
-
619
- ```typescript
620
- // In case "SessionStart":, after existing logic:
621
- // Clear any stale tool loop history
622
- const startTaskFile = await readTaskFile();
623
- if (startTaskFile?.taskId) {
624
- await clearToolHistory(startTaskFile.taskId);
625
- }
626
- ```
627
-
628
- ### Design decisions
629
-
630
- - **File-based history**: Hooks run as separate Bun processes (not a persistent server). State must persist across invocations → file-based storage in `/tmp`.
631
- - **Session-scoped**: History is keyed by taskId, so each task has independent tracking.
632
- - **Warning before blocking**: At 8 repeats, warn. At 15, block. This gives the agent a chance to self-correct.
633
- - **Critical blocks**: Only the `outputBlockResponse` path actually stops the agent. Warnings are advisory.
634
-
635
- ### Testing
636
-
637
- 1. Create a test task that deliberately loops (e.g., a prompt that says "read the same file 20 times")
638
- 2. Observe warning at 8 repeats, block at 15
639
- 3. Verify ping-pong detection with alternating tool calls
640
- 4. Verify history is cleared on session start (no contamination between tasks)
641
-
642
- ### Estimated changes: ~160 lines in tool-loop-detection.ts, ~30 lines in hook.ts
643
-
644
- ---
645
-
646
- ## Item 5 (P2): Graceful Task Cancellation (Push-based)
647
-
648
- **Goal**: When a task is cancelled, proactively signal the Claude subprocess instead of waiting for the next hook check.
649
-
650
- **Current state**: Cancellation is detected via `isTaskCancelled()` in the `PreToolUse` and `UserPromptSubmit` hooks (hook.ts:748-768, 849-858). If the agent is in a long-running operation (subprocess, waiting, etc.), it won't notice until the next tool call.
651
-
652
- ### Files to modify
653
-
654
- 1. **`src/commands/runner.ts`** — Main loop: check for cancelled tasks and signal subprocess
655
- 2. **`src/http.ts`** — Add a `POST /api/tasks/:id/cancel` webhook/notification path (optional, for future)
656
-
657
- ### Implementation
658
-
659
- #### Approach: Runner-side polling for cancellations
660
-
661
- The runner's main loop already iterates every ~2s (PollIntervalMs). Add a cancellation check there.
662
-
663
- In `runner.ts`, inside the main `while(true)` loop (after `checkCompletedProcesses` at line 2019), add:
664
-
665
- ```typescript
666
- // Check for cancelled tasks and signal their subprocesses
667
- for (const [taskId, task] of state.activeTasks) {
668
- try {
669
- const resp = await fetch(
670
- `${apiUrl}/cancelled-tasks?taskId=${encodeURIComponent(taskId)}`,
671
- {
672
- headers: {
673
- Authorization: `Bearer ${apiKey}`,
674
- "X-Agent-ID": agentId,
675
- },
676
- }
677
- );
678
- if (resp.ok) {
679
- const data = await resp.json() as { cancelled: Array<{ id: string }> };
680
- if (data.cancelled?.some(t => t.id === taskId)) {
681
- console.log(`[${role}] Task ${taskId.slice(0, 8)} was cancelled — sending SIGTERM to subprocess`);
682
- task.process.kill("SIGTERM");
683
- // Don't remove from activeTasks yet — checkCompletedProcesses will handle cleanup
684
- }
685
- }
686
- } catch {
687
- // Non-blocking — cancellation check is best-effort
688
- }
689
- }
690
- ```
691
-
692
- This checks each active task against the cancellation endpoint. If cancelled, it sends SIGTERM to the Claude subprocess, which triggers Claude Code's graceful shutdown (Stop hook fires, task gets cleaned up).
693
-
694
- #### Optimization: Batch check
695
-
696
- To avoid N API calls (one per active task), we can batch by checking all at once. However, the current `/cancelled-tasks` endpoint only supports `?taskId=` for a single task. Two options:
697
-
698
- **Option A (Simple)**: Keep per-task checks. With maxConcurrent typically being 1-2, this is fine.
699
-
700
- **Option B (Better)**: Modify the `/cancelled-tasks` endpoint to support `?taskIds=id1,id2,id3`. But this requires HTTP endpoint changes.
701
-
702
- **Recommendation**: Go with Option A for simplicity. The overhead of 1-2 extra API calls per 2s is negligible.
703
-
704
- #### Important: Only check when tasks are running
705
-
706
- ```typescript
707
- // Only check if there are active tasks (avoid unnecessary API calls)
708
- if (state.activeTasks.size > 0) {
709
- for (const [taskId, task] of state.activeTasks) {
710
- // ... check cancellation
711
- }
712
- }
713
- ```
714
-
715
- ### Why SIGTERM (not SIGUSR1)
716
-
717
- Claude Code's `--sigterm-timeout` flag controls how long it waits after SIGTERM before force-exiting. The Stop hook fires on SIGTERM, which gives the agent a chance to clean up. SIGUSR1 is not handled by Claude Code.
718
-
719
- ### Testing
720
-
721
- 1. Start a worker with a long-running task
722
- 2. Cancel the task via `cancel-task` MCP tool
723
- 3. Observe: within ~2 seconds, the subprocess receives SIGTERM
724
- 4. Verify the task is marked as cancelled/failed (via `ensureTaskFinished`)
725
-
726
- ### Estimated changes: ~25 lines in runner.ts
727
-
728
- ---
729
-
730
- ## Implementation Order Summary
731
-
732
- | Order | Item | Effort | Dependencies |
733
- |-------|------|--------|-------------|
734
- | 1st | **Item 2**: Active Session Heartbeat | ~15 lines | None |
735
- | 2nd | **Item 6**: Progress Deduplication | ~10 lines | None |
736
- | 3rd | **Item 3**: Missed Schedule Recovery | ~40 lines | None |
737
- | 4th | **Item 4**: Schedule Error Backoff | ~70 lines | Builds on Item 3's scheduler changes |
738
- | 5th | **Item 1**: Tool Loop Detection | ~190 lines | None (but most complex) |
739
- | 6th | **Item 5**: Push Cancellation | ~25 lines | None (but requires careful testing) |
740
-
741
- **Total estimated new code**: ~350 lines across 5 files
742
-
743
- ---
744
-
745
- ## Rollout Strategy
746
-
747
- ### Phase 1: Low-risk improvements (Items 2, 6)
748
- - Deploy together. Zero risk — heartbeat is fire-and-forget, dedup is a simple optimization.
749
- - Verify heartbeat updates are flowing before proceeding.
750
-
751
- ### Phase 2: Scheduler improvements (Items 3, 4)
752
- - Deploy together since Item 4 builds on Item 3.
753
- - Test with a non-critical schedule first.
754
- - Monitor `consecutiveErrors` column after deploy.
755
-
756
- ### Phase 3: Complex features (Items 1, 5)
757
- - Deploy Item 1 (tool loop detection) first, monitor for false positives.
758
- - Start with higher thresholds (e.g., 20 for critical) and lower once confident.
759
- - Deploy Item 5 (push cancellation) last — test in staging by cancelling tasks manually.
760
-
761
- ### Monitoring
762
-
763
- After deployment, track:
764
- - Active session heartbeat frequency (should update every few seconds during active tasks)
765
- - Progress dedup hit rate (log when a duplicate is skipped)
766
- - Schedule recovery events (look for "recovered" tagged tasks)
767
- - Schedule error backoff events (look for "Auto-disabled" log lines)
768
- - Tool loop detections (warning/critical counts)
769
- - Cancellation signal delivery latency (time between cancel API call and SIGTERM)
770
-
771
- ---
772
-
773
- ## Open Questions for Reviewer
774
-
775
- 1. **Tool loop thresholds**: Are 8/15 (warning/critical) appropriate, or should we start more conservative (e.g., 12/20)?
776
- 2. **Schedule recovery**: Should we run ALL missed executions (like openclaw) or just ONE catch-up per schedule (current plan)?
777
- 3. **Auto-disable threshold**: Is 5 consecutive errors the right threshold for auto-disabling a schedule?
778
- 4. **Cancellation signal**: Should we use SIGTERM (triggers full Stop hook) or a lighter signal? SIGTERM means the agent gets a chance to save progress.