Skip to content

fix(core): ask before shell commands in plan mode - #54165

Closed
argszero wants to merge 1 commit into
anomalyco:devfrom
argszero:plan-shell-ask
Closed

argszero wants to merge 1 commit into
anomalyco:devfrom
argszero:plan-shell-ask

Conversation

@argszero

@argszero argszero commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #53955

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

In Plan mode the agent is supposed to plan, not mutate the repository. The Plan agent's
permission set in packages/core/src/plugin/plan.ts denies the edit tool outside the Plan
directory, but it never declares a rule for the shell tool. Permission.evaluate uses
findLast, so every shell command falls through to the agent default (* -> allow) and
bash / python / git / rm / mv all run without any prompt. The Plan system reminder
is the only thing asking the model not to change files, and a model that ignores it can edit
the worktree through the shell — which is exactly what #53955 reports (the reporter saw a
shell command create src/repl.rs, edit and delete src/main.rs, and run git add /
git commit with no permission prompt).

The published agent docs describe Plan mode as setting both file edits and bash to
ask, so this is the documented behaviour not being implemented rather than a new policy.

The change is one additive rule:

item.permissions.push({ action: "shell", resource: "*", effect: "ask" })

The action string is "shell", not "bash": that is the tool's own export const name = "shell", and it is what tool/plugin/shell.ts passes to permission.assert. Read-only
commands keep working after one confirmation, while a destructive command now has to be
approved. explore is deliberately left alone — other built-in agents stay on the * -> allow
default, which an existing test/agent.test.ts case pins.

How did you verify your code works?

Added a case to the existing plan plugin mutations block in
packages/core/test/plugin/plan.test.ts:

it.effect("asks before running shell commands", () =>
  Effect.gen(function* () {
    const { planAgent } = yield* run()
    expect(Permission.evaluate("shell", "ls", planAgent.permissions).effect).toBe("ask")
    expect(Permission.evaluate("shell", "rm -rf src", planAgent.permissions).effect).toBe("ask")
  }),
)

Red-baselined both ways on the current v2 tip: with only the source hunk reverted to stock
the new case fails (12 pass / 1 fail, reading allow), and with the change it passes
(13 pass / 0 fail). tsgo -b tsconfig.json tsconfig.tests.json is clean for packages/core
and oxlint reports 0 warnings / 0 errors on both files.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #53530, #39491, #34190

Plan only denies the file-editing tools, so shell commands that modify
files ran unprompted and only the Plan system reminder asked the model
not to change anything. The documented Plan permission set asks before
every shell command, so add the missing shell rule.
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works. I ran OpenCode from source with a simulated model in the Plan agent and had it run the shell command from #53955. On the base the command ran without asking: it created src/repl.rs and deleted src/main.rs. With this change a permission prompt stops it. The plan and agent tests pass and packages/core type-checks.

The new rule uses shell, which is the action the shell tool checks. A user's agent.plan.permission config is applied after this plugin, so users can still loosen or tighten the rule. It also matches the documented Plan behaviour (bash set to ask).

The public API is unchanged. This only changes Plan's built-in permission defaults; no config keys, schema or SDK change.

No blocking concerns.

Related: #53530, #39491, #34190

Before

After

@argszero

Copy link
Copy Markdown
Contributor Author

Closing this to stay within GitHub's 3-open-pull-request limit for contributors without write access, which currently blocks every new pull request here. The branch remains on my fork, so I can reopen it on request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent is making edits when on plan mode

1 participant