@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
@@ -1,39 +1,9 @@
1
1
  import assert from 'node:assert/strict'
2
- import fs from 'node:fs'
3
- import os from 'node:os'
4
- import path from 'node:path'
5
- import { spawnSync } from 'node:child_process'
6
2
  import { describe, it } from 'node:test'
3
+ import { runWithTempDataDir } from '../test-utils/run-with-temp-data-dir'
7
4
 
8
5
  import { sanitizeConnectorOutboundContent } from './manager'
9
6
 
10
- const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../../..')
11
-
12
- function runWithTempDataDir(script: string) {
13
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-manager-test-'))
14
- try {
15
- const result = spawnSync(process.execPath, ['--import', 'tsx', '--input-type=module', '--eval', script], {
16
- cwd: repoRoot,
17
- env: {
18
- ...process.env,
19
- DATA_DIR: tempDir,
20
- WORKSPACE_DIR: path.join(tempDir, 'workspace'),
21
- },
22
- encoding: 'utf-8',
23
- })
24
- assert.equal(result.status, 0, result.stderr || result.stdout || 'subprocess failed')
25
- const lines = (result.stdout || '')
26
- .trim()
27
- .split('\n')
28
- .map((line) => line.trim())
29
- .filter(Boolean)
30
- const jsonLine = [...lines].reverse().find((line) => line.startsWith('{'))
31
- return JSON.parse(jsonLine || '{}')
32
- } finally {
33
- fs.rmSync(tempDir, { recursive: true, force: true })
34
- }
35
- }
36
-
37
7
  describe('sanitizeConnectorOutboundContent', () => {
38
8
  it('strips hidden control tokens from captions without suppressing the media send itself', () => {
39
9
  assert.deepEqual(
@@ -67,9 +37,9 @@ describe('sanitizeConnectorOutboundContent', () => {
67
37
 
68
38
  it('mirrors direct WhatsApp inbound and assistant replies into the session transcript', () => {
69
39
  const output = runWithTempDataDir(`
70
- const storageMod = await import('./src/lib/server/storage.ts')
71
- const managerMod = await import('./src/lib/server/connectors/manager.ts')
72
- const providersMod = await import('./src/lib/providers/index.ts')
40
+ const storageMod = await import('./src/lib/server/storage')
41
+ const managerMod = await import('./src/lib/server/connectors/manager')
42
+ const providersMod = await import('./src/lib/providers/index')
73
43
  const storage = storageMod.default || storageMod
74
44
  const manager = managerMod.default || managerMod
75
45
  const providers = providersMod.default || providersMod
@@ -169,11 +139,114 @@ describe('sanitizeConnectorOutboundContent', () => {
169
139
  assert.equal(output.mainSession.messages[1].historyExcluded, true)
170
140
  })
171
141
 
142
+ it('queues connector heartbeat wakes on the agent thread session and records the system event there', () => {
143
+ const output = runWithTempDataDir(`
144
+ const storageMod = await import('./src/lib/server/storage')
145
+ const managerMod = await import('./src/lib/server/connectors/manager')
146
+ const providersMod = await import('./src/lib/providers/index')
147
+ const heartbeatMod = await import('./src/lib/server/heartbeat-wake')
148
+ const systemEventsMod = await import('./src/lib/server/system-events')
149
+ const storage = storageMod.default || storageMod
150
+ const manager = managerMod.default || managerMod
151
+ const providers = providersMod.default || providersMod
152
+ const heartbeat = heartbeatMod.default || heartbeatMod
153
+ const systemEvents = systemEventsMod.default || systemEventsMod
154
+
155
+ const now = Date.now()
156
+ heartbeat.resetHeartbeatWakeStateForTests()
157
+ providers.PROVIDERS['test-provider'] = {
158
+ id: 'test-provider',
159
+ name: 'Test Provider',
160
+ models: ['test-model'],
161
+ requiresApiKey: false,
162
+ requiresEndpoint: false,
163
+ handler: {
164
+ streamChat: async (opts) => {
165
+ opts.write('data: ' + JSON.stringify({ t: 'r', text: 'Heartbeat target check' }) + '\\n')
166
+ return ''
167
+ },
168
+ },
169
+ }
170
+
171
+ storage.saveSettings({})
172
+ storage.saveAgents({
173
+ agent_1: {
174
+ id: 'agent_1',
175
+ name: 'Molly',
176
+ provider: 'test-provider',
177
+ model: 'test-model',
178
+ plugins: [],
179
+ threadSessionId: 'agent_thread',
180
+ createdAt: now,
181
+ updatedAt: now,
182
+ },
183
+ })
184
+ storage.saveConnectors({
185
+ conn_1: {
186
+ id: 'conn_1',
187
+ name: 'WhatsApp',
188
+ platform: 'whatsapp',
189
+ agentId: 'agent_1',
190
+ credentialId: null,
191
+ config: { inboundDebounceMs: 0 },
192
+ isEnabled: true,
193
+ status: 'running',
194
+ createdAt: now,
195
+ updatedAt: now,
196
+ },
197
+ })
198
+ storage.saveSessions({
199
+ agent_thread: {
200
+ id: 'agent_thread',
201
+ name: 'Molly',
202
+ cwd: process.env.WORKSPACE_DIR,
203
+ user: 'default',
204
+ provider: 'test-provider',
205
+ model: 'test-model',
206
+ claudeSessionId: null,
207
+ messages: [],
208
+ createdAt: now,
209
+ lastActiveAt: now,
210
+ sessionType: 'human',
211
+ agentId: 'agent_1',
212
+ plugins: [],
213
+ },
214
+ })
215
+
216
+ const connector = storage.loadConnectors().conn_1
217
+ await manager.routeConnectorMessageForTest(connector, {
218
+ platform: 'whatsapp',
219
+ channelId: '15550001111@s.whatsapp.net',
220
+ senderId: '15550001111@s.whatsapp.net',
221
+ senderName: 'Alice',
222
+ text: 'Did you get this?',
223
+ messageId: 'in-thread-target',
224
+ isGroup: false,
225
+ })
226
+
227
+ const sessions = storage.loadSessions()
228
+ const directSession = Object.values(sessions).find((entry) => entry.id !== 'agent_thread')
229
+ console.log(JSON.stringify({
230
+ wake: heartbeat.snapshotPendingHeartbeatWakesForTests()[0] || null,
231
+ threadEvents: systemEvents.peekSystemEvents('agent_thread'),
232
+ directEvents: directSession ? systemEvents.peekSystemEvents(directSession.id) : [],
233
+ directSessionId: directSession?.id || null,
234
+ }))
235
+ `)
236
+
237
+ assert.equal(output.wake.sessionId, 'agent_thread')
238
+ assert.equal(output.wake.agentId, 'agent_1')
239
+ assert.equal(output.threadEvents.length, 1)
240
+ assert.match(output.threadEvents[0].text, /Inbound message from whatsapp: Did you get this\?/i)
241
+ assert.equal(output.directEvents.length, 0)
242
+ assert.ok(output.directSessionId)
243
+ })
244
+
172
245
  it('mirrors same-channel connector_message_tool sends when the agent suppresses visible text', () => {
173
246
  const output = runWithTempDataDir(`
174
- const storageMod = await import('./src/lib/server/storage.ts')
175
- const managerMod = await import('./src/lib/server/connectors/manager.ts')
176
- const providersMod = await import('./src/lib/providers/index.ts')
247
+ const storageMod = await import('./src/lib/server/storage')
248
+ const managerMod = await import('./src/lib/server/connectors/manager')
249
+ const providersMod = await import('./src/lib/providers/index')
177
250
  const storage = storageMod.default || storageMod
178
251
  const manager = managerMod.default || managerMod
179
252
  const providers = providersMod.default || providersMod
@@ -276,14 +349,14 @@ describe('sanitizeConnectorOutboundContent', () => {
276
349
  assert.equal(output.directSession.messages[1].source.messageId, 'wa-out-1')
277
350
  assert.equal(output.directSession.connectorContext.lastOutboundMessageId, 'wa-out-1')
278
351
  assert.equal(output.mainSession.messages.length, 2)
279
- assert.equal(output.mainSession.messages.every((entry) => entry.historyExcluded === true), true)
352
+ assert.equal(output.mainSession.messages.every((entry: any) => entry.historyExcluded === true), true)
280
353
  })
281
354
 
282
355
  it('accepts WhatsApp allowlist matches through senderIdAlt when the primary sender id is a lid', () => {
283
356
  const output = runWithTempDataDir(`
284
- const storageMod = await import('./src/lib/server/storage.ts')
285
- const managerMod = await import('./src/lib/server/connectors/manager.ts')
286
- const providersMod = await import('./src/lib/providers/index.ts')
357
+ const storageMod = await import('./src/lib/server/storage')
358
+ const managerMod = await import('./src/lib/server/connectors/manager')
359
+ const providersMod = await import('./src/lib/providers/index')
287
360
  const storage = storageMod.default || storageMod
288
361
  const manager = managerMod.default || managerMod
289
362
  const providers = providersMod.default || providersMod
@@ -357,14 +430,114 @@ describe('sanitizeConnectorOutboundContent', () => {
357
430
  assert.equal(output.session.messages[0].source.messageId, 'in-3')
358
431
  })
359
432
 
433
+ it('reuses the same direct WhatsApp session when a contact flips from lid to phone jid', () => {
434
+ const output = runWithTempDataDir(`
435
+ const storageMod = await import('./src/lib/server/storage')
436
+ const managerMod = await import('./src/lib/server/connectors/manager')
437
+ const providersMod = await import('./src/lib/providers/index')
438
+ const storage = storageMod.default || storageMod
439
+ const manager = managerMod.default || managerMod
440
+ const providers = providersMod.default || providersMod
441
+
442
+ const now = Date.now()
443
+ providers.PROVIDERS['test-provider'] = {
444
+ id: 'test-provider',
445
+ name: 'Test Provider',
446
+ models: ['test-model'],
447
+ requiresApiKey: false,
448
+ requiresEndpoint: false,
449
+ handler: {
450
+ streamChat: async (opts) => {
451
+ opts.write('data: ' + JSON.stringify({ t: 'r', text: 'Reply: ' + String(opts.message || '').slice(0, 32) }) + '\\n')
452
+ return ''
453
+ },
454
+ },
455
+ }
456
+
457
+ storage.saveSettings({})
458
+ storage.saveAgents({
459
+ agent_1: {
460
+ id: 'agent_1',
461
+ name: 'Molly',
462
+ provider: 'test-provider',
463
+ model: 'test-model',
464
+ plugins: [],
465
+ createdAt: now,
466
+ updatedAt: now,
467
+ },
468
+ })
469
+ storage.saveConnectors({
470
+ conn_1: {
471
+ id: 'conn_1',
472
+ name: 'WhatsApp',
473
+ platform: 'whatsapp',
474
+ agentId: 'agent_1',
475
+ credentialId: null,
476
+ config: { inboundDebounceMs: 0 },
477
+ isEnabled: true,
478
+ status: 'running',
479
+ createdAt: now,
480
+ updatedAt: now,
481
+ },
482
+ })
483
+ storage.saveSessions({})
484
+
485
+ const connector = storage.loadConnectors().conn_1
486
+ await manager.routeConnectorMessageForTest(connector, {
487
+ platform: 'whatsapp',
488
+ channelId: '199900000001@lid',
489
+ channelIdAlt: '15550001111@s.whatsapp.net',
490
+ senderId: '199900000001@lid',
491
+ senderIdAlt: '15550001111@s.whatsapp.net',
492
+ senderName: 'Alice',
493
+ text: 'First lid message',
494
+ messageId: 'in-lid',
495
+ isGroup: false,
496
+ })
497
+ await manager.routeConnectorMessageForTest(connector, {
498
+ platform: 'whatsapp',
499
+ channelId: '15550001111@s.whatsapp.net',
500
+ senderId: '15550001111@s.whatsapp.net',
501
+ senderName: 'Alice',
502
+ text: 'Second phone-jid message',
503
+ messageId: 'in-phone',
504
+ isGroup: false,
505
+ })
506
+
507
+ const directSessions = Object.values(storage.loadSessions())
508
+ .filter((entry) => String(entry.name || '').startsWith('connector:'))
509
+ .map((entry) => ({
510
+ id: entry.id,
511
+ name: entry.name,
512
+ connectorContext: entry.connectorContext,
513
+ messages: entry.messages.map((message) => ({
514
+ role: message.role,
515
+ text: message.text,
516
+ source: message.source || null,
517
+ })),
518
+ }))
519
+ console.log(JSON.stringify({ directSessions }))
520
+ `)
521
+
522
+ assert.equal(output.directSessions.length, 1)
523
+ assert.equal(output.directSessions[0].messages.length, 4)
524
+ assert.equal(output.directSessions[0].connectorContext.channelId, '15550001111@s.whatsapp.net')
525
+ assert.equal(output.directSessions[0].connectorContext.channelIdAlt, '15550001111@s.whatsapp.net')
526
+ assert.equal(output.directSessions[0].connectorContext.senderIdAlt, '15550001111@s.whatsapp.net')
527
+ assert.deepEqual(
528
+ output.directSessions[0].messages.filter((message: { role: string }) => message.role === 'user').map((message: { source: { channelId?: string | null } | null }) => message.source?.channelId),
529
+ ['199900000001@lid', '15550001111@s.whatsapp.net'],
530
+ )
531
+ })
532
+
360
533
  it('routes send_voice_note to the current connector conversation when an audio file already exists', () => {
361
534
  const output = runWithTempDataDir(`
362
535
  const fs = await import('node:fs')
363
536
  const path = await import('node:path')
364
- const storageMod = await import('./src/lib/server/storage.ts')
365
- const managerMod = await import('./src/lib/server/connectors/manager.ts')
366
- const pluginsMod = await import('./src/lib/server/plugins.ts')
367
- const toolsMod = await import('./src/lib/server/session-tools/index.ts')
537
+ const storageMod = await import('./src/lib/server/storage')
538
+ const managerMod = await import('./src/lib/server/connectors/manager')
539
+ const pluginsMod = await import('./src/lib/server/plugins')
540
+ const toolsMod = await import('./src/lib/server/session-tools/index')
368
541
  const storage = storageMod.default || storageMod
369
542
  const manager = managerMod.default || managerMod
370
543
  const plugins = pluginsMod.default || pluginsMod
@@ -483,9 +656,9 @@ describe('sanitizeConnectorOutboundContent', () => {
483
656
 
484
657
  it('restarts a stale connector automatically when an outbound send fails with connection closed', () => {
485
658
  const output = runWithTempDataDir(`
486
- const storageMod = await import('./src/lib/server/storage.ts')
487
- const managerMod = await import('./src/lib/server/connectors/manager.ts')
488
- const pluginsMod = await import('./src/lib/server/plugins.ts')
659
+ const storageMod = await import('./src/lib/server/storage')
660
+ const managerMod = await import('./src/lib/server/connectors/manager')
661
+ const pluginsMod = await import('./src/lib/server/plugins')
489
662
  const storage = storageMod.default || storageMod
490
663
  const manager = managerMod.default || managerMod
491
664
  const plugins = pluginsMod.default || pluginsMod
@@ -551,10 +724,10 @@ describe('sanitizeConnectorOutboundContent', () => {
551
724
  const output = runWithTempDataDir(`
552
725
  const fs = await import('node:fs')
553
726
  const path = await import('node:path')
554
- const storageMod = await import('./src/lib/server/storage.ts')
555
- const managerMod = await import('./src/lib/server/connectors/manager.ts')
556
- const pluginsMod = await import('./src/lib/server/plugins.ts')
557
- const toolsMod = await import('./src/lib/server/session-tools/index.ts')
727
+ const storageMod = await import('./src/lib/server/storage')
728
+ const managerMod = await import('./src/lib/server/connectors/manager')
729
+ const pluginsMod = await import('./src/lib/server/plugins')
730
+ const toolsMod = await import('./src/lib/server/session-tools/index')
558
731
  const storage = storageMod.default || storageMod
559
732
  const manager = managerMod.default || managerMod
560
733
  const plugins = pluginsMod.default || pluginsMod
@@ -675,9 +848,9 @@ describe('sanitizeConnectorOutboundContent', () => {
675
848
 
676
849
  it('keeps direct connector sessions isolated across four inbound senders for the same agent and mirrors their metadata into the main thread', () => {
677
850
  const output = runWithTempDataDir(`
678
- const storageMod = await import('./src/lib/server/storage.ts')
679
- const managerMod = await import('./src/lib/server/connectors/manager.ts')
680
- const providersMod = await import('./src/lib/providers/index.ts')
851
+ const storageMod = await import('./src/lib/server/storage')
852
+ const managerMod = await import('./src/lib/server/connectors/manager')
853
+ const providersMod = await import('./src/lib/providers/index')
681
854
  const storage = storageMod.default || storageMod
682
855
  const manager = managerMod.default || managerMod
683
856
  const providers = providersMod.default || providersMod
@@ -777,17 +950,17 @@ describe('sanitizeConnectorOutboundContent', () => {
777
950
  `)
778
951
 
779
952
  assert.equal(output.directSessions.length, 4)
780
- assert.deepEqual(output.directSessions.map((entry) => entry.senderName), ['Alice', 'Bob', 'Gran', 'Wayde'])
781
- assert.equal(output.directSessions.every((entry) => entry.texts.length === 2), true)
782
- assert.equal(output.directSessions.every((entry) => entry.texts[0].source?.senderName === entry.senderName), true)
783
- assert.equal(output.directSessions.every((entry) => entry.texts[1].text === `Replying to ${entry.senderName}`), true)
953
+ assert.deepEqual(output.directSessions.map((entry: any) => entry.senderName), ['Alice', 'Bob', 'Gran', 'Wayde'])
954
+ assert.equal(output.directSessions.every((entry: any) => entry.texts.length === 2), true)
955
+ assert.equal(output.directSessions.every((entry: any) => entry.texts[0].source?.senderName === entry.senderName), true)
956
+ assert.equal(output.directSessions.every((entry: any) => entry.texts[1].text === `Replying to ${entry.senderName}`), true)
784
957
  assert.equal(output.threadMessages.length, 8)
785
958
  assert.deepEqual(
786
- output.threadMessages.filter((msg) => msg.role === 'user').map((msg) => msg.source?.senderName),
959
+ output.threadMessages.filter((msg: any) => msg.role === 'user').map((msg: any) => msg.source?.senderName),
787
960
  ['Alice', 'Bob', 'Gran', 'Wayde'],
788
961
  )
789
962
  assert.deepEqual(
790
- output.threadMessages.filter((msg) => msg.role === 'assistant').map((msg) => ({
963
+ output.threadMessages.filter((msg: any) => msg.role === 'assistant').map((msg: any) => ({
791
964
  text: msg.text,
792
965
  senderName: msg.source?.senderName,
793
966
  connectorId: msg.source?.connectorId,
@@ -799,15 +972,15 @@ describe('sanitizeConnectorOutboundContent', () => {
799
972
  { text: 'Replying to Wayde', senderName: 'Wayde', connectorId: 'conn_1' },
800
973
  ],
801
974
  )
802
- assert.equal(output.threadMessages.every((msg) => msg.historyExcluded === true), true)
975
+ assert.equal(output.threadMessages.every((msg: any) => msg.historyExcluded === true), true)
803
976
  })
804
977
 
805
978
  it('excludes mirrored connector transcript entries from direct agent-thread history', () => {
806
979
  const output = runWithTempDataDir(`
807
- const storageMod = await import('./src/lib/server/storage.ts')
808
- const managerMod = await import('./src/lib/server/connectors/manager.ts')
809
- const chatExecMod = await import('./src/lib/server/chat-execution.ts')
810
- const providersMod = await import('./src/lib/providers/index.ts')
980
+ const storageMod = await import('./src/lib/server/storage')
981
+ const managerMod = await import('./src/lib/server/connectors/manager')
982
+ const chatExecMod = await import('./src/lib/server/chat-execution')
983
+ const providersMod = await import('./src/lib/providers/index')
811
984
  const storage = storageMod.default || storageMod
812
985
  const manager = managerMod.default || managerMod
813
986
  const chatExec = chatExecMod.default || chatExecMod
@@ -911,18 +1084,18 @@ describe('sanitizeConnectorOutboundContent', () => {
911
1084
 
912
1085
  assert.equal(output.reply.senderNames.includes('Alice'), false)
913
1086
  assert.equal(output.reply.senderNames.includes('Gran'), false)
914
- assert.equal(output.reply.texts.some((entry) => /Alice|Gran/.test(String(entry))), false)
1087
+ assert.equal(output.reply.texts.some((entry: any) => /Alice|Gran/.test(String(entry))), false)
915
1088
  assert.equal(output.reply.historyCount >= 1, true)
916
- assert.equal(output.threadMessages.some((msg) => msg.historyExcluded === true && msg.source?.connectorId === 'conn_1'), true)
1089
+ assert.equal(output.threadMessages.some((msg: any) => msg.historyExcluded === true && msg.source?.connectorId === 'conn_1'), true)
917
1090
  })
918
1091
 
919
1092
  it('creates one reusable connector-sender approval for unknown allowlist senders and allows them after approval', () => {
920
1093
  const output = runWithTempDataDir(`
921
- const storageMod = await import('./src/lib/server/storage.ts')
922
- const managerMod = await import('./src/lib/server/connectors/manager.ts')
923
- const approvalsMod = await import('./src/lib/server/approvals.ts')
924
- const pairingMod = await import('./src/lib/server/connectors/pairing.ts')
925
- const providersMod = await import('./src/lib/providers/index.ts')
1094
+ const storageMod = await import('./src/lib/server/storage')
1095
+ const managerMod = await import('./src/lib/server/connectors/manager')
1096
+ const approvalsMod = await import('./src/lib/server/approvals')
1097
+ const pairingMod = await import('./src/lib/server/connectors/pairing')
1098
+ const providersMod = await import('./src/lib/providers/index')
926
1099
  const storage = storageMod.default || storageMod
927
1100
  const manager = managerMod.default || managerMod
928
1101
  const approvals = approvalsMod.default || approvalsMod
@@ -1041,14 +1214,116 @@ describe('sanitizeConnectorOutboundContent', () => {
1041
1214
  assert.equal(output.third, 'Approved hello to Bob')
1042
1215
  assert.equal(output.approvalsAfter.length, 1)
1043
1216
  assert.equal(output.approvalsAfter[0].status, 'approved')
1044
- assert.equal(output.threadMessages.some((msg) => msg.source?.senderName === 'Bob' && msg.historyExcluded === true), true)
1217
+ assert.equal(output.threadMessages.some((msg: any) => msg.source?.senderName === 'Bob' && msg.historyExcluded === true), true)
1218
+ })
1219
+
1220
+ it('allows WhatsApp senders listed in global settings without creating connector approvals', () => {
1221
+ const output = runWithTempDataDir(`
1222
+ const storageMod = await import('./src/lib/server/storage')
1223
+ const managerMod = await import('./src/lib/server/connectors/manager')
1224
+ const providersMod = await import('./src/lib/providers/index')
1225
+ const storage = storageMod.default || storageMod
1226
+ const manager = managerMod.default || managerMod
1227
+ const providers = providersMod.default || providersMod
1228
+
1229
+ const now = Date.now()
1230
+ providers.PROVIDERS['test-provider'] = {
1231
+ id: 'test-provider',
1232
+ name: 'Test Provider',
1233
+ models: ['test-model'],
1234
+ requiresApiKey: false,
1235
+ requiresEndpoint: false,
1236
+ handler: {
1237
+ streamChat: async (opts) => {
1238
+ opts.write('data: ' + JSON.stringify({ t: 'r', text: 'Approved hello to Bob' }) + '\\n')
1239
+ return ''
1240
+ },
1241
+ },
1242
+ }
1243
+
1244
+ storage.saveSettings({
1245
+ approvalsEnabled: true,
1246
+ whatsappApprovedContacts: [
1247
+ { id: 'family', label: 'Family', phone: '+16660002222' },
1248
+ ],
1249
+ })
1250
+ storage.saveAgents({
1251
+ agent_1: {
1252
+ id: 'agent_1',
1253
+ name: 'Molly',
1254
+ provider: 'test-provider',
1255
+ model: 'test-model',
1256
+ plugins: [],
1257
+ threadSessionId: 'agent_thread',
1258
+ createdAt: now,
1259
+ updatedAt: now,
1260
+ },
1261
+ })
1262
+ storage.saveConnectors({
1263
+ conn_1: {
1264
+ id: 'conn_1',
1265
+ name: 'WhatsApp',
1266
+ platform: 'whatsapp',
1267
+ agentId: 'agent_1',
1268
+ credentialId: null,
1269
+ config: {
1270
+ inboundDebounceMs: 0,
1271
+ dmPolicy: 'allowlist',
1272
+ allowFrom: '15550001111',
1273
+ },
1274
+ isEnabled: true,
1275
+ status: 'running',
1276
+ createdAt: now,
1277
+ updatedAt: now,
1278
+ },
1279
+ })
1280
+ storage.saveSessions({
1281
+ agent_thread: {
1282
+ id: 'agent_thread',
1283
+ name: 'Molly',
1284
+ cwd: process.env.WORKSPACE_DIR,
1285
+ user: 'default',
1286
+ provider: 'test-provider',
1287
+ model: 'test-model',
1288
+ claudeSessionId: null,
1289
+ messages: [],
1290
+ createdAt: now,
1291
+ lastActiveAt: now,
1292
+ sessionType: 'human',
1293
+ agentId: 'agent_1',
1294
+ plugins: [],
1295
+ },
1296
+ })
1297
+
1298
+ const connector = storage.loadConnectors().conn_1
1299
+ const reply = await manager.routeConnectorMessageForTest(connector, {
1300
+ platform: 'whatsapp',
1301
+ channelId: '16660002222@s.whatsapp.net',
1302
+ senderId: '16660002222@s.whatsapp.net',
1303
+ senderName: 'Bob',
1304
+ text: 'Hello from approved settings contact',
1305
+ messageId: 'in-b-settings',
1306
+ isGroup: false,
1307
+ })
1308
+ const approvals = Object.values(storage.loadApprovals())
1309
+ const thread = storage.loadSessions().agent_thread
1310
+ console.log(JSON.stringify({
1311
+ reply,
1312
+ approvals,
1313
+ threadMessages: thread.messages,
1314
+ }))
1315
+ `)
1316
+
1317
+ assert.equal(output.reply, 'Approved hello to Bob')
1318
+ assert.equal(output.approvals.length, 0)
1319
+ assert.equal(output.threadMessages.some((msg: any) => msg.source?.senderName === 'Bob' && msg.historyExcluded === true), true)
1045
1320
  })
1046
1321
 
1047
1322
  it('returns a friendly retry message instead of blank no-response when connector chat aborts', () => {
1048
1323
  const output = runWithTempDataDir(`
1049
- const storageMod = await import('./src/lib/server/storage.ts')
1050
- const managerMod = await import('./src/lib/server/connectors/manager.ts')
1051
- const providersMod = await import('./src/lib/providers/index.ts')
1324
+ const storageMod = await import('./src/lib/server/storage')
1325
+ const managerMod = await import('./src/lib/server/connectors/manager')
1326
+ const providersMod = await import('./src/lib/providers/index')
1052
1327
  const storage = storageMod.default || storageMod
1053
1328
  const manager = managerMod.default || managerMod
1054
1329
  const providers = providersMod.default || providersMod