Exec Tool: Shell Commands, Stdin Modes, and TTY Support

Learn how to use the Exec tool to run shell commands within your workspace, including stdin modes, TTY support, and foreground/background execution. Essential for developers automating tasks with Neura Market.

Read this when

  • Using or modifying the exec tool
  • Debugging stdin or TTY behavior

Execute shell commands within the workspace. exec is a shell surface that mutates state: commands can create, edit, or delete files wherever the chosen host or sandbox filesystem allows. Disabling OpenClaw filesystem tools like write, edit, or apply_patch does not make exec read-only.

Foreground and background execution are supported through process. When process is not permitted, exec runs synchronously and disregards yieldMs/background. Background sessions are scoped per agent; process can only see sessions from the same agent.

Parameters

  • command (string, required), The shell command to execute.

  • workdir (string, default: cwd), Sets the working directory for the command.

  • env (object), Environment variable overrides as key/value pairs, merged on top of the inherited environment.

  • yieldMs (number, default: 10000), Automatically backgrounds the command after this delay in milliseconds.

  • background (boolean, default: false), Backgrounds the command right away instead of waiting for yieldMs.

  • timeout (number, default: tools.exec.timeoutSeconds), Overrides the configured exec timeout for this call in seconds. Applies to foreground, background, yieldMs, gateway, sandbox, and node system.run execution. timeout: 0 disables the exec process timeout for that call.

  • pty (boolean, default: false), Runs inside a pseudo-terminal when one is available. Useful for TTY-only CLIs, coding agents, and terminal UIs.

  • host (auto' | 'sandbox' | 'gateway' | 'node, default: auto), Determines the execution location. auto resolves to sandbox when a sandbox runtime is active, and to gateway otherwise.

  • security (deny' | 'allowlist' | 'full), Ignored for normal tool calls. gateway/node security is derived from tools.exec.mode and the host approvals file; elevated mode can enforce full access only when the operator explicitly grants elevated access.

  • ask (off' | 'on-miss' | 'always), The baseline ask mode comes from tools.exec.mode and host approvals. For channel-origin model calls, per-call ask is ignored when the effective host ask is off; otherwise it can only increase to a stricter mode.

  • node (string), Node id or name when host=node.

  • elevated (boolean, default: false), Requests elevated mode to escape the sandbox onto the configured host path. security=full is forced only when elevated resolves to full.

Notes:

  • The only accepted values for host are auto, sandbox, gateway, or node. It does not function as a hostname selector; anything that looks like a hostname is refused before execution begins.
  • Per-invocation host=node is permitted from auto; per-invocation host=gateway is permitted only when no sandbox runtime is active.
  • Without additional configuration, host=auto still works out of the box: when no sandbox is present it defaults to gateway; when a sandbox is active it remains inside that sandbox.
  • elevated breaks out of the sandbox onto the configured host path: gateway by default, or node when tools.exec.host=node is set (or the session default is host=node). It is available only when elevated access is enabled for the current session or provider.
  • The host approvals file governs gateway and node approvals.
  • node needs a paired node, either a companion app or a headless node host. When multiple nodes exist, pick one by setting exec.node or tools.exec.node.
  • exec host=node is the sole shell-execution path for nodes; the old nodes.run wrapper has been removed.
  • On non-Windows hosts, exec uses SHELL when that variable is defined; if SHELL is fish, it first tries bash (or sh) from PATH to avoid bashisms that break fish, then falls back to SHELL if neither is found.
  • On Windows hosts, exec looks for PowerShell 7 (pwsh) by checking Program Files, ProgramW6432, then PATH, and falls back to Windows PowerShell 5.1.
  • On non-Windows gateway hosts, bash and zsh exec commands use a startup snapshot. OpenClaw captures sourceable aliases, functions, and a small safe environment set from shell startup files into $OPENCLAW_STATE_DIR/cache/shell-snapshots/, then sources that snapshot before each exec command. Variables that look secret are excluded; sandbox and node exec do not use this snapshot. To disable this snapshot path, set OPENCLAW_EXEC_SHELL_SNAPSHOT=0 in the Gateway process environment.
  • Host execution (gateway or node) rejects env.PATH and loader overrides (LD_* or DYLD_*) to prevent binary hijacking or injected code.
  • OpenClaw sets OPENCLAW_SHELL=exec in the spawned command environment (including PTY and sandbox execution) so that shell or profile rules can detect the exec-tool context.
  • For channel-origin runs, OpenClaw also exposes a narrow sender and chat identity JSON payload in OPENCLAW_CHANNEL_CONTEXT when the channel provided those identifiers.
  • exec cannot run openclaw channels login or /approve shell commands: openclaw channels login is an interactive channel-auth flow, and /approve must go through the approval command handler, not a shell. Run channel login in a terminal on the gateway host, or use a channel-specific login agent tool if one exists (for example whatsapp_login).
  • Important: sandboxing is off by default. When sandboxing is off, implicit host=auto resolves to gateway. Explicit host=sandbox still fails closed instead of silently running on the gateway host. Either enable sandboxing or use host=gateway with approvals.
  • Script preflight checks (for common Python and Node shell-syntax mistakes) inspect only files inside the effective workdir boundary. If a script path resolves outside workdir, preflight is skipped for that file. Preflight is also skipped entirely when host=gateway is set and the effective policy is security=full with ask=off.
  • For long-running work that starts now, start it once and rely on automatic completion wake when it is enabled and the command produces output or fails. Use process for logs, status, input, or intervention; do not emulate scheduling with sleep loops, timeout loops, or repeated polling.
  • Agent-started background commands appear in the Web, iOS, and Android background-task views until they finish. The task ledger is finalized before the completion heartbeat wakes the agent again.
  • For work that should happen later or on a schedule, use cron instead of exec sleep or delay patterns.

Config

KeyDefaultNotes
tools.exec.timeoutSeconds1800The default timeout for each command, measured in seconds. A per-call timeout takes precedence; setting a per-call timeout: 0 removes the exec process timeout entirely.
tools.exec.hostautoEvaluates to sandbox when a sandbox runtime is present, otherwise to gateway.
tools.exec.modehost-derivedThe primary policy setting. Refer to Modes below.
tools.exec.reviewer.modelconfigured agent primaryAn optional provider or model override used for mode=auto review.
tools.exec.reviewer.timeoutMs30000Time limit per stage for reviewer model setup and completion before falling back to a human.
tools.exec.nodeunset
tools.exec.notifyOnExittrueIf enabled, backgrounded exec sessions queue a system event and ask for a heartbeat once they exit.
tools.exec.approvalRunningNoticeMs10000Emit a single "running" notification when an approval-gated exec exceeds this duration (0 turns it off).
tools.exec.strictInlineEvalfalseConsult Inline eval.
tools.exec.commandHighlightingfalseWhen active, approval prompts can highlight parser-derived command spans within the command text. Set globally or per agent; it does not alter approval policy.
tools.exec.pathPrependunsetDirectories to add at the front of PATH for exec runs (gateway and sandbox only).
tools.exec.safeBinsunsetStdin-only safe binaries that do not require an explicit allowlist entry. See Safe bins.
tools.exec.safeBinTrustedDirs/bin, /usr/binAdditional directories explicitly trusted during safeBins path checks. PATH entries are never auto-trusted.
tools.exec.safeBinProfilesunsetOptional custom argv policy per safe bin (minPositional, maxPositional, allowedValueFlags, deniedFlags).

For gateway and node (mode=full), no-approval host exec is the default. This behavior comes from the host-policy defaults, not from host=auto. To enable approvals or allowlist behavior, configure tools.exec.mode and tighten the host approvals file; see Exec approvals. To force gateway or node routing regardless of sandbox state, set tools.exec.host or use /exec host=....

Example:

{
  tools: {
    exec: {
      pathPrepend: ["~/bin", "/opt/oss/bin"],
    },
  },
}

Modes

tools.exec.mode serves as the canonical persisted policy knob. Runtime security and approval behavior are derived from it.

ModesecurityaskBehavior
denydenyoffExec requests are blocked.
allowlistallowlistoffOnly commands on the allowlist or safe-bin list execute; no prompts appear for anything else.
askallowliston-missCommands matching the allowlist run directly; anything not on it triggers a human prompt.
autoallowliston-missAllowlisted and safe-bin commands execute directly; all others pass through OpenClaw's native auto reviewer before a human is asked.
fullfulloffNo approval step is enforced.

Regardless of the persisted mode, /exec ask=always always prompts a human on a per-session basis.

Auto-review approval applies to a single use only. On the gateway, OpenClaw provides the reviewer with the resolved executable path and locks execution to that same path. Commands that cannot be distilled into a single enforceable execution plan, like heredocs, shell expansions, or unsupported wrapper quoting, fall back to human approval, even if the model would otherwise permit them.

Codex app-server command approvals that are not already decided by explicit runtime or native policy use the human approval route. OpenClaw does not run its configured exec reviewer for these requests because Codex does not expose an enforceable resolved executable that can bind the review decision to the command Codex runs.

Inline eval (strictInlineEval)

When tools.exec.strictInlineEval is set to true, inline interpreter-eval forms require reviewer or explicit approval: python -c, node -e, ruby -e, perl -e, php -r, lua -e, osascript -e, and similar forms across other supported interpreters and command carriers (awk, find -exec, make, sed, xargs, and more). In mode=auto, the normal exec approval path may let the native auto reviewer allow a clearly low-risk one-off command; direct node-host system.run calls still require an explicit approval because they cannot hand the command to a human approval route. If the reviewer asks, the request goes to a human. allow-always can still persist benign interpreter/script invocations, but inline-eval forms do not become durable allow rules.

PATH handling

  • host=gateway: merges your login-shell PATH into the exec environment. env.PATH overrides are rejected for host execution. The daemon itself still runs with a minimal PATH:
    • macOS: /opt/homebrew/bin, /usr/local/bin, /usr/bin, /bin
    • Linux: /usr/local/bin, /usr/bin, /bin
    • To prevent user shell configuration (like ~/.zshenv or /etc/zshenv) from overriding priority paths during startup, tools.exec.pathPrepend entries are securely prepended to the final PATH inside the shell command right before execution.
  • host=sandbox: runs sh -lc (login shell) inside the container, so /etc/profile may reset PATH. OpenClaw prepends env.PATH after profile sourcing via an internal env var (no shell interpolation); tools.exec.pathPrepend applies here too.
  • host=node: only non-blocked env overrides you pass are sent to the node. env.PATH overrides are rejected for host execution and ignored by node hosts. If you need additional PATH entries on a node, configure the node host service environment (systemd/launchd) or install tools in standard locations.

Per-agent node binding (use the keyed agent ID in config):

openclaw config get agents.entries
openclaw config set 'agents.entries.main.tools.exec.node' "node-id-or-name"

Control UI: the Devices page includes a small "Exec node binding" panel for the same settings.

Session overrides (/exec)

Use /exec to define per-session defaults for host, security, ask, and node. Call /exec without any arguments to display the current values.

Example:

/exec host=auto security=allowlist ask=on-miss node=mac-1

/exec applies only to authorized senders via channel allowlists, pairing, and access groups. Access-group enforcement is always active. This updates session state exclusively and does not modify configuration. Authorized external channel senders can establish these session defaults. Internal gateway and webchat clients require operator.admin to make them permanent.

To completely disable exec, deny it through tool policy (tools.deny: ["exec"] or per-agent). Host approvals still take effect unless you explicitly configure security=full and ask=off.

Exec approvals (companion app / node host)

Sandboxed agents can demand per-request approval before exec executes on the gateway or node host. Refer to Exec approvals for the policy, allowlist, and UI workflow.

When human approval is necessary, node-host and non-native gateway flows immediately return status: "approval-pending" along with an approval id. Native chat and Web UI gateway flows can instead wait inline and deliver the final command result after approval. An approval-pending result indicates the command has not started, so foreground fallback warnings appear only if the approved command actually runs inline. Approved asynchronous runs emit command progress and completion system events (Exec running / Exec finished); denied or timed-out approvals are terminal and do not send a denial system event to wake the agent session.

On channels that offer native approval cards or buttons, the agent should prioritize that native UI first and include a manual /approve command only when the tool result explicitly states that chat approvals are unavailable or manual approval is the only option.

Allowlist + safe bins

Manual allowlist enforcement matches resolved binary path globs and bare command-name globs. Bare names match only commands invoked through PATH, so rg can match /opt/homebrew/bin/rg when the command is rg, but not ./rg or /tmp/rg.

When security=allowlist, shell commands are auto-allowed only if every pipeline segment is allowlisted or qualifies as a safe bin. Chaining (;, &&, ||) and redirections are rejected in allowlist mode unless every top-level segment satisfies the allowlist (including safe bins). Redirections remain unsupported. Durable allow-always trust does not bypass this rule: a chained command still requires every top-level segment to match.

autoAllowSkills is a separate convenience path within exec approvals, distinct from manual path allowlist entries. For strict explicit trust, keep autoAllowSkills disabled.

Use the two controls for different purposes:

  • tools.exec.safeBins: small, stdin-only stream filters.
  • tools.exec.safeBinTrustedDirs: explicit extra trusted directories for safe-bin executable paths.
  • tools.exec.safeBinProfiles: explicit argv policy for custom safe bins.
  • allowlist: explicit trust for executable paths.

Do not treat safeBins as a generic allowlist, and do not add interpreter or runtime binaries (for example python3, node, ruby, bash). If those are needed, use explicit allowlist entries and keep approval prompts enabled.

openclaw security audit warns when interpreter or runtime safeBins entries lack explicit profiles, and openclaw doctor --fix can scaffold missing custom safeBinProfiles entries. openclaw security audit and openclaw doctor also warn when you explicitly add broad-behavior binaries such as jq back into safeBins (jq can read environment data and load jq code from modules or startup files, so prefer explicit allowlist entries or approval-gated runs instead). jq is denied as a safe bin even when explicitly listed. If you explicitly allowlist interpreters, enable tools.exec.strictInlineEval so inline code-eval forms still require reviewer or explicit approval.

For complete policy details and examples, see Exec approvals and Safe bins versus allowlist.

Examples

Foreground:

{ "tool": "exec", "command": "ls -la" }

Background plus poll:

{"tool":"exec","command":"npm run build","yieldMs":1000}
{"tool":"process","action":"poll","sessionId":"<id>"}

Polling is for on-demand status, not for waiting loops. If automatic completion wake is enabled, the command can wake the session when it emits output or fails.

Send keys (tmux-style):

{"tool":"process","action":"send-keys","sessionId":"<id>","keys":["Enter"]}
{"tool":"process","action":"send-keys","sessionId":"<id>","keys":["C-c"]}
{"tool":"process","action":"send-keys","sessionId":"<id>","keys":["Up","Up","Enter"]}

Submit (send CR only):

{ "tool": "process", "action": "submit", "sessionId": "<id>" }

Paste (bracketed by default):

{ "tool": "process", "action": "paste", "sessionId": "<id>", "text": "line1\nline2\n" }

apply_patch

The apply_patch subtool of exec handles structured modifications across multiple files. It activates automatically and works with any model provider, though allowModels offers a way to limit its use. Only modify the config if you need to turn it off or limit it to particular models:

{
  tools: {
    exec: {
      applyPatch: { workspaceOnly: true, allowModels: ["gpt-5.6-sol"] },
    },
  },
}

Additional details:

  • Tool policy remains in effect; allow: ["write"] grants implicit permission for apply_patch.
  • deny: ["write"] does not block apply_patch; to prevent patch writes, explicitly deny apply_patch or apply deny: ["group:fs"].
  • Configuration resides under tools.exec.applyPatch.
  • By default, tools.exec.applyPatch.enabled is set to true; switching it to false deactivates the tool.
  • The default for tools.exec.applyPatch.workspaceOnly is true, which restricts operations to the workspace. Only assign false when you deliberately need apply_patch to create or delete files outside the workspace directory.
  • tools.exec.applyPatch.allowModels acts as an optional allowlist for model identifiers (either short, like gpt-5.4, or complete, like openai/gpt-5.4). When populated, only the listed models receive the tool; when empty, every model gets access.