mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 23:40:45 +00:00
555 lines
24 KiB
Markdown
555 lines
24 KiB
Markdown
---
|
|
summary: "Use ACP runtime sessions for Pi, Claude Code, Codex, OpenCode, Gemini CLI, and other harness agents"
|
|
read_when:
|
|
- Running coding harnesses through ACP
|
|
- Setting up thread-bound ACP sessions on thread-capable channels
|
|
- Binding Discord channels or Telegram forum topics to persistent ACP sessions
|
|
- Troubleshooting ACP backend and plugin wiring
|
|
- Operating /acp commands from chat
|
|
title: "ACP Agents"
|
|
---
|
|
|
|
# ACP agents
|
|
|
|
[Agent Client Protocol (ACP)](https://agentclientprotocol.com/) sessions let OpenClaw run external coding harnesses (for example Pi, Claude Code, Codex, OpenCode, and Gemini CLI) through an ACP backend plugin.
|
|
|
|
If you ask OpenClaw in plain language to "run this in Codex" or "start Claude Code in a thread", OpenClaw should route that request to the ACP runtime (not the native sub-agent runtime).
|
|
|
|
## Fast operator flow
|
|
|
|
Use this when you want a practical `/acp` runbook:
|
|
|
|
1. Spawn a session:
|
|
- `/acp spawn codex --mode persistent --thread auto`
|
|
2. Work in the bound thread (or target that session key explicitly).
|
|
3. Check runtime state:
|
|
- `/acp status`
|
|
4. Tune runtime options as needed:
|
|
- `/acp model <provider/model>`
|
|
- `/acp permissions <profile>`
|
|
- `/acp timeout <seconds>`
|
|
5. Nudge an active session without replacing context:
|
|
- `/acp steer tighten logging and continue`
|
|
6. Stop work:
|
|
- `/acp cancel` (stop current turn), or
|
|
- `/acp close` (close session + remove bindings)
|
|
|
|
## Quick start for humans
|
|
|
|
Examples of natural requests:
|
|
|
|
- "Start a persistent Codex session in a thread here and keep it focused."
|
|
- "Run this as a one-shot Claude Code ACP session and summarize the result."
|
|
- "Use Gemini CLI for this task in a thread, then keep follow-ups in that same thread."
|
|
|
|
What OpenClaw should do:
|
|
|
|
1. Pick `runtime: "acp"`.
|
|
2. Resolve the requested harness target (`agentId`, for example `codex`).
|
|
3. If thread binding is requested and the current channel supports it, bind the ACP session to the thread.
|
|
4. Route follow-up thread messages to that same ACP session until unfocused/closed/expired.
|
|
|
|
## ACP versus sub-agents
|
|
|
|
Use ACP when you want an external harness runtime. Use sub-agents when you want OpenClaw-native delegated runs.
|
|
|
|
| Area | ACP session | Sub-agent run |
|
|
| ------------- | ------------------------------------- | ---------------------------------- |
|
|
| Runtime | ACP backend plugin (for example acpx) | OpenClaw native sub-agent runtime |
|
|
| Session key | `agent:<agentId>:acp:<uuid>` | `agent:<agentId>:subagent:<uuid>` |
|
|
| Main commands | `/acp ...` | `/subagents ...` |
|
|
| Spawn tool | `sessions_spawn` with `runtime:"acp"` | `sessions_spawn` (default runtime) |
|
|
|
|
See also [Sub-agents](/tools/subagents).
|
|
|
|
## Thread-bound sessions (channel-agnostic)
|
|
|
|
When thread bindings are enabled for a channel adapter, ACP sessions can be bound to threads:
|
|
|
|
- OpenClaw binds a thread to a target ACP session.
|
|
- Follow-up messages in that thread route to the bound ACP session.
|
|
- ACP output is delivered back to the same thread.
|
|
- Unfocus/close/archive/idle-timeout or max-age expiry removes the binding.
|
|
|
|
Thread binding support is adapter-specific. If the active channel adapter does not support thread bindings, OpenClaw returns a clear unsupported/unavailable message.
|
|
|
|
Required feature flags for thread-bound ACP:
|
|
|
|
- `acp.enabled=true`
|
|
- `acp.dispatch.enabled` is on by default (set `false` to pause ACP dispatch)
|
|
- Channel-adapter ACP thread-spawn flag enabled (adapter-specific)
|
|
- Discord: `channels.discord.threadBindings.spawnAcpSessions=true`
|
|
- Telegram: `channels.telegram.threadBindings.spawnAcpSessions=true`
|
|
|
|
### Thread supporting channels
|
|
|
|
- Any channel adapter that exposes session/thread binding capability.
|
|
- Current built-in support:
|
|
- Discord threads/channels
|
|
- Telegram topics (forum topics in groups/supergroups and DM topics)
|
|
- Plugin channels can add support through the same binding interface.
|
|
|
|
## Channel specific settings
|
|
|
|
For non-ephemeral workflows, configure persistent ACP bindings in top-level `bindings[]` entries.
|
|
|
|
### Binding model
|
|
|
|
- `bindings[].type="acp"` marks a persistent ACP conversation binding.
|
|
- `bindings[].match` identifies the target conversation:
|
|
- Discord channel or thread: `match.channel="discord"` + `match.peer.id="<channelOrThreadId>"`
|
|
- Telegram forum topic: `match.channel="telegram"` + `match.peer.id="<chatId>:topic:<topicId>"`
|
|
- `bindings[].agentId` is the owning OpenClaw agent id.
|
|
- Optional ACP overrides live under `bindings[].acp`:
|
|
- `mode` (`persistent` or `oneshot`)
|
|
- `label`
|
|
- `cwd`
|
|
- `backend`
|
|
|
|
### Runtime defaults per agent
|
|
|
|
Use `agents.list[].runtime` to define ACP defaults once per agent:
|
|
|
|
- `agents.list[].runtime.type="acp"`
|
|
- `agents.list[].runtime.acp.agent` (harness id, for example `codex` or `claude`)
|
|
- `agents.list[].runtime.acp.backend`
|
|
- `agents.list[].runtime.acp.mode`
|
|
- `agents.list[].runtime.acp.cwd`
|
|
|
|
Override precedence for ACP bound sessions:
|
|
|
|
1. `bindings[].acp.*`
|
|
2. `agents.list[].runtime.acp.*`
|
|
3. global ACP defaults (for example `acp.backend`)
|
|
|
|
Example:
|
|
|
|
```json5
|
|
{
|
|
agents: {
|
|
list: [
|
|
{
|
|
id: "codex",
|
|
runtime: {
|
|
type: "acp",
|
|
acp: {
|
|
agent: "codex",
|
|
backend: "acpx",
|
|
mode: "persistent",
|
|
cwd: "/workspace/openclaw",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
id: "claude",
|
|
runtime: {
|
|
type: "acp",
|
|
acp: { agent: "claude", backend: "acpx", mode: "persistent" },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
bindings: [
|
|
{
|
|
type: "acp",
|
|
agentId: "codex",
|
|
match: {
|
|
channel: "discord",
|
|
accountId: "default",
|
|
peer: { kind: "channel", id: "222222222222222222" },
|
|
},
|
|
acp: { label: "codex-main" },
|
|
},
|
|
{
|
|
type: "acp",
|
|
agentId: "claude",
|
|
match: {
|
|
channel: "telegram",
|
|
accountId: "default",
|
|
peer: { kind: "group", id: "-1001234567890:topic:42" },
|
|
},
|
|
acp: { cwd: "/workspace/repo-b" },
|
|
},
|
|
{
|
|
type: "route",
|
|
agentId: "main",
|
|
match: { channel: "discord", accountId: "default" },
|
|
},
|
|
{
|
|
type: "route",
|
|
agentId: "main",
|
|
match: { channel: "telegram", accountId: "default" },
|
|
},
|
|
],
|
|
channels: {
|
|
discord: {
|
|
guilds: {
|
|
"111111111111111111": {
|
|
channels: {
|
|
"222222222222222222": { requireMention: false },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
telegram: {
|
|
groups: {
|
|
"-1001234567890": {
|
|
topics: { "42": { requireMention: false } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
Behavior:
|
|
|
|
- OpenClaw ensures the configured ACP session exists before use.
|
|
- Messages in that channel or topic route to the configured ACP session.
|
|
- In bound conversations, `/new` and `/reset` reset the same ACP session key in place.
|
|
- Temporary runtime bindings (for example created by thread-focus flows) still apply where present.
|
|
|
|
## Start ACP sessions (interfaces)
|
|
|
|
### From `sessions_spawn`
|
|
|
|
Use `runtime: "acp"` to start an ACP session from an agent turn or tool call.
|
|
|
|
```json
|
|
{
|
|
"task": "Open the repo and summarize failing tests",
|
|
"runtime": "acp",
|
|
"agentId": "codex",
|
|
"thread": true,
|
|
"mode": "session"
|
|
}
|
|
```
|
|
|
|
Notes:
|
|
|
|
- `runtime` defaults to `subagent`, so set `runtime: "acp"` explicitly for ACP sessions.
|
|
- If `agentId` is omitted, OpenClaw uses `acp.defaultAgent` when configured.
|
|
- `mode: "session"` requires `thread: true` to keep a persistent bound conversation.
|
|
|
|
Interface details:
|
|
|
|
- `task` (required): initial prompt sent to the ACP session.
|
|
- `runtime` (required for ACP): must be `"acp"`.
|
|
- `agentId` (optional): ACP target harness id. Falls back to `acp.defaultAgent` if set.
|
|
- `thread` (optional, default `false`): request thread binding flow where supported.
|
|
- `mode` (optional): `run` (one-shot) or `session` (persistent).
|
|
- default is `run`
|
|
- if `thread: true` and mode omitted, OpenClaw may default to persistent behavior per runtime path
|
|
- `mode: "session"` requires `thread: true`
|
|
- `cwd` (optional): requested runtime working directory (validated by backend/runtime policy).
|
|
- `label` (optional): operator-facing label used in session/banner text.
|
|
- `streamTo` (optional): `"parent"` streams initial ACP run progress summaries back to the requester session as system events.
|
|
- When available, accepted responses include `streamLogPath` pointing to a session-scoped JSONL log (`<sessionId>.acp-stream.jsonl`) you can tail for full relay history.
|
|
|
|
## Sandbox compatibility
|
|
|
|
ACP sessions currently run on the host runtime, not inside the OpenClaw sandbox.
|
|
|
|
Current limitations:
|
|
|
|
- If the requester session is sandboxed, ACP spawns are blocked for both `sessions_spawn({ runtime: "acp" })` and `/acp spawn`.
|
|
- Error: `Sandboxed sessions cannot spawn ACP sessions because runtime="acp" runs on the host. Use runtime="subagent" from sandboxed sessions.`
|
|
- `sessions_spawn` with `runtime: "acp"` does not support `sandbox: "require"`.
|
|
- Error: `sessions_spawn sandbox="require" is unsupported for runtime="acp" because ACP sessions run outside the sandbox. Use runtime="subagent" or sandbox="inherit".`
|
|
|
|
Use `runtime: "subagent"` when you need sandbox-enforced execution.
|
|
|
|
### From `/acp` command
|
|
|
|
Use `/acp spawn` for explicit operator control from chat when needed.
|
|
|
|
```text
|
|
/acp spawn codex --mode persistent --thread auto
|
|
/acp spawn codex --mode oneshot --thread off
|
|
/acp spawn codex --thread here
|
|
```
|
|
|
|
Key flags:
|
|
|
|
- `--mode persistent|oneshot`
|
|
- `--thread auto|here|off`
|
|
- `--cwd <absolute-path>`
|
|
- `--label <name>`
|
|
|
|
See [Slash Commands](/tools/slash-commands).
|
|
|
|
## Session target resolution
|
|
|
|
Most `/acp` actions accept an optional session target (`session-key`, `session-id`, or `session-label`).
|
|
|
|
Resolution order:
|
|
|
|
1. Explicit target argument (or `--session` for `/acp steer`)
|
|
- tries key
|
|
- then UUID-shaped session id
|
|
- then label
|
|
2. Current thread binding (if this conversation/thread is bound to an ACP session)
|
|
3. Current requester session fallback
|
|
|
|
If no target resolves, OpenClaw returns a clear error (`Unable to resolve session target: ...`).
|
|
|
|
## Spawn thread modes
|
|
|
|
`/acp spawn` supports `--thread auto|here|off`.
|
|
|
|
| Mode | Behavior |
|
|
| ------ | --------------------------------------------------------------------------------------------------- |
|
|
| `auto` | In an active thread: bind that thread. Outside a thread: create/bind a child thread when supported. |
|
|
| `here` | Require current active thread; fail if not in one. |
|
|
| `off` | No binding. Session starts unbound. |
|
|
|
|
Notes:
|
|
|
|
- On non-thread binding surfaces, default behavior is effectively `off`.
|
|
- Thread-bound spawn requires channel policy support:
|
|
- Discord: `channels.discord.threadBindings.spawnAcpSessions=true`
|
|
- Telegram: `channels.telegram.threadBindings.spawnAcpSessions=true`
|
|
|
|
## ACP controls
|
|
|
|
Available command family:
|
|
|
|
- `/acp spawn`
|
|
- `/acp cancel`
|
|
- `/acp steer`
|
|
- `/acp close`
|
|
- `/acp status`
|
|
- `/acp set-mode`
|
|
- `/acp set`
|
|
- `/acp cwd`
|
|
- `/acp permissions`
|
|
- `/acp timeout`
|
|
- `/acp model`
|
|
- `/acp reset-options`
|
|
- `/acp sessions`
|
|
- `/acp doctor`
|
|
- `/acp install`
|
|
|
|
`/acp status` shows the effective runtime options and, when available, both runtime-level and backend-level session identifiers.
|
|
|
|
Some controls depend on backend capabilities. If a backend does not support a control, OpenClaw returns a clear unsupported-control error.
|
|
|
|
## ACP command cookbook
|
|
|
|
| Command | What it does | Example |
|
|
| -------------------- | --------------------------------------------------------- | -------------------------------------------------------------- |
|
|
| `/acp spawn` | Create ACP session; optional thread bind. | `/acp spawn codex --mode persistent --thread auto --cwd /repo` |
|
|
| `/acp cancel` | Cancel in-flight turn for target session. | `/acp cancel agent:codex:acp:<uuid>` |
|
|
| `/acp steer` | Send steer instruction to running session. | `/acp steer --session support inbox prioritize failing tests` |
|
|
| `/acp close` | Close session and unbind thread targets. | `/acp close` |
|
|
| `/acp status` | Show backend, mode, state, runtime options, capabilities. | `/acp status` |
|
|
| `/acp set-mode` | Set runtime mode for target session. | `/acp set-mode plan` |
|
|
| `/acp set` | Generic runtime config option write. | `/acp set model openai/gpt-5.2` |
|
|
| `/acp cwd` | Set runtime working directory override. | `/acp cwd /Users/user/Projects/repo` |
|
|
| `/acp permissions` | Set approval policy profile. | `/acp permissions strict` |
|
|
| `/acp timeout` | Set runtime timeout (seconds). | `/acp timeout 120` |
|
|
| `/acp model` | Set runtime model override. | `/acp model anthropic/claude-opus-4-5` |
|
|
| `/acp reset-options` | Remove session runtime option overrides. | `/acp reset-options` |
|
|
| `/acp sessions` | List recent ACP sessions from store. | `/acp sessions` |
|
|
| `/acp doctor` | Backend health, capabilities, actionable fixes. | `/acp doctor` |
|
|
| `/acp install` | Print deterministic install and enable steps. | `/acp install` |
|
|
|
|
## Runtime options mapping
|
|
|
|
`/acp` has convenience commands and a generic setter.
|
|
|
|
Equivalent operations:
|
|
|
|
- `/acp model <id>` maps to runtime config key `model`.
|
|
- `/acp permissions <profile>` maps to runtime config key `approval_policy`.
|
|
- `/acp timeout <seconds>` maps to runtime config key `timeout`.
|
|
- `/acp cwd <path>` updates runtime cwd override directly.
|
|
- `/acp set <key> <value>` is the generic path.
|
|
- Special case: `key=cwd` uses the cwd override path.
|
|
- `/acp reset-options` clears all runtime overrides for target session.
|
|
|
|
## acpx harness support (current)
|
|
|
|
Current acpx built-in harness aliases:
|
|
|
|
- `pi`
|
|
- `claude`
|
|
- `codex`
|
|
- `opencode`
|
|
- `gemini`
|
|
- `kimi`
|
|
|
|
When OpenClaw uses the acpx backend, prefer these values for `agentId` unless your acpx config defines custom agent aliases.
|
|
|
|
Direct acpx CLI usage can also target arbitrary adapters via `--agent <command>`, but that raw escape hatch is an acpx CLI feature (not the normal OpenClaw `agentId` path).
|
|
|
|
## Required config
|
|
|
|
Core ACP baseline:
|
|
|
|
```json5
|
|
{
|
|
acp: {
|
|
enabled: true,
|
|
// Optional. Default is true; set false to pause ACP dispatch while keeping /acp controls.
|
|
dispatch: { enabled: true },
|
|
backend: "acpx",
|
|
defaultAgent: "codex",
|
|
allowedAgents: ["pi", "claude", "codex", "opencode", "gemini", "kimi"],
|
|
maxConcurrentSessions: 8,
|
|
stream: {
|
|
coalesceIdleMs: 300,
|
|
maxChunkChars: 1200,
|
|
},
|
|
runtime: {
|
|
ttlMinutes: 120,
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
Thread binding config is channel-adapter specific. Example for Discord:
|
|
|
|
```json5
|
|
{
|
|
session: {
|
|
threadBindings: {
|
|
enabled: true,
|
|
idleHours: 24,
|
|
maxAgeHours: 0,
|
|
},
|
|
},
|
|
channels: {
|
|
discord: {
|
|
threadBindings: {
|
|
enabled: true,
|
|
spawnAcpSessions: true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
If thread-bound ACP spawn does not work, verify the adapter feature flag first:
|
|
|
|
- Discord: `channels.discord.threadBindings.spawnAcpSessions=true`
|
|
|
|
See [Configuration Reference](/gateway/configuration-reference).
|
|
|
|
## Plugin setup for acpx backend
|
|
|
|
Install and enable plugin:
|
|
|
|
```bash
|
|
openclaw plugins install acpx
|
|
openclaw config set plugins.entries.acpx.enabled true
|
|
```
|
|
|
|
Local workspace install during development:
|
|
|
|
```bash
|
|
openclaw plugins install ./extensions/acpx
|
|
```
|
|
|
|
Then verify backend health:
|
|
|
|
```text
|
|
/acp doctor
|
|
```
|
|
|
|
### acpx command and version configuration
|
|
|
|
By default, the acpx plugin (published as `@openclaw/acpx`) uses the plugin-local pinned binary:
|
|
|
|
1. Command defaults to `extensions/acpx/node_modules/.bin/acpx`.
|
|
2. Expected version defaults to the extension pin.
|
|
3. Startup registers ACP backend immediately as not-ready.
|
|
4. A background ensure job verifies `acpx --version`.
|
|
5. If the plugin-local binary is missing or mismatched, it runs:
|
|
`npm install --omit=dev --no-save acpx@<pinned>` and re-verifies.
|
|
|
|
You can override command/version in plugin config:
|
|
|
|
```json
|
|
{
|
|
"plugins": {
|
|
"entries": {
|
|
"acpx": {
|
|
"enabled": true,
|
|
"config": {
|
|
"command": "../acpx/dist/cli.js",
|
|
"expectedVersion": "any"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Notes:
|
|
|
|
- `command` accepts an absolute path, relative path, or command name (`acpx`).
|
|
- Relative paths resolve from OpenClaw workspace directory.
|
|
- `expectedVersion: "any"` disables strict version matching.
|
|
- When `command` points to a custom binary/path, plugin-local auto-install is disabled.
|
|
- OpenClaw startup remains non-blocking while the backend health check runs.
|
|
|
|
See [Plugins](/tools/plugin).
|
|
|
|
## Permission configuration
|
|
|
|
ACP sessions run non-interactively — there is no TTY to approve or deny file-write and shell-exec permission prompts. The acpx plugin provides two config keys that control how permissions are handled:
|
|
|
|
### `permissionMode`
|
|
|
|
Controls which operations the harness agent can perform without prompting.
|
|
|
|
| Value | Behavior |
|
|
| --------------- | --------------------------------------------------------- |
|
|
| `approve-all` | Auto-approve all file writes and shell commands. |
|
|
| `approve-reads` | Auto-approve reads only; writes and exec require prompts. |
|
|
| `deny-all` | Deny all permission prompts. |
|
|
|
|
### `nonInteractivePermissions`
|
|
|
|
Controls what happens when a permission prompt would be shown but no interactive TTY is available (which is always the case for ACP sessions).
|
|
|
|
| Value | Behavior |
|
|
| ------ | ----------------------------------------------------------------- |
|
|
| `fail` | Abort the session with `AcpRuntimeError`. **(default)** |
|
|
| `deny` | Silently deny the permission and continue (graceful degradation). |
|
|
|
|
### Configuration
|
|
|
|
Set via plugin config:
|
|
|
|
```bash
|
|
openclaw config set plugins.entries.acpx.config.permissionMode approve-all
|
|
openclaw config set plugins.entries.acpx.config.nonInteractivePermissions fail
|
|
```
|
|
|
|
Restart the gateway after changing these values.
|
|
|
|
> **Important:** OpenClaw currently defaults to `permissionMode=approve-reads` and `nonInteractivePermissions=fail`. In non-interactive ACP sessions, any write or exec that triggers a permission prompt can fail with `AcpRuntimeError: Permission prompt unavailable in non-interactive mode`.
|
|
>
|
|
> If you need to restrict permissions, set `nonInteractivePermissions` to `deny` so sessions degrade gracefully instead of crashing.
|
|
|
|
## Troubleshooting
|
|
|
|
| Symptom | Likely cause | Fix |
|
|
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `ACP runtime backend is not configured` | Backend plugin missing or disabled. | Install and enable backend plugin, then run `/acp doctor`. |
|
|
| `ACP is disabled by policy (acp.enabled=false)` | ACP globally disabled. | Set `acp.enabled=true`. |
|
|
| `ACP dispatch is disabled by policy (acp.dispatch.enabled=false)` | Dispatch from normal thread messages disabled. | Set `acp.dispatch.enabled=true`. |
|
|
| `ACP agent "<id>" is not allowed by policy` | Agent not in allowlist. | Use allowed `agentId` or update `acp.allowedAgents`. |
|
|
| `Unable to resolve session target: ...` | Bad key/id/label token. | Run `/acp sessions`, copy exact key/label, retry. |
|
|
| `--thread here requires running /acp spawn inside an active ... thread` | `--thread here` used outside a thread context. | Move to target thread or use `--thread auto`/`off`. |
|
|
| `Only <user-id> can rebind this thread.` | Another user owns thread binding. | Rebind as owner or use a different thread. |
|
|
| `Thread bindings are unavailable for <channel>.` | Adapter lacks thread binding capability. | Use `--thread off` or move to supported adapter/channel. |
|
|
| `Sandboxed sessions cannot spawn ACP sessions ...` | ACP runtime is host-side; requester session is sandboxed. | Use `runtime="subagent"` from sandboxed sessions, or run ACP spawn from a non-sandboxed session. |
|
|
| `sessions_spawn sandbox="require" is unsupported for runtime="acp" ...` | `sandbox="require"` requested for ACP runtime. | Use `runtime="subagent"` for required sandboxing, or use ACP with `sandbox="inherit"` from a non-sandboxed session. |
|
|
| Missing ACP metadata for bound session | Stale/deleted ACP session metadata. | Recreate with `/acp spawn`, then rebind/focus thread. |
|
|
| `AcpRuntimeError: Permission prompt unavailable in non-interactive mode` | `permissionMode` blocks writes/exec in non-interactive ACP session. | Set `plugins.entries.acpx.config.permissionMode` to `approve-all` and restart gateway. See [Permission configuration](#permission-configuration). |
|
|
| ACP session fails early with little output | Permission prompts are blocked by `permissionMode`/`nonInteractivePermissions`. | Check gateway logs for `AcpRuntimeError`. For full permissions, set `permissionMode=approve-all`; for graceful degradation, set `nonInteractivePermissions=deny`. |
|
|
| ACP session stalls indefinitely after completing work | Harness process finished but ACP session did not report completion. | Monitor with `ps aux \| grep acpx`; kill stale processes manually. |
|