Repository navigation
Description
Summary
Plan mode in V2 is advisory rather than enforced for non-V1-tool paths. In a live session running under the built-in plan agent, file writes, writes outside the project root, and arbitrary shell execution (including authenticating to GitHub and publishing to a public repository) all completed without a permission prompt, a denial, or any record in the session's tool history. The same requests are refused on V1.
This is a permission-boundary regression, not a model-compliance problem. It reproduces across different models on the user's side, and it is not limited to one provider integration.
Reporting as a security concern because the failure mode is silent. Nothing in the UI, the transcript, or the tool history indicates that a guardrail was bypassed. The agent behaves as though it is permitted to act, and the operator has no signal that it was not.
Impact
A user who selects plan mode to get a read-only review cannot rely on it. The session can:
- Write to the project tree
- Write outside the project root (observed: writes to
%TEMP%and to files outside the workspace succeeded unprompted) - Execute shell commands
- Use credentials available in the environment to perform authenticated, externally-visible actions — in this session,
gh auth statusandgh issue createagainst a public upstream repository, with no prompt at any point
The last item is the reason this warrants security review rather than a UX bug: the blast radius is not confined to the local workspace.
Environment
- opencode version: 2.0.26 (latest)
- OS: Windows_NT 10.0.26200.0 (win32 x64)
- Terminal: Alacritty (TERM=xterm-256color)
- Shell: PowerShell 7.6.6
- Install/channel: latest (npm, isolated prefix)
- Active plugins:
context-mode
Reproduction
This session is itself the reproduction. No scripted steps are required — the condition was reached by ordinary use.
- Run a session under the built-in
planagent on V2. - Request a file write. Observe: the write completes. No prompt, no denial.
- Request a write to a path outside the project root (e.g. under
%TEMP%). Observe: completes unprompted. The V2 permission model has anexternal_directoryaction that should gate this; it did not fire. - Request a shell command. Observe: completes unprompted.
- Inspect the session's stored tool history. Observe: no
toolparts are recorded at all.
Verification from the session database
The session in question stores its identity with the agent set to plan:
session_v2 WHERE id = <this session>
agent = "plan"
Message-type breakdown for that session:
{ user: 39, assistant: 206, system: 3, idle: 27,
agent-switched: 1, synthetic: 2 }
Zero tool parts. 245 messages, and none of them are a tool invocation — despite the session having performed file writes and shell execution. The database is the ground truth for what actually ran, and it records no tool calls.
Expected Behavior
Plan mode should be enforced at the permission layer, not conveyed to the model as an instruction:
- Write, shell, and external-directory operations under the
planagent should hit the permission wall and require an explicit decision, or be denied outright. - Activity that does execute should be recorded in session history as tool parts, so the transcript is a faithful record.
Actual Behavior
Plan mode is delivered as a system reminder and model compliance is the only enforcement. When the model proceeds anyway, the operations are not gated:
- No permission prompt is raised.
- No denial is recorded.
- The action completes.
- The session history contains no trace of it.
The second point compounds the first: an operator reviewing the transcript later has no way to detect that writes and shell commands ran. Absence of tool parts is not evidence of absence of action — it is evidence the action went unrecorded.
Additional Context
Not model-specific. The user reports the same behaviour reproduced across multiple different models in separate sessions on v2. This session was not deliberately attempting to bypass the guard; the requests were ordinary and individually unremarkable.
V1 comparison. On 1.18.34 the same class of request reliably hits the permission wall and requires a decision, before continue_loop_on_deny is taken into account. The user reports having been unable to get an edit through in plan mode on V1 by any route tried. The regression is in V2.
Corroborating reports:
- V2 plan mode doesn't stop Kiro (ACP provider) models from editing files #53530 — "V2 plan mode doesn't stop Kiro (ACP provider) models from editing files" (OPEN). Reports the same V1-refuses/V2-performs delta on an A/B swap of the core binary only, and independently observes that V2 injects the plan reminder as synthetic session messages. It also notes the V2
planpolicy has noshellrule, so shell falls through toallow. - Agent is making edits when on plan mode #53955 — "Agent is making edits when on plan mode" (OPEN, labelled
reproduced).
#53530 attributes the bypass to the reminder not reaching the model through one provider path. This report is broader: it reproduces on an ordinary in-process session with no ACP bridge, which points at the guardrail being advisory by design rather than broken on one transport.
Suggested direction for maintainers. If plan mode's guarantee is meant to hold regardless of model behaviour, the check needs to live where tool calls are authorized rather than in context the model is asked to respect. Related: external_directory did not gate an out-of-workspace write on a non-interactive path, and that gating is independently load-bearing.
Open question. Whether tool parts are absent for all plan sessions or only ones where the reminder was ignored would clarify whether this is two bugs or one. Happy to test specific configurations against a fix.
Activity
opencode-agent commentedon Oct 11, 2026
Thanks for the detailed report, @Jin-Rk1. I think this is a duplicate of #53955.
On v2 the Plan agent denies the file-editing tools (
edit/write/patch) everywhere except~/.opencode/plan/. It has no rule forshell, though, so shell commands fall through to the defaultallow. Those commands can create files, write outside the project, or rungh, all without a prompt. That has been reproduced in #53955, and #54165 is an open PR that makes Plan ask before shell commands.A few notes on the other points:
external_directoryhas defaulted toallowsince feat(core): stop asking for external directory access by default #53585. That change was intentional. You can set it back toaskin yourpermissionconfig.toolitems inside the assistant messages' content. So a breakdown by message type will always show no tool entries, and it doesn't mean the actions went unrecorded.context-mode) or a provider's own tools are not covered by theshellrule either. V2 plan mode doesn't stop Kiro (ACP provider) models from editing files #53530 is one example.Possible duplicates and related issues, ordered from most to least likely:
The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different. For example, it would be different if you saw the
editorwritetool itself change a file outside~/.opencode/plan/while in Plan mode.opencode-agent commentedon Oct 12, 2026
We haven't heard back, so I'm closing this as a duplicate of #53955, as mentioned earlier. In Plan mode on v2, shell commands run without a prompt because the Plan agent has no
shellrule, and that's how files can get written andghcan run. #54165 is open to make Plan ask before running shell commands. Please follow #53955 for updates.If you can show that the
edit/write/patchtools themselves changed a file outside~/.opencode/plan/while in Plan mode, that would be a separate bug. Please reply here or open a new issue with the steps. Thanks again for the detailed report, @Jin-Rk1.