Files
PaperClipAI/packages/adapters/claude-local
Devin FoleyandClaude Opus 5.5 65c5d8d600 fix(claude-local): start approve-all sandbox ACP sessions in bypassPermissions (#19430)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The `claude_local` adapter runs Claude through the ACP engine
(claude-agent-acp) inside remote sandboxes
> - Paperclip seeds the sandbox Claude settings with
`permissions.defaultMode: "default"`, so Claude asks acpx for permission
before every MCP call and every Bash call that is not read-only
> - In the default `approve-all` mode, acpx approves every request. The
permission round trip gives no access control, but it is the step where
tool calls stall
> - Intermittently, the Claude CLI never sends that permission request.
The call stays `pending` until the 30-minute stalled-tool watchdog ends
the turn (`acpx_tool_stalled`)
> - This pull request starts approve-all sandbox sessions in
`bypassPermissions`, so Claude does not use the permission round trip at
all
> - The benefit is that sandbox runs no longer lose about 35 minutes per
stall, and `approve-reads` and `deny-all` agents keep their current
permission behavior

## Linked Issues or Issue Description

Refs #16688
Refs #16769

**What happened?**
Sandbox `claude_local` ACP runs ended with `acpx_tool_stalled`: "A
pending tool call (kind other) received no update for 30 minutes, so the
run ended the turn instead of waiting for the execution timeout." The
diagnostics from #16688 and #16769 show the shape. The stalled call got
its `pending` update and its input updates. Then its permission request
never reached acpx. Finished calls of the same kind in the same run did
send a request. The stall occurred on MCP calls and on Bash calls, in
the main agent and in background subagents. Calls that need no
permission (ToolSearch, read-only Bash) never stalled.

**Expected behavior**
In `approve-all` mode, a tool call must not wait on a permission round
trip that acpx always approves.

**Steps to reproduce**
1. Run a `claude_local` agent with the ACP engine on a sandbox target,
with the default `approve-all` permission mode.
2. Make many MCP or network Bash calls during the turn.
3. Sometimes a call stays `pending` with no permission request. The
watchdog then ends the turn after 30 minutes.

**Agent adapter(s) involved**
Claude Code (`claude_local`, ACP engine, sandbox target).

## What Changed

- `claude-config.ts`: `prepareClaudeConfigSeed` accepts
`permissionDefaultMode` (`"default"` or `"bypassPermissions"`). The
sanitized seed writes that value into `permissions.defaultMode`. In
bypass mode the seed writes `settings.json` also when the host has no
`settings.json`. Hooks, MCP servers and host permission rules are still
removed.
- `claude-config.ts`: `overrideRemoteClaudeLocalPermissionMode` sets
`bypassPermissions` in the **sandbox copy** of the workspace
`.claude/settings.local.json`. The engine writes that file with
`defaultMode: "default"`, and the local tier overrides the user seed.
Before workspace copy-back, the override is reset in the sandbox, so
copy-back never treats it as an agent edit. After copy-back, if the host
file still has `bypassPermissions`, only `defaultMode` is put back. Host
and agent edits to other settings are kept.
- `acp.ts`: the sandbox managed-home seam reads the run's effective ACPX
permission mode. If the mode is `approve-all`, the seam seeds
`bypassPermissions`, applies the workspace override above, and sets
`IS_SANDBOX=1` through the existing `claudeSandboxPermissionEnv` helper.
This lets the bypass apply when the sandbox user is root, the same rule
as the CLI lane. The mode resolution is the same as the engine's: an
absent value or `"default"` becomes `approve-all`. An unrecognized value
keeps `"default"`.
- These keep `defaultMode: "default"` and do not change:
`approve-reads`, `deny-all`, the CLI lane, the Test probes, managed AI
connections, and an operator `CLAUDE_CONFIG_DIR`.
- Tests: two seed tests in `claude-config.test.ts`. In `acp.test.ts`: an
end-to-end executor test for five permission modes (absent,
`approve-all`, `default`, `approve-reads`, `deny-all`) that checks the
mode Claude actually gets during the turn and that the host settings
keep `default`, and a test that host and agent edits to the workspace
settings survive copy-back without `bypassPermissions`.

## Verification

- `pnpm exec vitest run packages/adapters/claude-local`: 393 passed, 1
skipped (before the copy-back commits); full CI is green on the current
head.
- Regression proof: with the new tests and the old `acp.ts` and
`claude-config.ts`, the seed tests and the absent, `approve-all` and
`default` cases fail (`expected 'default' to be 'bypassPermissions'`).
The `approve-reads` and `deny-all` cases pass on both versions, because
their behavior does not change.
- `pnpm exec tsc --noEmit` in `packages/adapters/claude-local`: clean.

## Risks

- Behavior change for sandbox `claude_local` ACP runs in `approve-all`
mode (the default): Claude does not ask permission for tools. Paperclip
already approves every request in this mode, so the access outcome does
not change. Claude's bypass-immune safety checks still go through
`canUseTool`.
- The seed directory is content-addressed, so the bypass seed and the
default seed are different snapshots and do not overwrite each other.
- Agents that set `approve-reads` or `deny-all` keep the permission
round trip. The stall can still occur for them.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, with tool use and code
execution in an agent harness.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 06:44:43 -08:00
..