@swarmclawai/swarmclaw 1.2.6 → 1.2.9

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 (269) hide show
  1. package/README.md +54 -23
  2. package/next.config.ts +1 -0
  3. package/package.json +4 -3
  4. package/scripts/easy-setup.mjs +1 -1
  5. package/scripts/postinstall.mjs +1 -1
  6. package/skills/swarmclaw.md +115 -0
  7. package/skills/tools/browser.md +131 -0
  8. package/skills/tools/execute.md +98 -0
  9. package/skills/tools/files.md +98 -0
  10. package/skills/tools/memory.md +104 -0
  11. package/skills/tools/platform.md +144 -0
  12. package/skills/tools/skills.md +83 -0
  13. package/src/app/agents/[id]/page.tsx +1 -18
  14. package/src/app/api/agents/thread-route.test.ts +0 -1
  15. package/src/app/api/approvals/route.test.ts +6 -22
  16. package/src/app/api/chats/[id]/messages/route.ts +23 -19
  17. package/src/app/api/chats/messages-route.test.ts +105 -51
  18. package/src/app/api/connectors/route.ts +2 -2
  19. package/src/app/api/mcp-servers/[id]/test/route.ts +3 -2
  20. package/src/app/api/openclaw/deploy/route.ts +2 -0
  21. package/src/app/api/portability/export/route.ts +8 -0
  22. package/src/app/api/portability/import/route.test.ts +80 -0
  23. package/src/app/api/portability/import/route.ts +28 -0
  24. package/src/app/api/settings/route.ts +0 -2
  25. package/src/app/api/setup/doctor/route.ts +4 -4
  26. package/src/app/api/wallets/[id]/route.ts +15 -157
  27. package/src/app/api/wallets/generate/route.ts +22 -0
  28. package/src/app/api/wallets/route.test.ts +147 -0
  29. package/src/app/api/wallets/route.ts +13 -95
  30. package/src/app/autonomy/page.tsx +2 -57
  31. package/src/app/protocols/page.tsx +2 -21
  32. package/src/app/settings/page.tsx +0 -9
  33. package/src/app/wallets/page.tsx +105 -5
  34. package/src/cli/index.js +21 -33
  35. package/src/cli/spec.js +19 -30
  36. package/src/components/agents/agent-chat-list.tsx +23 -1
  37. package/src/components/agents/agent-sheet.tsx +2 -40
  38. package/src/components/agents/inspector-panel.tsx +165 -131
  39. package/src/components/chat/chat-area.tsx +38 -9
  40. package/src/components/chat/chat-card.tsx +0 -31
  41. package/src/components/chat/message-bubble.tsx +1 -108
  42. package/src/components/chat/message-list.tsx +33 -19
  43. package/src/components/connectors/connector-sheet.tsx +25 -1
  44. package/src/components/gateways/gateway-sheet.tsx +5 -2
  45. package/src/components/layout/sidebar-rail.tsx +6 -10
  46. package/src/components/projects/project-detail.tsx +3 -35
  47. package/src/components/projects/tabs/overview-tab.tsx +3 -59
  48. package/src/components/projects/tabs/work-tab.tsx +7 -77
  49. package/src/components/protocols/structured-session-launcher.tsx +1 -22
  50. package/src/components/shared/connector-platform-icon.tsx +1 -0
  51. package/src/components/tasks/task-card.tsx +4 -34
  52. package/src/components/tasks/task-sheet.tsx +6 -36
  53. package/src/components/wallets/wallet-list.tsx +150 -0
  54. package/src/lib/agent-execute-defaults.test.ts +24 -0
  55. package/src/lib/agent-execute-defaults.ts +62 -0
  56. package/src/lib/app/navigation.test.ts +0 -13
  57. package/src/lib/app/navigation.ts +2 -7
  58. package/src/lib/app/view-constants.ts +14 -19
  59. package/src/lib/chat/queued-message-queue.test.ts +134 -1
  60. package/src/lib/chat/queued-message-queue.ts +77 -2
  61. package/src/lib/server/agents/agent-service.ts +5 -0
  62. package/src/lib/server/agents/agent-thread-session.ts +0 -1
  63. package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
  64. package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
  65. package/src/lib/server/agents/delegation-jobs.ts +0 -25
  66. package/src/lib/server/agents/main-agent-loop.ts +1 -49
  67. package/src/lib/server/agents/subagent-runtime.ts +0 -1
  68. package/src/lib/server/approval-match.ts +0 -85
  69. package/src/lib/server/approvals.test.ts +6 -6
  70. package/src/lib/server/approvals.ts +0 -6
  71. package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
  72. package/src/lib/server/builtin-extensions.ts +1 -2
  73. package/src/lib/server/capability-router.test.ts +0 -2
  74. package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +1 -1
  75. package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +15 -14
  76. package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
  77. package/src/lib/server/chat-execution/chat-execution-utils.ts +2 -4
  78. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
  79. package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
  80. package/src/lib/server/chat-execution/chat-turn-preparation.ts +81 -64
  81. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -0
  82. package/src/lib/server/chat-execution/continuation-evaluator.ts +8 -0
  83. package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
  84. package/src/lib/server/chat-execution/memory-mutation-tools.ts +1 -1
  85. package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
  86. package/src/lib/server/chat-execution/message-classifier.ts +11 -16
  87. package/src/lib/server/chat-execution/prompt-builder.test.ts +27 -0
  88. package/src/lib/server/chat-execution/prompt-builder.ts +14 -31
  89. package/src/lib/server/chat-execution/prompt-mode.test.ts +24 -0
  90. package/src/lib/server/chat-execution/prompt-mode.ts +5 -1
  91. package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
  92. package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
  93. package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
  94. package/src/lib/server/chat-execution/stream-agent-chat.test.ts +13 -126
  95. package/src/lib/server/chat-execution/stream-agent-chat.ts +46 -21
  96. package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
  97. package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
  98. package/src/lib/server/chatrooms/chatroom-routing.test.ts +4 -0
  99. package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
  100. package/src/lib/server/chats/chat-session-service.ts +3 -5
  101. package/src/lib/server/connectors/connector-inbound.ts +0 -1
  102. package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
  103. package/src/lib/server/connectors/connector-service.ts +39 -9
  104. package/src/lib/server/connectors/discord.ts +2 -2
  105. package/src/lib/server/connectors/matrix.ts +3 -2
  106. package/src/lib/server/connectors/signal.ts +5 -4
  107. package/src/lib/server/connectors/slack.ts +10 -9
  108. package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
  109. package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
  110. package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
  111. package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
  112. package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
  113. package/src/lib/server/connectors/swarmdock.ts +255 -0
  114. package/src/lib/server/connectors/teams.ts +3 -2
  115. package/src/lib/server/connectors/telegram.ts +4 -4
  116. package/src/lib/server/connectors/whatsapp.ts +2 -2
  117. package/src/lib/server/daemon/controller.ts +7 -0
  118. package/src/lib/server/execution-brief.test.ts +2 -25
  119. package/src/lib/server/execution-brief.ts +12 -35
  120. package/src/lib/server/execution-engine/task-attempt.ts +0 -1
  121. package/src/lib/server/gateways/gateway-profile-service.ts +19 -1
  122. package/src/lib/server/messages/message-repository.test.ts +70 -0
  123. package/src/lib/server/messages/message-repository.ts +11 -6
  124. package/src/lib/server/openclaw/deploy.ts +32 -2
  125. package/src/lib/server/persistence/storage-context.ts +0 -5
  126. package/src/lib/server/plugins-advanced.test.ts +1 -2
  127. package/src/lib/server/portability/export.ts +109 -0
  128. package/src/lib/server/portability/import.ts +159 -0
  129. package/src/lib/server/protocols/protocol-normalization.ts +0 -4
  130. package/src/lib/server/protocols/protocol-queries.ts +0 -6
  131. package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
  132. package/src/lib/server/protocols/protocol-service.ts +0 -1
  133. package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
  134. package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
  135. package/src/lib/server/protocols/protocol-swarm.ts +0 -2
  136. package/src/lib/server/protocols/protocol-types.ts +0 -2
  137. package/src/lib/server/provider-health.ts +1 -10
  138. package/src/lib/server/runtime/daemon-state/core.ts +0 -9
  139. package/src/lib/server/runtime/daemon-state.test.ts +0 -35
  140. package/src/lib/server/runtime/heartbeat-service.ts +3 -23
  141. package/src/lib/server/runtime/process-manager.ts +13 -9
  142. package/src/lib/server/runtime/queue/core.ts +11 -33
  143. package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
  144. package/src/lib/server/runtime/scheduler.ts +0 -13
  145. package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
  146. package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
  147. package/src/lib/server/runtime/session-run-manager/queries.ts +15 -1
  148. package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
  149. package/src/lib/server/runtime/session-run-manager.test.ts +58 -28
  150. package/src/lib/server/sandbox/session-runtime.test.ts +18 -1
  151. package/src/lib/server/sandbox/session-runtime.ts +40 -28
  152. package/src/lib/server/session-tools/autonomy-tools.test.ts +7 -9
  153. package/src/lib/server/session-tools/context.ts +1 -1
  154. package/src/lib/server/session-tools/credential-env.ts +109 -0
  155. package/src/lib/server/session-tools/crud.ts +3 -17
  156. package/src/lib/server/session-tools/delegate.ts +0 -4
  157. package/src/lib/server/session-tools/edit_file.ts +3 -2
  158. package/src/lib/server/session-tools/execute.test.ts +58 -0
  159. package/src/lib/server/session-tools/execute.ts +334 -0
  160. package/src/lib/server/session-tools/files-tool.ts +635 -0
  161. package/src/lib/server/session-tools/index.ts +14 -8
  162. package/src/lib/server/session-tools/memory-tool.ts +242 -0
  163. package/src/lib/server/session-tools/memory.ts +1 -1
  164. package/src/lib/server/session-tools/openclaw-nodes.ts +3 -2
  165. package/src/lib/server/session-tools/openclaw-workspace.ts +3 -2
  166. package/src/lib/server/session-tools/platform-tool.ts +617 -0
  167. package/src/lib/server/session-tools/session-info.ts +3 -2
  168. package/src/lib/server/session-tools/session-tools-wiring.test.ts +3 -4
  169. package/src/lib/server/session-tools/shell.ts +7 -122
  170. package/src/lib/server/session-tools/skills-tool.ts +396 -0
  171. package/src/lib/server/session-tools/team-context.ts +0 -3
  172. package/src/lib/server/session-tools/web.ts +2 -2
  173. package/src/lib/server/storage-normalization.ts +10 -0
  174. package/src/lib/server/storage.ts +18 -45
  175. package/src/lib/server/tasks/task-checkout.ts +59 -0
  176. package/src/lib/server/tasks/task-lifecycle.ts +2 -0
  177. package/src/lib/server/tasks/task-route-service.ts +4 -26
  178. package/src/lib/server/tasks/task-service.ts +0 -7
  179. package/src/lib/server/tool-aliases.ts +2 -2
  180. package/src/lib/server/tool-capability-policy-advanced.test.ts +13 -6
  181. package/src/lib/server/tool-capability-policy.test.ts +2 -1
  182. package/src/lib/server/tool-capability-policy.ts +60 -35
  183. package/src/lib/server/tool-planning.ts +11 -12
  184. package/src/lib/server/universal-tool-access.ts +0 -1
  185. package/src/lib/server/wallets/wallet-crypto.ts +33 -0
  186. package/src/lib/server/wallets/wallet-repository.ts +24 -0
  187. package/src/lib/server/wallets/wallet-service.ts +119 -0
  188. package/src/lib/server/working-state/extraction.ts +8 -42
  189. package/src/lib/server/working-state/normalization.ts +10 -103
  190. package/src/lib/server/working-state/service.ts +12 -21
  191. package/src/lib/setup-defaults.ts +5 -0
  192. package/src/lib/strip-internal-metadata.test.ts +1 -1
  193. package/src/lib/strip-internal-metadata.ts +1 -1
  194. package/src/lib/tool-definitions.ts +1 -1
  195. package/src/lib/validation/schemas.test.ts +16 -0
  196. package/src/lib/validation/schemas.ts +49 -2
  197. package/src/stores/slices/data-slice.ts +5 -1
  198. package/src/stores/slices/ui-slice.ts +0 -4
  199. package/src/stores/use-chat-store.test.ts +231 -0
  200. package/src/stores/use-chat-store.ts +62 -13
  201. package/src/types/agent.ts +264 -0
  202. package/src/types/app-settings.ts +173 -0
  203. package/src/types/approval.ts +25 -0
  204. package/src/types/connector.ts +188 -0
  205. package/src/types/extension.ts +386 -0
  206. package/src/types/index.ts +16 -3555
  207. package/src/types/message.ts +56 -0
  208. package/src/types/misc.ts +737 -0
  209. package/src/types/protocol.ts +420 -0
  210. package/src/types/provider.ts +52 -0
  211. package/src/types/run.ts +180 -0
  212. package/src/types/schedule.ts +59 -0
  213. package/src/types/session.ts +215 -0
  214. package/src/types/skill.ts +157 -0
  215. package/src/types/swarmdock.ts +29 -0
  216. package/src/types/task.ts +144 -0
  217. package/src/types/working-state.ts +204 -0
  218. package/src/views/settings/section-heartbeat.tsx +2 -2
  219. package/src/views/settings/section-runtime-loop.tsx +0 -14
  220. package/src/app/api/canvas/[sessionId]/route.ts +0 -35
  221. package/src/app/api/missions/[id]/actions/route.ts +0 -31
  222. package/src/app/api/missions/[id]/events/route.ts +0 -14
  223. package/src/app/api/missions/[id]/route.ts +0 -10
  224. package/src/app/api/missions/route.test.ts +0 -244
  225. package/src/app/api/missions/route.ts +0 -57
  226. package/src/app/api/wallets/[id]/approve/route.ts +0 -79
  227. package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
  228. package/src/app/api/wallets/[id]/send/route.ts +0 -113
  229. package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
  230. package/src/app/missions/[id]/page.tsx +0 -3
  231. package/src/app/missions/page.tsx +0 -685
  232. package/src/components/canvas/canvas-panel.tsx +0 -267
  233. package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
  234. package/src/components/wallets/wallet-panel.tsx +0 -1010
  235. package/src/components/wallets/wallet-section.tsx +0 -260
  236. package/src/features/missions/queries.ts +0 -23
  237. package/src/lib/canvas-content.test.ts +0 -360
  238. package/src/lib/canvas-content.ts +0 -198
  239. package/src/lib/server/canvas-content.test.ts +0 -32
  240. package/src/lib/server/canvas-content.ts +0 -6
  241. package/src/lib/server/ethereum.ts +0 -591
  242. package/src/lib/server/evm-swap.ts +0 -476
  243. package/src/lib/server/missions/mission-intent.test.ts +0 -63
  244. package/src/lib/server/missions/mission-intent.ts +0 -569
  245. package/src/lib/server/missions/mission-repository.ts +0 -74
  246. package/src/lib/server/missions/mission-service/actions.ts +0 -6
  247. package/src/lib/server/missions/mission-service/bindings.ts +0 -9
  248. package/src/lib/server/missions/mission-service/context.ts +0 -4
  249. package/src/lib/server/missions/mission-service/core.ts +0 -2271
  250. package/src/lib/server/missions/mission-service/queries.ts +0 -12
  251. package/src/lib/server/missions/mission-service/recovery.ts +0 -5
  252. package/src/lib/server/missions/mission-service/ticks.ts +0 -9
  253. package/src/lib/server/missions/mission-service.test.ts +0 -888
  254. package/src/lib/server/missions/mission-service.ts +0 -6
  255. package/src/lib/server/session-tools/canvas.ts +0 -105
  256. package/src/lib/server/session-tools/sandbox.ts +0 -281
  257. package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
  258. package/src/lib/server/session-tools/wallet.ts +0 -1287
  259. package/src/lib/server/solana.ts +0 -327
  260. package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
  261. package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
  262. package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
  263. package/src/lib/server/wallet/wallet-service.test.ts +0 -81
  264. package/src/lib/server/wallet/wallet-service.ts +0 -225
  265. package/src/lib/wallet/wallet-transactions.test.ts +0 -75
  266. package/src/lib/wallet/wallet-transactions.ts +0 -43
  267. package/src/lib/wallet/wallet.test.ts +0 -333
  268. package/src/lib/wallet/wallet.ts +0 -183
  269. package/src/views/settings/section-wallets.tsx +0 -35
@@ -1,6 +0,0 @@
1
- export * from './mission-service/queries'
2
- export * from './mission-service/recovery'
3
- export * from './mission-service/ticks'
4
- export * from './mission-service/bindings'
5
- export * from './mission-service/actions'
6
- export * from './mission-service/context'
@@ -1,105 +0,0 @@
1
- import { z } from 'zod'
2
- import { tool, type StructuredToolInterface } from '@langchain/core/tools'
3
- import { loadSessions, saveSessions } from '../storage'
4
- import { notify } from '../ws-hub'
5
- import type { ToolBuildContext } from './context'
6
- import type { Extension, ExtensionHooks } from '@/types'
7
- import { registerNativeCapability } from '../native-capabilities'
8
- import { normalizeToolInputArgs } from './normalize-tool-args'
9
- import { normalizeCanvasContent, summarizeCanvasContent } from '@/lib/canvas-content'
10
- import { errorMessage } from '@/lib/shared-utils'
11
-
12
- /**
13
- * Core Canvas Execution Logic
14
- */
15
- async function executeCanvasAction(args: Record<string, unknown>, context: { sessionId?: string }) {
16
- const normalized = normalizeToolInputArgs(args)
17
- const action = normalized.action as string
18
- const content = normalized.content as string | undefined
19
- const document = normalized.document
20
- try {
21
- const sessionId = context.sessionId
22
- if (!sessionId) return 'Error: no active session for canvas.'
23
-
24
- const sessions = loadSessions()
25
- const session = sessions[sessionId]
26
- if (!session) return 'Error: session not found.'
27
-
28
- if (action === 'present') {
29
- const nextContent = normalizeCanvasContent(document ?? content)
30
- if (!nextContent) return 'Error: content or document is required for present action.'
31
- ;(session as unknown as Record<string, unknown>).canvasContent = nextContent
32
- session.lastActiveAt = Date.now()
33
- sessions[sessionId] = session
34
- saveSessions(sessions)
35
- notify(`canvas:${sessionId}`)
36
- return JSON.stringify({
37
- ok: true,
38
- action: 'present',
39
- ...summarizeCanvasContent(nextContent),
40
- })
41
- }
42
-
43
- if (action === 'hide') {
44
- ;(session as unknown as Record<string, unknown>).canvasContent = null
45
- session.lastActiveAt = Date.now()
46
- sessions[sessionId] = session
47
- saveSessions(sessions)
48
- notify(`canvas:${sessionId}`)
49
- return JSON.stringify({ ok: true, action: 'hide' })
50
- }
51
-
52
- if (action === 'snapshot') {
53
- const current = normalizeCanvasContent((session as unknown as Record<string, unknown>).canvasContent)
54
- return JSON.stringify({ ok: true, action: 'snapshot', ...summarizeCanvasContent(current) })
55
- }
56
-
57
- return `Unknown canvas action "${action}".`
58
- } catch (err: unknown) {
59
- return `Error: ${errorMessage(err)}`
60
- }
61
- }
62
-
63
- /**
64
- * Register as a Built-in Extension
65
- */
66
- const CanvasExtension: Extension = {
67
- name: 'Core Canvas',
68
- description: 'Present live HTML/CSS/JS content to the user in an interactive canvas panel.',
69
- hooks: {} as ExtensionHooks,
70
- tools: [
71
- {
72
- name: 'canvas',
73
- description: 'Interact with the live canvas panel.',
74
- parameters: {
75
- type: 'object',
76
- properties: {
77
- action: { type: 'string', enum: ['present', 'hide', 'snapshot'] },
78
- content: { type: 'string' },
79
- document: { type: 'object', additionalProperties: true },
80
- },
81
- required: ['action']
82
- },
83
- execute: async (args, context) => executeCanvasAction(args, { sessionId: context.session.id })
84
- }
85
- ]
86
- }
87
-
88
- registerNativeCapability('canvas', CanvasExtension)
89
-
90
- /**
91
- * Legacy Bridge
92
- */
93
- export function buildCanvasTools(bctx: ToolBuildContext): StructuredToolInterface[] {
94
- if (!bctx.hasExtension('canvas')) return []
95
- return [
96
- tool(
97
- async (args) => executeCanvasAction(args, { sessionId: bctx.ctx?.sessionId || undefined }),
98
- {
99
- name: 'canvas',
100
- description: CanvasExtension.tools![0].description,
101
- schema: z.object({}).passthrough()
102
- }
103
- )
104
- ]
105
- }
@@ -1,281 +0,0 @@
1
- import fs from 'fs'
2
- import path from 'path'
3
- import { spawnSync } from 'child_process'
4
- import { UPLOAD_DIR } from '../storage'
5
- import { truncate, MAX_OUTPUT } from './context'
6
- import type { Session } from '@/types'
7
- import { normalizeToolInputArgs } from './normalize-tool-args'
8
- import { detectDocker } from '@/lib/server/sandbox/docker-detect'
9
- import {
10
- ensureSessionSandbox,
11
- resolveSandboxRuntimeStatus,
12
- resolveSandboxWorkdir,
13
- type AgentSandboxConfig,
14
- } from '@/lib/server/sandbox/session-runtime'
15
- import { buildDockerExecArgs } from '@/lib/server/runtime/process-manager'
16
-
17
- export type SandboxContext = {
18
- sessionId?: string
19
- cwd?: string
20
- agentId?: string | null
21
- config?: AgentSandboxConfig | null
22
- resolveCurrentSession?: () => Session | null
23
- }
24
-
25
- const EXT_MAP: Record<string, string> = {
26
- javascript: 'js',
27
- typescript: 'ts',
28
- }
29
-
30
- function sandboxUnavailableError(reason: string): string {
31
- return JSON.stringify({
32
- error: reason,
33
- guidance: [
34
- 'Install Docker Desktop to keep sandbox_exec inside a container.',
35
- 'Use http_request for straightforward API calls.',
36
- 'Use extension_creator plus manage_schedules for recurring automations.',
37
- ],
38
- })
39
- }
40
-
41
- function quoteShell(value: string): string {
42
- return `'${value.replace(/'/g, `'\\''`)}'`
43
- }
44
-
45
- function createSandboxDir(baseCwd: string, sessionId: string): string {
46
- const root = path.join(baseCwd, '.swarmclaw-sandbox')
47
- fs.mkdirSync(root, { recursive: true })
48
- return fs.mkdtempSync(path.join(root, `${sessionId}-`))
49
- }
50
-
51
- function collectArtifacts(params: {
52
- sandboxDir: string
53
- ignoredFiles: Set<string>
54
- }): Array<{ name: string; url: string }> {
55
- const artifacts: { name: string; url: string }[] = []
56
- try {
57
- const files = fs.readdirSync(params.sandboxDir)
58
- for (const file of files) {
59
- if (params.ignoredFiles.has(file)) continue
60
- const src = path.join(params.sandboxDir, file)
61
- if (!fs.statSync(src).isFile()) continue
62
- fs.mkdirSync(UPLOAD_DIR, { recursive: true })
63
- const destName = `sandbox-${Date.now()}-${file}`
64
- const dest = path.join(UPLOAD_DIR, destName)
65
- fs.copyFileSync(src, dest)
66
- artifacts.push({ name: file, url: `/api/uploads/${encodeURIComponent(destName)}` })
67
- }
68
- } catch {
69
- // ignore artifact collection failures
70
- }
71
- return artifacts
72
- }
73
-
74
- function executeHostNode(params: {
75
- sandboxDir: string
76
- language: string
77
- scriptFile: string
78
- timeout: number
79
- }): {
80
- runtime: 'host'
81
- stdout: string
82
- stderr: string
83
- exitCode: number
84
- timedOut: boolean
85
- } {
86
- const tmpDir = path.join(params.sandboxDir, '.tmp')
87
- fs.mkdirSync(tmpDir, { recursive: true })
88
- const args = params.language === 'typescript'
89
- ? ['--no-warnings=ExperimentalWarning', '--experimental-strip-types', params.scriptFile]
90
- : [params.scriptFile]
91
- const result = spawnSync(process.execPath, args, {
92
- cwd: params.sandboxDir,
93
- encoding: 'utf-8',
94
- timeout: params.timeout,
95
- maxBuffer: MAX_OUTPUT,
96
- env: {
97
- ...process.env,
98
- HOME: params.sandboxDir,
99
- TMPDIR: tmpDir,
100
- WORKSPACE: params.sandboxDir,
101
- SESSION_CWD: params.sandboxDir,
102
- SWARMCLAW_SANDBOX_MODE: 'host',
103
- },
104
- })
105
- return {
106
- runtime: 'host',
107
- stdout: truncate((result.stdout || '').toString(), MAX_OUTPUT),
108
- stderr: truncate((result.stderr || '').toString(), MAX_OUTPUT),
109
- exitCode: result.status ?? (result.error ? 1 : 0),
110
- timedOut: !!(result.error?.message?.includes('ETIMEDOUT') || result.signal === 'SIGTERM'),
111
- }
112
- }
113
-
114
- async function executeContainerNode(params: {
115
- sandboxDir: string
116
- language: string
117
- scriptFile: string
118
- timeout: number
119
- context: SandboxContext
120
- }): Promise<{
121
- runtime: 'container'
122
- stdout: string
123
- stderr: string
124
- exitCode: number
125
- timedOut: boolean
126
- }> {
127
- const session = params.context.resolveCurrentSession?.() ?? null
128
- const sandbox = await ensureSessionSandbox({
129
- config: params.context.config,
130
- session,
131
- agentId: params.context.agentId ?? session?.agentId ?? null,
132
- sessionId: params.context.sessionId ?? session?.id ?? null,
133
- workspaceDir: params.context.cwd || process.cwd(),
134
- })
135
-
136
- if (!sandbox) {
137
- throw new Error('Container sandbox is not active for this session.')
138
- }
139
-
140
- const tmpDir = path.join(params.sandboxDir, '.tmp')
141
- fs.mkdirSync(tmpDir, { recursive: true })
142
- const resolved = resolveSandboxWorkdir({
143
- workspaceDir: sandbox.workspaceDir,
144
- hostWorkdir: params.sandboxDir,
145
- containerWorkdir: sandbox.containerWorkdir,
146
- })
147
- const containerCommand = params.language === 'typescript'
148
- ? `node --no-warnings=ExperimentalWarning --experimental-strip-types ${quoteShell(params.scriptFile)}`
149
- : `node ${quoteShell(params.scriptFile)}`
150
- const result = spawnSync('docker', buildDockerExecArgs({
151
- containerName: sandbox.containerName,
152
- command: containerCommand,
153
- workdir: resolved.containerWorkdir,
154
- env: {
155
- HOME: resolved.containerWorkdir,
156
- TMPDIR: path.posix.join(resolved.containerWorkdir, '.tmp'),
157
- WORKSPACE: sandbox.containerWorkdir,
158
- SESSION_CWD: resolved.containerWorkdir,
159
- SWARMCLAW_SANDBOX_MODE: 'container',
160
- },
161
- }), {
162
- encoding: 'utf-8',
163
- timeout: params.timeout,
164
- maxBuffer: MAX_OUTPUT,
165
- })
166
-
167
- return {
168
- runtime: 'container',
169
- stdout: truncate((result.stdout || '').toString(), MAX_OUTPUT),
170
- stderr: truncate((result.stderr || '').toString(), MAX_OUTPUT),
171
- exitCode: result.status ?? (result.error ? 1 : 0),
172
- timedOut: !!(result.error?.message?.includes('ETIMEDOUT') || result.signal === 'SIGTERM'),
173
- }
174
- }
175
-
176
- export async function executeSandboxExec(args: unknown, context: SandboxContext) {
177
- const normalized = normalizeToolInputArgs((args ?? {}) as Record<string, unknown>)
178
- const language = normalized.language as string
179
- const code = normalized.code as string
180
- const timeoutSec = normalized.timeoutSec as number | undefined
181
- const timeout = Math.min(Math.max(timeoutSec ?? 60, 5), 300) * 1000
182
- const ext = EXT_MAP[language]
183
- const sessionId = context.sessionId ?? 'unknown'
184
- const cwd = context.cwd || process.cwd()
185
-
186
- if (language !== 'javascript' && language !== 'typescript') {
187
- return sandboxUnavailableError('sandbox_exec currently supports only JavaScript and TypeScript via Node.js.')
188
- }
189
-
190
- let sandboxDir: string | null = null
191
- try {
192
- sandboxDir = createSandboxDir(cwd, sessionId)
193
- const sandboxRoot = sandboxDir
194
- const scriptFile = `script.${ext}`
195
- fs.writeFileSync(path.join(sandboxRoot, 'package.json'), JSON.stringify({ type: 'module' }), 'utf-8')
196
- fs.writeFileSync(path.join(sandboxRoot, scriptFile), code, 'utf-8')
197
-
198
- const warnings: string[] = []
199
- const docker = detectDocker()
200
- const runtimeResult = docker.available
201
- ? await executeContainerNode({
202
- sandboxDir: sandboxRoot,
203
- language,
204
- scriptFile,
205
- timeout,
206
- context,
207
- }).catch((err: unknown) => {
208
- warnings.push(err instanceof Error ? err.message : 'Container sandbox unavailable; used host Node fallback.')
209
- return executeHostNode({
210
- sandboxDir: sandboxRoot,
211
- language,
212
- scriptFile,
213
- timeout,
214
- })
215
- })
216
- : (() => {
217
- warnings.push('Docker is not available; used host Node fallback.')
218
- return executeHostNode({
219
- sandboxDir: sandboxRoot,
220
- language,
221
- scriptFile,
222
- timeout,
223
- })
224
- })()
225
-
226
- const artifacts = collectArtifacts({
227
- sandboxDir: sandboxRoot,
228
- ignoredFiles: new Set([scriptFile, 'package.json']),
229
- })
230
-
231
- return JSON.stringify({
232
- runtime: runtimeResult.runtime,
233
- exitCode: runtimeResult.exitCode,
234
- timedOut: runtimeResult.timedOut,
235
- stdout: runtimeResult.stdout,
236
- stderr: runtimeResult.stderr,
237
- artifacts,
238
- ...(warnings.length ? { warnings } : {}),
239
- })
240
- } catch (err: unknown) {
241
- return JSON.stringify({ error: err instanceof Error ? err.message : String(err) })
242
- } finally {
243
- if (sandboxDir) {
244
- try { fs.rmSync(sandboxDir, { recursive: true, force: true }) } catch { /* ignore */ }
245
- }
246
- }
247
- }
248
-
249
- export async function executeListRuntimes(context: SandboxContext) {
250
- const docker = detectDocker()
251
- const session = context.resolveCurrentSession?.() ?? null
252
- const status = resolveSandboxRuntimeStatus({
253
- config: context.config,
254
- session,
255
- agentId: context.agentId ?? session?.agentId ?? null,
256
- sessionId: context.sessionId ?? session?.id ?? null,
257
- })
258
-
259
- return JSON.stringify({
260
- node: {
261
- available: true,
262
- version: process.version,
263
- supportsTypeScript: true,
264
- },
265
- docker,
266
- sandbox: {
267
- enabledByConfig: Boolean(context.config?.enabled),
268
- sandboxedForSession: status.sandboxed,
269
- mode: status.mode,
270
- scope: status.scope,
271
- scopeKey: status.scopeKey,
272
- executionMode: docker.available && status.sandboxed ? 'container' : 'host',
273
- browserEnabledByConfig: context.config?.browser?.enabled === true,
274
- },
275
- guidance: docker.available
276
- ? []
277
- : ['Install Docker Desktop to keep shell, browser, and sandbox_exec inside containers.'],
278
- })
279
- }
280
-
281
- // Execution functions (executeSandboxExec, executeListRuntimes) are kept for shell.ts to import.
@@ -1,150 +0,0 @@
1
- import assert from 'node:assert/strict'
2
- import fs from 'node:fs'
3
- import os from 'node:os'
4
- import path from 'node:path'
5
- import { after, before, describe, it } from 'node:test'
6
-
7
- import type { Agent, Session } from '@/types'
8
-
9
- const originalEnv = {
10
- DATA_DIR: process.env.DATA_DIR,
11
- WORKSPACE_DIR: process.env.WORKSPACE_DIR,
12
- SWARMCLAW_BUILD_MODE: process.env.SWARMCLAW_BUILD_MODE,
13
- CREDENTIAL_SECRET: process.env.CREDENTIAL_SECRET,
14
- }
15
-
16
- let tempDir = ''
17
- let workspaceDir = ''
18
- let buildWalletTools: typeof import('./wallet').buildWalletTools
19
- let createAgentWallet: typeof import('@/lib/server/wallet/wallet-service').createAgentWallet
20
- let storage: typeof import('../storage')
21
-
22
- function makeAgent(): Agent {
23
- const now = Date.now()
24
- return {
25
- id: 'agent_wallet',
26
- name: 'Wallet Agent',
27
- description: 'Tests wallet actions',
28
- systemPrompt: 'test',
29
- provider: 'ollama',
30
- model: 'qwen3.5',
31
- extensions: ['wallet'],
32
- createdAt: now,
33
- updatedAt: now,
34
- }
35
- }
36
-
37
- function makeSession(): Session {
38
- const now = Date.now()
39
- return {
40
- id: 'session_wallet',
41
- name: 'Wallet Session',
42
- cwd: workspaceDir,
43
- user: 'tester',
44
- provider: 'ollama',
45
- model: 'qwen3.5',
46
- claudeSessionId: null,
47
- messages: [],
48
- createdAt: now,
49
- lastActiveAt: now,
50
- extensions: ['wallet'],
51
- agentId: 'agent_wallet',
52
- }
53
- }
54
-
55
- function makeBuildContext(session: Session) {
56
- return {
57
- cwd: workspaceDir,
58
- ctx: {
59
- sessionId: session.id,
60
- agentId: session.agentId || null,
61
- },
62
- hasExtension: (extensionId: string) => extensionId === 'wallet',
63
- hasTool: () => true,
64
- cleanupFns: [],
65
- commandTimeoutMs: 5000,
66
- claudeTimeoutMs: 5000,
67
- cliProcessTimeoutMs: 5000,
68
- persistDelegateResumeId: () => {},
69
- readStoredDelegateResumeId: () => null,
70
- resolveCurrentSession: () => session,
71
- activeExtensions: ['wallet'],
72
- }
73
- }
74
-
75
- before(async () => {
76
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-wallet-tool-'))
77
- workspaceDir = path.join(tempDir, 'workspace')
78
- process.env.DATA_DIR = path.join(tempDir, 'data')
79
- process.env.WORKSPACE_DIR = workspaceDir
80
- process.env.SWARMCLAW_BUILD_MODE = '1'
81
- process.env.CREDENTIAL_SECRET = '22'.repeat(32)
82
- fs.mkdirSync(process.env.DATA_DIR, { recursive: true })
83
- fs.mkdirSync(workspaceDir, { recursive: true })
84
-
85
- ;({ buildWalletTools } = await import('./wallet'))
86
- ;({ createAgentWallet } = await import('@/lib/server/wallet/wallet-service'))
87
- storage = await import('../storage')
88
- storage.saveAgents({ agent_wallet: makeAgent() })
89
- storage.saveSessions({ session_wallet: makeSession() })
90
- })
91
-
92
- after(() => {
93
- if (originalEnv.DATA_DIR === undefined) delete process.env.DATA_DIR
94
- else process.env.DATA_DIR = originalEnv.DATA_DIR
95
- if (originalEnv.WORKSPACE_DIR === undefined) delete process.env.WORKSPACE_DIR
96
- else process.env.WORKSPACE_DIR = originalEnv.WORKSPACE_DIR
97
- if (originalEnv.SWARMCLAW_BUILD_MODE === undefined) delete process.env.SWARMCLAW_BUILD_MODE
98
- else process.env.SWARMCLAW_BUILD_MODE = originalEnv.SWARMCLAW_BUILD_MODE
99
- if (originalEnv.CREDENTIAL_SECRET === undefined) delete process.env.CREDENTIAL_SECRET
100
- else process.env.CREDENTIAL_SECRET = originalEnv.CREDENTIAL_SECRET
101
- fs.rmSync(tempDir, { recursive: true, force: true })
102
- })
103
-
104
- describe('wallet tool generic execution', () => {
105
- it('signs messages directly without creating approval records', async () => {
106
- createAgentWallet({ agentId: 'agent_wallet', chain: 'ethereum' })
107
- const session = makeSession()
108
- const [walletTool] = buildWalletTools(makeBuildContext(session))
109
-
110
- const result = JSON.parse(String(await walletTool.invoke({
111
- action: 'sign_message',
112
- chain: 'ethereum',
113
- message: 'sign me',
114
- })))
115
-
116
- assert.equal(result.status, 'signed')
117
- assert.equal(result.chain, 'ethereum')
118
- assert.equal(typeof result.signature, 'string')
119
-
120
- const approvals = storage.loadApprovals()
121
- const walletApprovals = Object.values(approvals).filter((approval: any) => approval.category === 'wallet_action')
122
- assert.equal(walletApprovals.length, 0)
123
- })
124
-
125
- it('ignores legacy approval fields and still encodes contract calls', async () => {
126
- const session = makeSession()
127
- const [walletTool] = buildWalletTools(makeBuildContext(session))
128
-
129
- const signResult = JSON.parse(String(await walletTool.invoke({
130
- action: 'sign_message',
131
- chain: 'ethereum',
132
- message: 'signed',
133
- approved: true,
134
- approvalId: 'legacy-approval-id',
135
- })))
136
- assert.equal(signResult.status, 'signed')
137
- assert.equal(signResult.chain, 'ethereum')
138
- assert.equal(typeof signResult.signature, 'string')
139
-
140
- const encoded = JSON.parse(String(await walletTool.invoke({
141
- action: 'encode_contract_call',
142
- chain: 'ethereum',
143
- abi: JSON.stringify(['function approve(address spender,uint256 amount)']),
144
- functionName: 'approve',
145
- args: JSON.stringify(['0x000000000000000000000000000000000000dEaD', '5']),
146
- })))
147
- assert.equal(encoded.status, 'encoded')
148
- assert.equal(encoded.data.startsWith('0x095ea7b3'), true)
149
- })
150
- })