@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,391 @@
1
+ import assert from 'node:assert/strict'
2
+ import { afterEach, describe, it } from 'node:test'
3
+
4
+ import {
5
+ _clearSwarmRegistry,
6
+ getSwarm,
7
+ getSwarmSnapshot,
8
+ listSwarms,
9
+ removeSwarm,
10
+ aggregateResults,
11
+ type SwarmHandle,
12
+ type SwarmMember,
13
+ type SwarmSnapshot,
14
+ } from './subagent-swarm'
15
+
16
+ /**
17
+ * Unit tests for the swarm layer. Since spawnSubagent depends on storage,
18
+ * session-run-manager, and agent configs, we test the pure logic functions
19
+ * by constructing SwarmHandle objects directly and exercising the snapshot,
20
+ * aggregation, and registry code paths.
21
+ */
22
+
23
+ function fakeSwarmHandle(overrides?: Partial<SwarmHandle>): SwarmHandle {
24
+ const base: SwarmHandle = {
25
+ swarmId: 'swarm-test-1',
26
+ parentSessionId: 'parent-sess-1',
27
+ members: [],
28
+ status: 'running',
29
+ createdAt: Date.now() - 5000,
30
+ completedAt: null,
31
+ allSettled: Promise.resolve({
32
+ swarmId: 'swarm-test-1',
33
+ parentSessionId: 'parent-sess-1',
34
+ totalSpawned: 0,
35
+ totalCompleted: 0,
36
+ totalFailed: 0,
37
+ totalCancelled: 0,
38
+ totalSpawnErrors: 0,
39
+ durationMs: 0,
40
+ results: [],
41
+ }),
42
+ firstSettled: Promise.resolve({ index: -1, result: null as any }),
43
+ cancelAll: () => {},
44
+ ...overrides,
45
+ }
46
+ return base
47
+ }
48
+
49
+ function fakeMember(index: number, overrides?: Partial<SwarmMember>): SwarmMember {
50
+ return {
51
+ index,
52
+ handle: {
53
+ jobId: `job-${index}`,
54
+ sessionId: `sess-${index}`,
55
+ lineageId: `lin-${index}`,
56
+ agentId: `agent-${index}`,
57
+ agentName: `Agent ${index}`,
58
+ run: {
59
+ runId: `run-${index}`,
60
+ position: 0,
61
+ promise: Promise.resolve({ text: '', error: null, persisted: true, toolEvents: [], inputTokens: 0, outputTokens: 0, estimatedCost: 0 }),
62
+ abort: () => {},
63
+ unsubscribe: () => {},
64
+ },
65
+ promise: Promise.resolve({
66
+ jobId: `job-${index}`,
67
+ sessionId: `sess-${index}`,
68
+ lineageId: `lin-${index}`,
69
+ agentId: `agent-${index}`,
70
+ agentName: `Agent ${index}`,
71
+ status: 'completed' as const,
72
+ response: `Result from agent ${index}`,
73
+ error: null,
74
+ depth: 1,
75
+ parentSessionId: 'parent-sess-1',
76
+ childCount: 0,
77
+ durationMs: 1200,
78
+ }),
79
+ } as any,
80
+ result: null,
81
+ spawnError: null,
82
+ ...overrides,
83
+ }
84
+ }
85
+
86
+ describe('subagent-swarm', () => {
87
+ afterEach(() => {
88
+ _clearSwarmRegistry()
89
+ })
90
+
91
+ describe('SwarmHandle registry', () => {
92
+ it('stores and retrieves swarms', () => {
93
+ const swarm = fakeSwarmHandle()
94
+ const registry = (globalThis as any).__swarmclaw_swarm_registry__ as Map<string, SwarmHandle>
95
+ registry.set(swarm.swarmId, swarm)
96
+
97
+ const retrieved = getSwarm('swarm-test-1')
98
+ assert.ok(retrieved)
99
+ assert.equal(retrieved!.swarmId, 'swarm-test-1')
100
+ })
101
+
102
+ it('listSwarms filters by parentSessionId', () => {
103
+ const registry = (globalThis as any).__swarmclaw_swarm_registry__ as Map<string, SwarmHandle>
104
+ registry.set('s1', fakeSwarmHandle({ swarmId: 's1', parentSessionId: 'p1' }))
105
+ registry.set('s2', fakeSwarmHandle({ swarmId: 's2', parentSessionId: 'p2' }))
106
+ registry.set('s3', fakeSwarmHandle({ swarmId: 's3', parentSessionId: 'p1' }))
107
+
108
+ const p1Swarms = listSwarms('p1')
109
+ assert.equal(p1Swarms.length, 2)
110
+ assert.ok(p1Swarms.every((s) => s.parentSessionId === 'p1'))
111
+ })
112
+
113
+ it('removeSwarm deletes from registry', () => {
114
+ const registry = (globalThis as any).__swarmclaw_swarm_registry__ as Map<string, SwarmHandle>
115
+ registry.set('s1', fakeSwarmHandle({ swarmId: 's1' }))
116
+ assert.ok(getSwarm('s1'))
117
+ assert.equal(removeSwarm('s1'), true)
118
+ assert.equal(getSwarm('s1'), null)
119
+ })
120
+ })
121
+
122
+ describe('SwarmMember tracking', () => {
123
+ it('tracks members with spawn errors alongside successful spawns', () => {
124
+ const members = [
125
+ fakeMember(0),
126
+ fakeMember(1, { spawnError: 'Agent "missing" not found.', handle: null as any }),
127
+ fakeMember(2),
128
+ ]
129
+ const swarm = fakeSwarmHandle({ members })
130
+
131
+ assert.equal(swarm.members.length, 3)
132
+ assert.equal(swarm.members[0].spawnError, null)
133
+ assert.equal(swarm.members[1].spawnError, 'Agent "missing" not found.')
134
+ assert.equal(swarm.members[2].spawnError, null)
135
+ })
136
+
137
+ it('members can accumulate results independently', () => {
138
+ const m0 = fakeMember(0)
139
+ const m1 = fakeMember(1)
140
+ const swarm = fakeSwarmHandle({ members: [m0, m1] })
141
+
142
+ m0.result = {
143
+ jobId: 'job-0',
144
+ sessionId: 'sess-0',
145
+ lineageId: 'lin-0',
146
+ agentId: 'agent-0',
147
+ agentName: 'Agent 0',
148
+ status: 'completed',
149
+ response: 'Done!',
150
+ error: null,
151
+ depth: 1,
152
+ parentSessionId: 'parent-sess-1',
153
+ childCount: 0,
154
+ durationMs: 800,
155
+ }
156
+
157
+ assert.ok(m0.result)
158
+ assert.equal(m0.result.status, 'completed')
159
+ assert.equal(m1.result, null)
160
+ })
161
+ })
162
+
163
+ describe('SwarmSnapshot', () => {
164
+ it('builds a serializable snapshot for the UI', () => {
165
+ const m0 = fakeMember(0)
166
+ const m1 = fakeMember(1)
167
+ m0.result = {
168
+ jobId: 'job-0', sessionId: 'sess-0', lineageId: 'lin-0',
169
+ agentId: 'agent-0', agentName: 'Agent 0',
170
+ status: 'completed', response: 'All good', error: null,
171
+ depth: 1, parentSessionId: 'parent-sess-1',
172
+ childCount: 0, durationMs: 900,
173
+ }
174
+
175
+ const swarm = fakeSwarmHandle({
176
+ members: [m0, m1],
177
+ status: 'running',
178
+ })
179
+
180
+ const registry = (globalThis as any).__swarmclaw_swarm_registry__ as Map<string, SwarmHandle>
181
+ registry.set(swarm.swarmId, swarm)
182
+
183
+ const snapshot = getSwarmSnapshot(swarm.swarmId) as SwarmSnapshot
184
+ assert.ok(snapshot)
185
+ assert.equal(snapshot.memberCount, 2)
186
+ assert.equal(snapshot.completedCount, 1)
187
+ // m0 has result with status 'completed' — snapshot reads from result
188
+ assert.equal(snapshot.members[0].status, 'completed')
189
+ assert.equal(snapshot.members[0].resultPreview, 'All good')
190
+ // m1 has no result and no lineage node — falls back to 'running'
191
+ assert.equal(snapshot.members[1].status, 'running')
192
+ })
193
+
194
+ it('counts spawn errors in the snapshot', () => {
195
+ const members = [
196
+ fakeMember(0, { spawnError: 'Not found', handle: null as any }),
197
+ fakeMember(1),
198
+ ]
199
+ const swarm = fakeSwarmHandle({ members })
200
+ const registry = (globalThis as any).__swarmclaw_swarm_registry__ as Map<string, SwarmHandle>
201
+ registry.set(swarm.swarmId, swarm)
202
+
203
+ const snapshot = getSwarmSnapshot(swarm.swarmId) as SwarmSnapshot
204
+ assert.equal(snapshot.failedCount, 1)
205
+ assert.equal(snapshot.members[0].status, 'spawn_error')
206
+ assert.equal(snapshot.members[0].error, 'Not found')
207
+ })
208
+ })
209
+
210
+ describe('cancelAll', () => {
211
+ function buildCancelAll(swarm: SwarmHandle) {
212
+ return () => {
213
+ for (const member of swarm.members) {
214
+ if (member.handle && !member.result && !member.spawnError) {
215
+ try { member.handle.run.abort() } catch { /* best-effort */ }
216
+ }
217
+ }
218
+ swarm.status = 'failed'
219
+ }
220
+ }
221
+
222
+ it('invokes abort on all running member handles', () => {
223
+ let abortCount = 0
224
+ const m0 = fakeMember(0)
225
+ const m1 = fakeMember(1)
226
+ m0.handle.run.abort = () => { abortCount++ }
227
+ m1.handle.run.abort = () => { abortCount++ }
228
+
229
+ const swarm = fakeSwarmHandle({ members: [m0, m1] })
230
+ swarm.cancelAll = buildCancelAll(swarm)
231
+ swarm.cancelAll()
232
+
233
+ assert.equal(abortCount, 2)
234
+ assert.equal(swarm.status, 'failed')
235
+ })
236
+
237
+ it('skips members that already have results', () => {
238
+ let abortCount = 0
239
+ const m0 = fakeMember(0)
240
+ const m1 = fakeMember(1)
241
+ m0.handle.run.abort = () => { abortCount++ }
242
+ m1.handle.run.abort = () => { abortCount++ }
243
+ m0.result = { status: 'completed' } as any
244
+
245
+ const swarm = fakeSwarmHandle({ members: [m0, m1] })
246
+ swarm.cancelAll = buildCancelAll(swarm)
247
+ swarm.cancelAll()
248
+
249
+ assert.equal(abortCount, 1)
250
+ })
251
+
252
+ it('skips members with spawn errors', () => {
253
+ let abortCount = 0
254
+ const m0 = fakeMember(0, { spawnError: 'fail', handle: null as any })
255
+ const m1 = fakeMember(1)
256
+ m1.handle.run.abort = () => { abortCount++ }
257
+
258
+ const swarm = fakeSwarmHandle({ members: [m0, m1] })
259
+ swarm.cancelAll = buildCancelAll(swarm)
260
+ swarm.cancelAll()
261
+
262
+ assert.equal(abortCount, 1)
263
+ })
264
+ })
265
+
266
+ describe('aggregateResults (absorbed from batch)', () => {
267
+ it('returns not_found for unknown job IDs', () => {
268
+ const agg = aggregateResults(['unknown-job-1', 'unknown-job-2'])
269
+ assert.equal(agg.total, 2)
270
+ assert.equal(agg.failed, 2)
271
+ assert.equal(agg.allCompleted, true)
272
+ assert.equal(agg.pending.length, 0)
273
+ assert.equal(agg.results[0].status, 'not_found')
274
+ })
275
+ })
276
+
277
+ // ---------------------------------------------------------------------------
278
+ // Reliability fix: buildSwarmSnapshot null handle (#1)
279
+ // ---------------------------------------------------------------------------
280
+
281
+ describe('buildSwarmSnapshot — null handle without spawnError', () => {
282
+ it('returns spawn_error status instead of crashing on null handle', () => {
283
+ // Simulate a member that has handle: null but no explicit spawnError
284
+ // (edge case from race conditions during spawn)
285
+ const members = [
286
+ fakeMember(0),
287
+ fakeMember(1, { handle: null as any, spawnError: null }),
288
+ ]
289
+ const swarm = fakeSwarmHandle({ members })
290
+ const registry = (globalThis as any).__swarmclaw_swarm_registry__ as Map<string, SwarmHandle>
291
+ registry.set(swarm.swarmId, swarm)
292
+
293
+ const snapshot = getSwarmSnapshot(swarm.swarmId) as SwarmSnapshot
294
+ assert.ok(snapshot, 'Snapshot should not be null')
295
+ assert.equal(snapshot.members[1].status, 'spawn_error')
296
+ assert.equal(snapshot.members[1].error, 'Spawn failed (no handle)')
297
+ assert.equal(snapshot.members[1].agentId, '')
298
+ assert.equal(snapshot.failedCount, 1)
299
+ })
300
+
301
+ it('handles all members having null handles gracefully', () => {
302
+ const members = [
303
+ fakeMember(0, { handle: null as any, spawnError: 'Agent not found' }),
304
+ fakeMember(1, { handle: null as any, spawnError: null }),
305
+ fakeMember(2, { handle: null as any, spawnError: 'Config error' }),
306
+ ]
307
+ const swarm = fakeSwarmHandle({ members })
308
+ const registry = (globalThis as any).__swarmclaw_swarm_registry__ as Map<string, SwarmHandle>
309
+ registry.set(swarm.swarmId, swarm)
310
+
311
+ const snapshot = getSwarmSnapshot(swarm.swarmId) as SwarmSnapshot
312
+ assert.equal(snapshot.memberCount, 3)
313
+ assert.equal(snapshot.failedCount, 3)
314
+ assert.equal(snapshot.completedCount, 0)
315
+ // All three should be spawn_error
316
+ for (const m of snapshot.members) {
317
+ assert.equal(m.status, 'spawn_error')
318
+ }
319
+ // Member 0 and 2 have explicit errors, member 1 gets the fallback
320
+ assert.equal(snapshot.members[0].error, 'Agent not found')
321
+ assert.equal(snapshot.members[1].error, 'Spawn failed (no handle)')
322
+ assert.equal(snapshot.members[2].error, 'Config error')
323
+ })
324
+ })
325
+
326
+ // ---------------------------------------------------------------------------
327
+ // Reliability fix: firstSettled with zero memberPromises (#15)
328
+ // ---------------------------------------------------------------------------
329
+
330
+ describe('firstSettled — all spawn errors (zero promises)', () => {
331
+ it('firstSettled resolves with a valid SubagentResult, not null', async () => {
332
+ // Build a swarm where ALL members fail to spawn
333
+ const members = [
334
+ fakeMember(0, { handle: null as any, spawnError: 'Agent not found' }),
335
+ fakeMember(1, { handle: null as any, spawnError: 'Config error' }),
336
+ ]
337
+
338
+ // Simulate what spawnSwarm does: memberPromises is empty, so firstSettled
339
+ // falls back to allSettled.then(). We test this via the fakeSwarmHandle
340
+ // by constructing the same promise chain.
341
+ const allSettledResult = {
342
+ swarmId: 'swarm-all-fail',
343
+ parentSessionId: 'parent-1',
344
+ totalSpawned: 2,
345
+ totalCompleted: 0,
346
+ totalFailed: 2,
347
+ totalCancelled: 0,
348
+ totalSpawnErrors: 2,
349
+ durationMs: 100,
350
+ results: [
351
+ { index: 0, agentId: '', agentName: '', jobId: '', sessionId: '', status: 'spawn_error' as const, response: null, error: 'Agent not found', durationMs: 0 },
352
+ { index: 1, agentId: '', agentName: '', jobId: '', sessionId: '', status: 'spawn_error' as const, response: null, error: 'Config error', durationMs: 0 },
353
+ ],
354
+ }
355
+
356
+ const allSettled = Promise.resolve(allSettledResult)
357
+
358
+ // Mimic the firstSettled fallback: when memberPromises is empty,
359
+ // firstSettled = allSettled.then(agg => { first entry as SubagentResult })
360
+ const firstSettled = allSettled.then((agg) => {
361
+ const first = agg.results[0]
362
+ return {
363
+ index: first?.index ?? -1,
364
+ result: {
365
+ jobId: first?.jobId ?? '',
366
+ sessionId: first?.sessionId ?? '',
367
+ lineageId: '',
368
+ agentId: first?.agentId ?? '',
369
+ agentName: first?.agentName ?? '',
370
+ status: 'failed' as const,
371
+ response: null,
372
+ error: first?.error ?? 'No members spawned',
373
+ depth: 0,
374
+ parentSessionId: 'parent-1',
375
+ childCount: 0,
376
+ durationMs: 0,
377
+ },
378
+ }
379
+ })
380
+
381
+ const result = await firstSettled
382
+ assert.ok(result, 'firstSettled should resolve, not hang or crash')
383
+ assert.equal(result.result.status, 'failed')
384
+ assert.equal(result.result.error, 'Agent not found')
385
+ assert.equal(typeof result.index, 'number')
386
+ // Must be a proper SubagentResult, not null
387
+ assert.ok(result.result.jobId !== undefined)
388
+ assert.ok(result.result.sessionId !== undefined)
389
+ })
390
+ })
391
+ })