@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
@@ -10,9 +10,7 @@ import {
10
10
  removeManagedProcess,
11
11
  startManagedProcess,
12
12
  writeManagedProcessStdin,
13
- type SandboxOptions,
14
13
  } from '@/lib/server/runtime/process-manager'
15
- import { ensureSessionSandbox, resolveSandboxWorkdir, type AgentSandboxConfig } from '@/lib/server/sandbox/session-runtime'
16
14
  import type { ToolBuildContext } from './context'
17
15
  import { safePath, truncate, coerceEnvMap, MAX_OUTPUT } from './context'
18
16
  import { checkFileAccess } from './file-access-policy'
@@ -21,7 +19,6 @@ import { registerNativeCapability } from '../native-capabilities'
21
19
  import { safeJsonParseObject } from '../json-utils'
22
20
  import { normalizeToolInputArgs } from './normalize-tool-args'
23
21
  import { errorMessage } from '@/lib/shared-utils'
24
- import { executeSandboxExec, executeListRuntimes, type SandboxContext } from './sandbox'
25
22
 
26
23
  function resolveShellWorkdir(baseCwd: string, requestedWorkdir?: string, scope?: 'workspace' | 'machine'): string {
27
24
  const raw = typeof requestedWorkdir === 'string' ? requestedWorkdir.trim() : ''
@@ -43,12 +40,6 @@ export function rewriteShellWorkspaceAliases(baseCwd: string, command: string):
43
40
  return rewritten
44
41
  }
45
42
 
46
- export function rewriteShellWorkspaceAliasesForSandbox(command: string): string {
47
- let rewritten = command
48
- rewritten = rewritten.replace(/(^|[\s"'`(=;])workspace\//g, '$1/workspace/')
49
- return rewritten
50
- }
51
-
52
43
  export function stripManagedBackgroundSuffix(command: string): string {
53
44
  return command.replace(/\s*&\s*$/, '').trim()
54
45
  }
@@ -193,60 +184,6 @@ function checkShellFileAccessPolicy(
193
184
  return null
194
185
  }
195
186
 
196
- async function resolveSandboxOptions(params: {
197
- cwd: string
198
- workdir?: string
199
- session?: Session | null
200
- agentId?: string | null
201
- sessionId?: string | null
202
- env: Record<string, string>
203
- config?: AgentSandboxConfig | null
204
- filesystemScope?: 'workspace' | 'machine'
205
- }): Promise<{
206
- sandbox?: SandboxOptions
207
- hostWorkdir: string
208
- workspaceEnv: string
209
- sessionCwdEnv: string
210
- }> {
211
- const hostWorkdir = resolveShellWorkdir(params.cwd, params.workdir, params.filesystemScope)
212
- const sandboxSession = await ensureSessionSandbox({
213
- config: params.config,
214
- session: params.session,
215
- agentId: params.agentId,
216
- sessionId: params.sessionId,
217
- workspaceDir: params.cwd,
218
- })
219
-
220
- if (!sandboxSession) {
221
- return {
222
- hostWorkdir,
223
- workspaceEnv: params.cwd,
224
- sessionCwdEnv: hostWorkdir,
225
- }
226
- }
227
-
228
- const sandboxHostWorkdir = fs.existsSync(hostWorkdir) && fs.statSync(hostWorkdir).isDirectory()
229
- ? hostWorkdir
230
- : sandboxSession.workspaceDir
231
- const resolved = resolveSandboxWorkdir({
232
- workspaceDir: sandboxSession.workspaceDir,
233
- hostWorkdir: sandboxHostWorkdir,
234
- containerWorkdir: sandboxSession.containerWorkdir,
235
- })
236
-
237
- return {
238
- hostWorkdir: resolved.hostWorkdir,
239
- workspaceEnv: sandboxSession.containerWorkdir,
240
- sessionCwdEnv: resolved.containerWorkdir,
241
- sandbox: {
242
- kind: 'persistent',
243
- containerName: sandboxSession.containerName,
244
- containerWorkdir: resolved.containerWorkdir,
245
- env: params.env,
246
- },
247
- }
248
- }
249
-
250
187
  async function executeShellAction(
251
188
  args: Record<string, unknown>,
252
189
  bctx: {
@@ -255,7 +192,6 @@ async function executeShellAction(
255
192
  sessionId?: string | null
256
193
  resolveCurrentSession?: () => Session | null
257
194
  fileAccessPolicy?: { allowedPaths?: string[]; blockedPaths?: string[] } | null
258
- sandboxConfig?: AgentSandboxConfig | null
259
195
  filesystemScope?: 'workspace' | 'machine'
260
196
  commandTimeoutMs?: number
261
197
  },
@@ -284,37 +220,11 @@ async function executeShellAction(
284
220
  return 'Error: This command would terminate the SwarmClaw server. Use a more targeted command that only affects your project processes.'
285
221
  }
286
222
  const envMap = coerceEnvMap(env) || {}
287
- let sandbox: SandboxOptions | undefined
288
- let hostWorkdir = resolveShellWorkdir(bctx.cwd, workdir, bctx.filesystemScope)
289
- let commandForExecution = rewriteShellWorkspaceAliases(bctx.cwd, command)
290
- let sandboxMode: 'container' | 'host' | 'host-fallback' = 'host'
291
- try {
292
- const resolved = await resolveSandboxOptions({
293
- cwd: bctx.cwd,
294
- workdir,
295
- session: bctx.resolveCurrentSession?.() || null,
296
- agentId: bctx.agentId || null,
297
- sessionId: bctx.sessionId || null,
298
- env: envMap,
299
- config: bctx.sandboxConfig,
300
- filesystemScope: bctx.filesystemScope,
301
- })
302
- sandbox = resolved.sandbox
303
- hostWorkdir = resolved.hostWorkdir
304
- if (!envMap.WORKSPACE) envMap.WORKSPACE = resolved.workspaceEnv
305
- if (!envMap.SESSION_CWD) envMap.SESSION_CWD = resolved.sessionCwdEnv
306
- if (sandbox) {
307
- commandForExecution = rewriteShellWorkspaceAliasesForSandbox(command)
308
- if (!envMap.HOME) envMap.HOME = resolved.sessionCwdEnv
309
- if (!envMap.TMPDIR) envMap.TMPDIR = '/tmp'
310
- sandboxMode = 'container'
311
- }
312
- } catch {
313
- sandboxMode = 'host-fallback'
314
- }
223
+ const hostWorkdir = resolveShellWorkdir(bctx.cwd, workdir, bctx.filesystemScope)
224
+ const commandForExecution = rewriteShellWorkspaceAliases(bctx.cwd, command)
315
225
  if (!envMap.WORKSPACE) envMap.WORKSPACE = bctx.cwd
316
226
  if (!envMap.SESSION_CWD) envMap.SESSION_CWD = hostWorkdir
317
- if (!envMap.SWARMCLAW_SANDBOX_MODE) envMap.SWARMCLAW_SANDBOX_MODE = sandboxMode
227
+ if (!envMap.SWARMCLAW_SANDBOX_MODE) envMap.SWARMCLAW_SANDBOX_MODE = 'host'
318
228
  const effectiveBackground = !!background || (typeof commandForExecution === 'string' && isLikelyServerCommand(commandForExecution))
319
229
  const managedCommand = effectiveBackground ? stripManagedBackgroundSuffix(commandForExecution) : commandForExecution
320
230
  const result = await startManagedProcess({
@@ -329,7 +239,6 @@ async function executeShellAction(
329
239
  : effectiveBackground
330
240
  ? undefined // let process-manager use DEFAULT_TIMEOUT_MS (30 min)
331
241
  : (bctx.commandTimeoutMs || 30000),
332
- sandbox,
333
242
  })
334
243
  if (result.status === 'completed') return truncate(result.output || '(no output)', MAX_OUTPUT)
335
244
  return JSON.stringify({ status: 'running', processId: result.processId, tail: result.tail || '' }, null, 2)
@@ -344,27 +253,6 @@ async function executeShellAction(
344
253
  return killManagedProcess(processId!, killSignal).ok ? `Killed ${processId}` : `Error`
345
254
  }
346
255
  case 'remove': return removeManagedProcess(processId!).ok ? `Removed ${processId}` : `Error`
347
- case 'sandbox_exec': {
348
- const sandboxCtx: SandboxContext = {
349
- sessionId: bctx.sessionId || undefined,
350
- agentId: bctx.agentId || null,
351
- cwd: bctx.cwd,
352
- config: bctx.sandboxConfig,
353
- resolveCurrentSession: bctx.resolveCurrentSession,
354
- }
355
- return executeSandboxExec(normalized, sandboxCtx)
356
- }
357
- case 'sandbox_list_runtimes':
358
- case 'list_runtimes': {
359
- const sandboxCtx: SandboxContext = {
360
- sessionId: bctx.sessionId || undefined,
361
- agentId: bctx.agentId || null,
362
- cwd: bctx.cwd,
363
- config: bctx.sandboxConfig,
364
- resolveCurrentSession: bctx.resolveCurrentSession,
365
- }
366
- return executeListRuntimes(sandboxCtx)
367
- }
368
256
  default: return `Error: Unknown action "${action}"`
369
257
  }
370
258
  } catch (err: unknown) {
@@ -379,22 +267,20 @@ const ShellExtension: Extension = {
379
267
  name: 'Core Shell',
380
268
  description: 'Execute shell commands and manage background processes. Use for git, curl, and other CLI tools.',
381
269
  hooks: {
382
- getCapabilityDescription: () => 'I can run shell commands with the unified `shell` tool. Use action `execute` for commands (including git, curl, and other CLI tools), `sandbox_exec` to run JS/TS in a sandboxed environment, and `list` / `status` / `poll` / `log` for long-lived processes.',
383
- getOperatingGuidance: () => ['Shell: use `shell` with `{"action":"execute","command":"..."}` for servers, installs, scripts, git, and curl. Use `background=true` for long-lived processes.', 'Use `shell` with `{"action":"sandbox_exec","language":"javascript","code":"..."}` to run JS/TS in a Docker sandbox when available.', 'Verify servers with `shell` status/log actions and liveness probes before claiming success.', 'Resolve IPs/URLs via shell — never use placeholders. Retry path errors without workdir override.'],
270
+ getCapabilityDescription: () => 'I can run shell commands with the unified `shell` tool. Use action `execute` for commands (including git, curl, and other CLI tools) and `list` / `status` / `poll` / `log` for long-lived processes.',
271
+ getOperatingGuidance: () => ['Shell: use `shell` with `{"action":"execute","command":"..."}` for servers, installs, scripts, git, and curl. Use `background=true` for long-lived processes.', 'Use the separate `execute` tool for sandboxed just-bash execution.', 'Verify servers with `shell` status/log actions and liveness probes before claiming success.', 'Resolve IPs/URLs via shell — never use placeholders. Retry path errors without workdir override.'],
384
272
  } as ExtensionHooks,
385
273
  tools: [
386
274
  {
387
275
  name: 'shell',
388
- description: 'Execute commands and manage processes. Use for git, curl, and other CLI tools. Includes sandbox_exec for running JS/TS in Docker.',
276
+ description: 'Execute commands and manage processes. Use for git, curl, long-lived servers, and other host CLI tools.',
389
277
  parameters: {
390
278
  type: 'object',
391
279
  properties: {
392
- action: { type: 'string', enum: ['execute', 'list', 'status', 'poll', 'log', 'write', 'kill', 'remove', 'sandbox_exec', 'sandbox_list_runtimes'] },
280
+ action: { type: 'string', enum: ['execute', 'list', 'status', 'poll', 'log', 'write', 'kill', 'remove'] },
393
281
  command: { type: 'string' },
394
282
  processId: { type: 'string' },
395
283
  background: { type: 'boolean' },
396
- language: { type: 'string', enum: ['javascript', 'typescript'], description: 'Language for sandbox_exec action' },
397
- code: { type: 'string', description: 'Code to execute for sandbox_exec action' },
398
284
  },
399
285
  required: ['action']
400
286
  },
@@ -414,7 +300,6 @@ export function buildShellTools(bctx: ToolBuildContext) {
414
300
  cwd: bctx.cwd,
415
301
  resolveCurrentSession: bctx.resolveCurrentSession,
416
302
  fileAccessPolicy: bctx.fileAccessPolicy,
417
- sandboxConfig: bctx.sandboxConfig,
418
303
  filesystemScope: bctx.filesystemScope,
419
304
  commandTimeoutMs: bctx.commandTimeoutMs,
420
305
  }),
@@ -0,0 +1,396 @@
1
+ /**
2
+ * skills — Lightweight skill file reader.
3
+ *
4
+ * Agents call this tool to discover and load skill files (markdown docs)
5
+ * that teach them how to use tools, APIs, or workflows. Two directories
6
+ * are scanned: `skills/` (built-in, checked into the repo) and
7
+ * `data/skills/` (user-created at runtime).
8
+ *
9
+ * Actions:
10
+ * read — load a skill file by name
11
+ * list — browse available skills with descriptions
12
+ * search — find skills by keyword match in content
13
+ */
14
+
15
+ import fs from 'fs'
16
+ import path from 'path'
17
+ import { z } from 'zod'
18
+ import { tool } from '@langchain/core/tools'
19
+ import type { Extension, ExtensionHooks } from '@/types'
20
+ import { registerNativeCapability } from '../native-capabilities'
21
+ import { errorMessage } from '@/lib/shared-utils'
22
+ import { log } from '../logger'
23
+ import { normalizeToolInputArgs } from './normalize-tool-args'
24
+ import type { ToolBuildContext } from './context'
25
+ import { truncate, MAX_OUTPUT } from './context'
26
+
27
+ const TAG = 'skills-tool'
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Skill directory resolution
31
+ // ---------------------------------------------------------------------------
32
+
33
+ /** Root of the project — two levels above `src/lib/server/session-tools/` */
34
+ function projectRoot(): string {
35
+ return path.resolve(__dirname, '..', '..', '..', '..')
36
+ }
37
+
38
+ function builtinSkillsDir(): string {
39
+ return path.join(projectRoot(), 'skills')
40
+ }
41
+
42
+ function userSkillsDir(): string {
43
+ return path.join(projectRoot(), 'data', 'skills')
44
+ }
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Skill file discovery
48
+ // ---------------------------------------------------------------------------
49
+
50
+ interface SkillEntry {
51
+ name: string
52
+ description: string
53
+ source: 'builtin' | 'user'
54
+ filePath: string
55
+ }
56
+
57
+ /**
58
+ * Extract the first non-empty, non-frontmatter, non-heading content line
59
+ * from a markdown file to use as a short description.
60
+ */
61
+ function extractDescription(content: string): string {
62
+ let inFrontmatter = false
63
+ for (const rawLine of content.split('\n')) {
64
+ const line = rawLine.trim()
65
+ if (line === '---') {
66
+ inFrontmatter = !inFrontmatter
67
+ continue
68
+ }
69
+ if (inFrontmatter) continue
70
+ if (!line || line.startsWith('#')) continue
71
+ return line.length > 120 ? line.slice(0, 117) + '...' : line
72
+ }
73
+ return ''
74
+ }
75
+
76
+ /**
77
+ * Walk a skills directory and collect all `.md` files as skill entries.
78
+ * Supports both flat files (`skills/swarmclaw.md`) and directory-based
79
+ * skills (`skills/github/SKILL.md`).
80
+ */
81
+ function discoverSkillsInDir(dir: string, source: 'builtin' | 'user'): SkillEntry[] {
82
+ if (!fs.existsSync(dir)) return []
83
+
84
+ const entries: SkillEntry[] = []
85
+
86
+ try {
87
+ const items = fs.readdirSync(dir, { withFileTypes: true })
88
+
89
+ for (const item of items) {
90
+ if (item.name.startsWith('.')) continue
91
+
92
+ const fullPath = path.join(dir, item.name)
93
+
94
+ if (item.isFile() && item.name.endsWith('.md')) {
95
+ // Flat markdown file: skills/swarmclaw.md
96
+ const name = item.name.replace(/\.md$/i, '')
97
+ const content = readFileSafe(fullPath)
98
+ entries.push({
99
+ name,
100
+ description: extractDescription(content),
101
+ source,
102
+ filePath: fullPath,
103
+ })
104
+ } else if (item.isDirectory()) {
105
+ // Directory-based skill: walk subdirectory for .md files
106
+ const subItems = readdirSafe(fullPath)
107
+ for (const sub of subItems) {
108
+ if (sub.startsWith('.')) continue
109
+ const subPath = path.join(fullPath, sub)
110
+
111
+ if (sub.endsWith('.md') && fs.statSync(subPath).isFile()) {
112
+ const subName = sub === 'SKILL.md'
113
+ ? item.name
114
+ : `${item.name}/${sub.replace(/\.md$/i, '')}`
115
+ const content = readFileSafe(subPath)
116
+ entries.push({
117
+ name: subName,
118
+ description: extractDescription(content),
119
+ source,
120
+ filePath: subPath,
121
+ })
122
+ } else if (fs.statSync(subPath).isDirectory()) {
123
+ // One level deeper: skills/tools/files.md
124
+ const deepItems = readdirSafe(subPath)
125
+ for (const deep of deepItems) {
126
+ if (deep.startsWith('.') || !deep.endsWith('.md')) continue
127
+ const deepPath = path.join(subPath, deep)
128
+ if (!fs.statSync(deepPath).isFile()) continue
129
+ const deepName = `${item.name}/${sub}/${deep.replace(/\.md$/i, '')}`
130
+ const content = readFileSafe(deepPath)
131
+ entries.push({
132
+ name: deepName,
133
+ description: extractDescription(content),
134
+ source,
135
+ filePath: deepPath,
136
+ })
137
+ }
138
+ }
139
+ }
140
+
141
+ // If SKILL.md or named .md exist but weren't caught by the loop
142
+ // (they would have been), this is just for safety — dedup by filePath later
143
+ }
144
+ }
145
+ } catch (err: unknown) {
146
+ log.warn(TAG, `Failed to scan skill directory: ${dir}`, { error: errorMessage(err) })
147
+ }
148
+
149
+ return entries
150
+ }
151
+
152
+ function readdirSafe(dir: string): string[] {
153
+ try {
154
+ return fs.readdirSync(dir).filter((name) => !name.startsWith('.'))
155
+ } catch {
156
+ return []
157
+ }
158
+ }
159
+
160
+ function readFileSafe(filePath: string): string {
161
+ try {
162
+ return fs.readFileSync(filePath, 'utf-8')
163
+ } catch {
164
+ return ''
165
+ }
166
+ }
167
+
168
+ function discoverAllSkills(): SkillEntry[] {
169
+ const builtin = discoverSkillsInDir(builtinSkillsDir(), 'builtin')
170
+ const user = discoverSkillsInDir(userSkillsDir(), 'user')
171
+
172
+ // Dedup by filePath
173
+ const seen = new Set<string>()
174
+ const deduped: SkillEntry[] = []
175
+ for (const entry of [...builtin, ...user]) {
176
+ if (seen.has(entry.filePath)) continue
177
+ seen.add(entry.filePath)
178
+ deduped.push(entry)
179
+ }
180
+
181
+ return deduped
182
+ }
183
+
184
+ // ---------------------------------------------------------------------------
185
+ // Action: read
186
+ // ---------------------------------------------------------------------------
187
+
188
+ function resolveSkillFile(name: string): SkillEntry | null {
189
+ const skills = discoverAllSkills()
190
+ const normalized = name.trim().toLowerCase()
191
+
192
+ // Exact match by name
193
+ const exact = skills.find((s) => s.name.toLowerCase() === normalized)
194
+ if (exact) return exact
195
+
196
+ // Partial match: name ends with the query
197
+ const partial = skills.find((s) => s.name.toLowerCase().endsWith(normalized))
198
+ if (partial) return partial
199
+
200
+ // Partial match: name contains the query
201
+ const contains = skills.find((s) => s.name.toLowerCase().includes(normalized))
202
+ if (contains) return contains
203
+
204
+ return null
205
+ }
206
+
207
+ function actionRead(name: string): string {
208
+ if (!name) {
209
+ return 'Error: `name` parameter is required for action "read". Provide the skill name to load.'
210
+ }
211
+
212
+ const skill = resolveSkillFile(name)
213
+ if (!skill) {
214
+ const available = discoverAllSkills().map((s) => s.name)
215
+ return JSON.stringify({
216
+ error: `Skill "${name}" not found.`,
217
+ available: available.slice(0, 20),
218
+ hint: 'Use action="list" to see all available skills, or action="search" with a query.',
219
+ })
220
+ }
221
+
222
+ const content = readFileSafe(skill.filePath)
223
+ if (!content) {
224
+ return `Error: Skill file "${skill.filePath}" exists but could not be read.`
225
+ }
226
+
227
+ log.info(TAG, `Read skill: ${skill.name}`, { source: skill.source, filePath: skill.filePath })
228
+
229
+ return truncate(content, MAX_OUTPUT)
230
+ }
231
+
232
+ // ---------------------------------------------------------------------------
233
+ // Action: list
234
+ // ---------------------------------------------------------------------------
235
+
236
+ function actionList(): string {
237
+ const skills = discoverAllSkills()
238
+
239
+ if (skills.length === 0) {
240
+ return JSON.stringify({
241
+ skills: [],
242
+ message: 'No skill files found. Create .md files in skills/ or data/skills/ to add skills.',
243
+ })
244
+ }
245
+
246
+ const listing = skills.map((s) => ({
247
+ name: s.name,
248
+ description: s.description,
249
+ source: s.source,
250
+ }))
251
+
252
+ return JSON.stringify({ skills: listing, total: listing.length })
253
+ }
254
+
255
+ // ---------------------------------------------------------------------------
256
+ // Action: search
257
+ // ---------------------------------------------------------------------------
258
+
259
+ function actionSearch(query: string): string {
260
+ if (!query) {
261
+ return 'Error: `query` parameter is required for action "search".'
262
+ }
263
+
264
+ const skills = discoverAllSkills()
265
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean)
266
+
267
+ const results: Array<{ name: string; description: string; source: string; matchCount: number }> = []
268
+
269
+ for (const skill of skills) {
270
+ const content = readFileSafe(skill.filePath).toLowerCase()
271
+ const nameAndDesc = `${skill.name} ${skill.description}`.toLowerCase()
272
+
273
+ let matchCount = 0
274
+ for (const term of terms) {
275
+ if (nameAndDesc.includes(term)) matchCount += 2
276
+ else if (content.includes(term)) matchCount += 1
277
+ }
278
+
279
+ if (matchCount > 0) {
280
+ results.push({
281
+ name: skill.name,
282
+ description: skill.description,
283
+ source: skill.source,
284
+ matchCount,
285
+ })
286
+ }
287
+ }
288
+
289
+ results.sort((a, b) => b.matchCount - a.matchCount)
290
+
291
+ return JSON.stringify({
292
+ query,
293
+ results: results.slice(0, 15),
294
+ total: results.length,
295
+ })
296
+ }
297
+
298
+ // ---------------------------------------------------------------------------
299
+ // Main action dispatcher
300
+ // ---------------------------------------------------------------------------
301
+
302
+ function executeSkillsAction(rawArgs: Record<string, unknown>): string {
303
+ const normalized = normalizeToolInputArgs(rawArgs)
304
+ const action = typeof normalized.action === 'string' ? normalized.action.trim().toLowerCase() : ''
305
+ const name = typeof normalized.name === 'string' ? normalized.name.trim() : ''
306
+ const query = typeof normalized.query === 'string' ? normalized.query.trim() : ''
307
+
308
+ try {
309
+ switch (action) {
310
+ case 'read':
311
+ return actionRead(name || query)
312
+ case 'list':
313
+ return actionList()
314
+ case 'search':
315
+ return actionSearch(query || name)
316
+ default:
317
+ return `Error: Unknown action "${action}". Supported actions: read, list, search.`
318
+ }
319
+ } catch (err: unknown) {
320
+ return `Error: ${errorMessage(err)}`
321
+ }
322
+ }
323
+
324
+ // ---------------------------------------------------------------------------
325
+ // Extension registration
326
+ // ---------------------------------------------------------------------------
327
+
328
+ const SkillsExtension: Extension = {
329
+ name: 'Core Skills',
330
+ description: 'Discover and load skill files that teach agents how to use tools, APIs, and workflows.',
331
+ hooks: {
332
+ getCapabilityDescription: () =>
333
+ 'I can discover and read skill files with the `skills` tool. ' +
334
+ 'Skills are markdown documents that teach me how to use specific tools, APIs, or workflows. ' +
335
+ 'Built-in skills live in `skills/` and user-created skills in `data/skills/`.',
336
+ getOperatingGuidance: () =>
337
+ 'Use `skills` with action="list" to see what skills are available. ' +
338
+ 'Use action="read" with a name to load a specific skill. ' +
339
+ 'Use action="search" with a query to find skills by keyword. ' +
340
+ 'Load a skill before attempting unfamiliar tools or APIs.',
341
+ } as ExtensionHooks,
342
+ tools: [
343
+ {
344
+ name: 'skills',
345
+ description:
346
+ 'Discover and load skill files (markdown docs) that teach how to use tools, APIs, and workflows. ' +
347
+ 'Actions: "read" (load a skill by name), "list" (browse available skills), "search" (find by keyword). ' +
348
+ 'Load a skill before attempting unfamiliar tools or APIs.',
349
+ parameters: {
350
+ type: 'object',
351
+ properties: {
352
+ action: {
353
+ type: 'string',
354
+ enum: ['read', 'list', 'search'],
355
+ description: 'The action to perform',
356
+ },
357
+ name: {
358
+ type: 'string',
359
+ description: 'Skill name for action="read" (e.g., "tools/files", "swarmclaw")',
360
+ },
361
+ query: {
362
+ type: 'string',
363
+ description: 'Search query for action="search", or alternate name for action="read"',
364
+ },
365
+ },
366
+ required: ['action'],
367
+ },
368
+ execute: async (args) => executeSkillsAction(args),
369
+ },
370
+ ],
371
+ }
372
+
373
+ registerNativeCapability('skills', SkillsExtension)
374
+
375
+ // ---------------------------------------------------------------------------
376
+ // Tool builder (called from session-tools/index.ts)
377
+ // ---------------------------------------------------------------------------
378
+
379
+ export function buildSkillsTools(bctx: ToolBuildContext) {
380
+ if (!bctx.hasExtension('skills')) return []
381
+
382
+ return [
383
+ tool(
384
+ async (args) => executeSkillsAction((args ?? {}) as Record<string, unknown>),
385
+ {
386
+ name: 'skills',
387
+ description: SkillsExtension.tools![0].description,
388
+ schema: z.object({
389
+ action: z.enum(['read', 'list', 'search']).describe('The action to perform'),
390
+ name: z.string().optional().describe('Skill name for action="read"'),
391
+ query: z.string().optional().describe('Search query for action="search"'),
392
+ }).passthrough(),
393
+ },
394
+ ),
395
+ ]
396
+ }
@@ -168,9 +168,6 @@ async function formatPeerContext(agentId: string, peerId: string): Promise<strin
168
168
  const activeSession = Object.values(sessions).find(
169
169
  (s) => s.agentId === peerId && s.active,
170
170
  )
171
- if (activeSession?.missionId) {
172
- result.activeMission = { missionId: activeSession.missionId }
173
- }
174
171
  } catch { /* non-critical */ }
175
172
 
176
173
  // Enforce output cap — drop large fields instead of slicing mid-JSON
@@ -25,7 +25,7 @@ import {
25
25
  removeBrowserSessionRecord,
26
26
  upsertBrowserSessionRecord,
27
27
  } from '../browser-state'
28
- import { ensureSessionSandbox } from '@/lib/server/sandbox/session-runtime'
28
+ import { resolveSandboxSessionContext } from '@/lib/server/sandbox/session-runtime'
29
29
  import {
30
30
  destroySandboxBrowser,
31
31
  ensureSandboxBrowser,
@@ -328,7 +328,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
328
328
  if (sandboxRuntimePromise) return sandboxRuntimePromise
329
329
  sandboxRuntimePromise = (async () => {
330
330
  try {
331
- const sandbox = await ensureSessionSandbox({
331
+ const sandbox = resolveSandboxSessionContext({
332
332
  config: bctx.sandboxConfig,
333
333
  session: currentSession,
334
334
  agentId: currentSession.agentId ?? ctx?.agentId ?? null,
@@ -435,6 +435,7 @@ export function normalizeStoredRecord(
435
435
  && table !== 'schedules' && table !== 'sessions'
436
436
  && table !== 'provider_configs'
437
437
  && table !== 'runtime_runs' && table !== 'runtime_run_events'
438
+ && table !== 'wallets'
438
439
  ) {
439
440
  return { value, changed: false }
440
441
  }
@@ -484,6 +485,8 @@ function normalizeStoredRecordInner(
484
485
  delete agent.platformAssignScope
485
486
  delete agent.subAgentIds
486
487
  agent.sandboxConfig = normalizeAgentSandboxConfig(agent.sandboxConfig)
488
+ // Default executeConfig — null means not configured (falls back to defaults in execute.ts)
489
+ if (agent.executeConfig === undefined) agent.executeConfig = null
487
490
  // Default proactiveMemory to true for existing agents
488
491
  if (agent.proactiveMemory === undefined) agent.proactiveMemory = true
489
492
  if (!Array.isArray(agent.capabilities)) agent.capabilities = []
@@ -571,6 +574,13 @@ function normalizeStoredRecordInner(
571
574
  return normalizeStoredScheduleRecord(value, loadItem)
572
575
  }
573
576
 
577
+ if (table === 'wallets') {
578
+ const wallet = value as StoredObject
579
+ if (wallet.chain !== 'base') wallet.chain = 'base'
580
+ if (typeof wallet.createdAt !== 'number') wallet.createdAt = Date.now()
581
+ return wallet
582
+ }
583
+
574
584
  // sessions
575
585
  const session = value as StoredObject
576
586
  // Migrate legacy 'orchestrated' → 'delegated'