@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
@@ -7,6 +7,7 @@ import type { Plugin, PluginHooks } from '@/types'
7
7
  import { getPluginManager } from '../plugins'
8
8
  import { normalizeToolInputArgs } from './normalize-tool-args'
9
9
  import { canonicalizePluginId } from '../tool-aliases'
10
+ import { errorMessage, sleep } from '@/lib/shared-utils'
10
11
  import {
11
12
  appendDelegationCheckpoint,
12
13
  cancelDelegationJob,
@@ -87,9 +88,6 @@ function _computeDelegationDepth(
87
88
  return depth
88
89
  }
89
90
 
90
- function sleep(ms: number) {
91
- return new Promise((resolve) => setTimeout(resolve, ms))
92
- }
93
91
 
94
92
  function buildDelegateContextFromSessionish(session: unknown): DelegateContext {
95
93
  const record = session && typeof session === 'object' ? session as Record<string, unknown> : {}
@@ -560,7 +558,7 @@ async function executeDelegateAction(args: Record<string, unknown>, bctx: Delega
560
558
  return { backend, result }
561
559
  })
562
560
  .catch((err: unknown) => {
563
- const message = err instanceof Error ? err.message : String(err)
561
+ const message = errorMessage(err)
564
562
  const latest = getDelegationJob(job.id)
565
563
  if (latest?.status === 'cancelled') return { backend: requestedBackend, result: `Error: ${message}` }
566
564
  appendDelegationCheckpoint(job.id, `Delegate crashed on ${requestedBackend}: ${message}`, 'failed')
@@ -697,7 +695,7 @@ async function runCodexDelegate(binary: string, task: string, resume: boolean, r
697
695
  child.stdin?.end()
698
696
  })
699
697
  } catch (err: unknown) {
700
- return `Error: ${err instanceof Error ? err.message : String(err)}`
698
+ return `Error: ${errorMessage(err)}`
701
699
  }
702
700
  }
703
701
 
@@ -774,7 +772,7 @@ async function runOpenCodeDelegate(binary: string, task: string, resume: boolean
774
772
  })
775
773
  })
776
774
  } catch (err: unknown) {
777
- return `Error: ${err instanceof Error ? err.message : String(err)}`
775
+ return `Error: ${errorMessage(err)}`
778
776
  }
779
777
  }
780
778
 
@@ -855,7 +853,7 @@ async function runGeminiDelegate(binary: string, task: string, resume: boolean,
855
853
  })
856
854
  })
857
855
  } catch (err: unknown) {
858
- return `Error: ${err instanceof Error ? err.message : String(err)}`
856
+ return `Error: ${errorMessage(err)}`
859
857
  }
860
858
  }
861
859
 
@@ -913,7 +911,7 @@ async function runClaudeDelegate(binary: string, task: string, resume: boolean,
913
911
  child.stdin?.write(task)
914
912
  child.stdin?.end()
915
913
  })
916
- } catch (err: unknown) { return `Error: ${err instanceof Error ? err.message : String(err)}` }
914
+ } catch (err: unknown) { return `Error: ${errorMessage(err)}` }
917
915
  }
918
916
 
919
917
  /**
@@ -35,8 +35,8 @@ function runWithTempDataDir(script: string) {
35
35
  describe('discovery approval flows', () => {
36
36
  it('request_tool_access creates a real approval and grants the tool when auto-approved', () => {
37
37
  const output = runWithTempDataDir(`
38
- const storageMod = await import('./src/lib/server/storage.ts')
39
- const toolsMod = await import('./src/lib/server/session-tools/index.ts')
38
+ const storageMod = await import('./src/lib/server/storage')
39
+ const toolsMod = await import('./src/lib/server/session-tools/index')
40
40
  const storage = storageMod.default || storageMod
41
41
  const toolsApi = toolsMod.default || toolsMod
42
42
 
@@ -84,8 +84,8 @@ describe('discovery approval flows', () => {
84
84
 
85
85
  it('manage_capabilities request_access accepts query aliases for pluginId', () => {
86
86
  const output = runWithTempDataDir(`
87
- const storageMod = await import('./src/lib/server/storage.ts')
88
- const toolsMod = await import('./src/lib/server/session-tools/index.ts')
87
+ const storageMod = await import('./src/lib/server/storage')
88
+ const toolsMod = await import('./src/lib/server/session-tools/index')
89
89
  const storage = storageMod.default || storageMod
90
90
  const toolsApi = toolsMod.default || toolsMod
91
91
 
@@ -130,8 +130,8 @@ describe('discovery approval flows', () => {
130
130
 
131
131
  it('manage_capabilities request_access tells the agent to call already-available alias tools directly', () => {
132
132
  const output = runWithTempDataDir(`
133
- const storageMod = await import('./src/lib/server/storage.ts')
134
- const toolsMod = await import('./src/lib/server/session-tools/index.ts')
133
+ const storageMod = await import('./src/lib/server/storage')
134
+ const toolsMod = await import('./src/lib/server/session-tools/index')
135
135
  const storage = storageMod.default || storageMod
136
136
  const toolsApi = toolsMod.default || toolsMod
137
137
 
@@ -170,8 +170,8 @@ describe('discovery approval flows', () => {
170
170
 
171
171
  it('granting manage_schedules does not surface the manage_platform umbrella tool', () => {
172
172
  const output = runWithTempDataDir(`
173
- const storageMod = await import('./src/lib/server/storage.ts')
174
- const toolsMod = await import('./src/lib/server/session-tools/index.ts')
173
+ const storageMod = await import('./src/lib/server/storage')
174
+ const toolsMod = await import('./src/lib/server/session-tools/index')
175
175
  const storage = storageMod.default || storageMod
176
176
  const toolsApi = toolsMod.default || toolsMod
177
177
 
@@ -210,8 +210,8 @@ describe('discovery approval flows', () => {
210
210
 
211
211
  it('session-granted builtins disabled by default still appear in the next turn tool list', () => {
212
212
  const output = runWithTempDataDir(`
213
- const storageMod = await import('./src/lib/server/storage.ts')
214
- const toolsMod = await import('./src/lib/server/session-tools/index.ts')
213
+ const storageMod = await import('./src/lib/server/storage')
214
+ const toolsMod = await import('./src/lib/server/session-tools/index')
215
215
  const storage = storageMod.default || storageMod
216
216
  const toolsApi = toolsMod.default || toolsMod
217
217
 
@@ -249,8 +249,8 @@ describe('discovery approval flows', () => {
249
249
 
250
250
  it('discover reports session-granted builtin tools as available now', () => {
251
251
  const output = runWithTempDataDir(`
252
- const storageMod = await import('./src/lib/server/storage.ts')
253
- const toolsMod = await import('./src/lib/server/session-tools/index.ts')
252
+ const storageMod = await import('./src/lib/server/storage')
253
+ const toolsMod = await import('./src/lib/server/session-tools/index')
254
254
  const storage = storageMod.default || storageMod
255
255
  const toolsApi = toolsMod.default || toolsMod
256
256
 
@@ -293,9 +293,9 @@ describe('discovery approval flows', () => {
293
293
 
294
294
  it('hydrates agent-approved tools into stale connector sessions on the next turn', () => {
295
295
  const output = runWithTempDataDir(`
296
- const storageMod = await import('./src/lib/server/storage.ts')
297
- const toolsMod = await import('./src/lib/server/session-tools/index.ts')
298
- const approvalsMod = await import('./src/lib/server/approvals.ts')
296
+ const storageMod = await import('./src/lib/server/storage')
297
+ const toolsMod = await import('./src/lib/server/session-tools/index')
298
+ const approvalsMod = await import('./src/lib/server/approvals')
299
299
  const storage = storageMod.default || storageMod
300
300
  const toolsApi = toolsMod.default || toolsMod
301
301
  const approvals = approvalsMod.default || approvalsMod
@@ -8,6 +8,7 @@ import { normalizeToolInputArgs } from './normalize-tool-args'
8
8
  import { pluginIdMatches } from '../tool-aliases'
9
9
  import { loadSessions } from '../storage'
10
10
  import { inferPluginPublisherSourceFromUrl } from '@/lib/plugin-sources'
11
+ import { errorMessage } from '@/lib/shared-utils'
11
12
 
12
13
  function trimString(value: unknown): string {
13
14
  return typeof value === 'string' ? value.trim() : ''
@@ -91,7 +92,7 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
91
92
  })))
92
93
  }
93
94
  } catch (err: unknown) {
94
- console.error('[discovery] ClawHub search failed:', err instanceof Error ? err.message : String(err))
95
+ console.error('[discovery] ClawHub search failed:', errorMessage(err))
95
96
  }
96
97
 
97
98
  try {
@@ -123,7 +124,7 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
123
124
  }
124
125
  results.push(...registryResults.values())
125
126
  } catch (err: unknown) {
126
- console.error('[discovery] SC Registry search failed:', err instanceof Error ? err.message : String(err))
127
+ console.error('[discovery] SC Registry search failed:', errorMessage(err))
127
128
  }
128
129
 
129
130
  if (results.length === 0) {
@@ -214,7 +215,7 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
214
215
  return `Error: Unknown action "${action}"`
215
216
  }
216
217
  } catch (err: unknown) {
217
- const msg = err instanceof Error ? err.message : String(err)
218
+ const msg = errorMessage(err)
218
219
  console.error('[discovery] executeDiscoveryAction failed:', msg)
219
220
  return `Error: ${msg}`
220
221
  }
@@ -9,6 +9,7 @@ import { extractDocumentArtifact } from '../document-utils'
9
9
  import type { ToolBuildContext } from './context'
10
10
  import { findBinaryOnPath, safePath } from './context'
11
11
  import { normalizeToolInputArgs } from './normalize-tool-args'
12
+ import { errorMessage } from '@/lib/shared-utils'
12
13
 
13
14
  function parseMetadataInput(value: unknown): Record<string, unknown> {
14
15
  if (!value) return {}
@@ -219,7 +220,7 @@ async function executeDocumentAction(
219
220
 
220
221
  return `Error: Unknown action "${action}".`
221
222
  } catch (err: unknown) {
222
- return `Error: ${err instanceof Error ? err.message : String(err)}`
223
+ return `Error: ${errorMessage(err)}`
223
224
  }
224
225
  }
225
226
 
@@ -4,6 +4,7 @@ import type { Plugin, PluginHooks } from '@/types'
4
4
  import { getPluginManager } from '../plugins'
5
5
  import { normalizeToolInputArgs } from './normalize-tool-args'
6
6
  import type { ToolBuildContext } from './context'
7
+ import { errorMessage } from '@/lib/shared-utils'
7
8
 
8
9
  interface SmtpConfig {
9
10
  host: string
@@ -186,7 +187,7 @@ async function executeEmail(args: Record<string, unknown>): Promise<string> {
186
187
  const result = await sendSmtpEmail(cfg, recipients, subject, body, html)
187
188
  return `${result}\nTo: ${recipients.join(', ')}\nSubject: ${subject}`
188
189
  } catch (err: unknown) {
189
- return `Error sending email: ${err instanceof Error ? err.message : String(err)}`
190
+ return `Error sending email: ${errorMessage(err)}`
190
191
  }
191
192
  }
192
193
 
@@ -7,6 +7,7 @@ import { runStructuredExtraction } from '../structured-extract'
7
7
  import type { ToolBuildContext } from './context'
8
8
  import { safePath } from './context'
9
9
  import { normalizeToolInputArgs } from './normalize-tool-args'
10
+ import { errorMessage } from '@/lib/shared-utils'
10
11
 
11
12
  function resolveSessionForExtraction(bctx: ToolBuildContext) {
12
13
  const session = bctx.resolveCurrentSession?.()
@@ -67,7 +68,7 @@ async function executeExtractAction(args: Record<string, unknown>, bctx: ToolBui
67
68
  raw: normalized.includeRaw === true ? result.raw : undefined,
68
69
  })
69
70
  } catch (err: unknown) {
70
- return `Error: ${err instanceof Error ? err.message : String(err)}`
71
+ return `Error: ${errorMessage(err)}`
71
72
  }
72
73
  }
73
74
 
@@ -10,6 +10,7 @@ import { safePath, truncate, listDirRecursive, MAX_FILE } from './context'
10
10
  import type { Plugin, PluginHooks } from '@/types'
11
11
  import { getPluginManager } from '../plugins'
12
12
  import { normalizeToolInputArgs } from './normalize-tool-args'
13
+ import { dedup, errorMessage } from '@/lib/shared-utils'
13
14
 
14
15
  function pickNonEmptyString(...values: unknown[]): string | undefined {
15
16
  for (const value of values) {
@@ -269,7 +270,7 @@ export async function executeFileAction(args: Record<string, unknown>, bctx: { c
269
270
  return `Error: Unknown action "${action}"`
270
271
  }
271
272
  } catch (err: unknown) {
272
- return `Error: ${err instanceof Error ? err.message : String(err)}`
273
+ return `Error: ${errorMessage(err)}`
273
274
  }
274
275
  }
275
276
 
@@ -382,7 +383,7 @@ export function findRecentSendFileFallbackPaths(cwd: string, maxAgeMs = 10 * 60
382
383
  const resolvedRoot = path.resolve(cwd)
383
384
  const candidates: string[] = []
384
385
  collectRecentFiles(resolvedRoot, resolvedRoot, maxAgeMs, candidates, 0)
385
- return [...new Set(candidates)]
386
+ return dedup(candidates)
386
387
  }
387
388
 
388
389
  export function resolveSendFileSourcePath(cwd: string, rawPath: string): string {
@@ -445,7 +446,7 @@ async function executeSendFile(args: Record<string, unknown>, bctx: { cwd: strin
445
446
  if (errors.length === 0) return links.join('\n')
446
447
  return `${links.join('\n')}\n\nSkipped: ${errors.join('; ')}`
447
448
  } catch (err: unknown) {
448
- return `Error: ${err instanceof Error ? err.message : String(err)}`
449
+ return `Error: ${errorMessage(err)}`
449
450
  }
450
451
  }
451
452
 
@@ -6,6 +6,7 @@ import { withRetry } from '../tool-retry'
6
6
  import type { Plugin, PluginHooks } from '@/types'
7
7
  import { getPluginManager } from '../plugins'
8
8
  import { normalizeToolInputArgs } from './normalize-tool-args'
9
+ import { errorMessage } from '@/lib/shared-utils'
9
10
 
10
11
  interface HttpRequestArgs {
11
12
  method: string
@@ -58,7 +59,7 @@ async function executeHttpAction(args: HttpRequestArgs) {
58
59
  }
59
60
  return JSON.stringify({ status: res.status, statusText: res.statusText, headers: resHeaders, body: resBody })
60
61
  } catch (err: unknown) {
61
- return JSON.stringify({ error: err instanceof Error ? err.message : String(err) })
62
+ return JSON.stringify({ error: errorMessage(err) })
62
63
  }
63
64
  }, requestArgs)
64
65
  }
@@ -8,6 +8,7 @@ import { ackMailboxEnvelope, listMailbox, sendMailboxEnvelope } from '../session
8
8
  import { loadApprovals } from '../storage'
9
9
  import { requestApprovalMaybeAutoApprove } from '../approvals'
10
10
  import { createWatchJob, getWatchJob } from '../watch-jobs'
11
+ import { errorMessage } from '@/lib/shared-utils'
11
12
 
12
13
  async function executeHumanLoopAction(args: Record<string, unknown>, bctx: { sessionId?: string | null; agentId?: string | null }) {
13
14
  const normalized = normalizeToolInputArgs(args)
@@ -166,7 +167,7 @@ async function executeHumanLoopAction(args: Record<string, unknown>, bctx: { ses
166
167
 
167
168
  return `Error: Unknown action "${action}".`
168
169
  } catch (err: unknown) {
169
- return `Error: ${err instanceof Error ? err.message : String(err)}`
170
+ return `Error: ${errorMessage(err)}`
170
171
  }
171
172
  }
172
173
 
@@ -7,6 +7,7 @@ import { getPluginManager } from '../plugins'
7
7
  import { normalizeToolInputArgs } from './normalize-tool-args'
8
8
  import { UPLOAD_DIR } from '../storage'
9
9
  import type { ToolBuildContext } from './context'
10
+ import { errorMessage, sleep } from '@/lib/shared-utils'
10
11
 
11
12
  type ImageProvider = 'openai' | 'stability' | 'replicate' | 'fal' | 'together' | 'fireworks' | 'bfl' | 'custom'
12
13
 
@@ -87,7 +88,7 @@ async function generateReplicate(prompt: string, size: string, cfg: PluginConfig
87
88
  if (prediction.status !== 'succeeded' && prediction.status !== 'failed' && prediction.urls?.get) {
88
89
  const deadline = Date.now() + 120_000
89
90
  while (prediction.status !== 'succeeded' && prediction.status !== 'failed' && Date.now() < deadline) {
90
- await new Promise((r) => setTimeout(r, 2000))
91
+ await sleep(2000)
91
92
  const pollRes = await fetch(prediction.urls.get, {
92
93
  headers: { Authorization: `Bearer ${cfg.apiKey}` },
93
94
  signal: AbortSignal.timeout(10_000),
@@ -174,7 +175,7 @@ async function generateBFL(prompt: string, size: string, cfg: PluginConfig): Pro
174
175
  // Poll for result
175
176
  const deadline = Date.now() + 120_000
176
177
  while (Date.now() < deadline) {
177
- await new Promise((r) => setTimeout(r, 2000))
178
+ await sleep(2000)
178
179
  const pollRes = await fetch(pollingUrl, {
179
180
  headers: { 'x-key': cfg.apiKey },
180
181
  signal: AbortSignal.timeout(10_000),
@@ -270,7 +271,7 @@ async function executeImageGen(args: Record<string, unknown>): Promise<string> {
270
271
  const result = await generate(prompt, size, quality, cfg)
271
272
  return saveImageResult(result, prompt, filename)
272
273
  } catch (err: unknown) {
273
- return `Error: ${err instanceof Error ? err.message : String(err)}`
274
+ return `Error: ${errorMessage(err)}`
274
275
  }
275
276
  }
276
277
 
@@ -1,7 +1,8 @@
1
1
  import { z } from 'zod'
2
2
  import { tool, type StructuredToolInterface } from '@langchain/core/tools'
3
3
  import type { Session } from '@/types'
4
- import { loadApprovals, loadSettings, loadSessions, saveSessions, loadMcpServers } from '../storage'
4
+ import { dedup, errorMessage } from '@/lib/shared-utils'
5
+ import { loadApprovals, loadSettings, loadSession, loadMcpServers, patchSession } from '../storage'
5
6
  import { loadRuntimeSettings } from '../runtime-settings'
6
7
  import { log } from '../logger'
7
8
  import { resolveSessionToolPolicy } from '../tool-capability-policy'
@@ -80,23 +81,20 @@ export async function buildSessionTools(cwd: string, enabledPlugins: string[], c
80
81
  const cliProcessTimeoutMs = runtime.cliProcessTimeoutMs
81
82
  const appSettings = loadSettings()
82
83
  const grantedToolIds = approvedToolAccessIds(ctx)
83
- const effectiveEnabledPlugins = Array.from(new Set([
84
+ const effectiveEnabledPlugins = dedup([
84
85
  ...(Array.isArray(enabledPlugins) ? enabledPlugins : []),
85
86
  ...grantedToolIds,
86
- ]))
87
+ ])
87
88
  if (ctx?.sessionId && grantedToolIds.length > 0) {
88
- const sessions = loadSessions()
89
- const currentSession = sessions[ctx.sessionId]
90
- if (currentSession) {
89
+ patchSession(ctx.sessionId, (currentSession) => {
90
+ if (!currentSession) return currentSession
91
91
  const currentPlugins = Array.isArray(currentSession.plugins) ? currentSession.plugins : []
92
- const mergedPlugins = Array.from(new Set([...currentPlugins, ...grantedToolIds]))
93
- if (mergedPlugins.length !== currentPlugins.length) {
94
- currentSession.plugins = mergedPlugins
95
- currentSession.updatedAt = Date.now()
96
- sessions[ctx.sessionId] = currentSession
97
- saveSessions(sessions)
98
- }
99
- }
92
+ const mergedPlugins = dedup([...currentPlugins, ...grantedToolIds])
93
+ if (mergedPlugins.length === currentPlugins.length) return currentSession
94
+ currentSession.plugins = mergedPlugins
95
+ currentSession.updatedAt = Date.now()
96
+ return currentSession
97
+ })
100
98
  }
101
99
  const toolPolicy = resolveSessionToolPolicy(effectiveEnabledPlugins, appSettings)
102
100
  const expandedEnabled = expandPluginIds(toolPolicy.enabledPlugins)
@@ -124,8 +122,7 @@ export async function buildSessionTools(cwd: string, enabledPlugins: string[], c
124
122
 
125
123
  const resolveCurrentSession = (): Session | null => {
126
124
  if (!ctx?.sessionId) return null
127
- const sessions = loadSessions()
128
- return sessions[ctx.sessionId] || null
125
+ return loadSession(ctx.sessionId)
129
126
  }
130
127
 
131
128
  const readStoredDelegateResumeId = (key: 'claudeCode' | 'codex' | 'opencode' | 'gemini'): string | null => {
@@ -138,19 +135,18 @@ export async function buildSessionTools(cwd: string, enabledPlugins: string[], c
138
135
  const persistDelegateResumeId = (key: 'claudeCode' | 'codex' | 'opencode' | 'gemini', resumeId: string | null | undefined): void => {
139
136
  const normalized = typeof resumeId === 'string' ? resumeId.trim() : ''
140
137
  if (!normalized || !ctx?.sessionId) return
141
- const sessions = loadSessions()
142
- const target = sessions[ctx.sessionId]
143
- if (!target) return
144
- const current = (target.delegateResumeIds && typeof target.delegateResumeIds === 'object')
145
- ? target.delegateResumeIds
146
- : {}
147
- target.delegateResumeIds = {
148
- ...current,
149
- [key]: normalized,
150
- }
151
- target.updatedAt = Date.now()
152
- sessions[ctx.sessionId] = target
153
- saveSessions(sessions)
138
+ patchSession(ctx.sessionId, (target) => {
139
+ if (!target) return target
140
+ const current = (target.delegateResumeIds && typeof target.delegateResumeIds === 'object')
141
+ ? target.delegateResumeIds
142
+ : {}
143
+ target.delegateResumeIds = {
144
+ ...current,
145
+ [key]: normalized,
146
+ }
147
+ target.updatedAt = Date.now()
148
+ return target
149
+ })
154
150
  }
155
151
 
156
152
  const bctx: ToolBuildContext = {
@@ -266,7 +262,7 @@ export async function buildSessionTools(cwd: string, enabledPlugins: string[], c
266
262
  )
267
263
  }
268
264
  } catch (err: unknown) {
269
- log.error('session-tools', 'Failed to load plugin tools', { error: err instanceof Error ? err.message : String(err) })
265
+ log.error('session-tools', 'Failed to load plugin tools', { error: errorMessage(err) })
270
266
  }
271
267
 
272
268
  // 3. MCP server tools
@@ -12,6 +12,7 @@ import {
12
12
  replyMailboxMessage,
13
13
  } from '../mailbox-utils'
14
14
  import { createWatchJob } from '../watch-jobs'
15
+ import { errorMessage } from '@/lib/shared-utils'
15
16
 
16
17
  function parseMessageUid(value: unknown): number {
17
18
  const parsed = typeof value === 'number' ? value : typeof value === 'string' ? Number.parseInt(value, 10) : Number.NaN
@@ -191,7 +192,7 @@ async function executeMailboxAction(args: Record<string, unknown>, bctx: { cwd:
191
192
 
192
193
  return `Error: Unknown action "${action}".`
193
194
  } catch (err: unknown) {
194
- return `Error: ${err instanceof Error ? err.message : String(err)}`
195
+ return `Error: ${errorMessage(err)}`
195
196
  }
196
197
  }
197
198
 
@@ -35,8 +35,8 @@ function runWithTempDataDir(script: string) {
35
35
  describe('manage_connectors tool', () => {
36
36
  it('drops transient outbound-send args on create', () => {
37
37
  const output = runWithTempDataDir(`
38
- const storageMod = await import('./src/lib/server/storage.ts')
39
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
38
+ const storageMod = await import('./src/lib/server/storage')
39
+ const crudMod = await import('./src/lib/server/session-tools/crud')
40
40
  const storage = storageMod.default || storageMod
41
41
  const crud = crudMod.default || crudMod
42
42
 
@@ -84,8 +84,8 @@ describe('manage_connectors tool', () => {
84
84
 
85
85
  it('ignores send-like update payloads instead of mutating connector routing state', () => {
86
86
  const output = runWithTempDataDir(`
87
- const storageMod = await import('./src/lib/server/storage.ts')
88
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
87
+ const storageMod = await import('./src/lib/server/storage')
88
+ const crudMod = await import('./src/lib/server/session-tools/crud')
89
89
  const storage = storageMod.default || storageMod
90
90
  const crud = crudMod.default || crudMod
91
91
 
@@ -37,8 +37,8 @@ describe('manage_schedules tool', () => {
37
37
  const output = runWithTempDataDir(`
38
38
  import fs from 'node:fs'
39
39
  import path from 'node:path'
40
- const storageMod = await import('./src/lib/server/storage.ts')
41
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
40
+ const storageMod = await import('./src/lib/server/storage')
41
+ const crudMod = await import('./src/lib/server/session-tools/crud')
42
42
  const storage = storageMod.default || storageMod
43
43
  const crud = crudMod.default || crudMod
44
44
 
@@ -93,8 +93,8 @@ describe('manage_schedules tool', () => {
93
93
 
94
94
  it('stores the current connector recipient on new schedules created from a connector session', () => {
95
95
  const output = runWithTempDataDir(`
96
- const storageMod = await import('./src/lib/server/storage.ts')
97
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
96
+ const storageMod = await import('./src/lib/server/storage')
97
+ const crudMod = await import('./src/lib/server/session-tools/crud')
98
98
  const storage = storageMod.default || storageMod
99
99
  const crud = crudMod.default || crudMod
100
100
 
@@ -170,8 +170,8 @@ describe('manage_schedules tool', () => {
170
170
 
171
171
  it('rejects schedules whose referenced script path does not exist', () => {
172
172
  const output = runWithTempDataDir(`
173
- const storageMod = await import('./src/lib/server/storage.ts')
174
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
173
+ const storageMod = await import('./src/lib/server/storage')
174
+ const crudMod = await import('./src/lib/server/session-tools/crud')
175
175
  const storage = storageMod.default || storageMod
176
176
  const crud = crudMod.default || crudMod
177
177
 
@@ -214,8 +214,8 @@ describe('manage_schedules tool', () => {
214
214
 
215
215
  it('reuses a same-session recurring reminder instead of creating a near-duplicate', () => {
216
216
  const output = runWithTempDataDir(`
217
- const storageMod = await import('./src/lib/server/storage.ts')
218
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
217
+ const storageMod = await import('./src/lib/server/storage')
218
+ const crudMod = await import('./src/lib/server/session-tools/crud')
219
219
  const storage = storageMod.default || storageMod
220
220
  const crud = crudMod.default || crudMod
221
221
 
@@ -274,8 +274,8 @@ describe('manage_schedules tool', () => {
274
274
 
275
275
  it('pauses matching duplicate schedules together when an agent stops a reminder', () => {
276
276
  const output = runWithTempDataDir(`
277
- const storageMod = await import('./src/lib/server/storage.ts')
278
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
277
+ const storageMod = await import('./src/lib/server/storage')
278
+ const crudMod = await import('./src/lib/server/session-tools/crud')
279
279
  const storage = storageMod.default || storageMod
280
280
  const crud = crudMod.default || crudMod
281
281
 
@@ -348,8 +348,8 @@ describe('manage_schedules tool', () => {
348
348
 
349
349
  it('deletes matching duplicate schedules together when an agent removes a reminder cluster', () => {
350
350
  const output = runWithTempDataDir(`
351
- const storageMod = await import('./src/lib/server/storage.ts')
352
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
351
+ const storageMod = await import('./src/lib/server/storage')
352
+ const crudMod = await import('./src/lib/server/session-tools/crud')
353
353
  const storage = storageMod.default || storageMod
354
354
  const crud = crudMod.default || crudMod
355
355
 
@@ -35,8 +35,8 @@ function runWithTempDataDir(script: string) {
35
35
 
36
36
  /** Helper: seed agents + return manage_tasks / manage_projects tool invocation boilerplate. */
37
37
  const AGENT_SETUP = `
38
- const storageMod = await import('./src/lib/server/storage.ts')
39
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
38
+ const storageMod = await import('./src/lib/server/storage')
39
+ const crudMod = await import('./src/lib/server/session-tools/crud')
40
40
  const storage = storageMod.default || storageMod
41
41
  const crud = crudMod.default || crudMod
42
42
 
@@ -76,7 +76,7 @@ const AGENT_SETUP = `
76
76
 
77
77
  /** Helper to import dequeueNextRunnableTask (CJS-compatible). */
78
78
  const QUEUE_IMPORT = `
79
- const _queueMod = await import('./src/lib/server/queue.ts')
79
+ const _queueMod = await import('./src/lib/server/queue')
80
80
  const _queue = _queueMod.default || _queueMod
81
81
  const dequeueNextRunnableTask = _queue.dequeueNextRunnableTask
82
82
  `
@@ -782,8 +782,8 @@ describe('manage_tasks: fingerprint dedup', () => {
782
782
 
783
783
  it('29. different agents → not duplicates', () => {
784
784
  const output = runWithTempDataDir(`
785
- const storageMod = await import('./src/lib/server/storage.ts')
786
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
785
+ const storageMod = await import('./src/lib/server/storage')
786
+ const crudMod = await import('./src/lib/server/session-tools/crud')
787
787
  const storage = storageMod.default || storageMod
788
788
  const crud = crudMod.default || crudMod
789
789
 
@@ -37,8 +37,8 @@ describe('manage_tasks tool', () => {
37
37
  const output = runWithTempDataDir(`
38
38
  import fs from 'node:fs'
39
39
  import path from 'node:path'
40
- const storageMod = await import('./src/lib/server/storage.ts')
41
- const crudMod = await import('./src/lib/server/session-tools/crud.ts')
40
+ const storageMod = await import('./src/lib/server/storage')
41
+ const crudMod = await import('./src/lib/server/session-tools/crud')
42
42
  const storage = storageMod.default || storageMod
43
43
  const crud = crudMod.default || crudMod
44
44
 
@@ -9,6 +9,7 @@ import { safePath, truncate } from './context'
9
9
  import { normalizeToolInputArgs } from './normalize-tool-args'
10
10
  import { cancelWatchJob, createWatchJob, getWatchJob, listWatchJobs } from '../watch-jobs'
11
11
  import { ensureSessionBrowserProfileId, loadBrowserSessionRecord } from '../browser-state'
12
+ import { errorMessage } from '@/lib/shared-utils'
12
13
 
13
14
  type WatchKind = 'time' | 'http' | 'file' | 'task' | 'webhook' | 'page'
14
15
 
@@ -89,7 +90,7 @@ async function createDurableWatch(
89
90
  }
90
91
 
91
92
  function getErrorMessage(err: unknown): string {
92
- return err instanceof Error ? err.message : String(err)
93
+ return errorMessage(err)
93
94
  }
94
95
 
95
96
  /**
@@ -9,6 +9,7 @@ import { loadSettings } from '../storage'
9
9
  import { resolveSessionToolPolicy } from '../tool-capability-policy'
10
10
  import { loadRuntimeSettings } from '../runtime-settings'
11
11
  import { expandPluginIds } from '../tool-aliases'
12
+ import { dedup } from '@/lib/shared-utils'
12
13
 
13
14
  function parsePlatformData(value: unknown): Record<string, unknown> | null {
14
15
  if (!value) return null
@@ -146,7 +147,7 @@ export function normalizePlatformActionArgs(rawArgs: Record<string, unknown>): R
146
147
  }
147
148
 
148
149
  function uniqueStrings(values: Array<string | undefined>): string[] {
149
- return [...new Set(values.filter((value): value is string => Boolean(value)))]
150
+ return dedup(values.filter((value): value is string => Boolean(value)))
150
151
  }
151
152
 
152
153
  function resolvePlatformResourceAccess(toolId: string, bctx: ToolBuildContext): { allowed: boolean; reason: string | null } {
@@ -7,6 +7,7 @@ import type { ToolBuildContext } from './context'
7
7
  import type { ApprovalRequest, Plugin, PluginHooks } from '@/types'
8
8
  import { getPluginManager } from '../plugins'
9
9
  import { normalizeToolInputArgs } from './normalize-tool-args'
10
+ import { errorMessage } from '@/lib/shared-utils'
10
11
 
11
12
  const PLUGINS_DIR = path.join(DATA_DIR, 'plugins')
12
13
 
@@ -236,7 +237,7 @@ Key rules:
236
237
 
237
238
  return `Unknown action "${action}". Valid actions: get_spec, scaffold, read, edit, delete, install_dependencies`
238
239
  } catch (err: unknown) {
239
- return `Error: ${err instanceof Error ? err.message : String(err)}`
240
+ return `Error: ${errorMessage(err)}`
240
241
  }
241
242
  }
242
243