feat: add apply_patch tool (exec-gated)

This commit is contained in:
Peter Steinberger
2026-01-12 03:42:49 +00:00
parent 221c0b4cf8
commit 8b4bdaa8a4
25 changed files with 1055 additions and 41 deletions

View File

@@ -45,7 +45,10 @@ To disable bootstrap file creation entirely (for pre-seeded workspaces), set:
## Built-in tools
Core tools (read/exec/edit/write and related system tools) are always available. `TOOLS.md` does **not** control which tools exist; its guidance for how *you* want them used.
Core tools (read/exec/edit/write and related system tools) are always available,
subject to tool policy. `apply_patch` is optional and gated by
`tools.exec.applyPatch`. `TOOLS.md` does **not** control which tools exist; its
guidance for how *you* want them used.
## Skills

View File

@@ -217,7 +217,7 @@ Starting with v2026.1.6, each agent can have its own sandbox and tool restrictio
},
tools: {
allow: ["read"], // Only read tool
deny: ["exec", "write", "edit"], // Deny others
deny: ["exec", "write", "edit", "apply_patch"], // Deny others
},
},
],

View File

@@ -701,6 +701,7 @@
"tools",
"plugin",
"tools/exec",
"tools/apply-patch",
"tools/elevated",
"tools/browser",
"tools/browser-linux-troubleshooting",

View File

@@ -259,7 +259,7 @@ Save to `~/.clawdbot/clawdbot.json` and you can DM the bot from that number.
},
tools: {
allow: ["exec", "process", "read", "write", "edit"],
allow: ["exec", "process", "read", "write", "edit", "apply_patch"],
deny: ["browser", "canvas"],
exec: {
backgroundMs: 10000,

View File

@@ -638,7 +638,7 @@ Read-only tools + read-only workspace:
},
tools: {
allow: ["read", "sessions_list", "sessions_history", "sessions_send", "sessions_spawn", "session_status"],
deny: ["write", "edit", "exec", "process", "browser"]
deny: ["write", "edit", "apply_patch", "exec", "process", "browser"]
}
}
]
@@ -661,7 +661,7 @@ No filesystem access (messaging/session tools enabled):
},
tools: {
allow: ["sessions_list", "sessions_history", "sessions_send", "sessions_spawn", "session_status", "whatsapp", "telegram", "slack", "discord", "gateway"],
deny: ["read", "write", "edit", "exec", "process", "browser", "canvas", "nodes", "cron", "gateway", "image"]
deny: ["read", "write", "edit", "apply_patch", "exec", "process", "browser", "canvas", "nodes", "cron", "gateway", "image"]
}
}
]
@@ -1431,6 +1431,9 @@ of `every`, keep `HEARTBEAT.md` tiny, and/or choose a cheaper `model`.
- `backgroundMs`: time before auto-background (ms, default 10000)
- `timeoutSec`: auto-kill after this runtime (seconds, default 1800)
- `cleanupMs`: how long to keep finished sessions in memory (ms, default 1800000)
- `applyPatch.enabled`: enable experimental `apply_patch` (OpenAI/OpenAI Codex only; default false)
- `applyPatch.allowModels`: optional allowlist of model ids (e.g. `gpt-5.2` or `openai/gpt-5.2`)
Note: `applyPatch` is only under `tools.exec` (no `tools.bash` alias).
Legacy: `tools.bash` is still accepted as an alias.
`agents.defaults.subagents` configures sub-agent defaults:
@@ -1511,10 +1514,10 @@ Defaults (if enabled):
- Debian bookworm-slim based image
- agent workspace access: `workspaceAccess: "none"` (default)
- `"none"`: use a per-scope sandbox workspace under `~/.clawdbot/sandboxes`
- `"ro"`: keep the sandbox workspace at `/workspace`, and mount the agent workspace read-only at `/agent` (disables `write`/`edit`)
- `"ro"`: keep the sandbox workspace at `/workspace`, and mount the agent workspace read-only at `/agent` (disables `write`/`edit`/`apply_patch`)
- `"rw"`: mount the agent workspace read/write at `/workspace`
- auto-prune: idle > 24h OR age > 7d
- tool policy: allow only `exec`, `process`, `read`, `write`, `edit`, `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`, `session_status` (deny wins)
- tool policy: allow only `exec`, `process`, `read`, `write`, `edit`, `apply_patch`, `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`, `session_status` (deny wins)
- configure via `tools.sandbox.tools`, override per-agent via `agents.list[].tools.sandbox.tools`
- optional sandboxed browser (Chromium + CDP, noVNC observer)
- hardening knobs: `network`, `user`, `pidsLimit`, `memory`, `cpus`, `ulimits`, `seccompProfile`, `apparmorProfile`
@@ -1585,7 +1588,7 @@ Legacy: `perSession` is still supported (`true` → `scope: "session"`,
tools: {
sandbox: {
tools: {
allow: ["exec", "process", "read", "write", "edit", "sessions_list", "sessions_history", "sessions_send", "sessions_spawn", "session_status"],
allow: ["exec", "process", "read", "write", "edit", "apply_patch", "sessions_list", "sessions_history", "sessions_send", "sessions_spawn", "session_status"],
deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"]
}
}

View File

@@ -17,7 +17,7 @@ This is not a perfect security boundary, but it materially limits filesystem
and process access when the model does something dumb.
## What gets sandboxed
- Tool execution (`exec`, `read`, `write`, `edit`, `process`, etc.).
- Tool execution (`exec`, `read`, `write`, `edit`, `apply_patch`, `process`, etc.).
- Optional sandboxed browser (`agents.defaults.sandbox.browser`).
- By default, the sandbox browser auto-starts (ensures CDP is reachable) when the browser tool needs it.
Configure via `agents.defaults.sandbox.browser.autoStart` and `agents.defaults.sandbox.browser.autoStartTimeoutMs`.
@@ -47,7 +47,7 @@ Group/channel sessions use their own keys, so they count as non-main and will be
## Workspace access
`agents.defaults.sandbox.workspaceAccess` controls **what the sandbox can see**:
- `"none"` (default): tools see a sandbox workspace under `~/.clawdbot/sandboxes`.
- `"ro"`: mounts the agent workspace read-only at `/agent` (disables `write`/`edit`).
- `"ro"`: mounts the agent workspace read-only at `/agent` (disables `write`/`edit`/`apply_patch`).
- `"rw"`: mounts the agent workspace read/write at `/workspace`.
Inbound media is copied into the active sandbox workspace (`media/inbound/*`).

View File

@@ -184,7 +184,7 @@ Consider running your AI on a separate phone number from your personal one:
You can already build a read-only profile by combining:
- `agents.defaults.sandbox.workspaceAccess: "ro"` (or `"none"` for no workspace access)
- tool allow/deny lists that block `write`, `edit`, `exec`, `process`, etc.
- tool allow/deny lists that block `write`, `edit`, `apply_patch`, `exec`, `process`, etc.
We may add a single `readOnlyMode` flag later to simplify this configuration.
@@ -203,7 +203,7 @@ single container/workspace.
Also consider agent workspace access inside the sandbox:
- `agents.defaults.sandbox.workspaceAccess: "none"` (default) keeps the agent workspace off-limits; tools run against a sandbox workspace under `~/.clawdbot/sandboxes`
- `agents.defaults.sandbox.workspaceAccess: "ro"` mounts the agent workspace read-only at `/agent` (disables `write`/`edit`)
- `agents.defaults.sandbox.workspaceAccess: "ro"` mounts the agent workspace read-only at `/agent` (disables `write`/`edit`/`apply_patch`)
- `agents.defaults.sandbox.workspaceAccess: "rw"` mounts the agent workspace read/write at `/workspace`
Important: `tools.elevated` is the global baseline escape hatch that runs exec on the host. Keep `tools.elevated.allowFrom` tight and dont enable it for strangers. You can further restrict elevated per agent via `agents.list[].tools.elevated`. See [Elevated Mode](/tools/elevated).
@@ -261,7 +261,7 @@ Common use cases:
},
tools: {
allow: ["read"],
deny: ["write", "edit", "exec", "process", "browser"]
deny: ["write", "edit", "apply_patch", "exec", "process", "browser"]
}
}
]
@@ -285,7 +285,7 @@ Common use cases:
},
tools: {
allow: ["sessions_list", "sessions_history", "sessions_send", "sessions_spawn", "session_status", "whatsapp", "telegram", "slack", "discord", "gateway"],
deny: ["read", "write", "edit", "exec", "process", "browser", "canvas", "nodes", "cron", "gateway", "image"]
deny: ["read", "write", "edit", "apply_patch", "exec", "process", "browser", "canvas", "nodes", "cron", "gateway", "image"]
}
}
]

View File

@@ -246,7 +246,7 @@ precedence, and troubleshooting.
- Image: `clawdbot-sandbox:bookworm-slim`
- One container per agent
- Agent workspace access: `workspaceAccess: "none"` (default) uses `~/.clawdbot/sandboxes`
- `"ro"` keeps the sandbox workspace at `/workspace` and mounts the agent workspace read-only at `/agent` (disables `write`/`edit`)
- `"ro"` keeps the sandbox workspace at `/workspace` and mounts the agent workspace read-only at `/agent` (disables `write`/`edit`/`apply_patch`)
- `"rw"` mounts the agent workspace read/write at `/workspace`
- Auto-prune: idle > 24h OR age > 7d
- Network: `none` by default (explicitly opt-in if you need egress)
@@ -424,7 +424,7 @@ Example:
### Security notes
- Hard wall only applies to **tools** (exec/read/write/edit).
- Hard wall only applies to **tools** (exec/read/write/edit/apply_patch).
- Host-only tools like browser/camera/canvas are blocked by default.
- Allowing `browser` in sandbox **breaks isolation** (browser runs on host).

View File

@@ -48,7 +48,7 @@ For debugging “why is this blocked?”, see [Sandbox vs Tool Policy vs Elevate
},
"tools": {
"allow": ["read"],
"deny": ["exec", "write", "edit", "process", "browser"]
"deny": ["exec", "write", "edit", "apply_patch", "process", "browser"]
}
}
]
@@ -95,7 +95,7 @@ For debugging “why is this blocked?”, see [Sandbox vs Tool Policy vs Elevate
"workspaceRoot": "/tmp/work-sandboxes"
},
"tools": {
"allow": ["read", "write", "exec"],
"allow": ["read", "write", "apply_patch", "exec"],
"deny": ["browser", "gateway", "discord"]
}
}
@@ -134,7 +134,7 @@ For debugging “why is this blocked?”, see [Sandbox vs Tool Policy vs Elevate
},
"tools": {
"allow": ["read"],
"deny": ["exec", "write", "edit"]
"deny": ["exec", "write", "edit", "apply_patch"]
}
}
]
@@ -200,7 +200,7 @@ Mitigation patterns:
"tools": {
"sandbox": {
"tools": {
"allow": ["read", "write", "exec"],
"allow": ["read", "write", "apply_patch", "exec"],
"deny": []
}
}
@@ -235,7 +235,7 @@ Legacy `agent.*` configs are migrated by `clawdbot doctor`; prefer `agents.defau
{
"tools": {
"allow": ["read"],
"deny": ["exec", "write", "edit", "process"]
"deny": ["exec", "write", "edit", "apply_patch", "process"]
}
}
```
@@ -245,7 +245,7 @@ Legacy `agent.*` configs are migrated by `clawdbot doctor`; prefer `agents.defau
{
"tools": {
"allow": ["read", "exec", "process"],
"deny": ["write", "edit", "browser", "gateway"]
"deny": ["write", "edit", "apply_patch", "browser", "gateway"]
}
}
```
@@ -255,7 +255,7 @@ Legacy `agent.*` configs are migrated by `clawdbot doctor`; prefer `agents.defau
{
"tools": {
"allow": ["sessions_list", "sessions_send", "sessions_history", "session_status"],
"deny": ["exec", "write", "edit", "read", "browser"]
"deny": ["exec", "write", "edit", "apply_patch", "read", "browser"]
}
}
```

49
docs/tools/apply-patch.md Normal file
View File

@@ -0,0 +1,49 @@
---
summary: "Apply multi-file patches with the apply_patch tool"
read_when:
- You need structured file edits across multiple files
- You want to document or debug patch-based edits
---
# apply_patch tool
Apply file changes using a structured patch format. This is ideal for multi-file
or multi-hunk edits where a single `edit` call would be brittle.
The tool accepts a single `input` string that wraps one or more file operations:
```
*** Begin Patch
*** Add File: path/to/file.txt
+line 1
+line 2
*** Update File: src/app.ts
@@
-old line
+new line
*** Delete File: obsolete.txt
*** End Patch
```
## Parameters
- `input` (required): Full patch contents including `*** Begin Patch` and `*** End Patch`.
## Notes
- Paths are resolved relative to the workspace root.
- Use `*** Move to:` within an `*** Update File:` hunk to rename files.
- `*** End of File` marks an EOF-only insert when needed.
- Experimental and disabled by default. Enable with `tools.exec.applyPatch.enabled`.
- OpenAI-only (including OpenAI Codex). Optionally gate by model via
`tools.exec.applyPatch.allowModels`.
- Config is only under `tools.exec` (no `tools.bash` alias).
## Example
```json
{
"tool": "apply_patch",
"input": "*** Begin Patch\n*** Update File: src/index.ts\n@@\n-const foo = 1\n+const foo = 2\n*** End Patch"
}
```

View File

@@ -33,3 +33,23 @@ Background + poll:
{"tool":"exec","command":"npm run build","yieldMs":1000}
{"tool":"process","action":"poll","sessionId":"<id>"}
```
## apply_patch (experimental)
`apply_patch` is a subtool of `exec` for structured multi-file edits.
Enable it explicitly:
```json5
{
tools: {
exec: {
applyPatch: { enabled: true, allowModels: ["gpt-5.2"] }
}
}
}
```
Notes:
- Only available for OpenAI/OpenAI Codex models.
- Tool policy still applies; `allow: ["exec"]` implicitly allows `apply_patch`.
- Config lives under `tools.exec.applyPatch` (no `tools.bash` alias).

View File

@@ -31,6 +31,10 @@ alongside tools (for example, the voice-call plugin).
## Tool inventory
### `apply_patch`
Apply structured patches across one or more files. Use for multi-hunk edits.
Experimental: enable via `tools.exec.applyPatch.enabled` (OpenAI models only).
### `exec`
Run shell commands in the workspace.