@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,142 @@
1
+ import type { Connector, MessageSource } from '@/types'
2
+ import { loadConnectors } from '../storage'
3
+ import { notify } from '../ws-hub'
4
+ import { resolveConnectorSessionPolicy, shouldReplyToInboundMessage } from './policy'
5
+ import { runningConnectors } from './runtime-state'
6
+ import { findDirectSessionForInbound, persistSessionRecord } from './session'
7
+ import type { InboundMessage } from './types'
8
+
9
+ export function getConnectorReplySendOptions(params: {
10
+ connectorId: string
11
+ inbound: InboundMessage
12
+ }): { replyToMessageId?: string; threadId?: string } {
13
+ const connectors = loadConnectors()
14
+ const connector = connectors[params.connectorId] as Connector | undefined
15
+ if (!connector) return {}
16
+ const session = findDirectSessionForInbound(connector, params.inbound)
17
+ const policy = resolveConnectorSessionPolicy(connector, params.inbound, session)
18
+ return shouldReplyToInboundMessage({
19
+ msg: params.inbound,
20
+ session,
21
+ policy,
22
+ })
23
+ }
24
+
25
+ export function statusReactionForPlatform(platform: string, state: 'processing' | 'sent' | 'silent'): string {
26
+ if (platform === 'slack') {
27
+ if (state === 'processing') return 'eyes'
28
+ if (state === 'sent') return 'white_check_mark'
29
+ return 'zipper_mouth_face'
30
+ }
31
+ if (state === 'processing') return '👀'
32
+ if (state === 'sent') return '✅'
33
+ return '🤐'
34
+ }
35
+
36
+ export async function maybeSendStatusReaction(
37
+ connector: Connector,
38
+ msg: InboundMessage,
39
+ state: 'processing' | 'sent' | 'silent',
40
+ ): Promise<void> {
41
+ if (!msg.messageId) return
42
+ const session = findDirectSessionForInbound(connector, msg)
43
+ const policy = resolveConnectorSessionPolicy(connector, msg, session)
44
+ if (!policy.statusReactions) return
45
+ const instance = runningConnectors.get(connector.id)
46
+ if (!instance?.sendReaction) return
47
+ try {
48
+ await instance.sendReaction(msg.channelId, msg.messageId, statusReactionForPlatform(connector.platform, state))
49
+ } catch {
50
+ // Status reactions are best-effort only.
51
+ }
52
+ }
53
+
54
+ export async function recordConnectorOutboundDelivery(params: {
55
+ connectorId: string
56
+ inbound: InboundMessage
57
+ messageId?: string
58
+ state?: 'sent' | 'silent'
59
+ }): Promise<void> {
60
+ const connectors = loadConnectors()
61
+ const connector = connectors[params.connectorId] as Connector | undefined
62
+ if (!connector) return
63
+ const session = findDirectSessionForInbound(connector, params.inbound)
64
+ if (session) {
65
+ session.connectorContext = {
66
+ ...(session.connectorContext || {}),
67
+ lastOutboundAt: Date.now(),
68
+ lastOutboundMessageId: params.messageId || session.connectorContext?.lastOutboundMessageId || null,
69
+ threadId: params.inbound.threadId || session.connectorContext?.threadId || null,
70
+ }
71
+ const history = Array.isArray(session.messages) ? session.messages : []
72
+ for (let i = history.length - 1; i >= 0; i -= 1) {
73
+ const entry = history[i]
74
+ if (entry?.role !== 'assistant') continue
75
+ const source: Partial<MessageSource> = entry?.source || {}
76
+ if (source.connectorId !== connector.id) continue
77
+ if (source.channelId !== params.inbound.channelId) continue
78
+ if (!source.messageId && params.messageId) {
79
+ entry.source = {
80
+ platform: source.platform || connector.platform,
81
+ connectorId: source.connectorId || connector.id,
82
+ connectorName: source.connectorName || connector.name,
83
+ channelId: source.channelId || params.inbound.channelId,
84
+ senderId: source.senderId,
85
+ senderName: source.senderName,
86
+ messageId: params.messageId,
87
+ replyToMessageId: source.replyToMessageId || params.inbound.messageId,
88
+ threadId: source.threadId || params.inbound.threadId,
89
+ }
90
+ }
91
+ break
92
+ }
93
+ persistSessionRecord(session)
94
+ notify(`messages:${session.id}`)
95
+ }
96
+ if (params.state) {
97
+ await maybeSendStatusReaction(connector, params.inbound, params.state)
98
+ }
99
+ }
100
+
101
+ export function splitConnectorText(text: string, maxChunkLength: number): string[] {
102
+ if (!text) return ['']
103
+ if (text.length <= maxChunkLength) return [text]
104
+ return text.match(new RegExp(`[\\s\\S]{1,${Math.max(1, maxChunkLength)}}`, 'g')) || [text]
105
+ }
106
+
107
+ export async function deliverChunkedConnectorText(params: {
108
+ connectorId: string
109
+ inbound: InboundMessage
110
+ text: string
111
+ maxSingleMessageLength: number
112
+ chunkLength: number
113
+ sendChunk: (
114
+ chunk: string,
115
+ meta: { isFirstChunk: boolean; replyToMessageId?: string; threadId?: string },
116
+ ) => Promise<string | undefined>
117
+ }): Promise<string | undefined> {
118
+ const replyOptions = getConnectorReplySendOptions({
119
+ connectorId: params.connectorId,
120
+ inbound: params.inbound,
121
+ })
122
+ const chunks = params.text.length <= params.maxSingleMessageLength
123
+ ? [params.text]
124
+ : splitConnectorText(params.text, params.chunkLength)
125
+
126
+ let lastMessageId: string | undefined
127
+ for (let index = 0; index < chunks.length; index += 1) {
128
+ lastMessageId = await params.sendChunk(chunks[index], {
129
+ isFirstChunk: index === 0,
130
+ replyToMessageId: replyOptions.replyToMessageId,
131
+ threadId: replyOptions.threadId,
132
+ })
133
+ }
134
+
135
+ await recordConnectorOutboundDelivery({
136
+ connectorId: params.connectorId,
137
+ inbound: params.inbound,
138
+ messageId: lastMessageId,
139
+ state: 'sent',
140
+ })
141
+ return lastMessageId
142
+ }
@@ -3,8 +3,10 @@ import fs from 'fs'
3
3
  import path from 'path'
4
4
  import type { Connector } from '@/types'
5
5
  import type { PlatformConnector, ConnectorInstance, InboundMessage, InboundThreadHistoryEntry } from './types'
6
+ import { resolveConnectorIngressReply } from './ingress-delivery'
7
+ import { deliverChunkedConnectorText } from './delivery'
6
8
  import { downloadInboundMediaToUpload, inferInboundMediaType } from './media'
7
- import { getConnectorReplySendOptions, isNoMessage, recordConnectorOutboundDelivery } from './manager'
9
+ import { errorMessage } from '@/lib/shared-utils'
8
10
 
9
11
  function buildDiscordThreadTitle(params: {
10
12
  threadName?: string
@@ -87,7 +89,7 @@ async function hydrateDiscordThreadContext(message: any, inbound: InboundMessage
87
89
  }].filter((entry) => entry.text.trim().length > 0)
88
90
  }
89
91
  } catch (err: unknown) {
90
- console.warn(`[discord] Thread context bootstrap failed: ${err instanceof Error ? err.message : String(err)}`)
92
+ console.warn(`[discord] Thread context bootstrap failed: ${errorMessage(err)}`)
91
93
  }
92
94
  }
93
95
 
@@ -152,7 +154,7 @@ const discord: PlatformConnector = {
152
154
  continue
153
155
  }
154
156
  } catch (err: unknown) {
155
- const errMsg = err instanceof Error ? err.message : String(err)
157
+ const errMsg = errorMessage(err)
156
158
  console.warn(`[discord] Media download failed (${attachment.name || 'file'}):`, errMsg)
157
159
  }
158
160
  }
@@ -188,44 +190,29 @@ const discord: PlatformConnector = {
188
190
  try {
189
191
  // Show typing indicator
190
192
  await message.channel.sendTyping()
191
- const response = await onMessage(inbound)
192
-
193
- if (isNoMessage(response)) return
194
-
195
- const replyOptions = getConnectorReplySendOptions({ connectorId: connector.id, inbound })
196
- const targetChannelId = replyOptions.threadId || inbound.channelId
197
- const sendChunk = async (chunk: string, isFirstChunk: boolean) => {
198
- const channel = await resolveTextChannel(targetChannelId)
199
- const payload: Record<string, unknown> = {
200
- content: chunk,
201
- allowedMentions: { repliedUser: false },
202
- }
203
- if (isFirstChunk && replyOptions.replyToMessageId) {
204
- payload.reply = {
205
- messageReference: replyOptions.replyToMessageId,
206
- failIfNotExists: false,
207
- }
208
- }
209
- const sent = await channel.send(payload)
210
- return String(sent.id || '')
211
- }
212
-
213
- let lastMessageId: string | undefined
214
- // Discord has a 2000 char limit per message
215
- if (response.length <= 2000) {
216
- lastMessageId = await sendChunk(response, true)
217
- } else {
218
- // Split into chunks
219
- const chunks = response.match(/[\s\S]{1,1990}/g) || [response]
220
- for (let i = 0; i < chunks.length; i += 1) {
221
- lastMessageId = await sendChunk(chunks[i], i === 0)
222
- }
223
- }
224
- await recordConnectorOutboundDelivery({
193
+ const reply = await resolveConnectorIngressReply(onMessage, inbound)
194
+ if (!reply) return
195
+ await deliverChunkedConnectorText({
225
196
  connectorId: connector.id,
226
197
  inbound,
227
- messageId: lastMessageId,
228
- state: 'sent',
198
+ text: reply.visibleText,
199
+ maxSingleMessageLength: 2000,
200
+ chunkLength: 1990,
201
+ sendChunk: async (chunk, meta) => {
202
+ const channel = await resolveTextChannel(meta.threadId || inbound.channelId)
203
+ const payload: Record<string, unknown> = {
204
+ content: chunk,
205
+ allowedMentions: { repliedUser: false },
206
+ }
207
+ if (meta.isFirstChunk && meta.replyToMessageId) {
208
+ payload.reply = {
209
+ messageReference: meta.replyToMessageId,
210
+ failIfNotExists: false,
211
+ }
212
+ }
213
+ const sent = await channel.send(payload)
214
+ return String(sent.id || '')
215
+ },
229
216
  })
230
217
  } catch (err: any) {
231
218
  console.error(`[discord] Error handling message:`, err.message)
@@ -238,7 +225,7 @@ const discord: PlatformConnector = {
238
225
  await client.login(botToken)
239
226
  console.log(`[discord] Bot logged in as ${client.user?.tag}`)
240
227
 
241
- return {
228
+ const instance: ConnectorInstance = {
242
229
  connector,
243
230
  isAlive() {
244
231
  return client.isReady()
@@ -300,6 +287,16 @@ const discord: PlatformConnector = {
300
287
  console.log(`[discord] Bot disconnected`)
301
288
  },
302
289
  }
290
+
291
+ // Terminal disconnect — discord.js won't auto-reconnect for these
292
+ client.once('invalidated', () => {
293
+ instance.onCrash?.('Discord session invalidated')
294
+ })
295
+ client.on('shardError', (error) => {
296
+ console.error(`[discord] Shard error:`, error.message)
297
+ })
298
+
299
+ return instance
303
300
  },
304
301
  }
305
302
 
@@ -3,7 +3,8 @@ import { createTransport, type Transporter } from 'nodemailer'
3
3
  import { simpleParser } from 'mailparser'
4
4
  import type { Connector } from '@/types'
5
5
  import type { PlatformConnector, ConnectorInstance, InboundMessage } from './types'
6
- import { isNoMessage } from './manager'
6
+ import { resolveConnectorIngressReply } from './ingress-delivery'
7
+ import { errorMessage } from '@/lib/shared-utils'
7
8
 
8
9
  interface EmailConfig {
9
10
  imapHost: string
@@ -92,7 +93,7 @@ const email: PlatformConnector = {
92
93
  }
93
94
  } catch (err: unknown) {
94
95
  connected = false
95
- const msg = err instanceof Error ? err.message : String(err)
96
+ const msg = errorMessage(err)
96
97
  console.error(`[email] IMAP connection failed: ${msg}`)
97
98
  throw err
98
99
  }
@@ -105,7 +106,7 @@ const email: PlatformConnector = {
105
106
  try {
106
107
  lock = await imap.getMailboxLock(folder)
107
108
  } catch (err: unknown) {
108
- const msg = err instanceof Error ? err.message : String(err)
109
+ const msg = errorMessage(err)
109
110
  console.error(`[email] Failed to acquire mailbox lock: ${msg}`)
110
111
  connected = false
111
112
  return
@@ -125,7 +126,7 @@ const email: PlatformConnector = {
125
126
  try {
126
127
  await processMessage(msg)
127
128
  } catch (err: unknown) {
128
- const errMsg = err instanceof Error ? err.message : String(err)
129
+ const errMsg = errorMessage(err)
129
130
  console.error(`[email] Error processing message UID ${msg.uid}: ${errMsg}`)
130
131
  }
131
132
  if (msg.uid > highwaterUid) {
@@ -133,7 +134,7 @@ const email: PlatformConnector = {
133
134
  }
134
135
  }
135
136
  } catch (err: unknown) {
136
- const errMsg = err instanceof Error ? err.message : String(err)
137
+ const errMsg = errorMessage(err)
137
138
  // A fetch on an empty range can throw; that's normal
138
139
  if (!errMsg.includes('Nothing to fetch')) {
139
140
  console.error(`[email] Poll error: ${errMsg}`)
@@ -191,13 +192,13 @@ const email: PlatformConnector = {
191
192
  }
192
193
 
193
194
  try {
194
- const response = await onMessage(inbound)
195
- if (isNoMessage(response)) return
195
+ const reply = await resolveConnectorIngressReply(onMessage, inbound)
196
+ if (!reply) return
196
197
 
197
198
  // Reply via SMTP
198
- await sendReply(channelId, response)
199
+ await sendReply(channelId, reply.visibleText)
199
200
  } catch (err: unknown) {
200
- const errMsg = err instanceof Error ? err.message : String(err)
201
+ const errMsg = errorMessage(err)
201
202
  console.error(`[email] Error handling message from ${fromAddr}: ${errMsg}`)
202
203
  }
203
204
  }
@@ -229,7 +230,7 @@ const email: PlatformConnector = {
229
230
 
230
231
  pollTimer = setInterval(() => {
231
232
  pollForNewMessages().catch((err: unknown) => {
232
- const msg = err instanceof Error ? err.message : String(err)
233
+ const msg = errorMessage(err)
233
234
  console.error(`[email] Poll interval error: ${msg}`)
234
235
  })
235
236
  }, pollMs)
@@ -1,5 +1,5 @@
1
1
  import type { PlatformConnector, ConnectorInstance, InboundMessage } from './types'
2
- import { isNoMessage } from './manager'
2
+ import { resolveConnectorIngressReply } from './ingress-delivery'
3
3
 
4
4
  const googlechat: PlatformConnector = {
5
5
  async start(connector, botToken, onMessage): Promise<ConnectorInstance> {
@@ -70,9 +70,9 @@ const googlechat: PlatformConnector = {
70
70
  text,
71
71
  }
72
72
 
73
- const response = await onMessage(inbound)
74
- if (!response || isNoMessage(response)) return {}
75
- return { text: response }
73
+ const reply = await resolveConnectorIngressReply(onMessage, inbound)
74
+ if (!reply) return {}
75
+ return { text: reply.visibleText }
76
76
  }
77
77
 
78
78
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -3,6 +3,7 @@ import path from 'node:path'
3
3
  import { decryptKey, loadCredentials, loadSettings } from '../storage'
4
4
  import { mimeFromPath } from './media'
5
5
  import type { InboundMessage, InboundMedia } from './types'
6
+ import { errorMessage } from '@/lib/shared-utils'
6
7
 
7
8
  const PLACEHOLDER_TEXT = new Set([
8
9
  '',
@@ -252,7 +253,7 @@ export async function enrichInboundMessageWithAudioTranscript(params: {
252
253
  console.log(`[connector] Inbound audio transcribed via ${attempt.provider}: ${path.basename(localPath)}`)
253
254
  return { ...msg, text: transcript }
254
255
  } catch (err: unknown) {
255
- const reason = err instanceof Error ? err.message : String(err)
256
+ const reason = errorMessage(err)
256
257
  console.warn(`[connector] Inbound audio transcription failed via ${attempt.provider}: ${reason}`)
257
258
  }
258
259
  }
@@ -0,0 +1,23 @@
1
+ import { isNoMessage } from './message-sentinel'
2
+ import { normalizeConnectorIngressResult, type ConnectorIngressResult, type ConnectorRouteResult, type InboundMessage } from './types'
3
+
4
+ export interface ConnectorIngressReply {
5
+ routeResult: ConnectorRouteResult
6
+ visibleText: string
7
+ }
8
+
9
+ export async function resolveConnectorIngressReply(
10
+ onMessage: (msg: InboundMessage) => Promise<ConnectorIngressResult>,
11
+ inbound: InboundMessage,
12
+ ): Promise<ConnectorIngressReply | null> {
13
+ const routeResult = normalizeConnectorIngressResult(await onMessage(inbound))
14
+ if (routeResult.managerHandled || routeResult.delivery === 'silent') return null
15
+
16
+ const visibleText = routeResult.visibleText
17
+ if (!visibleText || isNoMessage(visibleText)) return null
18
+
19
+ return {
20
+ routeResult,
21
+ visibleText,
22
+ }
23
+ }