@swarmclawai/swarmclaw 0.8.4 → 0.8.7

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 (394) hide show
  1. package/README.md +9 -9
  2. package/bin/swarmclaw.js +5 -1
  3. package/bin/worker-cmd.js +73 -0
  4. package/package.json +2 -1
  5. package/src/app/api/agents/[id]/route.ts +17 -7
  6. package/src/app/api/agents/route.ts +21 -8
  7. package/src/app/api/approvals/route.test.ts +6 -6
  8. package/src/app/api/approvals/route.ts +2 -1
  9. package/src/app/api/auth/route.ts +2 -3
  10. package/src/app/api/chatrooms/[id]/chat/route.test.ts +299 -0
  11. package/src/app/api/chatrooms/[id]/chat/route.ts +3 -2
  12. package/src/app/api/chatrooms/[id]/route.ts +7 -6
  13. package/src/app/api/chats/[id]/chat/route.test.ts +496 -0
  14. package/src/app/api/chats/[id]/chat/route.ts +7 -3
  15. package/src/app/api/chats/[id]/clear/route.ts +9 -9
  16. package/src/app/api/chats/[id]/devserver/route.ts +2 -1
  17. package/src/app/api/chats/[id]/edit-resend/route.ts +3 -4
  18. package/src/app/api/chats/[id]/fork/route.ts +3 -5
  19. package/src/app/api/chats/[id]/restore/route.ts +6 -7
  20. package/src/app/api/chats/[id]/retry/route.ts +3 -4
  21. package/src/app/api/chats/[id]/route.ts +61 -62
  22. package/src/app/api/chats/route.ts +7 -1
  23. package/src/app/api/connectors/[id]/route.ts +7 -8
  24. package/src/app/api/connectors/route.ts +5 -4
  25. package/src/app/api/eval/run/route.ts +2 -1
  26. package/src/app/api/eval/suite/route.ts +2 -1
  27. package/src/app/api/external-agents/route.test.ts +1 -1
  28. package/src/app/api/external-agents/route.ts +2 -2
  29. package/src/app/api/files/serve/route.ts +1 -1
  30. package/src/app/api/gateways/[id]/route.ts +7 -5
  31. package/src/app/api/gateways/route.ts +1 -1
  32. package/src/app/api/knowledge/upload/route.ts +1 -1
  33. package/src/app/api/logs/route.ts +5 -7
  34. package/src/app/api/memory-images/[filename]/route.ts +2 -3
  35. package/src/app/api/openclaw/agent-files/route.ts +4 -3
  36. package/src/app/api/openclaw/approvals/route.ts +3 -4
  37. package/src/app/api/openclaw/config-sync/route.ts +3 -2
  38. package/src/app/api/openclaw/cron/route.ts +3 -2
  39. package/src/app/api/openclaw/dotenv-keys/route.ts +2 -1
  40. package/src/app/api/openclaw/exec-config/route.ts +3 -2
  41. package/src/app/api/openclaw/gateway/route.ts +5 -4
  42. package/src/app/api/openclaw/history/route.ts +3 -2
  43. package/src/app/api/openclaw/media/route.ts +2 -1
  44. package/src/app/api/openclaw/permissions/route.ts +3 -2
  45. package/src/app/api/openclaw/sandbox-env/route.ts +3 -2
  46. package/src/app/api/openclaw/skills/install/route.ts +2 -1
  47. package/src/app/api/openclaw/skills/remove/route.ts +2 -1
  48. package/src/app/api/openclaw/skills/route.ts +3 -2
  49. package/src/app/api/orchestrator/run/route.ts +5 -14
  50. package/src/app/api/perf/route.ts +43 -0
  51. package/src/app/api/plugins/dependencies/route.ts +2 -1
  52. package/src/app/api/plugins/install/route.ts +2 -1
  53. package/src/app/api/plugins/marketplace/route.ts +3 -2
  54. package/src/app/api/plugins/settings/route.ts +2 -1
  55. package/src/app/api/preview-server/route.ts +11 -10
  56. package/src/app/api/projects/[id]/route.ts +1 -1
  57. package/src/app/api/schedules/[id]/route.test.ts +128 -0
  58. package/src/app/api/schedules/[id]/route.ts +43 -43
  59. package/src/app/api/schedules/[id]/run/route.ts +11 -62
  60. package/src/app/api/schedules/route.ts +21 -87
  61. package/src/app/api/settings/route.ts +2 -0
  62. package/src/app/api/setup/doctor/route.ts +9 -8
  63. package/src/app/api/tasks/[id]/approve/route.ts +33 -30
  64. package/src/app/api/tasks/[id]/route.ts +12 -35
  65. package/src/app/api/tasks/import/github/route.ts +2 -1
  66. package/src/app/api/tasks/route.ts +79 -91
  67. package/src/app/api/wallets/[id]/approve/route.ts +2 -1
  68. package/src/app/api/wallets/[id]/route.ts +13 -19
  69. package/src/app/api/wallets/[id]/send/route.ts +2 -1
  70. package/src/app/api/wallets/route.ts +2 -1
  71. package/src/app/api/webhooks/[id]/route.ts +2 -1
  72. package/src/app/api/webhooks/route.test.ts +3 -1
  73. package/src/app/page.tsx +23 -331
  74. package/src/cli/index.js +19 -0
  75. package/src/cli/index.ts +38 -7
  76. package/src/cli/spec.js +9 -0
  77. package/src/components/activity/activity-feed.tsx +7 -4
  78. package/src/components/agents/agent-card.tsx +32 -6
  79. package/src/components/agents/agent-chat-list.tsx +55 -22
  80. package/src/components/agents/agent-files-editor.tsx +3 -2
  81. package/src/components/agents/agent-sheet.tsx +123 -22
  82. package/src/components/agents/inspector-panel.tsx +1 -1
  83. package/src/components/agents/openclaw-skills-panel.tsx +2 -1
  84. package/src/components/agents/trash-list.tsx +1 -1
  85. package/src/components/auth/access-key-gate.tsx +8 -2
  86. package/src/components/auth/setup-wizard.tsx +10 -9
  87. package/src/components/auth/user-picker.tsx +3 -2
  88. package/src/components/chat/chat-area.tsx +20 -1
  89. package/src/components/chat/chat-card.tsx +18 -3
  90. package/src/components/chat/chat-header.tsx +24 -4
  91. package/src/components/chat/chat-list.tsx +2 -11
  92. package/src/components/chat/heartbeat-history-panel.tsx +2 -1
  93. package/src/components/chat/message-bubble.tsx +45 -6
  94. package/src/components/chat/message-list.tsx +280 -145
  95. package/src/components/chat/streaming-bubble.tsx +217 -60
  96. package/src/components/chat/swarm-panel.test.ts +274 -0
  97. package/src/components/chat/swarm-panel.tsx +410 -0
  98. package/src/components/chat/swarm-status-card.tsx +346 -0
  99. package/src/components/chat/tool-call-bubble.tsx +48 -23
  100. package/src/components/chatrooms/chatroom-list.tsx +8 -5
  101. package/src/components/chatrooms/chatroom-message.tsx +10 -7
  102. package/src/components/chatrooms/chatroom-view.tsx +12 -9
  103. package/src/components/connectors/connector-health.tsx +6 -4
  104. package/src/components/connectors/connector-list.tsx +16 -11
  105. package/src/components/connectors/connector-sheet.tsx +12 -6
  106. package/src/components/home/home-view.tsx +38 -24
  107. package/src/components/input/chat-input.tsx +10 -1
  108. package/src/components/layout/app-layout.tsx +2 -38
  109. package/src/components/layout/sheet-layer.tsx +50 -0
  110. package/src/components/mcp-servers/mcp-server-list.tsx +37 -5
  111. package/src/components/mcp-servers/mcp-server-sheet.tsx +12 -2
  112. package/src/components/plugins/plugin-list.tsx +8 -4
  113. package/src/components/plugins/plugin-sheet.tsx +2 -1
  114. package/src/components/providers/provider-list.tsx +3 -2
  115. package/src/components/providers/provider-sheet.tsx +2 -1
  116. package/src/components/runs/run-list.tsx +11 -7
  117. package/src/components/schedules/schedule-card.tsx +5 -3
  118. package/src/components/shared/agent-switch-dialog.tsx +1 -1
  119. package/src/components/shared/attachment-chip.tsx +19 -3
  120. package/src/components/shared/notification-center.tsx +6 -3
  121. package/src/components/shared/settings/plugin-manager.tsx +3 -2
  122. package/src/components/shared/settings/section-embedding.tsx +2 -1
  123. package/src/components/shared/settings/section-orchestrator.tsx +2 -1
  124. package/src/components/shared/settings/section-user-preferences.tsx +107 -0
  125. package/src/components/shared/settings/settings-page.tsx +13 -9
  126. package/src/components/skills/clawhub-browser.tsx +15 -4
  127. package/src/components/skills/skill-list.tsx +15 -4
  128. package/src/components/tasks/approvals-panel.tsx +2 -1
  129. package/src/components/tasks/task-board.tsx +35 -37
  130. package/src/components/tasks/task-sheet.tsx +4 -3
  131. package/src/components/ui/full-screen-loader.tsx +164 -0
  132. package/src/components/wallets/wallet-approval-dialog.tsx +2 -1
  133. package/src/components/wallets/wallet-panel.tsx +6 -5
  134. package/src/components/wallets/wallet-section.tsx +3 -2
  135. package/src/components/webhooks/webhook-list.tsx +4 -5
  136. package/src/components/webhooks/webhook-sheet.tsx +6 -6
  137. package/src/hooks/use-app-bootstrap.ts +202 -0
  138. package/src/hooks/use-mounted-ref.ts +14 -0
  139. package/src/hooks/use-now.ts +31 -0
  140. package/src/hooks/use-openclaw-gateway.ts +2 -1
  141. package/src/instrumentation.ts +20 -8
  142. package/src/lib/agent-default-tools.test.ts +52 -0
  143. package/src/lib/agent-default-tools.ts +40 -0
  144. package/src/lib/api-client.test.ts +21 -0
  145. package/src/lib/api-client.ts +6 -11
  146. package/src/lib/canvas-content.test.ts +360 -0
  147. package/src/lib/chat-streaming-state.test.ts +49 -2
  148. package/src/lib/chat-streaming-state.ts +26 -10
  149. package/src/lib/fetch-timeout.test.ts +54 -0
  150. package/src/lib/fetch-timeout.ts +60 -3
  151. package/src/lib/live-tool-events.test.ts +77 -0
  152. package/src/lib/live-tool-events.ts +73 -0
  153. package/src/lib/local-observability.test.ts +2 -2
  154. package/src/lib/openclaw-endpoint.test.ts +1 -1
  155. package/src/lib/providers/anthropic.ts +12 -16
  156. package/src/lib/providers/index.ts +4 -2
  157. package/src/lib/providers/ollama.ts +9 -6
  158. package/src/lib/providers/openai.ts +11 -14
  159. package/src/lib/runtime-env.test.ts +8 -8
  160. package/src/lib/schedule-dedupe-advanced.test.ts +2 -2
  161. package/src/lib/schedule-dedupe.test.ts +1 -1
  162. package/src/lib/schedule-dedupe.ts +3 -2
  163. package/src/lib/server/agent-thread-session.test.ts +6 -6
  164. package/src/lib/server/agent-thread-session.ts +6 -9
  165. package/src/lib/server/alert-dispatch.ts +2 -1
  166. package/src/lib/server/api-routes.test.ts +6 -6
  167. package/src/lib/server/approval-connector-notify.test.ts +4 -4
  168. package/src/lib/server/approvals-auto-approve.test.ts +29 -29
  169. package/src/lib/server/approvals.test.ts +317 -0
  170. package/src/lib/server/approvals.ts +5 -4
  171. package/src/lib/server/autonomy-runtime.test.ts +11 -11
  172. package/src/lib/server/browser-state.ts +2 -2
  173. package/src/lib/server/capability-router.test.ts +1 -1
  174. package/src/lib/server/capability-router.ts +3 -2
  175. package/src/lib/server/chat-execution-advanced.test.ts +15 -2
  176. package/src/lib/server/chat-execution-connector-delivery.ts +67 -0
  177. package/src/lib/server/chat-execution-disabled.test.ts +3 -3
  178. package/src/lib/server/chat-execution-eval-history.test.ts +3 -3
  179. package/src/lib/server/chat-execution-heartbeat.test.ts +42 -1
  180. package/src/lib/server/chat-execution-session-sync.test.ts +119 -0
  181. package/src/lib/server/chat-execution-tool-events.ts +116 -0
  182. package/src/lib/server/chat-execution-utils.test.ts +479 -0
  183. package/src/lib/server/chat-execution-utils.ts +533 -0
  184. package/src/lib/server/chat-execution.ts +153 -748
  185. package/src/lib/server/chat-streaming-utils.ts +174 -0
  186. package/src/lib/server/chat-turn-tool-routing.ts +310 -0
  187. package/src/lib/server/chatroom-session-persistence.test.ts +2 -2
  188. package/src/lib/server/clawhub-client.ts +2 -1
  189. package/src/lib/server/collection-helpers.test.ts +92 -0
  190. package/src/lib/server/collection-helpers.ts +25 -3
  191. package/src/lib/server/connectors/access.ts +146 -0
  192. package/src/lib/server/connectors/bluebubbles.test.ts +1 -1
  193. package/src/lib/server/connectors/bluebubbles.ts +4 -4
  194. package/src/lib/server/connectors/commands.ts +367 -0
  195. package/src/lib/server/connectors/connector-routing.test.ts +4 -4
  196. package/src/lib/server/connectors/delivery.ts +142 -0
  197. package/src/lib/server/connectors/discord.ts +37 -40
  198. package/src/lib/server/connectors/email.ts +11 -10
  199. package/src/lib/server/connectors/googlechat.ts +4 -4
  200. package/src/lib/server/connectors/inbound-audio-transcription.ts +2 -1
  201. package/src/lib/server/connectors/ingress-delivery.ts +23 -0
  202. package/src/lib/server/connectors/manager-roundtrip.test.ts +300 -0
  203. package/src/lib/server/connectors/manager.test.ts +352 -77
  204. package/src/lib/server/connectors/manager.ts +134 -673
  205. package/src/lib/server/connectors/matrix.ts +4 -4
  206. package/src/lib/server/connectors/message-sentinel.ts +7 -0
  207. package/src/lib/server/connectors/openclaw.test.ts +1 -1
  208. package/src/lib/server/connectors/openclaw.ts +8 -10
  209. package/src/lib/server/connectors/outbox.test.ts +192 -0
  210. package/src/lib/server/connectors/outbox.ts +369 -0
  211. package/src/lib/server/connectors/pairing.test.ts +18 -1
  212. package/src/lib/server/connectors/pairing.ts +49 -4
  213. package/src/lib/server/connectors/policy.ts +9 -3
  214. package/src/lib/server/connectors/reconnect-state.ts +71 -0
  215. package/src/lib/server/connectors/response-media.ts +256 -0
  216. package/src/lib/server/connectors/runtime-state.ts +67 -0
  217. package/src/lib/server/connectors/session.test.ts +357 -0
  218. package/src/lib/server/connectors/session.ts +422 -0
  219. package/src/lib/server/connectors/signal.ts +7 -7
  220. package/src/lib/server/connectors/slack.ts +43 -43
  221. package/src/lib/server/connectors/teams.ts +4 -4
  222. package/src/lib/server/connectors/telegram.ts +37 -43
  223. package/src/lib/server/connectors/types.ts +31 -1
  224. package/src/lib/server/connectors/whatsapp.test.ts +108 -0
  225. package/src/lib/server/connectors/whatsapp.ts +106 -34
  226. package/src/lib/server/context-manager.test.ts +409 -0
  227. package/src/lib/server/cost.test.ts +1 -1
  228. package/src/lib/server/daemon-policy.ts +78 -0
  229. package/src/lib/server/daemon-state-connectors.test.ts +167 -0
  230. package/src/lib/server/daemon-state.test.ts +283 -55
  231. package/src/lib/server/daemon-state.ts +106 -109
  232. package/src/lib/server/data-dir.test.ts +5 -5
  233. package/src/lib/server/data-dir.ts +4 -0
  234. package/src/lib/server/delegation-jobs-advanced.test.ts +1 -1
  235. package/src/lib/server/delegation-jobs.test.ts +87 -0
  236. package/src/lib/server/delegation-jobs.ts +42 -48
  237. package/src/lib/server/devserver-launch.ts +1 -1
  238. package/src/lib/server/document-utils.ts +7 -9
  239. package/src/lib/server/elevenlabs.ts +2 -1
  240. package/src/lib/server/embeddings.test.ts +105 -0
  241. package/src/lib/server/ethereum.ts +3 -2
  242. package/src/lib/server/eval/agent-regression.ts +3 -2
  243. package/src/lib/server/eval/runner.ts +2 -1
  244. package/src/lib/server/eval/scorer.ts +2 -1
  245. package/src/lib/server/evm-swap.ts +2 -1
  246. package/src/lib/server/gateway/protocol.test.ts +1 -1
  247. package/src/lib/server/guardian.ts +2 -1
  248. package/src/lib/server/heartbeat-blocked-suppression.test.ts +151 -0
  249. package/src/lib/server/heartbeat-service-timer.test.ts +6 -6
  250. package/src/lib/server/heartbeat-service.test.ts +406 -0
  251. package/src/lib/server/heartbeat-service.ts +54 -7
  252. package/src/lib/server/heartbeat-wake.test.ts +19 -0
  253. package/src/lib/server/heartbeat-wake.ts +17 -16
  254. package/src/lib/server/integrity-monitor.test.ts +149 -0
  255. package/src/lib/server/json-utils.ts +22 -0
  256. package/src/lib/server/knowledge-db.test.ts +13 -13
  257. package/src/lib/server/link-understanding.ts +2 -1
  258. package/src/lib/server/llm-response-cache.test.ts +1 -1
  259. package/src/lib/server/main-agent-loop-advanced.test.ts +65 -3
  260. package/src/lib/server/main-agent-loop.test.ts +6 -6
  261. package/src/lib/server/main-agent-loop.ts +21 -7
  262. package/src/lib/server/mcp-client.test.ts +1 -1
  263. package/src/lib/server/mcp-conformance.test.ts +1 -1
  264. package/src/lib/server/mcp-conformance.ts +3 -2
  265. package/src/lib/server/memory-consolidation.ts +2 -1
  266. package/src/lib/server/memory-db.test.ts +485 -0
  267. package/src/lib/server/memory-db.ts +39 -26
  268. package/src/lib/server/memory-graph.test.ts +2 -2
  269. package/src/lib/server/memory-policy.test.ts +7 -7
  270. package/src/lib/server/memory-retrieval.test.ts +1 -1
  271. package/src/lib/server/openclaw-config-sync.ts +2 -1
  272. package/src/lib/server/openclaw-deploy.test.ts +1 -1
  273. package/src/lib/server/openclaw-deploy.ts +8 -12
  274. package/src/lib/server/openclaw-exec-config.ts +2 -1
  275. package/src/lib/server/openclaw-gateway.ts +6 -7
  276. package/src/lib/server/openclaw-skills-normalize.ts +2 -1
  277. package/src/lib/server/openclaw-sync.ts +7 -5
  278. package/src/lib/server/orchestrator-lg-structure.test.ts +17 -0
  279. package/src/lib/server/orchestrator-lg.ts +199 -327
  280. package/src/lib/server/path-utils.ts +31 -0
  281. package/src/lib/server/perf.ts +161 -0
  282. package/src/lib/server/plugins-approval-guidance.ts +115 -0
  283. package/src/lib/server/plugins.test.ts +1 -1
  284. package/src/lib/server/plugins.ts +22 -132
  285. package/src/lib/server/process-manager.ts +5 -8
  286. package/src/lib/server/provider-health.test.ts +137 -0
  287. package/src/lib/server/provider-health.ts +3 -3
  288. package/src/lib/server/provider-model-discovery.ts +3 -12
  289. package/src/lib/server/queue-followups.test.ts +9 -9
  290. package/src/lib/server/queue-reconcile.test.ts +2 -2
  291. package/src/lib/server/queue-recovery.test.ts +269 -0
  292. package/src/lib/server/queue.test.ts +570 -0
  293. package/src/lib/server/queue.ts +62 -455
  294. package/src/lib/server/resolve-image.ts +30 -0
  295. package/src/lib/server/runtime-settings.test.ts +4 -4
  296. package/src/lib/server/runtime-storage-write-paths.test.ts +60 -0
  297. package/src/lib/server/schedule-normalization.test.ts +279 -0
  298. package/src/lib/server/schedule-service.ts +263 -0
  299. package/src/lib/server/scheduler.ts +17 -74
  300. package/src/lib/server/session-mailbox.test.ts +191 -0
  301. package/src/lib/server/session-run-manager.test.ts +640 -0
  302. package/src/lib/server/session-run-manager.ts +59 -15
  303. package/src/lib/server/session-tools/autonomy-tools.test.ts +20 -20
  304. package/src/lib/server/session-tools/calendar.ts +2 -1
  305. package/src/lib/server/session-tools/canvas.ts +2 -1
  306. package/src/lib/server/session-tools/chatroom.ts +2 -1
  307. package/src/lib/server/session-tools/connector.ts +26 -28
  308. package/src/lib/server/session-tools/context-mgmt.ts +3 -2
  309. package/src/lib/server/session-tools/crawl.ts +4 -3
  310. package/src/lib/server/session-tools/crud.ts +105 -324
  311. package/src/lib/server/session-tools/delegate-fallback.test.ts +9 -9
  312. package/src/lib/server/session-tools/delegate.ts +6 -8
  313. package/src/lib/server/session-tools/discovery-approvals.test.ts +15 -15
  314. package/src/lib/server/session-tools/discovery.ts +4 -3
  315. package/src/lib/server/session-tools/document.ts +2 -1
  316. package/src/lib/server/session-tools/email.ts +2 -1
  317. package/src/lib/server/session-tools/extract.ts +2 -1
  318. package/src/lib/server/session-tools/file.ts +4 -3
  319. package/src/lib/server/session-tools/http.ts +2 -1
  320. package/src/lib/server/session-tools/human-loop.ts +2 -1
  321. package/src/lib/server/session-tools/image-gen.ts +4 -3
  322. package/src/lib/server/session-tools/index.ts +26 -30
  323. package/src/lib/server/session-tools/mailbox.ts +2 -1
  324. package/src/lib/server/session-tools/manage-connectors.test.ts +4 -4
  325. package/src/lib/server/session-tools/manage-schedules.test.ts +12 -12
  326. package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +5 -5
  327. package/src/lib/server/session-tools/manage-tasks.test.ts +2 -2
  328. package/src/lib/server/session-tools/monitor.ts +2 -1
  329. package/src/lib/server/session-tools/platform.ts +2 -1
  330. package/src/lib/server/session-tools/plugin-creator.ts +2 -1
  331. package/src/lib/server/session-tools/replicate.ts +3 -2
  332. package/src/lib/server/session-tools/session-tools-wiring.test.ts +6 -6
  333. package/src/lib/server/session-tools/shell.ts +4 -9
  334. package/src/lib/server/session-tools/subagent.ts +322 -170
  335. package/src/lib/server/session-tools/table.ts +6 -5
  336. package/src/lib/server/session-tools/wallet-tool.test.ts +3 -3
  337. package/src/lib/server/session-tools/wallet.ts +7 -6
  338. package/src/lib/server/session-tools/web-browser-config.test.ts +1 -0
  339. package/src/lib/server/session-tools/web-utils.ts +317 -0
  340. package/src/lib/server/session-tools/web.ts +62 -328
  341. package/src/lib/server/skill-prompt-budget.test.ts +1 -1
  342. package/src/lib/server/skills-normalize.ts +2 -1
  343. package/src/lib/server/storage-item-access.test.ts +302 -0
  344. package/src/lib/server/storage.ts +366 -314
  345. package/src/lib/server/stream-agent-chat.test.ts +82 -3
  346. package/src/lib/server/stream-agent-chat.ts +146 -510
  347. package/src/lib/server/stream-continuation.ts +412 -0
  348. package/src/lib/server/subagent-lineage.test.ts +647 -0
  349. package/src/lib/server/subagent-lineage.ts +435 -0
  350. package/src/lib/server/subagent-runtime.test.ts +484 -0
  351. package/src/lib/server/subagent-runtime.ts +419 -0
  352. package/src/lib/server/subagent-swarm.test.ts +391 -0
  353. package/src/lib/server/subagent-swarm.ts +564 -0
  354. package/src/lib/server/system-events.ts +3 -3
  355. package/src/lib/server/task-followups.test.ts +491 -0
  356. package/src/lib/server/task-followups.ts +391 -0
  357. package/src/lib/server/task-lifecycle.test.ts +205 -0
  358. package/src/lib/server/task-lifecycle.ts +200 -0
  359. package/src/lib/server/task-quality-gate.test.ts +1 -1
  360. package/src/lib/server/task-resume.ts +208 -0
  361. package/src/lib/server/task-service.test.ts +108 -0
  362. package/src/lib/server/task-service.ts +264 -0
  363. package/src/lib/server/task-validation.test.ts +1 -1
  364. package/src/lib/server/test-utils/run-with-temp-data-dir.ts +42 -0
  365. package/src/lib/server/tool-capability-policy.test.ts +2 -2
  366. package/src/lib/server/tool-capability-policy.ts +3 -2
  367. package/src/lib/server/tool-planning.ts +2 -1
  368. package/src/lib/server/tool-retry.ts +2 -3
  369. package/src/lib/server/wake-dispatcher.test.ts +303 -0
  370. package/src/lib/server/wake-dispatcher.ts +318 -0
  371. package/src/lib/server/wake-mode.test.ts +161 -0
  372. package/src/lib/server/wake-mode.ts +174 -0
  373. package/src/lib/server/wallet-service.ts +8 -9
  374. package/src/lib/server/watch-jobs.ts +2 -1
  375. package/src/lib/server/workspace-context.ts +2 -2
  376. package/src/lib/shared-utils.test.ts +142 -0
  377. package/src/lib/shared-utils.ts +62 -0
  378. package/src/lib/tool-event-summary.ts +2 -1
  379. package/src/lib/view-routes.test.ts +100 -0
  380. package/src/lib/wallet.test.ts +322 -6
  381. package/src/proxy.test.ts +4 -4
  382. package/src/proxy.ts +2 -3
  383. package/src/stores/set-if-changed.ts +40 -0
  384. package/src/stores/slices/agent-slice.ts +111 -0
  385. package/src/stores/slices/auth-slice.ts +25 -0
  386. package/src/stores/slices/data-slice.ts +301 -0
  387. package/src/stores/slices/index.ts +7 -0
  388. package/src/stores/slices/session-slice.ts +112 -0
  389. package/src/stores/slices/task-slice.ts +63 -0
  390. package/src/stores/slices/ui-slice.ts +192 -0
  391. package/src/stores/use-app-store.ts +17 -822
  392. package/src/stores/use-approval-store.ts +2 -1
  393. package/src/stores/use-chat-store.ts +8 -1
  394. package/src/types/index.ts +10 -0
@@ -0,0 +1,564 @@
1
+ /**
2
+ * Subagent Swarm — Parallel Spawn & Result Aggregation
3
+ *
4
+ * Unified module for spawning multiple subagents in parallel and collecting
5
+ * their results. Supports both event-driven (swarm) and poll-based (aggregate)
6
+ * result collection patterns.
7
+ *
8
+ * Replaces the separate subagent-batch module — batch operations are now
9
+ * thin wrappers over spawnSwarm.
10
+ */
11
+
12
+ import { genId } from '@/lib/id'
13
+ import { errorMessage, hmrSingleton, sleep } from '@/lib/shared-utils'
14
+ import { notify } from './ws-hub'
15
+ import {
16
+ spawnSubagent,
17
+ type SubagentContext,
18
+ type SubagentHandle,
19
+ type SubagentResult,
20
+ } from './subagent-runtime'
21
+ import { loadSessions } from './storage'
22
+ import { getDelegationJob } from './delegation-jobs'
23
+ import {
24
+ getLineageNode,
25
+ cancelLineageNode,
26
+ type SubagentState,
27
+ } from './subagent-lineage'
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Types
31
+ // ---------------------------------------------------------------------------
32
+
33
+ export type SwarmStatus = 'spawning' | 'running' | 'completed' | 'partial' | 'failed'
34
+
35
+ export interface SwarmMember {
36
+ /** Position in the spawn order */
37
+ index: number
38
+ /** Agent handle from spawnSubagent */
39
+ handle: SubagentHandle
40
+ /** Result (populated when the agent completes) */
41
+ result: SubagentResult | null
42
+ /** Error if spawn itself failed (before execution) */
43
+ spawnError: string | null
44
+ }
45
+
46
+ export interface SwarmHandle {
47
+ /** Unique swarm ID */
48
+ swarmId: string
49
+ /** Session that spawned this swarm */
50
+ parentSessionId: string | null
51
+ /** All members (spawned or failed-to-spawn) */
52
+ members: SwarmMember[]
53
+ /** Swarm-level status */
54
+ status: SwarmStatus
55
+ /** When the swarm was created */
56
+ createdAt: number
57
+ /** When all members finished (null if still running) */
58
+ completedAt: number | null
59
+ /** Promise that resolves when ALL members complete */
60
+ allSettled: Promise<SwarmAggregateResult>
61
+ /** Promise that resolves when the FIRST member completes */
62
+ firstSettled: Promise<{ index: number; result: SubagentResult }>
63
+ /** Cancel all running members */
64
+ cancelAll: () => void
65
+ }
66
+
67
+ export interface SwarmAggregateResult {
68
+ swarmId: string
69
+ parentSessionId: string | null
70
+ totalSpawned: number
71
+ totalCompleted: number
72
+ totalFailed: number
73
+ totalCancelled: number
74
+ totalSpawnErrors: number
75
+ durationMs: number
76
+ results: Array<{
77
+ index: number
78
+ agentId: string
79
+ agentName: string
80
+ jobId: string
81
+ sessionId: string
82
+ status: SubagentResult['status'] | 'spawn_error'
83
+ response: string | null
84
+ error: string | null
85
+ durationMs: number
86
+ }>
87
+ }
88
+
89
+ export interface BatchSpawnInput {
90
+ /** Tasks to spawn — each gets its own subagent */
91
+ tasks: Array<{
92
+ agentId: string
93
+ message: string
94
+ cwd?: string
95
+ shareBrowserProfile?: boolean
96
+ }>
97
+ /** Optional swarm-level label */
98
+ label?: string
99
+ /** Callback when each member completes */
100
+ onMemberComplete?: (member: SwarmMember, swarm: SwarmHandle) => void
101
+ /** Callback when all members complete */
102
+ onSwarmComplete?: (result: SwarmAggregateResult) => void
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // Batch types (absorbed from subagent-batch)
107
+ // ---------------------------------------------------------------------------
108
+
109
+ export interface BatchTask {
110
+ agentId: string
111
+ message: string
112
+ cwd?: string
113
+ shareBrowserProfile?: boolean
114
+ }
115
+
116
+ export interface AggregatedResult {
117
+ results: Array<{
118
+ jobId: string
119
+ status: string
120
+ response: string | null
121
+ error: string | null
122
+ agentName: string | null
123
+ }>
124
+ pending: string[]
125
+ allCompleted: boolean
126
+ completed: number
127
+ failed: number
128
+ cancelled: number
129
+ total: number
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Storage (globalThis-scoped for HMR safety)
134
+ // ---------------------------------------------------------------------------
135
+
136
+ const swarmRegistry = hmrSingleton('__swarmclaw_swarm_registry__', () => new Map<string, SwarmHandle>())
137
+
138
+ function notifySwarmChanged() {
139
+ notify('swarm_status')
140
+ }
141
+
142
+ // ---------------------------------------------------------------------------
143
+ // Core: Spawn Swarm
144
+ // ---------------------------------------------------------------------------
145
+
146
+ /**
147
+ * Spawn multiple subagents in parallel. Returns immediately with handles
148
+ * for all agents. Each agent runs with waitForCompletion: false.
149
+ *
150
+ * Usage:
151
+ * const swarm = spawnSwarm({
152
+ * tasks: [
153
+ * { agentId: 'researcher', message: 'Find API docs' },
154
+ * { agentId: 'coder', message: 'Scaffold the module' },
155
+ * ],
156
+ * }, { sessionId: parentSession, cwd: '/workspace' })
157
+ *
158
+ * const aggregate = await swarm.allSettled
159
+ */
160
+ export function spawnSwarm(
161
+ input: BatchSpawnInput,
162
+ context: SubagentContext,
163
+ ): SwarmHandle {
164
+ const swarmId = genId(10)
165
+ const createdAt = Date.now()
166
+ const members: SwarmMember[] = []
167
+
168
+ // Pre-load sessions once for all spawns (avoids N SQLite reads)
169
+ const cachedSessions = context._sessions ?? loadSessions()
170
+ const cachedContext: SubagentContext = { ...context, _sessions: cachedSessions }
171
+
172
+ // Spawn all agents — failures are captured per-member, not thrown
173
+ let spawnErrorCount = 0
174
+ for (let i = 0; i < input.tasks.length; i++) {
175
+ const task = input.tasks[i]
176
+ try {
177
+ const handle = spawnSubagent(
178
+ {
179
+ agentId: task.agentId,
180
+ message: task.message,
181
+ cwd: task.cwd,
182
+ shareBrowserProfile: task.shareBrowserProfile,
183
+ waitForCompletion: false,
184
+ },
185
+ cachedContext,
186
+ )
187
+ members.push({ index: i, handle, result: null, spawnError: null })
188
+ } catch (err: unknown) {
189
+ spawnErrorCount++
190
+ members.push({
191
+ index: i,
192
+ handle: null as unknown as SubagentHandle,
193
+ result: null,
194
+ spawnError: errorMessage(err),
195
+ })
196
+ }
197
+ }
198
+
199
+ // Incremental counters — O(1) per completion instead of O(n)
200
+ const counters = { completed: 0, failed: 0, cancelled: 0 }
201
+
202
+ // Track completion per member
203
+ const memberPromises: Promise<{ index: number; result: SubagentResult }>[] = []
204
+
205
+ const swarm: SwarmHandle = {
206
+ swarmId,
207
+ parentSessionId: context.sessionId || null,
208
+ members,
209
+ status: 'running',
210
+ createdAt,
211
+ completedAt: null,
212
+ allSettled: null as unknown as Promise<SwarmAggregateResult>,
213
+ firstSettled: null as unknown as Promise<{ index: number; result: SubagentResult }>,
214
+ cancelAll: () => {
215
+ for (const member of members) {
216
+ if (member.handle && !member.result && !member.spawnError) {
217
+ try {
218
+ member.handle.run.abort()
219
+ cancelLineageNode(member.handle.lineageId)
220
+ } catch { /* best-effort */ }
221
+ }
222
+ }
223
+ swarm.status = 'failed'
224
+ notifySwarmChanged()
225
+ },
226
+ }
227
+
228
+ for (const member of members) {
229
+ if (member.spawnError || !member.handle) continue
230
+
231
+ const memberPromise = member.handle.promise.then((result) => {
232
+ member.result = result
233
+
234
+ // Increment counters
235
+ if (result.status === 'completed') counters.completed++
236
+ else if (result.status === 'failed' || result.status === 'timed_out') counters.failed++
237
+ else if (result.status === 'cancelled') counters.cancelled++
238
+
239
+ if (input.onMemberComplete) {
240
+ try { input.onMemberComplete(member, swarm) } catch { /* callback errors don't break the swarm */ }
241
+ }
242
+
243
+ // Update swarm status using counters (O(1))
244
+ updateSwarmStatus(swarm, counters, spawnErrorCount)
245
+ notifySwarmChanged()
246
+
247
+ return { index: member.index, result }
248
+ })
249
+
250
+ memberPromises.push(memberPromise)
251
+ }
252
+
253
+ // allSettled — resolves when every member is done (or had a spawn error)
254
+ swarm.allSettled = Promise.allSettled(memberPromises).then(() => {
255
+ swarm.completedAt = Date.now()
256
+ updateSwarmStatus(swarm, counters, spawnErrorCount)
257
+ const aggregate = buildAggregateResult(swarm)
258
+ if (input.onSwarmComplete) {
259
+ try { input.onSwarmComplete(aggregate) } catch { /* callback errors don't break */ }
260
+ }
261
+ notifySwarmChanged()
262
+ return aggregate
263
+ })
264
+
265
+ // firstSettled — resolves when the first member completes
266
+ swarm.firstSettled = memberPromises.length > 0
267
+ ? Promise.race(memberPromises)
268
+ : swarm.allSettled.then((agg) => {
269
+ // All members had spawn errors — return first spawn error entry
270
+ const first = agg.results[0]
271
+ return {
272
+ index: first?.index ?? -1,
273
+ result: {
274
+ jobId: first?.jobId ?? '',
275
+ sessionId: first?.sessionId ?? '',
276
+ lineageId: '',
277
+ agentId: first?.agentId ?? '',
278
+ agentName: first?.agentName ?? '',
279
+ status: 'failed' as const,
280
+ response: null,
281
+ error: first?.error ?? 'No members spawned',
282
+ depth: 0,
283
+ parentSessionId: context.sessionId || null,
284
+ childCount: 0,
285
+ durationMs: 0,
286
+ } satisfies SubagentResult,
287
+ }
288
+ })
289
+
290
+ // Register in swarm registry
291
+ swarmRegistry.set(swarmId, swarm)
292
+ notifySwarmChanged()
293
+
294
+ return swarm
295
+ }
296
+
297
+ // ---------------------------------------------------------------------------
298
+ // Status computation (O(1) with incremental counters)
299
+ // ---------------------------------------------------------------------------
300
+
301
+ function updateSwarmStatus(
302
+ swarm: SwarmHandle,
303
+ counters: { completed: number; failed: number; cancelled: number },
304
+ spawnErrors: number,
305
+ ): void {
306
+ const total = swarm.members.length
307
+ const settled = spawnErrors + counters.completed + counters.failed + counters.cancelled
308
+
309
+ if (settled >= total) {
310
+ if (counters.failed + spawnErrors === total) {
311
+ swarm.status = 'failed'
312
+ } else if (counters.completed === total) {
313
+ swarm.status = 'completed'
314
+ } else {
315
+ swarm.status = 'partial'
316
+ }
317
+ } else {
318
+ swarm.status = 'running'
319
+ }
320
+ }
321
+
322
+ // ---------------------------------------------------------------------------
323
+ // Result aggregation
324
+ // ---------------------------------------------------------------------------
325
+
326
+ function buildAggregateResult(swarm: SwarmHandle): SwarmAggregateResult {
327
+ const results = swarm.members.map((member) => {
328
+ if (member.spawnError) {
329
+ return {
330
+ index: member.index,
331
+ agentId: '',
332
+ agentName: '',
333
+ jobId: '',
334
+ sessionId: '',
335
+ status: 'spawn_error' as const,
336
+ response: null,
337
+ error: member.spawnError,
338
+ durationMs: 0,
339
+ }
340
+ }
341
+ const r = member.result
342
+ return {
343
+ index: member.index,
344
+ agentId: r?.agentId || member.handle?.agentId || '',
345
+ agentName: r?.agentName || member.handle?.agentName || '',
346
+ jobId: r?.jobId || member.handle?.jobId || '',
347
+ sessionId: r?.sessionId || member.handle?.sessionId || '',
348
+ status: r?.status || 'failed' as SubagentResult['status'],
349
+ response: r?.response || null,
350
+ error: r?.error || null,
351
+ durationMs: r?.durationMs || 0,
352
+ }
353
+ })
354
+
355
+ return {
356
+ swarmId: swarm.swarmId,
357
+ parentSessionId: swarm.parentSessionId,
358
+ totalSpawned: swarm.members.length,
359
+ totalCompleted: results.filter((r) => r.status === 'completed').length,
360
+ totalFailed: results.filter((r) => r.status === 'failed' || r.status === 'timed_out').length,
361
+ totalCancelled: results.filter((r) => r.status === 'cancelled').length,
362
+ totalSpawnErrors: results.filter((r) => r.status === 'spawn_error').length,
363
+ durationMs: (swarm.completedAt || Date.now()) - swarm.createdAt,
364
+ results,
365
+ }
366
+ }
367
+
368
+ // ---------------------------------------------------------------------------
369
+ // Delegation job polling (absorbed from subagent-batch)
370
+ // ---------------------------------------------------------------------------
371
+
372
+ /**
373
+ * Poll delegation jobs for instant snapshot of results.
374
+ * Useful when the caller only has job IDs (no SwarmHandle).
375
+ */
376
+ export function aggregateResults(jobIds: string[]): AggregatedResult {
377
+ const results: AggregatedResult['results'] = []
378
+ const pending: string[] = []
379
+ let completed = 0
380
+ let failed = 0
381
+ let cancelled = 0
382
+
383
+ for (const jobId of jobIds) {
384
+ const job = getDelegationJob(jobId)
385
+ if (!job) {
386
+ results.push({
387
+ jobId,
388
+ status: 'not_found',
389
+ response: null,
390
+ error: `Job "${jobId}" not found`,
391
+ agentName: null,
392
+ })
393
+ failed++
394
+ continue
395
+ }
396
+
397
+ results.push({
398
+ jobId,
399
+ status: job.status,
400
+ response: job.resultPreview || job.result || null,
401
+ error: job.error || null,
402
+ agentName: job.agentName || null,
403
+ })
404
+
405
+ if (job.status === 'completed') completed++
406
+ else if (job.status === 'failed') failed++
407
+ else if (job.status === 'cancelled') cancelled++
408
+ else pending.push(jobId)
409
+ }
410
+
411
+ return {
412
+ results,
413
+ pending,
414
+ allCompleted: pending.length === 0,
415
+ completed,
416
+ failed,
417
+ cancelled,
418
+ total: jobIds.length,
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Wait for multiple jobs to complete (poll-based with timeout).
424
+ */
425
+ export async function waitForAll(
426
+ jobIds: string[],
427
+ timeoutSec = 300,
428
+ ): Promise<AggregatedResult> {
429
+ const timeoutAt = Date.now() + Math.max(1, timeoutSec) * 1000
430
+ const pollIntervalMs = 1000
431
+
432
+ while (Date.now() < timeoutAt) {
433
+ const snapshot = aggregateResults(jobIds)
434
+ if (snapshot.allCompleted) return snapshot
435
+ await sleep(pollIntervalMs)
436
+ }
437
+
438
+ // Final snapshot after timeout
439
+ return aggregateResults(jobIds)
440
+ }
441
+
442
+ // ---------------------------------------------------------------------------
443
+ // Query API (for UI / session tools)
444
+ // ---------------------------------------------------------------------------
445
+
446
+ export function getSwarm(swarmId: string): SwarmHandle | null {
447
+ return swarmRegistry.get(swarmId) || null
448
+ }
449
+
450
+ export function listSwarms(parentSessionId?: string): SwarmHandle[] {
451
+ const all = Array.from(swarmRegistry.values())
452
+ if (parentSessionId) {
453
+ return all.filter((s) => s.parentSessionId === parentSessionId)
454
+ }
455
+ return all
456
+ }
457
+
458
+ /**
459
+ * Get a serializable snapshot of a swarm's current state.
460
+ * Used by the UI to render SwarmStatusCard.
461
+ */
462
+ export function getSwarmSnapshot(swarmId: string): SwarmSnapshot | null {
463
+ const swarm = swarmRegistry.get(swarmId)
464
+ if (!swarm) return null
465
+ return buildSwarmSnapshot(swarm)
466
+ }
467
+
468
+ export interface SwarmMemberSnapshot {
469
+ index: number
470
+ agentId: string
471
+ agentName: string
472
+ jobId: string
473
+ sessionId: string
474
+ task: string
475
+ status: SubagentState | 'spawn_error'
476
+ resultPreview: string | null
477
+ error: string | null
478
+ durationMs: number
479
+ }
480
+
481
+ export interface SwarmSnapshot {
482
+ swarmId: string
483
+ parentSessionId: string | null
484
+ status: SwarmStatus
485
+ createdAt: number
486
+ completedAt: number | null
487
+ memberCount: number
488
+ completedCount: number
489
+ failedCount: number
490
+ members: SwarmMemberSnapshot[]
491
+ }
492
+
493
+ function buildSwarmSnapshot(swarm: SwarmHandle): SwarmSnapshot {
494
+ const members: SwarmMemberSnapshot[] = swarm.members.map((m) => {
495
+ if (m.spawnError || !m.handle) {
496
+ return {
497
+ index: m.index,
498
+ agentId: '',
499
+ agentName: '',
500
+ jobId: '',
501
+ sessionId: '',
502
+ task: '',
503
+ status: 'spawn_error' as const,
504
+ resultPreview: null,
505
+ error: m.spawnError || 'Spawn failed (no handle)',
506
+ durationMs: 0,
507
+ }
508
+ }
509
+ // Read state from lineage node (single source of truth) with fallbacks
510
+ const node = getLineageNode(m.handle.lineageId)
511
+ const status = m.result?.status ?? node?.status ?? 'running'
512
+ return {
513
+ index: m.index,
514
+ agentId: m.handle.agentId,
515
+ agentName: m.handle.agentName,
516
+ jobId: m.handle.jobId,
517
+ sessionId: m.handle.sessionId,
518
+ task: getDelegationJob(m.handle.jobId)?.task || '',
519
+ status,
520
+ resultPreview: m.result?.response?.slice(0, 500) || null,
521
+ error: m.result?.error || null,
522
+ durationMs: m.result?.durationMs || (Date.now() - swarm.createdAt),
523
+ }
524
+ })
525
+
526
+ return {
527
+ swarmId: swarm.swarmId,
528
+ parentSessionId: swarm.parentSessionId,
529
+ status: swarm.status,
530
+ createdAt: swarm.createdAt,
531
+ completedAt: swarm.completedAt,
532
+ memberCount: members.length,
533
+ completedCount: members.filter((m) => m.status === 'completed').length,
534
+ failedCount: members.filter((m) =>
535
+ m.status === 'failed' || m.status === 'timed_out' || m.status === 'spawn_error',
536
+ ).length,
537
+ members,
538
+ }
539
+ }
540
+
541
+ // ---------------------------------------------------------------------------
542
+ // Cleanup
543
+ // ---------------------------------------------------------------------------
544
+
545
+ export function removeSwarm(swarmId: string): boolean {
546
+ return swarmRegistry.delete(swarmId)
547
+ }
548
+
549
+ export function cleanupFinishedSwarms(maxAgeMs = 60 * 60_000): number {
550
+ const threshold = Date.now() - maxAgeMs
551
+ let cleaned = 0
552
+ for (const [id, swarm] of swarmRegistry.entries()) {
553
+ if (swarm.completedAt && swarm.completedAt < threshold) {
554
+ swarmRegistry.delete(id)
555
+ cleaned++
556
+ }
557
+ }
558
+ return cleaned
559
+ }
560
+
561
+ /** For tests only */
562
+ export function _clearSwarmRegistry(): void {
563
+ swarmRegistry.clear()
564
+ }
@@ -3,6 +3,8 @@
3
3
  * Events are accumulated between heartbeat ticks and drained into heartbeat prompts.
4
4
  */
5
5
 
6
+ import { hmrSingleton } from '@/lib/shared-utils'
7
+
6
8
  export interface SystemEvent {
7
9
  text: string
8
10
  timestamp: number
@@ -11,9 +13,7 @@ export interface SystemEvent {
11
13
 
12
14
  const MAX_EVENTS_PER_SESSION = 20
13
15
 
14
- const globalKey = '__swarmclaw_system_events__' as const
15
- const globalScope = globalThis as typeof globalThis & { [globalKey]?: Map<string, SystemEvent[]> }
16
- const queues: Map<string, SystemEvent[]> = globalScope[globalKey] ?? (globalScope[globalKey] = new Map())
16
+ const queues: Map<string, SystemEvent[]> = hmrSingleton('__swarmclaw_system_events__', () => new Map<string, SystemEvent[]>())
17
17
 
18
18
  /** Push an event for a session. Deduplicates consecutive identical text, caps at MAX_EVENTS_PER_SESSION. */
19
19
  export function enqueueSystemEvent(sessionId: string, text: string, contextKey?: string): void {