@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.
- package/README.md +54 -23
- package/next.config.ts +1 -0
- package/package.json +4 -3
- package/scripts/easy-setup.mjs +1 -1
- package/scripts/postinstall.mjs +1 -1
- package/skills/swarmclaw.md +115 -0
- package/skills/tools/browser.md +131 -0
- package/skills/tools/execute.md +98 -0
- package/skills/tools/files.md +98 -0
- package/skills/tools/memory.md +104 -0
- package/skills/tools/platform.md +144 -0
- package/skills/tools/skills.md +83 -0
- package/src/app/agents/[id]/page.tsx +1 -18
- package/src/app/api/agents/thread-route.test.ts +0 -1
- package/src/app/api/approvals/route.test.ts +6 -22
- package/src/app/api/chats/[id]/messages/route.ts +23 -19
- package/src/app/api/chats/messages-route.test.ts +105 -51
- package/src/app/api/connectors/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/test/route.ts +3 -2
- package/src/app/api/openclaw/deploy/route.ts +2 -0
- package/src/app/api/portability/export/route.ts +8 -0
- package/src/app/api/portability/import/route.test.ts +80 -0
- package/src/app/api/portability/import/route.ts +28 -0
- package/src/app/api/settings/route.ts +0 -2
- package/src/app/api/setup/doctor/route.ts +4 -4
- package/src/app/api/wallets/[id]/route.ts +15 -157
- package/src/app/api/wallets/generate/route.ts +22 -0
- package/src/app/api/wallets/route.test.ts +147 -0
- package/src/app/api/wallets/route.ts +13 -95
- package/src/app/autonomy/page.tsx +2 -57
- package/src/app/protocols/page.tsx +2 -21
- package/src/app/settings/page.tsx +0 -9
- package/src/app/wallets/page.tsx +105 -5
- package/src/cli/index.js +21 -33
- package/src/cli/spec.js +19 -30
- package/src/components/agents/agent-chat-list.tsx +23 -1
- package/src/components/agents/agent-sheet.tsx +2 -40
- package/src/components/agents/inspector-panel.tsx +165 -131
- package/src/components/chat/chat-area.tsx +38 -9
- package/src/components/chat/chat-card.tsx +0 -31
- package/src/components/chat/message-bubble.tsx +1 -108
- package/src/components/chat/message-list.tsx +33 -19
- package/src/components/connectors/connector-sheet.tsx +25 -1
- package/src/components/gateways/gateway-sheet.tsx +5 -2
- package/src/components/layout/sidebar-rail.tsx +6 -10
- package/src/components/projects/project-detail.tsx +3 -35
- package/src/components/projects/tabs/overview-tab.tsx +3 -59
- package/src/components/projects/tabs/work-tab.tsx +7 -77
- package/src/components/protocols/structured-session-launcher.tsx +1 -22
- package/src/components/shared/connector-platform-icon.tsx +1 -0
- package/src/components/tasks/task-card.tsx +4 -34
- package/src/components/tasks/task-sheet.tsx +6 -36
- package/src/components/wallets/wallet-list.tsx +150 -0
- package/src/lib/agent-execute-defaults.test.ts +24 -0
- package/src/lib/agent-execute-defaults.ts +62 -0
- package/src/lib/app/navigation.test.ts +0 -13
- package/src/lib/app/navigation.ts +2 -7
- package/src/lib/app/view-constants.ts +14 -19
- package/src/lib/chat/queued-message-queue.test.ts +134 -1
- package/src/lib/chat/queued-message-queue.ts +77 -2
- package/src/lib/server/agents/agent-service.ts +5 -0
- package/src/lib/server/agents/agent-thread-session.ts +0 -1
- package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
- package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
- package/src/lib/server/agents/delegation-jobs.ts +0 -25
- package/src/lib/server/agents/main-agent-loop.ts +1 -49
- package/src/lib/server/agents/subagent-runtime.ts +0 -1
- package/src/lib/server/approval-match.ts +0 -85
- package/src/lib/server/approvals.test.ts +6 -6
- package/src/lib/server/approvals.ts +0 -6
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
- package/src/lib/server/builtin-extensions.ts +1 -2
- package/src/lib/server/capability-router.test.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +1 -1
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +15 -14
- package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-utils.ts +2 -4
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +81 -64
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -0
- package/src/lib/server/chat-execution/continuation-evaluator.ts +8 -0
- package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
- package/src/lib/server/chat-execution/memory-mutation-tools.ts +1 -1
- package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
- package/src/lib/server/chat-execution/message-classifier.ts +11 -16
- package/src/lib/server/chat-execution/prompt-builder.test.ts +27 -0
- package/src/lib/server/chat-execution/prompt-builder.ts +14 -31
- package/src/lib/server/chat-execution/prompt-mode.test.ts +24 -0
- package/src/lib/server/chat-execution/prompt-mode.ts +5 -1
- package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
- package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
- package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +13 -126
- package/src/lib/server/chat-execution/stream-agent-chat.ts +46 -21
- package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
- package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
- package/src/lib/server/chatrooms/chatroom-routing.test.ts +4 -0
- package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
- package/src/lib/server/chats/chat-session-service.ts +3 -5
- package/src/lib/server/connectors/connector-inbound.ts +0 -1
- package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
- package/src/lib/server/connectors/connector-service.ts +39 -9
- package/src/lib/server/connectors/discord.ts +2 -2
- package/src/lib/server/connectors/matrix.ts +3 -2
- package/src/lib/server/connectors/signal.ts +5 -4
- package/src/lib/server/connectors/slack.ts +10 -9
- package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
- package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
- package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
- package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
- package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
- package/src/lib/server/connectors/swarmdock.ts +255 -0
- package/src/lib/server/connectors/teams.ts +3 -2
- package/src/lib/server/connectors/telegram.ts +4 -4
- package/src/lib/server/connectors/whatsapp.ts +2 -2
- package/src/lib/server/daemon/controller.ts +7 -0
- package/src/lib/server/execution-brief.test.ts +2 -25
- package/src/lib/server/execution-brief.ts +12 -35
- package/src/lib/server/execution-engine/task-attempt.ts +0 -1
- package/src/lib/server/gateways/gateway-profile-service.ts +19 -1
- package/src/lib/server/messages/message-repository.test.ts +70 -0
- package/src/lib/server/messages/message-repository.ts +11 -6
- package/src/lib/server/openclaw/deploy.ts +32 -2
- package/src/lib/server/persistence/storage-context.ts +0 -5
- package/src/lib/server/plugins-advanced.test.ts +1 -2
- package/src/lib/server/portability/export.ts +109 -0
- package/src/lib/server/portability/import.ts +159 -0
- package/src/lib/server/protocols/protocol-normalization.ts +0 -4
- package/src/lib/server/protocols/protocol-queries.ts +0 -6
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
- package/src/lib/server/protocols/protocol-service.ts +0 -1
- package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
- package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
- package/src/lib/server/protocols/protocol-swarm.ts +0 -2
- package/src/lib/server/protocols/protocol-types.ts +0 -2
- package/src/lib/server/provider-health.ts +1 -10
- package/src/lib/server/runtime/daemon-state/core.ts +0 -9
- package/src/lib/server/runtime/daemon-state.test.ts +0 -35
- package/src/lib/server/runtime/heartbeat-service.ts +3 -23
- package/src/lib/server/runtime/process-manager.ts +13 -9
- package/src/lib/server/runtime/queue/core.ts +11 -33
- package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
- package/src/lib/server/runtime/scheduler.ts +0 -13
- package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
- package/src/lib/server/runtime/session-run-manager/queries.ts +15 -1
- package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
- package/src/lib/server/runtime/session-run-manager.test.ts +58 -28
- package/src/lib/server/sandbox/session-runtime.test.ts +18 -1
- package/src/lib/server/sandbox/session-runtime.ts +40 -28
- package/src/lib/server/session-tools/autonomy-tools.test.ts +7 -9
- package/src/lib/server/session-tools/context.ts +1 -1
- package/src/lib/server/session-tools/credential-env.ts +109 -0
- package/src/lib/server/session-tools/crud.ts +3 -17
- package/src/lib/server/session-tools/delegate.ts +0 -4
- package/src/lib/server/session-tools/edit_file.ts +3 -2
- package/src/lib/server/session-tools/execute.test.ts +58 -0
- package/src/lib/server/session-tools/execute.ts +334 -0
- package/src/lib/server/session-tools/files-tool.ts +635 -0
- package/src/lib/server/session-tools/index.ts +14 -8
- package/src/lib/server/session-tools/memory-tool.ts +242 -0
- package/src/lib/server/session-tools/memory.ts +1 -1
- package/src/lib/server/session-tools/openclaw-nodes.ts +3 -2
- package/src/lib/server/session-tools/openclaw-workspace.ts +3 -2
- package/src/lib/server/session-tools/platform-tool.ts +617 -0
- package/src/lib/server/session-tools/session-info.ts +3 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +3 -4
- package/src/lib/server/session-tools/shell.ts +7 -122
- package/src/lib/server/session-tools/skills-tool.ts +396 -0
- package/src/lib/server/session-tools/team-context.ts +0 -3
- package/src/lib/server/session-tools/web.ts +2 -2
- package/src/lib/server/storage-normalization.ts +10 -0
- package/src/lib/server/storage.ts +18 -45
- package/src/lib/server/tasks/task-checkout.ts +59 -0
- package/src/lib/server/tasks/task-lifecycle.ts +2 -0
- package/src/lib/server/tasks/task-route-service.ts +4 -26
- package/src/lib/server/tasks/task-service.ts +0 -7
- package/src/lib/server/tool-aliases.ts +2 -2
- package/src/lib/server/tool-capability-policy-advanced.test.ts +13 -6
- package/src/lib/server/tool-capability-policy.test.ts +2 -1
- package/src/lib/server/tool-capability-policy.ts +60 -35
- package/src/lib/server/tool-planning.ts +11 -12
- package/src/lib/server/universal-tool-access.ts +0 -1
- package/src/lib/server/wallets/wallet-crypto.ts +33 -0
- package/src/lib/server/wallets/wallet-repository.ts +24 -0
- package/src/lib/server/wallets/wallet-service.ts +119 -0
- package/src/lib/server/working-state/extraction.ts +8 -42
- package/src/lib/server/working-state/normalization.ts +10 -103
- package/src/lib/server/working-state/service.ts +12 -21
- package/src/lib/setup-defaults.ts +5 -0
- package/src/lib/strip-internal-metadata.test.ts +1 -1
- package/src/lib/strip-internal-metadata.ts +1 -1
- package/src/lib/tool-definitions.ts +1 -1
- package/src/lib/validation/schemas.test.ts +16 -0
- package/src/lib/validation/schemas.ts +49 -2
- package/src/stores/slices/data-slice.ts +5 -1
- package/src/stores/slices/ui-slice.ts +0 -4
- package/src/stores/use-chat-store.test.ts +231 -0
- package/src/stores/use-chat-store.ts +62 -13
- package/src/types/agent.ts +264 -0
- package/src/types/app-settings.ts +173 -0
- package/src/types/approval.ts +25 -0
- package/src/types/connector.ts +188 -0
- package/src/types/extension.ts +386 -0
- package/src/types/index.ts +16 -3555
- package/src/types/message.ts +56 -0
- package/src/types/misc.ts +737 -0
- package/src/types/protocol.ts +420 -0
- package/src/types/provider.ts +52 -0
- package/src/types/run.ts +180 -0
- package/src/types/schedule.ts +59 -0
- package/src/types/session.ts +215 -0
- package/src/types/skill.ts +157 -0
- package/src/types/swarmdock.ts +29 -0
- package/src/types/task.ts +144 -0
- package/src/types/working-state.ts +204 -0
- package/src/views/settings/section-heartbeat.tsx +2 -2
- package/src/views/settings/section-runtime-loop.tsx +0 -14
- package/src/app/api/canvas/[sessionId]/route.ts +0 -35
- package/src/app/api/missions/[id]/actions/route.ts +0 -31
- package/src/app/api/missions/[id]/events/route.ts +0 -14
- package/src/app/api/missions/[id]/route.ts +0 -10
- package/src/app/api/missions/route.test.ts +0 -244
- package/src/app/api/missions/route.ts +0 -57
- package/src/app/api/wallets/[id]/approve/route.ts +0 -79
- package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
- package/src/app/api/wallets/[id]/send/route.ts +0 -113
- package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
- package/src/app/missions/[id]/page.tsx +0 -3
- package/src/app/missions/page.tsx +0 -685
- package/src/components/canvas/canvas-panel.tsx +0 -267
- package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
- package/src/components/wallets/wallet-panel.tsx +0 -1010
- package/src/components/wallets/wallet-section.tsx +0 -260
- package/src/features/missions/queries.ts +0 -23
- package/src/lib/canvas-content.test.ts +0 -360
- package/src/lib/canvas-content.ts +0 -198
- package/src/lib/server/canvas-content.test.ts +0 -32
- package/src/lib/server/canvas-content.ts +0 -6
- package/src/lib/server/ethereum.ts +0 -591
- package/src/lib/server/evm-swap.ts +0 -476
- package/src/lib/server/missions/mission-intent.test.ts +0 -63
- package/src/lib/server/missions/mission-intent.ts +0 -569
- package/src/lib/server/missions/mission-repository.ts +0 -74
- package/src/lib/server/missions/mission-service/actions.ts +0 -6
- package/src/lib/server/missions/mission-service/bindings.ts +0 -9
- package/src/lib/server/missions/mission-service/context.ts +0 -4
- package/src/lib/server/missions/mission-service/core.ts +0 -2271
- package/src/lib/server/missions/mission-service/queries.ts +0 -12
- package/src/lib/server/missions/mission-service/recovery.ts +0 -5
- package/src/lib/server/missions/mission-service/ticks.ts +0 -9
- package/src/lib/server/missions/mission-service.test.ts +0 -888
- package/src/lib/server/missions/mission-service.ts +0 -6
- package/src/lib/server/session-tools/canvas.ts +0 -105
- package/src/lib/server/session-tools/sandbox.ts +0 -281
- package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
- package/src/lib/server/session-tools/wallet.ts +0 -1287
- package/src/lib/server/solana.ts +0 -327
- package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
- package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
- package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
- package/src/lib/server/wallet/wallet-service.test.ts +0 -81
- package/src/lib/server/wallet/wallet-service.ts +0 -225
- package/src/lib/wallet/wallet-transactions.test.ts +0 -75
- package/src/lib/wallet/wallet-transactions.ts +0 -43
- package/src/lib/wallet/wallet.test.ts +0 -333
- package/src/lib/wallet/wallet.ts +0 -183
- package/src/views/settings/section-wallets.tsx +0 -35
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* execute — Unified code execution tool with dual backends.
|
|
3
|
+
*
|
|
4
|
+
* Sandbox backend (default): just-bash with OverlayFS. Reads workspace
|
|
5
|
+
* files from disk, writes stay in memory. Credential injection + secret
|
|
6
|
+
* redaction. No npm, no background processes, no persistent writes.
|
|
7
|
+
*
|
|
8
|
+
* Host backend (opt-in per agent config): Real bash on the host. Full
|
|
9
|
+
* system access — npm, git, background processes, persistent writes.
|
|
10
|
+
* Retains current safety guards from shell.ts.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { z } from 'zod'
|
|
14
|
+
import { tool } from '@langchain/core/tools'
|
|
15
|
+
import { spawn, type ChildProcess } from 'child_process'
|
|
16
|
+
import type { Extension, ExtensionHooks, Agent } from '@/types'
|
|
17
|
+
import { registerNativeCapability } from '../native-capabilities'
|
|
18
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
19
|
+
import { log } from '../logger'
|
|
20
|
+
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
21
|
+
import { buildCredentialEnv, redactSecrets } from './credential-env'
|
|
22
|
+
import type { ToolBuildContext } from './context'
|
|
23
|
+
import { truncate, MAX_OUTPUT } from './context'
|
|
24
|
+
import {
|
|
25
|
+
DEFAULT_AGENT_EXECUTE_CONFIG,
|
|
26
|
+
normalizeAgentExecuteConfig,
|
|
27
|
+
type AgentExecuteConfig,
|
|
28
|
+
} from '@/lib/agent-execute-defaults'
|
|
29
|
+
|
|
30
|
+
const TAG = 'execute'
|
|
31
|
+
|
|
32
|
+
export type ExecuteConfig = AgentExecuteConfig
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Sandbox backend — just-bash with OverlayFS
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
async function executeSandbox(
|
|
39
|
+
code: string,
|
|
40
|
+
cwd: string,
|
|
41
|
+
config: ExecuteConfig,
|
|
42
|
+
signal?: AbortSignal,
|
|
43
|
+
): Promise<{ stdout: string; stderr: string; exit_code: number }> {
|
|
44
|
+
// Dynamic import to avoid loading just-bash when not needed
|
|
45
|
+
const { Bash, OverlayFs } = await import('just-bash')
|
|
46
|
+
|
|
47
|
+
const fs = new OverlayFs({
|
|
48
|
+
root: cwd,
|
|
49
|
+
mountPoint: '/workspace',
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const timeoutMs = (config.timeout ?? DEFAULT_AGENT_EXECUTE_CONFIG.timeout ?? 30) * 1000
|
|
53
|
+
|
|
54
|
+
// Build credential env vars
|
|
55
|
+
const { env: credEnv, secrets } = buildCredentialEnv(config.credentials ?? [])
|
|
56
|
+
|
|
57
|
+
// Base env vars
|
|
58
|
+
const env: Record<string, string> = {
|
|
59
|
+
HOME: '/home/user',
|
|
60
|
+
WORKSPACE: '/workspace',
|
|
61
|
+
PATH: '/usr/local/bin:/usr/bin:/bin',
|
|
62
|
+
TERM: 'xterm-256color',
|
|
63
|
+
...credEnv,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Network configuration
|
|
67
|
+
let network: Record<string, unknown> | undefined
|
|
68
|
+
if (config.network?.enabled) {
|
|
69
|
+
if (config.network.allowedUrls?.length) {
|
|
70
|
+
network = {
|
|
71
|
+
allowedUrls: config.network.allowedUrls.map((url: string) => ({
|
|
72
|
+
url,
|
|
73
|
+
methods: ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'],
|
|
74
|
+
})),
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
// Full internet access
|
|
78
|
+
network = { allowedUrls: [{ url: '*', methods: ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'] }] }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const bash = new Bash({
|
|
83
|
+
fs,
|
|
84
|
+
env,
|
|
85
|
+
cwd: '/workspace',
|
|
86
|
+
executionLimits: {
|
|
87
|
+
maxCommandCount: 1000,
|
|
88
|
+
maxLoopIterations: 10000,
|
|
89
|
+
maxCallDepth: 50,
|
|
90
|
+
},
|
|
91
|
+
python: config.runtimes?.python ?? false,
|
|
92
|
+
javascript: config.runtimes?.javascript ?? false,
|
|
93
|
+
...(network ? { network: network as Record<string, unknown> } : {}),
|
|
94
|
+
defenseInDepth: true,
|
|
95
|
+
} as Record<string, unknown>)
|
|
96
|
+
|
|
97
|
+
// Execute with timeout
|
|
98
|
+
const controller = new AbortController()
|
|
99
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs)
|
|
100
|
+
|
|
101
|
+
// Compose parent signal with our timeout
|
|
102
|
+
if (signal?.aborted) {
|
|
103
|
+
clearTimeout(timer)
|
|
104
|
+
return { stdout: '', stderr: 'Execution cancelled', exit_code: 130 }
|
|
105
|
+
}
|
|
106
|
+
signal?.addEventListener('abort', () => {
|
|
107
|
+
clearTimeout(timer)
|
|
108
|
+
controller.abort()
|
|
109
|
+
}, { once: true })
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const result = await bash.exec(code, {
|
|
113
|
+
signal: controller.signal,
|
|
114
|
+
})
|
|
115
|
+
clearTimeout(timer)
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
stdout: redactSecrets(result.stdout, secrets),
|
|
119
|
+
stderr: redactSecrets(result.stderr, secrets),
|
|
120
|
+
exit_code: result.exitCode,
|
|
121
|
+
}
|
|
122
|
+
} catch (err: unknown) {
|
|
123
|
+
clearTimeout(timer)
|
|
124
|
+
const msg = errorMessage(err)
|
|
125
|
+
if (msg.includes('abort') || msg.includes('cancel') || msg.includes('Timeout')) {
|
|
126
|
+
return { stdout: '', stderr: `Execution timed out after ${config.timeout ?? DEFAULT_AGENT_EXECUTE_CONFIG.timeout ?? 30}s`, exit_code: 124 }
|
|
127
|
+
}
|
|
128
|
+
return { stdout: '', stderr: `Execution error: ${redactSecrets(msg, secrets)}`, exit_code: 1 }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// Host backend — real bash via child_process
|
|
134
|
+
//
|
|
135
|
+
// NOTE: This intentionally spawns bash to execute agent code. This is the
|
|
136
|
+
// same pattern used by the existing shell.ts tool. Agent code execution is
|
|
137
|
+
// the explicit purpose of this tool — this is not a command injection risk.
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
async function executeHost(
|
|
141
|
+
code: string,
|
|
142
|
+
cwd: string,
|
|
143
|
+
config: ExecuteConfig,
|
|
144
|
+
): Promise<{ stdout: string; stderr: string; exit_code: number }> {
|
|
145
|
+
const timeoutMs = (config.timeout ?? DEFAULT_AGENT_EXECUTE_CONFIG.timeout ?? 30) * 1000
|
|
146
|
+
|
|
147
|
+
// Build credential env vars
|
|
148
|
+
const { env: credEnv, secrets } = buildCredentialEnv(config.credentials ?? [])
|
|
149
|
+
|
|
150
|
+
const env: Record<string, string> = {
|
|
151
|
+
...Object.fromEntries(
|
|
152
|
+
Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] != null),
|
|
153
|
+
),
|
|
154
|
+
WORKSPACE: cwd,
|
|
155
|
+
SESSION_CWD: cwd,
|
|
156
|
+
SWARMCLAW_SANDBOX_MODE: 'host',
|
|
157
|
+
...credEnv,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return new Promise((resolve) => {
|
|
161
|
+
const proc: ChildProcess = spawn('/bin/bash', ['-c', code], {
|
|
162
|
+
cwd,
|
|
163
|
+
env: env as NodeJS.ProcessEnv,
|
|
164
|
+
timeout: timeoutMs,
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
let stdout = ''
|
|
168
|
+
let stderr = ''
|
|
169
|
+
const maxBytes = MAX_OUTPUT
|
|
170
|
+
|
|
171
|
+
proc.stdout?.on('data', (chunk: Buffer) => {
|
|
172
|
+
if (stdout.length < maxBytes) stdout += chunk.toString('utf-8')
|
|
173
|
+
})
|
|
174
|
+
proc.stderr?.on('data', (chunk: Buffer) => {
|
|
175
|
+
if (stderr.length < maxBytes) stderr += chunk.toString('utf-8')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
proc.on('close', (exitCode: number | null) => {
|
|
179
|
+
resolve({
|
|
180
|
+
stdout: redactSecrets(stdout, secrets),
|
|
181
|
+
stderr: redactSecrets(stderr, secrets),
|
|
182
|
+
exit_code: exitCode ?? 1,
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
proc.on('error', (err: Error) => {
|
|
187
|
+
resolve({
|
|
188
|
+
stdout: redactSecrets(stdout, secrets),
|
|
189
|
+
stderr: redactSecrets(`Process error: ${err.message}`, secrets),
|
|
190
|
+
exit_code: 1,
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
// Main execute action
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
interface ExecuteActionContext {
|
|
201
|
+
cwd: string
|
|
202
|
+
agentId?: string | null
|
|
203
|
+
sessionId?: string | null
|
|
204
|
+
executeConfig?: ExecuteConfig | null
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function executeAction(
|
|
208
|
+
args: Record<string, unknown>,
|
|
209
|
+
ctx: ExecuteActionContext,
|
|
210
|
+
): Promise<string> {
|
|
211
|
+
const normalized = normalizeToolInputArgs(args)
|
|
212
|
+
const code = (normalized.code as string | undefined)?.trim()
|
|
213
|
+
|
|
214
|
+
if (!code) {
|
|
215
|
+
return 'Error: `code` parameter is required. Provide the bash script to execute.'
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const config = normalizeAgentExecuteConfig(ctx.executeConfig)
|
|
219
|
+
|
|
220
|
+
const persistent = normalized.persistent === true
|
|
221
|
+
const timeoutOverride = typeof normalized.timeout === 'number' ? normalized.timeout : undefined
|
|
222
|
+
if (timeoutOverride) {
|
|
223
|
+
config.timeout = Math.min(Math.max(timeoutOverride, 1), 300) // Clamp 1-300s
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (persistent && config.backend !== 'host') {
|
|
227
|
+
return 'Error: `persistent=true` requires `executeConfig.backend = "host"` for this agent.'
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
log.info(TAG, `Executing code (backend=${config.backend}, persistent=${persistent})`, {
|
|
231
|
+
agentId: ctx.agentId,
|
|
232
|
+
sessionId: ctx.sessionId,
|
|
233
|
+
codeLength: code.length,
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
let result: { stdout: string; stderr: string; exit_code: number }
|
|
238
|
+
|
|
239
|
+
if (config.backend === 'host') {
|
|
240
|
+
// Host backend for persistent mode or explicit host config
|
|
241
|
+
result = await executeHost(code, ctx.cwd, config)
|
|
242
|
+
} else {
|
|
243
|
+
// Sandbox backend (default)
|
|
244
|
+
result = await executeSandbox(code, ctx.cwd, config)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Format output
|
|
248
|
+
const parts: string[] = []
|
|
249
|
+
if (result.stdout) parts.push(result.stdout)
|
|
250
|
+
if (result.stderr) parts.push(`[stderr] ${result.stderr}`)
|
|
251
|
+
if (result.exit_code !== 0) parts.push(`[exit code: ${result.exit_code}]`)
|
|
252
|
+
|
|
253
|
+
const output = parts.join('\n') || '(no output)'
|
|
254
|
+
return truncate(output, MAX_OUTPUT)
|
|
255
|
+
} catch (err: unknown) {
|
|
256
|
+
return `Error: ${errorMessage(err)}`
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
// Extension registration
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
|
|
264
|
+
const ExecuteExtension: Extension = {
|
|
265
|
+
name: 'Core Execute',
|
|
266
|
+
description: 'Execute code in a sandboxed or host bash environment with credential injection and secret redaction.',
|
|
267
|
+
hooks: {
|
|
268
|
+
getCapabilityDescription: () =>
|
|
269
|
+
'I can execute bash scripts with the `execute` tool. ' +
|
|
270
|
+
'By default, code runs in a sandboxed environment (just-bash) that reads workspace files but keeps writes in memory. ' +
|
|
271
|
+
'For tasks requiring persistent writes, npm, or git, the agent can be configured to use the host backend. ' +
|
|
272
|
+
'Credentials are injected as environment variables and automatically redacted from output.',
|
|
273
|
+
getOperatingGuidance: () =>
|
|
274
|
+
'Use `execute` for: data processing (jq, awk, sed), API calls (curl), file inspection (cat, grep, find), ' +
|
|
275
|
+
'computation, and any CLI tool. ' +
|
|
276
|
+
'In sandbox mode, writes are ephemeral — use the `files` tool for persistent file changes. ' +
|
|
277
|
+
'In host mode, writes persist to the real filesystem.',
|
|
278
|
+
} as ExtensionHooks,
|
|
279
|
+
tools: [
|
|
280
|
+
{
|
|
281
|
+
name: 'execute',
|
|
282
|
+
description:
|
|
283
|
+
'Execute a bash script. Supports curl, jq, awk, sed, grep, and 70+ Unix commands. ' +
|
|
284
|
+
'Credentials are injected as environment variables (e.g., $API_KEY). ' +
|
|
285
|
+
'By default runs sandboxed — workspace files are readable, writes stay in memory. ' +
|
|
286
|
+
'Set persistent=true for real filesystem writes only when the agent is configured for host execution.',
|
|
287
|
+
parameters: {
|
|
288
|
+
type: 'object',
|
|
289
|
+
properties: {
|
|
290
|
+
code: { type: 'string', description: 'The bash script to execute' },
|
|
291
|
+
persistent: { type: 'boolean', description: 'Use host backend for persistent writes (default: false)' },
|
|
292
|
+
timeout: { type: 'number', description: 'Timeout in seconds (default: 30, max: 300)' },
|
|
293
|
+
},
|
|
294
|
+
required: ['code'],
|
|
295
|
+
},
|
|
296
|
+
execute: async (args, context) =>
|
|
297
|
+
executeAction(args as Record<string, unknown>, {
|
|
298
|
+
cwd: context.session?.cwd || process.cwd(),
|
|
299
|
+
}),
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
registerNativeCapability('execute', ExecuteExtension)
|
|
305
|
+
|
|
306
|
+
// ---------------------------------------------------------------------------
|
|
307
|
+
// Tool builder (called from session-tools/index.ts)
|
|
308
|
+
// ---------------------------------------------------------------------------
|
|
309
|
+
|
|
310
|
+
export function buildExecuteTools(bctx: ToolBuildContext) {
|
|
311
|
+
if (!bctx.hasExtension('execute')) return []
|
|
312
|
+
|
|
313
|
+
// Resolve execute config from the agent
|
|
314
|
+
const session = bctx.resolveCurrentSession?.()
|
|
315
|
+
const agent = session?.agent as (Agent & { executeConfig?: ExecuteConfig }) | undefined
|
|
316
|
+
const executeConfig = normalizeAgentExecuteConfig(agent?.executeConfig)
|
|
317
|
+
|
|
318
|
+
return [
|
|
319
|
+
tool(
|
|
320
|
+
async (args) =>
|
|
321
|
+
executeAction(args, {
|
|
322
|
+
cwd: bctx.cwd,
|
|
323
|
+
agentId: bctx.ctx?.agentId,
|
|
324
|
+
sessionId: bctx.ctx?.sessionId,
|
|
325
|
+
executeConfig,
|
|
326
|
+
}),
|
|
327
|
+
{
|
|
328
|
+
name: 'execute',
|
|
329
|
+
description: ExecuteExtension.tools![0].description,
|
|
330
|
+
schema: z.object({}).passthrough(),
|
|
331
|
+
},
|
|
332
|
+
),
|
|
333
|
+
]
|
|
334
|
+
}
|