@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
@@ -3,7 +3,7 @@ import { tool } from '@langchain/core/tools'
3
3
  import { StateGraph, MessagesAnnotation, START, END } from '@langchain/langgraph'
4
4
  import { ToolNode } from '@langchain/langgraph/prebuilt'
5
5
  import { AIMessage } from '@langchain/core/messages'
6
- import { loadSessions, saveSessions, loadAgents, loadCredentials, loadSettings, loadSecrets, loadTasks, saveTasks, decryptKey, loadSkills } from './storage'
6
+ import { loadSessions, saveSessions, loadAgents, loadCredentials, loadSettings, loadSecrets, loadTasks, patchTask, upsertTask, decryptKey, loadSkills } from './storage'
7
7
  import { WORKSPACE_DIR } from './data-dir'
8
8
  import { loadRuntimeSettings, getOrchestratorLoopRecursionLimit } from './runtime-settings'
9
9
  import { getMemoryDb } from './memory-db'
@@ -13,6 +13,7 @@ import { notify } from './ws-hub'
13
13
  import { pushMainLoopEventToMainSessions } from './main-agent-loop'
14
14
  import { buildCurrentDateTimePromptContext } from './prompt-runtime-context'
15
15
  import { getPluginManager } from './plugins'
16
+ import { buildBoardTask } from './task-lifecycle'
16
17
  import './builtin-plugins'
17
18
  import { genId } from '@/lib/id'
18
19
  import { NON_LANGGRAPH_PROVIDER_IDS } from '@/lib/provider-sets'
@@ -145,38 +146,20 @@ async function executeSubTaskViaCli(agent: Agent, task: string, parentSessionId:
145
146
  return result
146
147
  }
147
148
 
148
- export async function executeLangGraphOrchestrator(
149
- orchestrator: Agent,
150
- task: string,
151
- sessionId: string,
152
- taskId?: string,
153
- ): Promise<string> {
154
- const allAgents = loadAgents()
155
-
156
- // Build available agents list
157
- const agentIds = orchestrator.subAgentIds || []
158
- const agents = agentIds.map((id) => allAgents[id]).filter(Boolean) as Agent[]
159
- const agentListContext = agents.length
160
- ? '\n\nAvailable agents:\n' + agents.map((a) => {
161
- const plugins = (a.plugins || a.tools)?.length ? ` [plugins: ${(a.plugins || a.tools)!.join(', ')}]` : ''
162
- const skills = a.skills?.length ? ` [skills: ${a.skills.join(', ')}]` : ''
163
- return `- ${a.name}: ${a.description}${plugins}${skills}`
164
- }).join('\n')
165
- : '\n\n(No agents available for delegation.)'
166
-
167
- // Load relevant memories
149
+ function createOrchestratorTools(params: {
150
+ orchestrator: Agent
151
+ agents: Agent[]
152
+ sessionId: string
153
+ availableSecrets: Array<{ name: string; service: string; value: string }>
154
+ }) {
155
+ const { orchestrator, agents, sessionId, availableSecrets } = params
168
156
  const db = getMemoryDb()
169
- const memories = db.getByAgent(orchestrator.id)
170
- const memoryContext = memories.length
171
- ? '\n\nRelevant memories:\n' + memories.slice(0, 10).map((m) => `[${m.category}] ${m.title}: ${m.content.slice(0, 200)}`).join('\n')
172
- : ''
173
157
 
174
- // Define tools
175
158
  const delegateTool = tool(
176
159
  async ({ agentName, task: agentTask }) => {
177
- const agent = agents.find((a) => a.name.toLowerCase() === agentName.toLowerCase())
160
+ const agent = agents.find((candidate) => candidate.name.toLowerCase() === agentName.toLowerCase())
178
161
  if (!agent) {
179
- return `Agent "${agentName}" not found. Available: ${agents.map((a) => a.name).join(', ')}`
162
+ return `Agent "${agentName}" not found. Available: ${agents.map((candidate) => candidate.name).join(', ')}`
180
163
  }
181
164
  console.log(`[orchestrator-lg] Delegating to ${agent.name}: ${agentTask.slice(0, 80)}`)
182
165
  getPluginManager().runHook(
@@ -229,7 +212,7 @@ export async function executeLangGraphOrchestrator(
229
212
  async ({ query }) => {
230
213
  const results = db.search(query, orchestrator.id)
231
214
  if (!results.length) return 'No matching memories found.'
232
- return results.map((m) => `[${m.category}] ${m.title}: ${m.content.slice(0, 300)}`).join('\n')
215
+ return results.map((memory) => `[${memory.category}] ${memory.title}: ${memory.content.slice(0, 300)}`).join('\n')
233
216
  },
234
217
  {
235
218
  name: 'search_memory',
@@ -254,16 +237,13 @@ export async function executeLangGraphOrchestrator(
254
237
  },
255
238
  )
256
239
 
257
- // Secrets
258
- const availableSecrets = getSecretsForOrchestrator(orchestrator.id)
259
-
260
240
  const getSecretTool = tool(
261
241
  async ({ serviceName }) => {
262
242
  const match = availableSecrets.find(
263
- (s) => s.service.toLowerCase() === serviceName.toLowerCase() || s.name.toLowerCase() === serviceName.toLowerCase(),
243
+ (secret) => secret.service.toLowerCase() === serviceName.toLowerCase() || secret.name.toLowerCase() === serviceName.toLowerCase(),
264
244
  )
265
245
  if (!match) {
266
- return `No secret found for "${serviceName}". Available services: ${availableSecrets.map((s) => s.service).join(', ') || 'none'}`
246
+ return `No secret found for "${serviceName}". Available services: ${availableSecrets.map((secret) => secret.service).join(', ') || 'none'}`
267
247
  }
268
248
  console.log(`[orchestrator-lg] Retrieved secret for service: ${match.service}`)
269
249
  return JSON.stringify({ name: match.name, service: match.service, value: match.value })
@@ -277,23 +257,23 @@ export async function executeLangGraphOrchestrator(
277
257
  },
278
258
  )
279
259
 
280
- // Task board tools
281
260
  const commentOnTaskTool = tool(
282
261
  async ({ taskId, comment }) => {
283
- const tasks = loadTasks()
284
- const t = tasks[taskId]
262
+ const t = patchTask(taskId, (current) => {
263
+ if (!current) return current
264
+ if (!current.comments) current.comments = []
265
+ const nextComment: TaskComment = {
266
+ id: genId(),
267
+ author: orchestrator.name,
268
+ agentId: orchestrator.id,
269
+ text: comment,
270
+ createdAt: Date.now(),
271
+ }
272
+ current.comments.push(nextComment)
273
+ current.updatedAt = Date.now()
274
+ return current
275
+ })
285
276
  if (!t) return `Task "${taskId}" not found.`
286
- if (!t.comments) t.comments = []
287
- const c: TaskComment = {
288
- id: genId(),
289
- author: orchestrator.name,
290
- agentId: orchestrator.id,
291
- text: comment,
292
- createdAt: Date.now(),
293
- }
294
- t.comments.push(c)
295
- t.updatedAt = Date.now()
296
- saveTasks(tasks)
297
277
  console.log(`[orchestrator-lg] Commented on task "${t.title}": ${comment.slice(0, 80)}`)
298
278
  return `Comment added to task "${t.title}".`
299
279
  },
@@ -309,27 +289,19 @@ export async function executeLangGraphOrchestrator(
309
289
 
310
290
  const createTaskTool = tool(
311
291
  async ({ title, description: desc }) => {
312
- const tasks = loadTasks()
313
- const id = genId()
314
- tasks[id] = {
315
- id,
292
+ const now = Date.now()
293
+ const taskRecord = buildBoardTask({
316
294
  title,
317
295
  description: desc,
318
- status: 'backlog',
319
296
  agentId: orchestrator.id,
320
- sessionId: null,
321
- result: null,
322
- error: null,
323
- comments: [],
324
- createdAt: Date.now(),
325
- updatedAt: Date.now(),
326
- queuedAt: null,
327
- startedAt: null,
328
- completedAt: null,
329
- }
330
- saveTasks(tasks)
331
- console.log(`[orchestrator-lg] Created backlog task: "${title}" (${id})`)
332
- return `Task "${title}" created in backlog (id: ${id}). The user can review and queue it.`
297
+ now,
298
+ seed: {
299
+ comments: [],
300
+ },
301
+ })
302
+ upsertTask(taskRecord.id, taskRecord)
303
+ console.log(`[orchestrator-lg] Created backlog task: "${title}" (${taskRecord.id})`)
304
+ return `Task "${title}" created in backlog (id: ${taskRecord.id}). The user can review and queue it.`
333
305
  },
334
306
  {
335
307
  name: 'create_task',
@@ -341,6 +313,126 @@ export async function executeLangGraphOrchestrator(
341
313
  },
342
314
  )
343
315
 
316
+ return [delegateTool, storeMemoryTool, searchMemoryTool, getSecretTool, commentOnTaskTool, createTaskTool, markCompleteTool]
317
+ }
318
+
319
+ function compileOrchestratorGraph(params: {
320
+ llmWithTools: any
321
+ allTools: ReturnType<typeof createOrchestratorTools>
322
+ checkpointSaver: any
323
+ approvalInterruptsEnabled: boolean
324
+ systemMessage?: string
325
+ enableDelegateFallback?: boolean
326
+ }) {
327
+ const { llmWithTools, allTools, checkpointSaver, approvalInterruptsEnabled, systemMessage, enableDelegateFallback = false } = params
328
+ const toolNode = new ToolNode(allTools)
329
+ let fallbackAttempts = 0
330
+ const maxFallbackAttempts = 2
331
+
332
+ async function agentNode(state: typeof MessagesAnnotation.State) {
333
+ const promptMessages = systemMessage
334
+ ? [{ role: 'system' as const, content: systemMessage }, ...state.messages]
335
+ : state.messages
336
+ const response = await llmWithTools.invoke(promptMessages)
337
+ return { messages: [response] }
338
+ }
339
+
340
+ function routerNode(state: typeof MessagesAnnotation.State) {
341
+ if (!enableDelegateFallback) return { messages: [] }
342
+ const messages = state.messages
343
+ const lastMsg = messages[messages.length - 1]
344
+ if (lastMsg && typeof (lastMsg as any).content === 'string') {
345
+ const content = (lastMsg as any).content as string
346
+ const isError = content.startsWith('Error:') || content.startsWith('Agent "') && content.includes('not found')
347
+ if (isError && fallbackAttempts < maxFallbackAttempts) {
348
+ fallbackAttempts++
349
+ const failedToolCall = [...messages].reverse().find(
350
+ (message) => (message as any).tool_calls?.some((toolCall: any) => toolCall.name === 'delegate_to_agent'),
351
+ )
352
+ if (failedToolCall) {
353
+ const toolCall = (failedToolCall as any).tool_calls?.find((entry: any) => entry.name === 'delegate_to_agent')
354
+ const failedAgentName = toolCall?.args?.agentName || 'unknown'
355
+ const fallbackHint = `The agent "${failedAgentName}" failed. Try delegating to a different agent with matching capabilities, or re-plan your approach. Fallback attempt ${fallbackAttempts}/${maxFallbackAttempts}.`
356
+ return { messages: [new AIMessage({ content: fallbackHint })] }
357
+ }
358
+ }
359
+ }
360
+ return { messages: [] }
361
+ }
362
+
363
+ function shouldContinue(state: typeof MessagesAnnotation.State) {
364
+ const lastMsg = state.messages[state.messages.length - 1]
365
+ const toolCalls = (lastMsg as any)?.tool_calls
366
+ return toolCalls && toolCalls.length > 0 ? 'tools' : END
367
+ }
368
+
369
+ function afterRouter() {
370
+ return 'agent'
371
+ }
372
+
373
+ const graph = new StateGraph(MessagesAnnotation)
374
+ .addNode('agent', agentNode)
375
+ .addNode('tools', toolNode)
376
+ .addNode('router', routerNode)
377
+ .addEdge(START, 'agent')
378
+ .addConditionalEdges('agent', shouldContinue, { tools: 'tools', [END]: END })
379
+ .addEdge('tools', 'router')
380
+ .addConditionalEdges('router', afterRouter, { agent: 'agent' })
381
+
382
+ const compiledGraph = graph.compile({
383
+ checkpointer: checkpointSaver,
384
+ ...(approvalInterruptsEnabled ? { interruptBefore: ['tools'] } : {}),
385
+ })
386
+
387
+ ;(compiledGraph as any).__graphStructure = {
388
+ nodes: ['agent', 'tools', 'router'],
389
+ edges: [
390
+ { from: START, to: 'agent' },
391
+ { from: 'agent', to: 'tools', condition: 'has_tool_calls' },
392
+ { from: 'agent', to: END, condition: 'no_tool_calls' },
393
+ { from: 'tools', to: 'router' },
394
+ { from: 'router', to: 'agent', condition: enableDelegateFallback ? 'fallback_or_continue' : 'continue' },
395
+ ],
396
+ }
397
+
398
+ return compiledGraph
399
+ }
400
+
401
+ export async function executeLangGraphOrchestrator(
402
+ orchestrator: Agent,
403
+ task: string,
404
+ sessionId: string,
405
+ taskId?: string,
406
+ ): Promise<string> {
407
+ const allAgents = loadAgents()
408
+
409
+ // Build available agents list
410
+ const agentIds = orchestrator.subAgentIds || []
411
+ const agents = agentIds.map((id) => allAgents[id]).filter(Boolean) as Agent[]
412
+ const agentListContext = agents.length
413
+ ? '\n\nAvailable agents:\n' + agents.map((a) => {
414
+ const plugins = (a.plugins || a.tools)?.length ? ` [plugins: ${(a.plugins || a.tools)!.join(', ')}]` : ''
415
+ const skills = a.skills?.length ? ` [skills: ${a.skills.join(', ')}]` : ''
416
+ return `- ${a.name}: ${a.description}${plugins}${skills}`
417
+ }).join('\n')
418
+ : '\n\n(No agents available for delegation.)'
419
+
420
+ // Load relevant memories
421
+ const db = getMemoryDb()
422
+ const memories = db.getByAgent(orchestrator.id)
423
+ const memoryContext = memories.length
424
+ ? '\n\nRelevant memories:\n' + memories.slice(0, 10).map((m) => `[${m.category}] ${m.title}: ${m.content.slice(0, 200)}`).join('\n')
425
+ : ''
426
+
427
+ // Secrets
428
+ const availableSecrets = getSecretsForOrchestrator(orchestrator.id)
429
+ const allTools = createOrchestratorTools({
430
+ orchestrator,
431
+ agents,
432
+ sessionId,
433
+ availableSecrets,
434
+ })
435
+
344
436
  // Build secrets context for the system prompt
345
437
  const secretsContext = availableSecrets.length
346
438
  ? '\n\nAvailable secrets (use get_secret tool to retrieve values):\n' + availableSecrets.map((s) => `- ${s.name} (${s.service})`).join('\n')
@@ -400,100 +492,16 @@ export async function executeLangGraphOrchestrator(
400
492
  const checkpointSaver = getCheckpointSaver()
401
493
  const isStrictMode = settings.capabilityPolicyMode === 'strict'
402
494
  const approvalInterruptsEnabled = isStrictMode && settings.approvalsEnabled === true
403
- const allTools = [delegateTool, storeMemoryTool, searchMemoryTool, getSecretTool, commentOnTaskTool, createTaskTool, markCompleteTool]
404
495
  const llmWithTools = llm.bindTools(allTools)
405
- const toolNode = new ToolNode(allTools)
406
-
407
- // Track fallback attempts for delegate_to_agent failures
408
- let fallbackAttempts = 0
409
- const MAX_FALLBACK_ATTEMPTS = 2
410
-
411
- // Agent node: calls LLM with tools
412
- async function agentNode(state: typeof MessagesAnnotation.State) {
413
- const response = await llmWithTools.invoke([
414
- { role: 'system' as const, content: systemMessage },
415
- ...state.messages,
416
- ])
417
- return { messages: [response] }
418
- }
419
-
420
- // Router node: inspects tool results, decides next step
421
- function routerNode(state: typeof MessagesAnnotation.State) {
422
- const messages = state.messages
423
- const lastMsg = messages[messages.length - 1]
424
-
425
- // Check if the last tool message contains an error from delegate_to_agent
426
- if (lastMsg && typeof (lastMsg as any).content === 'string') {
427
- const content = (lastMsg as any).content as string
428
- const isError = content.startsWith('Error:') || content.startsWith('Agent "') && content.includes('not found')
429
-
430
- if (isError && fallbackAttempts < MAX_FALLBACK_ATTEMPTS) {
431
- fallbackAttempts++
432
- // Look for a delegate tool call in recent messages and try to find alternative agent
433
- const failedToolCall = [...messages].reverse().find(
434
- (m) => (m as any).tool_calls?.some((tc: any) => tc.name === 'delegate_to_agent')
435
- )
436
- if (failedToolCall) {
437
- const tc = (failedToolCall as any).tool_calls?.find((tc: any) => tc.name === 'delegate_to_agent')
438
- const failedAgentName = tc?.args?.agentName || 'unknown'
439
- const fallbackHint = `The agent "${failedAgentName}" failed. Try delegating to a different agent with matching capabilities, or re-plan your approach. Fallback attempt ${fallbackAttempts}/${MAX_FALLBACK_ATTEMPTS}.`
440
- return { messages: [new AIMessage({ content: fallbackHint })] }
441
- }
442
- }
443
- }
444
-
445
- // No fallback needed — pass through
446
- return { messages: [] }
447
- }
448
-
449
- // Routing function: after agent node, check if there are tool calls
450
- function shouldContinue(state: typeof MessagesAnnotation.State) {
451
- const lastMsg = state.messages[state.messages.length - 1]
452
- const toolCalls = (lastMsg as any)?.tool_calls
453
- if (toolCalls && toolCalls.length > 0) {
454
- return 'tools'
455
- }
456
- return END
457
- }
458
-
459
- // After router, decide whether to go back to agent or end
460
- function afterRouter(state: typeof MessagesAnnotation.State) {
461
- const messages = state.messages
462
- // If router added a fallback hint, route back to agent
463
- const lastMsg = messages[messages.length - 1]
464
- if (lastMsg && typeof (lastMsg as any).content === 'string' && (lastMsg as any).content.includes('Fallback attempt')) {
465
- return 'agent'
466
- }
467
- return 'agent'
468
- }
469
-
470
- // Build the StateGraph
471
- const graph = new StateGraph(MessagesAnnotation)
472
- .addNode('agent', agentNode)
473
- .addNode('tools', toolNode)
474
- .addNode('router', routerNode)
475
- .addEdge(START, 'agent')
476
- .addConditionalEdges('agent', shouldContinue, { tools: 'tools', [END]: END })
477
- .addEdge('tools', 'router')
478
- .addConditionalEdges('router', afterRouter, { agent: 'agent' })
479
-
480
- const compiledGraph = graph.compile({
481
- checkpointer: checkpointSaver,
482
- ...(approvalInterruptsEnabled ? { interruptBefore: ['tools'] } : {}),
496
+ const compiledGraph = compileOrchestratorGraph({
497
+ llmWithTools,
498
+ allTools,
499
+ checkpointSaver,
500
+ approvalInterruptsEnabled,
501
+ systemMessage,
502
+ enableDelegateFallback: true,
483
503
  })
484
504
 
485
- // Export graph structure for introspection
486
- ;(compiledGraph as any).__graphStructure = {
487
- nodes: ['agent', 'tools', 'router'],
488
- edges: [
489
- { from: START, to: 'agent' },
490
- { from: 'agent', to: 'tools', condition: 'has_tool_calls' },
491
- { from: 'agent', to: END, condition: 'no_tool_calls' },
492
- { from: 'tools', to: 'router' },
493
- { from: 'router', to: 'agent', condition: 'fallback_or_continue' },
494
- ],
495
- }
496
-
497
505
  // Save initial user message
498
506
  saveMessage(sessionId, 'user', task)
499
507
 
@@ -574,25 +582,26 @@ export async function executeLangGraphOrchestrator(
574
582
  // OpenClaw approval bridge not available — fall through
575
583
  }
576
584
 
577
- const tasks = loadTasks()
578
- const t = tasks[taskId]
579
- if (t) {
580
- t.pendingApproval = {
585
+ const t = patchTask(taskId, (current) => {
586
+ if (!current) return current
587
+ current.pendingApproval = {
581
588
  toolName: pendingCall.name || pendingCall.function?.name || 'unknown',
582
589
  args: pendingCall.args || (pendingCall.function?.arguments ? JSON.parse(pendingCall.function.arguments) : {}),
583
590
  threadId,
584
591
  }
585
- t.updatedAt = Date.now()
586
- saveTasks(tasks)
592
+ current.updatedAt = Date.now()
593
+ return current
594
+ })
595
+ if (t) {
587
596
  notify('tasks')
588
- const approvalMsg = `[Awaiting approval] Tool: ${t.pendingApproval.toolName}\nArgs: ${JSON.stringify(t.pendingApproval.args).slice(0, 300)}\n\nApprove or reject in the task board.`
597
+ const approvalMsg = `[Awaiting approval] Tool: ${t.pendingApproval?.toolName}\nArgs: ${JSON.stringify(t.pendingApproval?.args).slice(0, 300)}\n\nApprove or reject in the task board.`
589
598
  saveMessage(sessionId, 'assistant', approvalMsg)
590
599
  notify(`messages:${sessionId}`)
591
600
  pushMainLoopEventToMainSessions({
592
601
  type: 'pending_approval',
593
- text: `Task "${t.title}" needs approval: ${t.pendingApproval.toolName}(${JSON.stringify(t.pendingApproval.args).slice(0, 100)})`,
602
+ text: `Task "${t.title}" needs approval: ${t.pendingApproval?.toolName}(${JSON.stringify(t.pendingApproval?.args).slice(0, 100)})`,
594
603
  })
595
- console.log(`[orchestrator-lg] Interrupt: waiting for approval of tool "${t.pendingApproval.toolName}" on task ${taskId}`)
604
+ console.log(`[orchestrator-lg] Interrupt: waiting for approval of tool "${t.pendingApproval?.toolName}" on task ${taskId}`)
596
605
  return approvalMsg
597
606
  }
598
607
  }
@@ -628,131 +637,13 @@ export async function resumeLangGraphOrchestrator(
628
637
  const allAgents = loadAgents()
629
638
  const agentIds = orchestrator.subAgentIds || []
630
639
  const agents = agentIds.map((id) => allAgents[id]).filter(Boolean) as Agent[]
631
-
632
- // Recreate the same tools
633
- const delegateTool = tool(
634
- async ({ agentName, task: agentTask }) => {
635
- const agent = agents.find((a) => a.name.toLowerCase() === agentName.toLowerCase())
636
- if (!agent) return `Agent "${agentName}" not found. Available: ${agents.map((a) => a.name).join(', ')}`
637
- getPluginManager().runHook(
638
- 'onAgentDelegation',
639
- { sourceAgentId: orchestrator.id, targetAgentId: agent.id, task: agentTask },
640
- { enabledIds: orchestrator.plugins || [] },
641
- )
642
- const result = await executeSubTaskViaCli(agent, agentTask, sessionId)
643
- saveMessage(sessionId, 'assistant', `Delegated to ${agent.name}: ${agentTask.slice(0, 100)}`, [{
644
- name: 'delegate_to_agent',
645
- input: JSON.stringify({ agentName: agent.name, agentId: agent.id, task: agentTask }),
646
- output: result.slice(0, 2000),
647
- }])
648
- return result
649
- },
650
- {
651
- name: 'delegate_to_agent',
652
- description: 'Delegate a task to one of the available agents.',
653
- schema: z.object({
654
- agentName: z.string().describe('Name of the agent to delegate to'),
655
- task: z.string().describe('The task description for the agent'),
656
- }),
657
- },
658
- )
659
-
660
- const db = getMemoryDb()
661
- const storeMemoryTool = tool(
662
- async ({ category, title, content }) => {
663
- db.add({ agentId: orchestrator.id, sessionId, category, title, content })
664
- return 'Memory stored successfully.'
665
- },
666
- {
667
- name: 'store_memory',
668
- description: 'Store information in long-term memory.',
669
- schema: z.object({
670
- category: z.string(), title: z.string(), content: z.string(),
671
- }),
672
- },
673
- )
674
-
675
- const searchMemoryTool = tool(
676
- async ({ query }) => {
677
- const results = db.search(query, orchestrator.id)
678
- if (!results.length) return 'No matching memories found.'
679
- return results.map((m) => `[${m.category}] ${m.title}: ${m.content.slice(0, 300)}`).join('\n')
680
- },
681
- {
682
- name: 'search_memory',
683
- description: 'Search long-term memory.',
684
- schema: z.object({ query: z.string() }),
685
- },
686
- )
687
-
688
- const markCompleteTool = tool(
689
- async ({ summary }) => `ORCHESTRATION_COMPLETE: ${summary}`,
690
- {
691
- name: 'mark_complete',
692
- description: 'Signal orchestration is done.',
693
- schema: z.object({ summary: z.string() }),
694
- },
695
- )
696
-
697
640
  const availableSecrets = getSecretsForOrchestrator(orchestrator.id)
698
- const getSecretTool = tool(
699
- async ({ serviceName }) => {
700
- const match = availableSecrets.find(
701
- (s) => s.service.toLowerCase() === serviceName.toLowerCase() || s.name.toLowerCase() === serviceName.toLowerCase(),
702
- )
703
- if (!match) return `No secret found for "${serviceName}".`
704
- return JSON.stringify({ name: match.name, service: match.service, value: match.value })
705
- },
706
- {
707
- name: 'get_secret',
708
- description: 'Retrieve a stored credential/secret by service name.',
709
- schema: z.object({ serviceName: z.string() }),
710
- },
711
- )
712
-
713
- const commentOnTaskTool = tool(
714
- async ({ taskId, comment }) => {
715
- const tasks = loadTasks()
716
- const t = tasks[taskId]
717
- if (!t) return `Task "${taskId}" not found.`
718
- if (!t.comments) t.comments = []
719
- t.comments.push({
720
- id: genId(),
721
- author: orchestrator.name,
722
- agentId: orchestrator.id,
723
- text: comment,
724
- createdAt: Date.now(),
725
- })
726
- t.updatedAt = Date.now()
727
- saveTasks(tasks)
728
- return `Comment added to task "${t.title}".`
729
- },
730
- {
731
- name: 'comment_on_task',
732
- description: 'Add a comment to a task.',
733
- schema: z.object({ taskId: z.string(), comment: z.string() }),
734
- },
735
- )
736
-
737
- const createTaskTool = tool(
738
- async ({ title, description: desc }) => {
739
- const tasks = loadTasks()
740
- const id = genId()
741
- tasks[id] = {
742
- id, title, description: desc, status: 'backlog',
743
- agentId: orchestrator.id, sessionId: null, result: null, error: null,
744
- comments: [], createdAt: Date.now(), updatedAt: Date.now(),
745
- queuedAt: null, startedAt: null, completedAt: null,
746
- }
747
- saveTasks(tasks)
748
- return `Task "${title}" created in backlog (id: ${id}).`
749
- },
750
- {
751
- name: 'create_task',
752
- description: 'Create a new task in the backlog.',
753
- schema: z.object({ title: z.string(), description: z.string() }),
754
- },
755
- )
641
+ const allTools = createOrchestratorTools({
642
+ orchestrator,
643
+ agents,
644
+ sessionId,
645
+ availableSecrets,
646
+ })
756
647
 
757
648
  const engine = getOrchestrationEngineConfig(orchestrator)
758
649
  const llm = buildChatModel({
@@ -765,36 +656,13 @@ export async function resumeLangGraphOrchestrator(
765
656
  const isStrictMode = settings.capabilityPolicyMode === 'strict'
766
657
  const approvalInterruptsEnabled = isStrictMode && settings.approvalsEnabled === true
767
658
 
768
- const allTools = [delegateTool, storeMemoryTool, searchMemoryTool, getSecretTool, commentOnTaskTool, createTaskTool, markCompleteTool]
769
659
  const llmWithTools = llm.bindTools(allTools)
770
- const toolNode = new ToolNode(allTools)
771
-
772
- async function agentNode(state: typeof MessagesAnnotation.State) {
773
- const response = await llmWithTools.invoke(state.messages)
774
- return { messages: [response] }
775
- }
776
- function routerNode(state: typeof MessagesAnnotation.State) {
777
- return { messages: [] }
778
- }
779
- function shouldContinue(state: typeof MessagesAnnotation.State) {
780
- const lastMsg = state.messages[state.messages.length - 1]
781
- const toolCalls = (lastMsg as any)?.tool_calls
782
- if (toolCalls && toolCalls.length > 0) return 'tools'
783
- return END
784
- }
785
-
786
- const graphAgent = new StateGraph(MessagesAnnotation)
787
- .addNode('agent', agentNode)
788
- .addNode('tools', toolNode)
789
- .addNode('router', routerNode)
790
- .addEdge(START, 'agent')
791
- .addConditionalEdges('agent', shouldContinue, { tools: 'tools', [END]: END })
792
- .addEdge('tools', 'router')
793
- .addEdge('router', 'agent')
794
- .compile({
795
- checkpointer: checkpointSaver,
796
- ...(approvalInterruptsEnabled ? { interruptBefore: ['tools'] } : {}),
797
- })
660
+ const graphAgent = compileOrchestratorGraph({
661
+ llmWithTools,
662
+ allTools,
663
+ checkpointSaver,
664
+ approvalInterruptsEnabled,
665
+ })
798
666
 
799
667
  let finalResult = ''
800
668
  const runtime = loadRuntimeSettings()
@@ -838,15 +706,19 @@ export async function resumeLangGraphOrchestrator(
838
706
  const tasks = loadTasks()
839
707
  for (const t of Object.values(tasks)) {
840
708
  if (t.pendingApproval?.threadId === threadId || t.id === threadId) {
841
- t.pendingApproval = {
842
- toolName: pendingCall.name || pendingCall.function?.name || 'unknown',
843
- args: pendingCall.args || {},
844
- threadId,
845
- }
846
- t.updatedAt = Date.now()
847
- saveTasks(tasks)
709
+ const updated = patchTask(t.id, (current) => {
710
+ if (!current) return current
711
+ current.pendingApproval = {
712
+ toolName: pendingCall.name || pendingCall.function?.name || 'unknown',
713
+ args: pendingCall.args || {},
714
+ threadId,
715
+ }
716
+ current.updatedAt = Date.now()
717
+ return current
718
+ })
719
+ if (!updated) continue
848
720
  notify('tasks')
849
- return `Waiting for approval: ${t.pendingApproval.toolName}`
721
+ return `Waiting for approval: ${updated.pendingApproval?.toolName}`
850
722
  }
851
723
  }
852
724
  }
@@ -0,0 +1,31 @@
1
+ import path from 'path'
2
+ import { fileURLToPath, pathToFileURL } from 'url'
3
+
4
+ function normalizeBaseDir(baseDir: string): string {
5
+ const normalized = path.normalize(baseDir)
6
+ if (!normalized) return path.sep
7
+ return normalized
8
+ }
9
+
10
+ function baseDirPrefix(baseDir: string): string {
11
+ const normalized = normalizeBaseDir(baseDir)
12
+ return normalized.endsWith(path.sep) ? normalized : `${normalized}${path.sep}`
13
+ }
14
+
15
+ export function resolvePathWithinBaseDir(baseDir: string, targetPath: string): string {
16
+ const normalizedBase = normalizeBaseDir(baseDir)
17
+ const baseUrl = pathToFileURL(baseDirPrefix(normalizedBase))
18
+ const resolved = path.normalize(fileURLToPath(new URL(targetPath.replace(/\\/g, '/'), baseUrl)))
19
+ if (resolved !== normalizedBase && !resolved.startsWith(baseDirPrefix(normalizedBase))) {
20
+ throw new Error('Path traversal not allowed')
21
+ }
22
+ return resolved
23
+ }
24
+
25
+ export function tryResolvePathWithinBaseDir(baseDir: string, targetPath: string): string | null {
26
+ try {
27
+ return resolvePathWithinBaseDir(baseDir, targetPath)
28
+ } catch {
29
+ return null
30
+ }
31
+ }