@swarmclawai/swarmclaw 0.7.7 → 0.8.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 (281) hide show
  1. package/README.md +12 -14
  2. package/next.config.ts +13 -2
  3. package/package.json +4 -2
  4. package/src/app/api/agents/[id]/thread/route.ts +9 -0
  5. package/src/app/api/agents/route.ts +4 -0
  6. package/src/app/api/agents/thread-route.test.ts +133 -0
  7. package/src/app/api/approvals/route.test.ts +148 -0
  8. package/src/app/api/canvas/[sessionId]/route.ts +3 -1
  9. package/src/app/api/chatrooms/[id]/chat/route.ts +4 -2
  10. package/src/app/api/chats/[id]/devserver/route.ts +48 -7
  11. package/src/app/api/chats/[id]/messages/route.ts +42 -18
  12. package/src/app/api/chats/[id]/route.ts +1 -1
  13. package/src/app/api/chats/[id]/stop/route.ts +5 -4
  14. package/src/app/api/chats/route.ts +23 -2
  15. package/src/app/api/clawhub/install/route.ts +28 -8
  16. package/src/app/api/connectors/[id]/route.ts +46 -3
  17. package/src/app/api/connectors/route.ts +12 -8
  18. package/src/app/api/external-agents/route.test.ts +165 -0
  19. package/src/app/api/gateways/[id]/health/route.ts +27 -12
  20. package/src/app/api/gateways/[id]/route.ts +2 -0
  21. package/src/app/api/gateways/health-route.test.ts +135 -0
  22. package/src/app/api/gateways/route.ts +2 -0
  23. package/src/app/api/mcp-servers/route.test.ts +130 -0
  24. package/src/app/api/openclaw/deploy/route.ts +38 -5
  25. package/src/app/api/plugins/install/route.ts +46 -6
  26. package/src/app/api/plugins/marketplace/route.ts +48 -15
  27. package/src/app/api/preview-server/route.ts +26 -11
  28. package/src/app/api/projects/[id]/route.ts +6 -2
  29. package/src/app/api/projects/route.ts +4 -3
  30. package/src/app/api/schedules/[id]/run/route.ts +4 -0
  31. package/src/app/api/schedules/route.test.ts +86 -0
  32. package/src/app/api/schedules/route.ts +6 -1
  33. package/src/app/api/secrets/[id]/route.ts +1 -0
  34. package/src/app/api/secrets/route.ts +2 -1
  35. package/src/app/api/settings/route.ts +2 -0
  36. package/src/app/api/setup/check-provider/route.test.ts +19 -0
  37. package/src/app/api/setup/check-provider/route.ts +40 -10
  38. package/src/app/api/skills/[id]/route.ts +12 -0
  39. package/src/app/api/skills/import/route.ts +14 -12
  40. package/src/app/api/skills/route.ts +13 -1
  41. package/src/app/api/tasks/[id]/route.ts +10 -1
  42. package/src/app/api/tasks/import/github/route.test.ts +65 -0
  43. package/src/app/api/tasks/import/github/route.ts +337 -0
  44. package/src/app/api/wallets/[id]/approve/route.ts +17 -3
  45. package/src/app/api/wallets/[id]/route.ts +79 -33
  46. package/src/app/api/wallets/[id]/send/route.ts +19 -33
  47. package/src/app/api/wallets/route.ts +78 -61
  48. package/src/app/api/webhooks/[id]/route.ts +33 -6
  49. package/src/app/api/webhooks/route.test.ts +272 -0
  50. package/src/cli/index.js +1 -0
  51. package/src/cli/spec.js +1 -0
  52. package/src/components/agents/agent-card.tsx +9 -2
  53. package/src/components/agents/agent-chat-list.tsx +18 -2
  54. package/src/components/agents/agent-list.tsx +1 -0
  55. package/src/components/agents/agent-sheet.tsx +257 -38
  56. package/src/components/agents/inspector-panel.tsx +41 -0
  57. package/src/components/canvas/canvas-panel.tsx +236 -65
  58. package/src/components/chat/chat-area.tsx +36 -19
  59. package/src/components/chat/chat-card.tsx +36 -13
  60. package/src/components/chat/chat-header.tsx +48 -16
  61. package/src/components/chat/chat-list.tsx +28 -4
  62. package/src/components/chat/checkpoint-timeline.tsx +50 -34
  63. package/src/components/chat/delegation-banner.test.ts +14 -1
  64. package/src/components/chat/delegation-banner.tsx +1 -1
  65. package/src/components/chat/message-bubble.tsx +208 -145
  66. package/src/components/chat/message-list.tsx +48 -19
  67. package/src/components/chatrooms/chatroom-message.tsx +2 -2
  68. package/src/components/chatrooms/chatroom-sheet.tsx +16 -2
  69. package/src/components/connectors/connector-health.tsx +1 -1
  70. package/src/components/connectors/connector-list.tsx +7 -2
  71. package/src/components/connectors/connector-sheet.tsx +337 -148
  72. package/src/components/gateways/gateway-sheet.tsx +2 -2
  73. package/src/components/layout/app-layout.tsx +40 -23
  74. package/src/components/mcp-servers/mcp-server-list.tsx +26 -5
  75. package/src/components/mcp-servers/mcp-server-sheet.tsx +19 -2
  76. package/src/components/openclaw/openclaw-deploy-panel.tsx +269 -21
  77. package/src/components/plugins/plugin-list.tsx +45 -9
  78. package/src/components/plugins/plugin-sheet.tsx +55 -7
  79. package/src/components/projects/project-detail.tsx +217 -0
  80. package/src/components/projects/project-sheet.tsx +176 -4
  81. package/src/components/providers/provider-list.tsx +2 -1
  82. package/src/components/providers/provider-sheet.tsx +21 -2
  83. package/src/components/schedules/schedule-card.tsx +25 -1
  84. package/src/components/schedules/schedule-sheet.tsx +44 -2
  85. package/src/components/secrets/secret-sheet.tsx +21 -2
  86. package/src/components/shared/agent-switch-dialog.tsx +12 -1
  87. package/src/components/shared/bottom-sheet.tsx +13 -3
  88. package/src/components/shared/command-palette.tsx +8 -1
  89. package/src/components/shared/confirm-dialog.tsx +19 -4
  90. package/src/components/shared/connector-platform-icon.test.ts +28 -0
  91. package/src/components/shared/connector-platform-icon.tsx +39 -6
  92. package/src/components/shared/settings/plugin-manager.tsx +29 -6
  93. package/src/components/shared/settings/section-capability-policy.tsx +45 -3
  94. package/src/components/shared/settings/section-voice.tsx +11 -3
  95. package/src/components/skills/skill-list.tsx +25 -0
  96. package/src/components/skills/skill-sheet.tsx +84 -12
  97. package/src/components/tasks/approvals-panel.tsx +289 -34
  98. package/src/components/tasks/task-board.tsx +410 -25
  99. package/src/components/tasks/task-card.tsx +66 -8
  100. package/src/components/tasks/task-sheet.tsx +16 -4
  101. package/src/components/ui/dialog.tsx +2 -2
  102. package/src/components/wallets/wallet-approval-dialog.tsx +4 -2
  103. package/src/components/wallets/wallet-panel.tsx +435 -90
  104. package/src/components/wallets/wallet-section.tsx +198 -48
  105. package/src/components/webhooks/webhook-sheet.tsx +22 -2
  106. package/src/lib/approval-display.ts +20 -0
  107. package/src/lib/canvas-content.ts +198 -0
  108. package/src/lib/chat-artifact-summary.ts +165 -0
  109. package/src/lib/chat-display.test.ts +91 -0
  110. package/src/lib/chat-display.ts +58 -0
  111. package/src/lib/chat-streaming-state.test.ts +47 -1
  112. package/src/lib/chat-streaming-state.ts +42 -0
  113. package/src/lib/ollama-model.ts +10 -0
  114. package/src/lib/openclaw-endpoint.test.ts +8 -0
  115. package/src/lib/openclaw-endpoint.ts +6 -1
  116. package/src/lib/plugin-install-cors.ts +46 -0
  117. package/src/lib/plugin-sources.test.ts +43 -0
  118. package/src/lib/plugin-sources.ts +77 -0
  119. package/src/lib/providers/ollama.ts +16 -6
  120. package/src/lib/providers/openclaw.test.ts +54 -0
  121. package/src/lib/providers/openclaw.ts +127 -11
  122. package/src/lib/schedule-dedupe-advanced.test.ts +1335 -0
  123. package/src/lib/schedule-dedupe.test.ts +66 -1
  124. package/src/lib/schedule-dedupe.ts +169 -12
  125. package/src/lib/schedule-origin.test.ts +20 -0
  126. package/src/lib/schedule-origin.ts +15 -0
  127. package/src/lib/server/__fixtures__/fake-mcp-stdio-server.mjs +27 -0
  128. package/src/lib/server/agent-availability.ts +16 -0
  129. package/src/lib/server/agent-runtime-config.ts +12 -4
  130. package/src/lib/server/agent-thread-session.test.ts +51 -0
  131. package/src/lib/server/agent-thread-session.ts +7 -0
  132. package/src/lib/server/approval-match.ts +205 -0
  133. package/src/lib/server/approvals-auto-approve.test.ts +538 -1
  134. package/src/lib/server/approvals.ts +214 -1
  135. package/src/lib/server/assistant-control.test.ts +29 -0
  136. package/src/lib/server/assistant-control.ts +23 -0
  137. package/src/lib/server/build-llm.test.ts +79 -0
  138. package/src/lib/server/build-llm.ts +14 -4
  139. package/src/lib/server/canvas-content.test.ts +32 -0
  140. package/src/lib/server/canvas-content.ts +6 -0
  141. package/src/lib/server/capability-router.test.ts +33 -0
  142. package/src/lib/server/capability-router.ts +80 -19
  143. package/src/lib/server/chat-execution-advanced.test.ts +651 -0
  144. package/src/lib/server/chat-execution-disabled.test.ts +94 -0
  145. package/src/lib/server/chat-execution-tool-events.test.ts +157 -0
  146. package/src/lib/server/chat-execution.ts +378 -73
  147. package/src/lib/server/clawhub-client.test.ts +14 -8
  148. package/src/lib/server/connectors/manager-reconnect.test.ts +47 -0
  149. package/src/lib/server/connectors/manager.test.ts +1147 -0
  150. package/src/lib/server/connectors/manager.ts +461 -137
  151. package/src/lib/server/connectors/pairing.ts +26 -5
  152. package/src/lib/server/connectors/types.ts +2 -0
  153. package/src/lib/server/connectors/whatsapp.test.ts +134 -0
  154. package/src/lib/server/connectors/whatsapp.ts +271 -47
  155. package/src/lib/server/context-manager.ts +6 -1
  156. package/src/lib/server/daemon-state.ts +84 -47
  157. package/src/lib/server/data-dir.test.ts +37 -0
  158. package/src/lib/server/data-dir.ts +20 -1
  159. package/src/lib/server/delegation-jobs-advanced.test.ts +513 -0
  160. package/src/lib/server/devserver-launch.test.ts +60 -0
  161. package/src/lib/server/devserver-launch.ts +85 -0
  162. package/src/lib/server/elevenlabs.test.ts +247 -1
  163. package/src/lib/server/elevenlabs.ts +147 -43
  164. package/src/lib/server/ethereum.ts +590 -0
  165. package/src/lib/server/eval/agent-regression-advanced.test.ts +302 -0
  166. package/src/lib/server/eval/agent-regression.test.ts +18 -1
  167. package/src/lib/server/eval/agent-regression.ts +383 -11
  168. package/src/lib/server/evm-swap.ts +475 -0
  169. package/src/lib/server/execution-log.ts +1 -0
  170. package/src/lib/server/heartbeat-service-timer.test.ts +173 -0
  171. package/src/lib/server/heartbeat-service.ts +20 -11
  172. package/src/lib/server/heartbeat-wake.test.ts +112 -0
  173. package/src/lib/server/heartbeat-wake.ts +338 -57
  174. package/src/lib/server/main-agent-loop-advanced.test.ts +538 -0
  175. package/src/lib/server/main-agent-loop.test.ts +260 -0
  176. package/src/lib/server/main-agent-loop.ts +559 -14
  177. package/src/lib/server/mcp-client.test.ts +16 -0
  178. package/src/lib/server/mcp-client.ts +25 -0
  179. package/src/lib/server/memory-integration.test.ts +719 -0
  180. package/src/lib/server/memory-policy.test.ts +43 -0
  181. package/src/lib/server/memory-policy.ts +132 -0
  182. package/src/lib/server/memory-tiers.test.ts +60 -0
  183. package/src/lib/server/memory-tiers.ts +16 -0
  184. package/src/lib/server/ollama-runtime.ts +58 -0
  185. package/src/lib/server/openclaw-deploy.test.ts +109 -1
  186. package/src/lib/server/openclaw-deploy.ts +557 -81
  187. package/src/lib/server/openclaw-gateway.test.ts +131 -0
  188. package/src/lib/server/openclaw-gateway.ts +10 -4
  189. package/src/lib/server/openclaw-health.test.ts +35 -0
  190. package/src/lib/server/openclaw-health.ts +215 -47
  191. package/src/lib/server/orchestrator-lg.ts +3 -2
  192. package/src/lib/server/orchestrator.ts +2 -0
  193. package/src/lib/server/plugins-advanced.test.ts +351 -0
  194. package/src/lib/server/plugins.ts +211 -6
  195. package/src/lib/server/project-context.ts +162 -0
  196. package/src/lib/server/project-utils.ts +150 -0
  197. package/src/lib/server/queue-advanced.test.ts +528 -0
  198. package/src/lib/server/queue-followups.test.ts +409 -2
  199. package/src/lib/server/queue-reconcile.test.ts +128 -0
  200. package/src/lib/server/queue.ts +527 -68
  201. package/src/lib/server/scheduler.ts +29 -1
  202. package/src/lib/server/session-note.test.ts +36 -0
  203. package/src/lib/server/session-note.ts +42 -0
  204. package/src/lib/server/session-run-manager.ts +83 -4
  205. package/src/lib/server/session-tools/canvas.ts +14 -12
  206. package/src/lib/server/session-tools/connector-inputs.test.ts +37 -0
  207. package/src/lib/server/session-tools/connector.test.ts +138 -0
  208. package/src/lib/server/session-tools/connector.ts +366 -54
  209. package/src/lib/server/session-tools/context.ts +17 -3
  210. package/src/lib/server/session-tools/crud.ts +484 -84
  211. package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
  212. package/src/lib/server/session-tools/delegate-resume.test.ts +50 -0
  213. package/src/lib/server/session-tools/delegate.ts +102 -10
  214. package/src/lib/server/session-tools/discovery-approvals.test.ts +142 -0
  215. package/src/lib/server/session-tools/discovery.ts +80 -12
  216. package/src/lib/server/session-tools/file-normalize.test.ts +36 -0
  217. package/src/lib/server/session-tools/file.ts +43 -4
  218. package/src/lib/server/session-tools/human-loop.ts +35 -5
  219. package/src/lib/server/session-tools/index.ts +44 -9
  220. package/src/lib/server/session-tools/manage-connectors.test.ts +139 -0
  221. package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +564 -0
  222. package/src/lib/server/session-tools/manage-schedules.test.ts +283 -0
  223. package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +852 -0
  224. package/src/lib/server/session-tools/manage-tasks.test.ts +114 -0
  225. package/src/lib/server/session-tools/memory.test.ts +93 -0
  226. package/src/lib/server/session-tools/memory.ts +554 -75
  227. package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
  228. package/src/lib/server/session-tools/platform-access.test.ts +58 -0
  229. package/src/lib/server/session-tools/platform.ts +60 -19
  230. package/src/lib/server/session-tools/plugin-creator.ts +57 -1
  231. package/src/lib/server/session-tools/primitive-tools.test.ts +6 -0
  232. package/src/lib/server/session-tools/schedule.ts +6 -1
  233. package/src/lib/server/session-tools/shell-normalize.test.ts +25 -1
  234. package/src/lib/server/session-tools/shell.ts +22 -3
  235. package/src/lib/server/session-tools/wallet-tool.test.ts +254 -0
  236. package/src/lib/server/session-tools/wallet.ts +1374 -139
  237. package/src/lib/server/session-tools/web-inputs.test.ts +178 -0
  238. package/src/lib/server/session-tools/web.ts +621 -70
  239. package/src/lib/server/skill-discovery.ts +128 -0
  240. package/src/lib/server/skill-eligibility.test.ts +84 -0
  241. package/src/lib/server/skill-eligibility.ts +95 -0
  242. package/src/lib/server/skill-prompt-budget.test.ts +102 -0
  243. package/src/lib/server/skill-prompt-budget.ts +125 -0
  244. package/src/lib/server/skills-normalize.test.ts +54 -0
  245. package/src/lib/server/skills-normalize.ts +372 -26
  246. package/src/lib/server/solana.ts +214 -29
  247. package/src/lib/server/storage.ts +65 -36
  248. package/src/lib/server/stream-agent-chat.test.ts +437 -2
  249. package/src/lib/server/stream-agent-chat.ts +957 -79
  250. package/src/lib/server/system-events.ts +1 -1
  251. package/src/lib/server/tool-aliases.ts +2 -0
  252. package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
  253. package/src/lib/server/tool-capability-policy.test.ts +24 -0
  254. package/src/lib/server/tool-capability-policy.ts +29 -1
  255. package/src/lib/server/tool-loop-detection.test.ts +105 -0
  256. package/src/lib/server/tool-loop-detection.ts +260 -0
  257. package/src/lib/server/tool-planning.test.ts +44 -0
  258. package/src/lib/server/tool-planning.ts +271 -0
  259. package/src/lib/server/wallet-execution.test.ts +198 -0
  260. package/src/lib/server/wallet-portfolio.test.ts +98 -0
  261. package/src/lib/server/wallet-portfolio.ts +724 -0
  262. package/src/lib/server/wallet-service.test.ts +57 -0
  263. package/src/lib/server/wallet-service.ts +213 -0
  264. package/src/lib/server/watch-jobs-advanced.test.ts +594 -0
  265. package/src/lib/server/watch-jobs.ts +17 -2
  266. package/src/lib/server/workspace-context.ts +111 -0
  267. package/src/lib/skill-save-payload.test.ts +39 -0
  268. package/src/lib/skill-save-payload.ts +37 -0
  269. package/src/lib/tasks.ts +28 -0
  270. package/src/lib/tool-definitions.ts +2 -1
  271. package/src/lib/tool-event-summary.test.ts +30 -0
  272. package/src/lib/tool-event-summary.ts +37 -0
  273. package/src/lib/validation/schemas.ts +1 -0
  274. package/src/lib/wallet-transactions.test.ts +75 -0
  275. package/src/lib/wallet-transactions.ts +43 -0
  276. package/src/lib/wallet.test.ts +17 -0
  277. package/src/lib/wallet.ts +183 -0
  278. package/src/proxy.test.ts +31 -0
  279. package/src/proxy.ts +34 -2
  280. package/src/stores/use-chat-store.ts +15 -1
  281. package/src/types/index.ts +249 -14
@@ -2,7 +2,7 @@ import fs from 'fs'
2
2
  import http from 'http'
3
3
  import https from 'https'
4
4
  import type { StreamChatOptions } from './index'
5
- import { PROVIDER_DEFAULTS } from './provider-defaults'
5
+ import { resolveOllamaRuntimeConfig } from '@/lib/server/ollama-runtime'
6
6
 
7
7
  const IMAGE_EXTS = /\.(png|jpg|jpeg|gif|webp|bmp)$/i
8
8
  const TEXT_EXTS = /\.(txt|md|csv|json|xml|html|js|ts|tsx|jsx|py|go|rs|java|c|cpp|h|yml|yaml|toml|env|log|sh|sql|css|scss)$/i
@@ -10,9 +10,19 @@ const TEXT_EXTS = /\.(txt|md|csv|json|xml|html|js|ts|tsx|jsx|py|go|rs|java|c|cpp
10
10
  export function streamOllamaChat({ session, message, imagePath, apiKey, write, active, loadHistory, onUsage, signal }: StreamChatOptions): Promise<string> {
11
11
  return new Promise((resolve) => {
12
12
  const messages = buildMessages(session, message, imagePath, loadHistory)
13
- const model = session.model || 'llama3'
14
- // Cloud: no endpoint but API key present → use Ollama cloud
15
- const endpoint = session.apiEndpoint || (apiKey ? PROVIDER_DEFAULTS.ollamaCloud : PROVIDER_DEFAULTS.ollama)
13
+ const runtime = resolveOllamaRuntimeConfig({
14
+ model: session.model,
15
+ apiKey,
16
+ apiEndpoint: session.apiEndpoint,
17
+ })
18
+ const model = runtime.model || 'llama3'
19
+ const endpoint = runtime.endpoint
20
+ if (runtime.useCloud && !runtime.apiKey) {
21
+ write(`data: ${JSON.stringify({ t: 'err', text: 'Ollama Cloud model requires an API key. Set OLLAMA_API_KEY or attach an Ollama credential.' })}\n\n`)
22
+ active.delete(session.id)
23
+ resolve('')
24
+ return
25
+ }
16
26
 
17
27
  const parsed = new URL(endpoint)
18
28
  const isHttps = parsed.protocol === 'https:'
@@ -43,8 +53,8 @@ export function streamOllamaChat({ session, message, imagePath, apiKey, write, a
43
53
  const headers: Record<string, string> = {
44
54
  'Content-Type': 'application/json',
45
55
  }
46
- if (apiKey) {
47
- headers['Authorization'] = `Bearer ${apiKey}`
56
+ if (runtime.apiKey) {
57
+ headers['Authorization'] = `Bearer ${runtime.apiKey}`
48
58
  }
49
59
 
50
60
  const apiReq = transport.request({
@@ -0,0 +1,54 @@
1
+ import assert from 'node:assert/strict'
2
+ import { afterEach, test } from 'node:test'
3
+
4
+ import { resolveGatewayAgentId } from './openclaw'
5
+ import { loadAgents, saveAgents } from '../server/storage'
6
+ import type { Agent } from '@/types'
7
+
8
+ const originalAgents = loadAgents({ includeTrashed: true })
9
+
10
+ afterEach(() => {
11
+ saveAgents(originalAgents)
12
+ })
13
+
14
+ test('resolveGatewayAgentId prefers the matching OpenClaw agent name from storage', () => {
15
+ const agents = loadAgents({ includeTrashed: true })
16
+ agents['openclaw-agent-test'] = {
17
+ id: 'openclaw-agent-test',
18
+ name: 'Research Operator',
19
+ systemPrompt: '',
20
+ provider: 'openclaw',
21
+ model: 'default',
22
+ createdAt: Date.now(),
23
+ updatedAt: Date.now(),
24
+ } as Agent
25
+ saveAgents(agents)
26
+
27
+ const resolved = resolveGatewayAgentId({
28
+ id: 'session-1',
29
+ agentId: 'openclaw-agent-test',
30
+ shortcutForAgentId: 'openclaw-agent-test',
31
+ name: 'Some Other Name',
32
+ })
33
+
34
+ assert.equal(resolved, 'research-operator')
35
+ })
36
+
37
+ test('resolveGatewayAgentId honors explicit OpenClaw agent ids when provided', () => {
38
+ const resolved = resolveGatewayAgentId({
39
+ id: 'session-2',
40
+ openclawAgentId: 'Custom Gateway Agent',
41
+ name: 'Ignored',
42
+ })
43
+
44
+ assert.equal(resolved, 'custom-gateway-agent')
45
+ })
46
+
47
+ test('resolveGatewayAgentId falls back to the session name when no OpenClaw agent is available', () => {
48
+ const resolved = resolveGatewayAgentId({
49
+ id: 'session-3',
50
+ name: 'Fallback Agent Name',
51
+ })
52
+
53
+ assert.equal(resolved, 'fallback-agent-name')
54
+ })
@@ -3,6 +3,14 @@ import crypto, { randomUUID } from 'crypto'
3
3
  import fs from 'fs'
4
4
  import path from 'path'
5
5
  import type { StreamChatOptions } from './index'
6
+ import type { Agent } from '@/types'
7
+ import { deriveOpenClawWsUrl } from '@/lib/openclaw-endpoint'
8
+ import { normalizeOpenClawAgentId } from '@/lib/openclaw-agent-id'
9
+ import { loadAgents } from '../server/storage'
10
+ import {
11
+ resolveOpenClawGatewayAgentIdFromList,
12
+ type OpenClawGatewayAgentSummary,
13
+ } from '../server/openclaw-agent-resolver'
6
14
 
7
15
  // --- Device Identity (Ed25519 keypair for gateway auth) ---
8
16
 
@@ -105,13 +113,6 @@ export function getDeviceId(): string {
105
113
 
106
114
  // --- Protocol helpers ---
107
115
 
108
- function normalizeWsUrl(raw: string): string {
109
- let url = raw.replace(/\/+$/, '')
110
- if (!/^(https?|wss?):\/\//i.test(url)) url = `http://${url}`
111
- url = url.replace(/^ws:/i, 'http:').replace(/^wss:/i, 'https:')
112
- return url.replace(/^http:/i, 'ws:').replace(/^https:/i, 'wss:')
113
- }
114
-
115
116
  /**
116
117
  * Build connect params for the OpenClaw gateway protocol.
117
118
  *
@@ -277,6 +278,121 @@ async function connectToGateway(
277
278
  return wsConnect(wsUrl, token, true, timeoutMs)
278
279
  }
279
280
 
281
+ export function resolveGatewayAgentId(session: Record<string, unknown> & { id: string }): string {
282
+ const explicit = typeof session.openclawAgentId === 'string' && session.openclawAgentId.trim()
283
+ ? session.openclawAgentId.trim()
284
+ : ''
285
+ if (explicit) return normalizeOpenClawAgentId(explicit)
286
+
287
+ const agentId = typeof session.shortcutForAgentId === 'string' && session.shortcutForAgentId.trim()
288
+ ? session.shortcutForAgentId.trim()
289
+ : typeof session.agentId === 'string' && session.agentId.trim()
290
+ ? session.agentId.trim()
291
+ : ''
292
+ if (agentId) {
293
+ const agents = loadAgents({ includeTrashed: true })
294
+ const agent = agents[agentId] as { name?: string; provider?: string } | undefined
295
+ if (agent?.provider === 'openclaw' && typeof agent.name === 'string' && agent.name.trim()) {
296
+ return normalizeOpenClawAgentId(agent.name)
297
+ }
298
+ }
299
+
300
+ const sessionName = typeof session.name === 'string' && session.name.trim() ? session.name.trim() : ''
301
+ if (sessionName) return normalizeOpenClawAgentId(sessionName)
302
+ return 'main'
303
+ }
304
+
305
+ async function rpcOnConnectedGateway(
306
+ ws: InstanceType<typeof WebSocket>,
307
+ method: string,
308
+ params: unknown,
309
+ timeoutMs = 5_000,
310
+ ): Promise<unknown> {
311
+ return new Promise((resolve, reject) => {
312
+ const requestId = randomUUID()
313
+ let settled = false
314
+
315
+ const cleanup = () => {
316
+ clearTimeout(timer)
317
+ ws.off('message', onMessage)
318
+ ws.off('close', onClose)
319
+ ws.off('error', onError)
320
+ }
321
+
322
+ const finish = (fn: () => void) => {
323
+ if (settled) return
324
+ settled = true
325
+ cleanup()
326
+ fn()
327
+ }
328
+
329
+ const onMessage = (data: unknown) => {
330
+ try {
331
+ const msg = JSON.parse(String(data))
332
+ if (msg.type !== 'res' || msg.id !== requestId) return
333
+ if (msg.ok) {
334
+ finish(() => resolve(msg.payload))
335
+ } else {
336
+ const message = typeof msg.error?.message === 'string'
337
+ ? msg.error.message
338
+ : `${method} failed`
339
+ finish(() => reject(new Error(message)))
340
+ }
341
+ } catch {
342
+ // Ignore unrelated or malformed frames while waiting for our response.
343
+ }
344
+ }
345
+
346
+ const onClose = () => finish(() => reject(new Error('Gateway closed before RPC completed')))
347
+ const onError = (err: Error) => finish(() => reject(err))
348
+ const timer = setTimeout(() => {
349
+ finish(() => reject(new Error(`${method} timed out`)))
350
+ }, timeoutMs)
351
+
352
+ ws.on('message', onMessage)
353
+ ws.on('close', onClose)
354
+ ws.on('error', onError)
355
+ ws.send(JSON.stringify({ type: 'req', id: requestId, method, params }))
356
+ })
357
+ }
358
+
359
+ async function resolveConnectedGatewayAgentId(
360
+ ws: InstanceType<typeof WebSocket>,
361
+ session: Record<string, unknown> & { id: string },
362
+ ): Promise<string> {
363
+ const fallback = resolveGatewayAgentId(session)
364
+ const explicitAgentId = typeof session.openclawAgentId === 'string' && session.openclawAgentId.trim()
365
+ ? session.openclawAgentId.trim()
366
+ : ''
367
+ const localAgentId = typeof session.shortcutForAgentId === 'string' && session.shortcutForAgentId.trim()
368
+ ? session.shortcutForAgentId.trim()
369
+ : typeof session.agentId === 'string' && session.agentId.trim()
370
+ ? session.agentId.trim()
371
+ : ''
372
+ const agentRef = explicitAgentId || localAgentId || fallback
373
+
374
+ try {
375
+ const payload = await rpcOnConnectedGateway(ws, 'agents.list', {})
376
+ const gatewayAgents = Array.isArray((payload as { agents?: unknown[] } | undefined)?.agents)
377
+ ? (payload as { agents: OpenClawGatewayAgentSummary[] }).agents
378
+ : []
379
+ if (gatewayAgents.length === 0) return fallback
380
+
381
+ const localAgents = localAgentId ? loadAgents({ includeTrashed: true }) : {}
382
+ const localAgent = localAgentId
383
+ ? (localAgents[localAgentId] as Agent | undefined) || null
384
+ : null
385
+
386
+ return resolveOpenClawGatewayAgentIdFromList({
387
+ agentRef,
388
+ gatewayAgents,
389
+ localAgent: localAgent?.provider === 'openclaw' ? localAgent : null,
390
+ }) || fallback
391
+ } catch {
392
+ return fallback
393
+ }
394
+ }
395
+
280
396
  // --- Provider ---
281
397
 
282
398
  export function streamOpenClawChat({ session, message, imagePath, apiKey, write, active, signal }: StreamChatOptions): Promise<string> {
@@ -285,9 +401,8 @@ export function streamOpenClawChat({ session, message, imagePath, apiKey, write,
285
401
  prompt = `[The user has shared an image at: ${imagePath}]\n\n${message}`
286
402
  }
287
403
 
288
- const wsUrl = session.apiEndpoint ? normalizeWsUrl(session.apiEndpoint) : 'ws://127.0.0.1:18789'
404
+ const wsUrl = session.apiEndpoint ? deriveOpenClawWsUrl(session.apiEndpoint) : 'ws://127.0.0.1:18789'
289
405
  const token = apiKey || session.apiKey || undefined
290
-
291
406
  return new Promise((resolve) => {
292
407
  let fullResponse = ''
293
408
  let settled = false
@@ -302,13 +417,14 @@ export function streamOpenClawChat({ session, message, imagePath, apiKey, write,
302
417
  resolve(fullResponse)
303
418
  }
304
419
 
305
- connectToGateway(wsUrl, token).then((result) => {
420
+ connectToGateway(wsUrl, token).then(async (result) => {
306
421
  if (!result.ok || !result.ws) {
307
422
  finish(result.message)
308
423
  return
309
424
  }
310
425
 
311
426
  const ws = result.ws
427
+ const gatewayAgentId = await resolveConnectedGatewayAgentId(ws, session)
312
428
  const timeout = setTimeout(() => {
313
429
  ws.close()
314
430
  finish('OpenClaw gateway timed out after 120s.')
@@ -328,7 +444,7 @@ export function streamOpenClawChat({ session, message, imagePath, apiKey, write,
328
444
  method: 'agent',
329
445
  params: {
330
446
  message: prompt,
331
- agentId: 'main',
447
+ agentId: gatewayAgentId,
332
448
  timeout: 120,
333
449
  idempotencyKey: randomUUID(),
334
450
  },