@swarmclawai/swarmclaw 0.2.0

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 (319) hide show
  1. package/README.md +577 -0
  2. package/bin/server-cmd.js +359 -0
  3. package/bin/swarmclaw.js +29 -0
  4. package/bin/swarmclaw.mjs +1504 -0
  5. package/next.config.ts +33 -0
  6. package/package.json +112 -0
  7. package/postcss.config.mjs +7 -0
  8. package/public/branding/swarmclaw-org-avatar.png +0 -0
  9. package/public/branding/swarmclaw-org-avatar.svg +58 -0
  10. package/public/file.svg +1 -0
  11. package/public/globe.svg +1 -0
  12. package/public/next.svg +1 -0
  13. package/public/screenshots/agents.png +0 -0
  14. package/public/screenshots/connectors.png +0 -0
  15. package/public/screenshots/dashboard.png +0 -0
  16. package/public/screenshots/new-session-openclaw.png +0 -0
  17. package/public/screenshots/providers.png +0 -0
  18. package/public/screenshots/schedules.png +0 -0
  19. package/public/screenshots/tasks.png +0 -0
  20. package/public/vercel.svg +1 -0
  21. package/public/window.svg +1 -0
  22. package/src/app/api/agents/[id]/route.ts +30 -0
  23. package/src/app/api/agents/[id]/thread/route.ts +66 -0
  24. package/src/app/api/agents/generate/route.ts +42 -0
  25. package/src/app/api/agents/route.ts +33 -0
  26. package/src/app/api/auth/route.ts +25 -0
  27. package/src/app/api/claude-skills/route.ts +42 -0
  28. package/src/app/api/clawhub/install/route.ts +39 -0
  29. package/src/app/api/clawhub/search/route.ts +11 -0
  30. package/src/app/api/connectors/[id]/route.ts +79 -0
  31. package/src/app/api/connectors/route.ts +60 -0
  32. package/src/app/api/credentials/[id]/route.ts +14 -0
  33. package/src/app/api/credentials/route.ts +31 -0
  34. package/src/app/api/daemon/health-check/route.ts +11 -0
  35. package/src/app/api/daemon/route.ts +22 -0
  36. package/src/app/api/dirs/pick/route.ts +60 -0
  37. package/src/app/api/dirs/route.ts +29 -0
  38. package/src/app/api/documents/[id]/route.ts +47 -0
  39. package/src/app/api/documents/route.ts +93 -0
  40. package/src/app/api/files/serve/route.ts +69 -0
  41. package/src/app/api/generate/info/route.ts +12 -0
  42. package/src/app/api/generate/route.ts +106 -0
  43. package/src/app/api/ip/route.ts +6 -0
  44. package/src/app/api/knowledge/[id]/route.ts +61 -0
  45. package/src/app/api/knowledge/route.ts +48 -0
  46. package/src/app/api/knowledge/upload/route.ts +86 -0
  47. package/src/app/api/logs/route.ts +65 -0
  48. package/src/app/api/mcp-servers/[id]/route.ts +32 -0
  49. package/src/app/api/mcp-servers/[id]/test/route.ts +23 -0
  50. package/src/app/api/mcp-servers/[id]/tools/route.ts +32 -0
  51. package/src/app/api/mcp-servers/route.ts +27 -0
  52. package/src/app/api/memory/[id]/route.ts +126 -0
  53. package/src/app/api/memory/maintenance/route.ts +63 -0
  54. package/src/app/api/memory/route.ts +111 -0
  55. package/src/app/api/memory-images/[filename]/route.ts +36 -0
  56. package/src/app/api/orchestrator/run/route.ts +43 -0
  57. package/src/app/api/plugins/install/route.ts +58 -0
  58. package/src/app/api/plugins/marketplace/route.ts +33 -0
  59. package/src/app/api/plugins/route.ts +21 -0
  60. package/src/app/api/preview-server/route.ts +339 -0
  61. package/src/app/api/providers/[id]/models/route.ts +29 -0
  62. package/src/app/api/providers/[id]/route.ts +34 -0
  63. package/src/app/api/providers/configs/route.ts +7 -0
  64. package/src/app/api/providers/ollama/route.ts +30 -0
  65. package/src/app/api/providers/openclaw/health/route.ts +23 -0
  66. package/src/app/api/providers/route.ts +28 -0
  67. package/src/app/api/runs/[id]/route.ts +9 -0
  68. package/src/app/api/runs/route.ts +13 -0
  69. package/src/app/api/schedules/[id]/route.ts +28 -0
  70. package/src/app/api/schedules/[id]/run/route.ts +104 -0
  71. package/src/app/api/schedules/route.ts +78 -0
  72. package/src/app/api/secrets/[id]/route.ts +29 -0
  73. package/src/app/api/secrets/route.ts +42 -0
  74. package/src/app/api/sessions/[id]/browser/route.ts +13 -0
  75. package/src/app/api/sessions/[id]/chat/route.ts +96 -0
  76. package/src/app/api/sessions/[id]/clear/route.ts +19 -0
  77. package/src/app/api/sessions/[id]/deploy/route.ts +34 -0
  78. package/src/app/api/sessions/[id]/devserver/route.ts +69 -0
  79. package/src/app/api/sessions/[id]/mailbox/route.ts +70 -0
  80. package/src/app/api/sessions/[id]/main-loop/route.ts +94 -0
  81. package/src/app/api/sessions/[id]/messages/route.ts +9 -0
  82. package/src/app/api/sessions/[id]/retry/route.ts +28 -0
  83. package/src/app/api/sessions/[id]/route.ts +103 -0
  84. package/src/app/api/sessions/[id]/stop/route.ts +13 -0
  85. package/src/app/api/sessions/heartbeat/route.ts +26 -0
  86. package/src/app/api/sessions/route.ts +85 -0
  87. package/src/app/api/settings/route.ts +58 -0
  88. package/src/app/api/setup/check-provider/route.ts +326 -0
  89. package/src/app/api/setup/doctor/route.ts +250 -0
  90. package/src/app/api/skills/[id]/route.ts +40 -0
  91. package/src/app/api/skills/import/route.ts +69 -0
  92. package/src/app/api/skills/route.ts +28 -0
  93. package/src/app/api/tasks/[id]/route.ts +102 -0
  94. package/src/app/api/tasks/route.ts +115 -0
  95. package/src/app/api/tts/route.ts +40 -0
  96. package/src/app/api/upload/route.ts +18 -0
  97. package/src/app/api/uploads/[filename]/route.ts +59 -0
  98. package/src/app/api/usage/route.ts +35 -0
  99. package/src/app/api/version/route.ts +81 -0
  100. package/src/app/api/version/update/route.ts +95 -0
  101. package/src/app/api/webhooks/[id]/history/route.ts +13 -0
  102. package/src/app/api/webhooks/[id]/route.ts +204 -0
  103. package/src/app/api/webhooks/route.ts +37 -0
  104. package/src/app/favicon.ico +0 -0
  105. package/src/app/globals.css +370 -0
  106. package/src/app/layout.tsx +52 -0
  107. package/src/app/page.tsx +172 -0
  108. package/src/cli/index.js +1232 -0
  109. package/src/cli/index.test.js +281 -0
  110. package/src/cli/index.ts +1158 -0
  111. package/src/cli/spec.js +284 -0
  112. package/src/components/agents/agent-card.tsx +219 -0
  113. package/src/components/agents/agent-chat-list.tsx +165 -0
  114. package/src/components/agents/agent-list.tsx +110 -0
  115. package/src/components/agents/agent-sheet.tsx +1220 -0
  116. package/src/components/auth/access-key-gate.tsx +248 -0
  117. package/src/components/auth/setup-wizard.tsx +940 -0
  118. package/src/components/auth/user-picker.tsx +88 -0
  119. package/src/components/chat/chat-area.tsx +406 -0
  120. package/src/components/chat/chat-header.tsx +491 -0
  121. package/src/components/chat/chat-tool-toggles.tsx +161 -0
  122. package/src/components/chat/code-block.tsx +146 -0
  123. package/src/components/chat/dev-server-bar.tsx +39 -0
  124. package/src/components/chat/message-bubble.tsx +486 -0
  125. package/src/components/chat/message-list.tsx +299 -0
  126. package/src/components/chat/session-debug-panel.tsx +196 -0
  127. package/src/components/chat/streaming-bubble.tsx +85 -0
  128. package/src/components/chat/thinking-indicator.tsx +26 -0
  129. package/src/components/chat/tool-call-bubble.tsx +438 -0
  130. package/src/components/chat/tool-request-banner.tsx +103 -0
  131. package/src/components/connectors/connector-list.tsx +196 -0
  132. package/src/components/connectors/connector-sheet.tsx +804 -0
  133. package/src/components/input/chat-input.tsx +235 -0
  134. package/src/components/knowledge/knowledge-list.tsx +206 -0
  135. package/src/components/knowledge/knowledge-sheet.tsx +316 -0
  136. package/src/components/layout/app-layout.tsx +1016 -0
  137. package/src/components/layout/daemon-indicator.tsx +56 -0
  138. package/src/components/layout/mobile-header.tsx +31 -0
  139. package/src/components/layout/network-banner.tsx +17 -0
  140. package/src/components/layout/update-banner.tsx +130 -0
  141. package/src/components/logs/log-list.tsx +358 -0
  142. package/src/components/mcp-servers/mcp-server-list.tsx +122 -0
  143. package/src/components/mcp-servers/mcp-server-sheet.tsx +243 -0
  144. package/src/components/memory/memory-card.tsx +63 -0
  145. package/src/components/memory/memory-detail.tsx +339 -0
  146. package/src/components/memory/memory-list.tsx +198 -0
  147. package/src/components/memory/memory-sheet.tsx +70 -0
  148. package/src/components/plugins/plugin-list.tsx +60 -0
  149. package/src/components/plugins/plugin-sheet.tsx +311 -0
  150. package/src/components/providers/provider-list.tsx +96 -0
  151. package/src/components/providers/provider-sheet.tsx +542 -0
  152. package/src/components/runs/run-list.tsx +231 -0
  153. package/src/components/schedules/schedule-card.tsx +63 -0
  154. package/src/components/schedules/schedule-list.tsx +76 -0
  155. package/src/components/schedules/schedule-sheet.tsx +336 -0
  156. package/src/components/secrets/secret-sheet.tsx +180 -0
  157. package/src/components/secrets/secrets-list.tsx +91 -0
  158. package/src/components/sessions/new-session-sheet.tsx +478 -0
  159. package/src/components/sessions/session-card.tsx +144 -0
  160. package/src/components/sessions/session-list.tsx +202 -0
  161. package/src/components/shared/ai-gen-block.tsx +77 -0
  162. package/src/components/shared/avatar.tsx +48 -0
  163. package/src/components/shared/bottom-sheet.tsx +30 -0
  164. package/src/components/shared/confirm-dialog.tsx +47 -0
  165. package/src/components/shared/connector-platform-icon.tsx +113 -0
  166. package/src/components/shared/dir-browser.tsx +285 -0
  167. package/src/components/shared/dropdown.tsx +55 -0
  168. package/src/components/shared/icon-button.tsx +25 -0
  169. package/src/components/shared/settings/plugin-manager.tsx +207 -0
  170. package/src/components/shared/settings/section-capability-policy.tsx +93 -0
  171. package/src/components/shared/settings/section-embedding.tsx +99 -0
  172. package/src/components/shared/settings/section-heartbeat.tsx +168 -0
  173. package/src/components/shared/settings/section-memory.tsx +77 -0
  174. package/src/components/shared/settings/section-orchestrator.tsx +108 -0
  175. package/src/components/shared/settings/section-providers.tsx +181 -0
  176. package/src/components/shared/settings/section-runtime-loop.tsx +183 -0
  177. package/src/components/shared/settings/section-secrets.tsx +132 -0
  178. package/src/components/shared/settings/section-user-preferences.tsx +24 -0
  179. package/src/components/shared/settings/section-voice.tsx +53 -0
  180. package/src/components/shared/settings/settings-sheet.tsx +88 -0
  181. package/src/components/shared/settings/types.ts +7 -0
  182. package/src/components/shared/settings/utils.ts +13 -0
  183. package/src/components/shared/settings-sheet.tsx +1 -0
  184. package/src/components/shared/skeleton.tsx +19 -0
  185. package/src/components/shared/usage-badge.tsx +28 -0
  186. package/src/components/skills/clawhub-browser.tsx +225 -0
  187. package/src/components/skills/skill-list.tsx +70 -0
  188. package/src/components/skills/skill-sheet.tsx +254 -0
  189. package/src/components/tasks/task-board.tsx +96 -0
  190. package/src/components/tasks/task-card.tsx +179 -0
  191. package/src/components/tasks/task-column.tsx +73 -0
  192. package/src/components/tasks/task-list.tsx +118 -0
  193. package/src/components/tasks/task-sheet.tsx +415 -0
  194. package/src/components/ui/avatar.tsx +109 -0
  195. package/src/components/ui/badge.tsx +48 -0
  196. package/src/components/ui/button.tsx +64 -0
  197. package/src/components/ui/card.tsx +92 -0
  198. package/src/components/ui/dialog.tsx +158 -0
  199. package/src/components/ui/dropdown-menu.tsx +257 -0
  200. package/src/components/ui/input.tsx +21 -0
  201. package/src/components/ui/scroll-area.tsx +58 -0
  202. package/src/components/ui/select.tsx +190 -0
  203. package/src/components/ui/separator.tsx +28 -0
  204. package/src/components/ui/sheet.tsx +143 -0
  205. package/src/components/ui/sonner.tsx +22 -0
  206. package/src/components/ui/textarea.tsx +18 -0
  207. package/src/components/ui/tooltip.tsx +56 -0
  208. package/src/components/usage/usage-list.tsx +105 -0
  209. package/src/components/webhooks/webhook-list.tsx +166 -0
  210. package/src/components/webhooks/webhook-sheet.tsx +402 -0
  211. package/src/hooks/use-auto-resize.ts +20 -0
  212. package/src/hooks/use-media-query.ts +21 -0
  213. package/src/hooks/use-speech-recognition.ts +83 -0
  214. package/src/instrumentation.ts +8 -0
  215. package/src/lib/agents.ts +13 -0
  216. package/src/lib/api-client.ts +100 -0
  217. package/src/lib/chat.ts +60 -0
  218. package/src/lib/memory.ts +42 -0
  219. package/src/lib/openclaw-endpoint.test.ts +48 -0
  220. package/src/lib/openclaw-endpoint.ts +67 -0
  221. package/src/lib/provider-config.ts +13 -0
  222. package/src/lib/providers/anthropic.ts +135 -0
  223. package/src/lib/providers/claude-cli.ts +202 -0
  224. package/src/lib/providers/codex-cli.ts +260 -0
  225. package/src/lib/providers/index.ts +351 -0
  226. package/src/lib/providers/ollama.ts +131 -0
  227. package/src/lib/providers/openai.ts +164 -0
  228. package/src/lib/providers/openclaw.ts +330 -0
  229. package/src/lib/providers/opencode-cli.ts +164 -0
  230. package/src/lib/runtime-loop.ts +15 -0
  231. package/src/lib/schedule-dedupe.test.ts +84 -0
  232. package/src/lib/schedule-dedupe.ts +174 -0
  233. package/src/lib/schedule-name.ts +62 -0
  234. package/src/lib/schedules.ts +16 -0
  235. package/src/lib/server/agent-registry.ts +70 -0
  236. package/src/lib/server/api-routes.test.ts +362 -0
  237. package/src/lib/server/autonomy-contract.ts +200 -0
  238. package/src/lib/server/build-llm.ts +155 -0
  239. package/src/lib/server/capability-router.test.ts +21 -0
  240. package/src/lib/server/capability-router.ts +172 -0
  241. package/src/lib/server/chat-execution.ts +894 -0
  242. package/src/lib/server/clawhub-client.test.ts +161 -0
  243. package/src/lib/server/clawhub-client.ts +26 -0
  244. package/src/lib/server/connectors/connector-routing.test.ts +243 -0
  245. package/src/lib/server/connectors/discord.ts +116 -0
  246. package/src/lib/server/connectors/googlechat.ts +66 -0
  247. package/src/lib/server/connectors/manager.ts +559 -0
  248. package/src/lib/server/connectors/matrix.ts +78 -0
  249. package/src/lib/server/connectors/media.ts +149 -0
  250. package/src/lib/server/connectors/openclaw.test.ts +375 -0
  251. package/src/lib/server/connectors/openclaw.ts +1132 -0
  252. package/src/lib/server/connectors/signal.ts +183 -0
  253. package/src/lib/server/connectors/slack.ts +258 -0
  254. package/src/lib/server/connectors/teams.ts +94 -0
  255. package/src/lib/server/connectors/telegram.ts +221 -0
  256. package/src/lib/server/connectors/types.ts +62 -0
  257. package/src/lib/server/connectors/whatsapp.ts +349 -0
  258. package/src/lib/server/context-manager.ts +232 -0
  259. package/src/lib/server/cost.ts +31 -0
  260. package/src/lib/server/daemon-state.ts +354 -0
  261. package/src/lib/server/data-dir.ts +3 -0
  262. package/src/lib/server/embeddings.ts +111 -0
  263. package/src/lib/server/execution-log.ts +257 -0
  264. package/src/lib/server/gateway/protocol.test.ts +54 -0
  265. package/src/lib/server/gateway/protocol.ts +114 -0
  266. package/src/lib/server/heartbeat-service.ts +366 -0
  267. package/src/lib/server/knowledge-db.test.ts +441 -0
  268. package/src/lib/server/logger.ts +47 -0
  269. package/src/lib/server/main-agent-loop.ts +1017 -0
  270. package/src/lib/server/mcp-client.test.ts +342 -0
  271. package/src/lib/server/mcp-client.ts +130 -0
  272. package/src/lib/server/memory-db.ts +1078 -0
  273. package/src/lib/server/memory-graph.test.ts +153 -0
  274. package/src/lib/server/memory-graph.ts +138 -0
  275. package/src/lib/server/openclaw-health.ts +245 -0
  276. package/src/lib/server/orchestrator-lg.ts +431 -0
  277. package/src/lib/server/orchestrator.ts +364 -0
  278. package/src/lib/server/playwright-proxy.mjs +70 -0
  279. package/src/lib/server/plugins.ts +229 -0
  280. package/src/lib/server/process-manager.ts +327 -0
  281. package/src/lib/server/provider-health.ts +113 -0
  282. package/src/lib/server/queue.ts +859 -0
  283. package/src/lib/server/runtime-settings.ts +119 -0
  284. package/src/lib/server/scheduler.ts +196 -0
  285. package/src/lib/server/session-mailbox.ts +129 -0
  286. package/src/lib/server/session-run-manager.ts +512 -0
  287. package/src/lib/server/session-tools/connector.ts +124 -0
  288. package/src/lib/server/session-tools/context-mgmt.ts +103 -0
  289. package/src/lib/server/session-tools/context.ts +114 -0
  290. package/src/lib/server/session-tools/crud.ts +673 -0
  291. package/src/lib/server/session-tools/delegate.ts +708 -0
  292. package/src/lib/server/session-tools/file.ts +264 -0
  293. package/src/lib/server/session-tools/index.ts +164 -0
  294. package/src/lib/server/session-tools/memory.ts +230 -0
  295. package/src/lib/server/session-tools/session-info.ts +422 -0
  296. package/src/lib/server/session-tools/session-tools-wiring.test.ts +166 -0
  297. package/src/lib/server/session-tools/shell.ts +171 -0
  298. package/src/lib/server/session-tools/web.ts +408 -0
  299. package/src/lib/server/session-tools.ts +9 -0
  300. package/src/lib/server/skills-normalize.ts +130 -0
  301. package/src/lib/server/storage-mcp.test.ts +161 -0
  302. package/src/lib/server/storage.ts +670 -0
  303. package/src/lib/server/stream-agent-chat.ts +571 -0
  304. package/src/lib/server/task-reports.ts +122 -0
  305. package/src/lib/server/task-result.ts +161 -0
  306. package/src/lib/server/task-validation.test.ts +27 -0
  307. package/src/lib/server/task-validation.ts +90 -0
  308. package/src/lib/server/tool-capability-policy.test.ts +58 -0
  309. package/src/lib/server/tool-capability-policy.ts +262 -0
  310. package/src/lib/sessions.ts +68 -0
  311. package/src/lib/tasks.ts +20 -0
  312. package/src/lib/tts.ts +42 -0
  313. package/src/lib/upload.ts +10 -0
  314. package/src/lib/utils.ts +6 -0
  315. package/src/proxy.ts +43 -0
  316. package/src/stores/use-app-store.ts +468 -0
  317. package/src/stores/use-chat-store.ts +323 -0
  318. package/src/types/index.ts +621 -0
  319. package/tsconfig.json +34 -0
@@ -0,0 +1,431 @@
1
+ import { z } from 'zod'
2
+ import { tool } from '@langchain/core/tools'
3
+ import { createReactAgent } from '@langchain/langgraph/prebuilt'
4
+ import { loadSessions, saveSessions, loadAgents, loadCredentials, loadSettings, loadSecrets, loadTasks, saveTasks, decryptKey, loadSkills } from './storage'
5
+ import { loadRuntimeSettings, getOrchestratorLoopRecursionLimit } from './runtime-settings'
6
+ import { getMemoryDb } from './memory-db'
7
+ import { buildChatModel } from './build-llm'
8
+ import crypto from 'crypto'
9
+ import type { Agent, TaskComment } from '@/types'
10
+
11
+ const NON_LANGGRAPH_PROVIDER_IDS = new Set(['claude-cli', 'codex-cli', 'opencode-cli'])
12
+
13
+ function resolveCredential(credentialId: string | null | undefined): string | null {
14
+ if (!credentialId) return null
15
+ const creds = loadCredentials()
16
+ const cred = creds[credentialId]
17
+ if (!cred?.encryptedKey) return null
18
+ try { return decryptKey(cred.encryptedKey) } catch { return null }
19
+ }
20
+
21
+ /** Resolve which provider/model/key the orchestration routing layer should use */
22
+ function getOrchestrationEngineConfig(orchestrator: Agent): { provider: string; model: string; apiKey: string | null; apiEndpoint: string | null } {
23
+ const settings = loadSettings()
24
+ const configuredProvider = typeof settings.langGraphProvider === 'string'
25
+ ? settings.langGraphProvider.trim()
26
+ : ''
27
+ const configuredModel = typeof settings.langGraphModel === 'string'
28
+ ? settings.langGraphModel.trim()
29
+ : ''
30
+ const configuredApiKey = resolveCredential(settings.langGraphCredentialId)
31
+ const configuredEndpoint = typeof settings.langGraphEndpoint === 'string' && settings.langGraphEndpoint.trim()
32
+ ? settings.langGraphEndpoint.trim()
33
+ : null
34
+
35
+ const fallbackProvider = orchestrator.provider === 'claude-cli' ? 'anthropic' : orchestrator.provider
36
+ const fallbackModel = orchestrator.model || ''
37
+ const fallbackApiKey = resolveCredential(orchestrator.credentialId)
38
+ const fallbackEndpoint = orchestrator.apiEndpoint || null
39
+
40
+ const useConfiguredEngine = configuredProvider.length > 0 && !NON_LANGGRAPH_PROVIDER_IDS.has(configuredProvider)
41
+
42
+ if (useConfiguredEngine) {
43
+ return {
44
+ provider: configuredProvider,
45
+ model: configuredModel,
46
+ apiKey: configuredApiKey,
47
+ apiEndpoint: configuredEndpoint,
48
+ }
49
+ }
50
+
51
+ return {
52
+ provider: fallbackProvider,
53
+ model: fallbackModel,
54
+ apiKey: fallbackApiKey,
55
+ apiEndpoint: fallbackEndpoint,
56
+ }
57
+ }
58
+
59
+ /** Resolve secrets available to this orchestrator */
60
+ function getSecretsForOrchestrator(orchestratorId: string): { name: string; service: string; value: string }[] {
61
+ const allSecrets = loadSecrets()
62
+ const result: { name: string; service: string; value: string }[] = []
63
+ for (const secret of Object.values(allSecrets) as any[]) {
64
+ const isGlobal = secret.scope === 'global'
65
+ const isScoped = secret.scope === 'agent' && secret.agentIds?.includes(orchestratorId)
66
+ if (isGlobal || isScoped) {
67
+ try {
68
+ const value = decryptKey(secret.encryptedValue)
69
+ result.push({ name: secret.name, service: secret.service, value })
70
+ } catch { /* skip if decrypt fails */ }
71
+ }
72
+ }
73
+ return result
74
+ }
75
+
76
+ function saveMessage(sessionId: string, role: 'user' | 'assistant', text: string) {
77
+ const sessions = loadSessions()
78
+ const session = sessions[sessionId]
79
+ if (!session) return
80
+ session.messages.push({ role, text, time: Date.now() })
81
+ session.lastActiveAt = Date.now()
82
+ saveSessions(sessions)
83
+ }
84
+
85
+ /** Import the existing sub-task execution from the old orchestrator */
86
+ async function executeSubTaskViaCli(agent: Agent, task: string, parentSessionId: string): Promise<string> {
87
+ // Dynamic import to avoid circular deps
88
+ const { callProvider } = await import('./orchestrator')
89
+ const crypto = await import('crypto')
90
+ const { loadSessions: ls, saveSessions: ss } = await import('./storage')
91
+
92
+ const sessions = ls()
93
+ const parentSession = sessions[parentSessionId]
94
+ const childId = crypto.randomBytes(4).toString('hex')
95
+ sessions[childId] = {
96
+ id: childId,
97
+ name: `[Agent] ${agent.name}: ${task.slice(0, 40)}`,
98
+ cwd: parentSession?.cwd || process.cwd(),
99
+ user: 'system',
100
+ provider: agent.provider,
101
+ model: agent.model,
102
+ credentialId: agent.credentialId || null,
103
+ apiEndpoint: agent.apiEndpoint || null,
104
+ claudeSessionId: null,
105
+ codexThreadId: null,
106
+ opencodeSessionId: null,
107
+ delegateResumeIds: {
108
+ claudeCode: null,
109
+ codex: null,
110
+ opencode: null,
111
+ },
112
+ messages: [],
113
+ createdAt: Date.now(),
114
+ lastActiveAt: Date.now(),
115
+ sessionType: 'orchestrated' as const,
116
+ agentId: agent.id,
117
+ parentSessionId,
118
+ tools: agent.tools || [],
119
+ }
120
+ ss(sessions)
121
+
122
+ const result = await callProvider(agent, agent.systemPrompt, [{ role: 'user', text: task }])
123
+
124
+ const s2 = ls()
125
+ if (s2[childId]) {
126
+ s2[childId].messages.push({ role: 'user', text: task, time: Date.now() })
127
+ s2[childId].messages.push({ role: 'assistant', text: result, time: Date.now() })
128
+ s2[childId].lastActiveAt = Date.now()
129
+ ss(s2)
130
+ }
131
+
132
+ return result
133
+ }
134
+
135
+ export async function executeLangGraphOrchestrator(
136
+ orchestrator: Agent,
137
+ task: string,
138
+ sessionId: string,
139
+ ): Promise<string> {
140
+ const allAgents = loadAgents()
141
+
142
+ // Build available agents list
143
+ const agentIds = orchestrator.subAgentIds || []
144
+ const agents = agentIds.map((id) => allAgents[id]).filter(Boolean) as Agent[]
145
+ const agentListContext = agents.length
146
+ ? '\n\nAvailable agents:\n' + agents.map((a) => {
147
+ const tools = a.tools?.length ? ` [tools: ${a.tools.join(', ')}]` : ''
148
+ const skills = a.skills?.length ? ` [skills: ${a.skills.join(', ')}]` : ''
149
+ return `- ${a.name}: ${a.description}${tools}${skills}`
150
+ }).join('\n')
151
+ : '\n\n(No agents available for delegation.)'
152
+
153
+ // Load relevant memories
154
+ const db = getMemoryDb()
155
+ const memories = db.getByAgent(orchestrator.id)
156
+ const memoryContext = memories.length
157
+ ? '\n\nRelevant memories:\n' + memories.slice(0, 10).map((m) => `[${m.category}] ${m.title}: ${m.content.slice(0, 200)}`).join('\n')
158
+ : ''
159
+
160
+ // Define tools
161
+ const delegateTool = tool(
162
+ async ({ agentName, task: agentTask }) => {
163
+ const agent = agents.find((a) => a.name.toLowerCase() === agentName.toLowerCase())
164
+ if (!agent) {
165
+ return `Agent "${agentName}" not found. Available: ${agents.map((a) => a.name).join(', ')}`
166
+ }
167
+ console.log(`[orchestrator-lg] Delegating to ${agent.name}: ${agentTask.slice(0, 80)}`)
168
+ saveMessage(sessionId, 'assistant', `[Delegating to ${agent.name}]: ${agentTask}`)
169
+ const result = await executeSubTaskViaCli(agent, agentTask, sessionId)
170
+ saveMessage(sessionId, 'user', `[Agent ${agent.name} result]: ${result.slice(0, 2000)}`)
171
+ return result
172
+ },
173
+ {
174
+ name: 'delegate_to_agent',
175
+ description: 'Delegate a task to one of the available agents. The agent will execute the task and return its result.',
176
+ schema: z.object({
177
+ agentName: z.string().describe('Name of the agent to delegate to'),
178
+ task: z.string().describe('The task description for the agent'),
179
+ }),
180
+ },
181
+ )
182
+
183
+ const storeMemoryTool = tool(
184
+ async ({ category, title, content }) => {
185
+ db.add({
186
+ agentId: orchestrator.id,
187
+ sessionId,
188
+ category,
189
+ title,
190
+ content,
191
+ })
192
+ console.log(`[orchestrator-lg] Stored memory: [${category}] ${title}`)
193
+ return 'Memory stored successfully.'
194
+ },
195
+ {
196
+ name: 'store_memory',
197
+ description: 'Store information in long-term memory for future reference.',
198
+ schema: z.object({
199
+ category: z.string().describe('Category keyword (e.g. "seo", "deployment", "finding")'),
200
+ title: z.string().describe('Short descriptive title'),
201
+ content: z.string().describe('The content to remember'),
202
+ }),
203
+ },
204
+ )
205
+
206
+ const searchMemoryTool = tool(
207
+ async ({ query }) => {
208
+ const results = db.search(query, orchestrator.id)
209
+ if (!results.length) return 'No matching memories found.'
210
+ return results.map((m) => `[${m.category}] ${m.title}: ${m.content.slice(0, 300)}`).join('\n')
211
+ },
212
+ {
213
+ name: 'search_memory',
214
+ description: 'Search long-term memory for relevant information.',
215
+ schema: z.object({
216
+ query: z.string().describe('Search terms'),
217
+ }),
218
+ },
219
+ )
220
+
221
+ const markCompleteTool = tool(
222
+ async ({ summary }) => {
223
+ console.log(`[orchestrator-lg] Marked complete: ${summary.slice(0, 100)}`)
224
+ return `ORCHESTRATION_COMPLETE: ${summary}`
225
+ },
226
+ {
227
+ name: 'mark_complete',
228
+ description: 'Signal that the orchestration task is done. Call this when all work is finished.',
229
+ schema: z.object({
230
+ summary: z.string().describe('Summary of what was accomplished'),
231
+ }),
232
+ },
233
+ )
234
+
235
+ // Secrets
236
+ const availableSecrets = getSecretsForOrchestrator(orchestrator.id)
237
+
238
+ const getSecretTool = tool(
239
+ async ({ serviceName }) => {
240
+ const match = availableSecrets.find(
241
+ (s) => s.service.toLowerCase() === serviceName.toLowerCase() || s.name.toLowerCase() === serviceName.toLowerCase(),
242
+ )
243
+ if (!match) {
244
+ return `No secret found for "${serviceName}". Available services: ${availableSecrets.map((s) => s.service).join(', ') || 'none'}`
245
+ }
246
+ console.log(`[orchestrator-lg] Retrieved secret for service: ${match.service}`)
247
+ return JSON.stringify({ name: match.name, service: match.service, value: match.value })
248
+ },
249
+ {
250
+ name: 'get_secret',
251
+ description: 'Retrieve a stored credential/secret by service name (e.g. "gmail", "ahrefs"). Returns the decrypted value. Use this when you need API keys or login credentials for external services.',
252
+ schema: z.object({
253
+ serviceName: z.string().describe('The service name or secret name to look up'),
254
+ }),
255
+ },
256
+ )
257
+
258
+ // Task board tools
259
+ const commentOnTaskTool = tool(
260
+ async ({ taskId, comment }) => {
261
+ const tasks = loadTasks()
262
+ const t = tasks[taskId]
263
+ if (!t) return `Task "${taskId}" not found.`
264
+ if (!t.comments) t.comments = []
265
+ const c: TaskComment = {
266
+ id: crypto.randomBytes(4).toString('hex'),
267
+ author: orchestrator.name,
268
+ agentId: orchestrator.id,
269
+ text: comment,
270
+ createdAt: Date.now(),
271
+ }
272
+ t.comments.push(c)
273
+ t.updatedAt = Date.now()
274
+ saveTasks(tasks)
275
+ console.log(`[orchestrator-lg] Commented on task "${t.title}": ${comment.slice(0, 80)}`)
276
+ return `Comment added to task "${t.title}".`
277
+ },
278
+ {
279
+ name: 'comment_on_task',
280
+ description: 'Add a comment to a task on the task board. Use this to provide status updates, ask questions, or leave notes for the user.',
281
+ schema: z.object({
282
+ taskId: z.string().describe('The task ID to comment on'),
283
+ comment: z.string().describe('The comment text'),
284
+ }),
285
+ },
286
+ )
287
+
288
+ const createTaskTool = tool(
289
+ async ({ title, description: desc }) => {
290
+ const tasks = loadTasks()
291
+ const id = crypto.randomBytes(4).toString('hex')
292
+ tasks[id] = {
293
+ id,
294
+ title,
295
+ description: desc,
296
+ status: 'backlog',
297
+ agentId: orchestrator.id,
298
+ sessionId: null,
299
+ result: null,
300
+ error: null,
301
+ comments: [],
302
+ createdAt: Date.now(),
303
+ updatedAt: Date.now(),
304
+ queuedAt: null,
305
+ startedAt: null,
306
+ completedAt: null,
307
+ }
308
+ saveTasks(tasks)
309
+ console.log(`[orchestrator-lg] Created backlog task: "${title}" (${id})`)
310
+ return `Task "${title}" created in backlog (id: ${id}). The user can review and queue it.`
311
+ },
312
+ {
313
+ name: 'create_task',
314
+ description: 'Create a new task in the backlog for the user to review. Use this when you identify follow-up work, need user input, or want to suggest next steps.',
315
+ schema: z.object({
316
+ title: z.string().describe('Short task title'),
317
+ description: z.string().describe('Detailed description of what needs to be done'),
318
+ }),
319
+ },
320
+ )
321
+
322
+ // Build secrets context for the system prompt
323
+ const secretsContext = availableSecrets.length
324
+ ? '\n\nAvailable secrets (use get_secret tool to retrieve values):\n' + availableSecrets.map((s) => `- ${s.name} (${s.service})`).join('\n')
325
+ : ''
326
+
327
+ // Build task context
328
+ const allTasks = loadTasks()
329
+ const taskList = Object.values(allTasks)
330
+ const taskContext = taskList.length
331
+ ? '\n\nCurrent task board:\n' + taskList.slice(0, 20).map((t: any) => `- [${t.status}] "${t.title}" (id: ${t.id})`).join('\n')
332
+ : ''
333
+
334
+ // Build routing LLM from Settings -> Orchestrator Engine (fallback: orchestrator's own provider)
335
+ const engine = getOrchestrationEngineConfig(orchestrator)
336
+ const llm = buildChatModel({
337
+ provider: engine.provider,
338
+ model: engine.model,
339
+ apiKey: engine.apiKey,
340
+ apiEndpoint: engine.apiEndpoint,
341
+ })
342
+ // Build system message: [userPrompt] \n\n [soul] \n\n [systemPrompt] \n\n [orchestrator context]
343
+ const settings = loadSettings()
344
+ const promptParts: string[] = []
345
+ if (settings.userPrompt) promptParts.push(settings.userPrompt)
346
+ if (orchestrator.soul) promptParts.push(orchestrator.soul)
347
+ if (orchestrator.systemPrompt) promptParts.push(orchestrator.systemPrompt)
348
+ // Inject dynamic skills
349
+ if (orchestrator.skillIds?.length) {
350
+ const allSkills = loadSkills()
351
+ for (const skillId of orchestrator.skillIds) {
352
+ const skill = allSkills[skillId]
353
+ if (skill?.content) promptParts.push(`## Skill: ${skill.name}\n${skill.content}`)
354
+ }
355
+ }
356
+ const basePrompt = promptParts.join('\n\n')
357
+
358
+ const systemMessage = [
359
+ basePrompt,
360
+ '\nYou are an orchestrator. Use the provided tools to delegate tasks to agents, store and search memories, retrieve secrets for external services, and mark tasks complete.',
361
+ '\nAgents with [tools: browser] have access to a Playwright browser and can navigate websites, scrape data, fill forms, take screenshots, and interact with web pages.',
362
+ '\nAgents with [skills: ...] have specialized Claude Code skills. When delegating to them, mention the skill in your task instructions. For example, if an agent has [skills: frontend-design], tell it "Use /frontend-design to build this UI". Available skills: frontend-design (production-grade UI), site-builder (build from spec), site-tester (QA in Chrome), seo-site-auditor (SEO audit), keyword-researcher (keyword research via Ahrefs).',
363
+ '\nWhen delegating to an agent that needs credentials, use get_secret first and include the credential in the task instructions.',
364
+ '\nYou can comment on tasks to provide status updates and create new backlog tasks for follow-up work.',
365
+ '\nAlways call mark_complete when you are done.',
366
+ agentListContext,
367
+ memoryContext,
368
+ secretsContext,
369
+ taskContext,
370
+ ].join('\n')
371
+
372
+ const agent = createReactAgent({
373
+ llm,
374
+ tools: [delegateTool, storeMemoryTool, searchMemoryTool, getSecretTool, commentOnTaskTool, createTaskTool, markCompleteTool],
375
+ stateModifier: systemMessage,
376
+ })
377
+
378
+ // Save initial user message
379
+ saveMessage(sessionId, 'user', task)
380
+
381
+ let finalResult = ''
382
+ const runtime = loadRuntimeSettings()
383
+ const recursionLimit = getOrchestratorLoopRecursionLimit(runtime)
384
+ const abortController = new AbortController()
385
+ let timedOut = false
386
+ const loopTimer = runtime.loopMode === 'ongoing' && runtime.ongoingLoopMaxRuntimeMs
387
+ ? setTimeout(() => {
388
+ timedOut = true
389
+ abortController.abort()
390
+ }, runtime.ongoingLoopMaxRuntimeMs)
391
+ : null
392
+
393
+ try {
394
+ const stream = await agent.stream(
395
+ { messages: [{ role: 'user' as const, content: task }] },
396
+ { recursionLimit, signal: abortController.signal },
397
+ )
398
+
399
+ for await (const chunk of stream) {
400
+ // chunk has 'agent' or 'tools' keys with messages arrays
401
+ const agentChunk = (chunk as any).agent
402
+ if (agentChunk?.messages) {
403
+ const msgs = Array.isArray(agentChunk.messages) ? agentChunk.messages : [agentChunk.messages]
404
+ for (const msg of msgs) {
405
+ const text = typeof msg.content === 'string'
406
+ ? msg.content
407
+ : Array.isArray(msg.content)
408
+ ? msg.content.map((c: any) => c.text || '').join('')
409
+ : ''
410
+ if (text) {
411
+ finalResult = text
412
+ saveMessage(sessionId, 'assistant', text)
413
+ }
414
+ }
415
+ }
416
+ }
417
+ } catch (err: any) {
418
+ const errMsg = timedOut
419
+ ? 'Ongoing loop stopped after reaching the configured runtime limit.'
420
+ : err.message || String(err)
421
+ console.error(`[orchestrator-lg] Error:`, errMsg)
422
+ saveMessage(sessionId, 'assistant', `[Error] ${errMsg}`)
423
+ throw new Error(errMsg)
424
+ } finally {
425
+ if (loopTimer) clearTimeout(loopTimer)
426
+ }
427
+
428
+ // Extract summary from mark_complete if present
429
+ const completeMatch = finalResult.match(/ORCHESTRATION_COMPLETE:\s*([\s\S]+)/)
430
+ return completeMatch ? completeMatch[1].trim() : finalResult
431
+ }