Skip to content

fix(ai): let the classifier govern shell and preference permissions - #3173

Merged
abose merged 2 commits into
mainfrom
ai
Sep 5, 2026
Merged

fix(ai): let the classifier govern shell and preference permissions#3173
abose merged 2 commits into
mainfrom
ai

Conversation

@abose

@abose abose commented Sep 5, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to the plan-mode work already on main. This tightens which AI panel actions can reach the permission classifier, after verifying against the Claude CLI's own debug log what actually decides each call.

The starting question was whether Auto mode ever asks the user anything, or whether we were silently skipping prompts. It turned out the classifier does decide, but two of our permission allow-rules were letting calls past it.

  • Bash off allowedTools. Nothing in that list can pre-approve a shell command now. The CLI already discards a Bash allow rule itself (Ignoring dangerous permission Bash(*) from cliArg (bypasses classifier)), so this is a no-op today; it removes the dependency on that behaviour continuing to hold.
  • Edit Mode's Bash confirm returns an explicit allow rather than {}, so a command the user just approved is not then re-checked by the CLI.
  • editorPreferences off allowedTools. It can write, so in Auto the classifier now weighs each call instead of a rule waving all of them through. A PreToolUse hook allows get and list outright, so a preference read never prompts and never sits through a classifier round-trip. The tool reads and writes through one entry point, so it cannot carry a static readOnlyHint the way getEditorState does; the operation has to be inspected per call.
  • Permission asks are logged with the tool, the mode and the SDK's decisionReason, so a genuine ask can be told apart from a silent auto-allow.

Verification

Measured in the desktop app from the CLI debug log, using permissionDecisionMs and whether a classifier request was issued:

Case Decision Result
Auto, preference read 0 ms No card, no classifier call (was 1957 ms)
Auto, preference write 1386 ms Classified as operation=set id=fontSize ..., allowed
Plan mode, preference read 0 ms No card, previously raised one
Edit Mode, preference read 0 ms No card
Auto, chmod -R 777 1595 ms Classifier consulted, allowed
Edit Mode, Bash One confirm card, allow runs the command, no second prompt

Confirms the classifier is genuinely in the loop (classifier_request_started / _finished for Bash and for preference writes) and that its asks reach the panel's Allow/Deny card.

… asks

Verified against the CLI's own debug log that Auto mode's classifier does
decide each Bash command (classifier_request_started / _finished, with
permissionDecisionMs going from 10ms on a fast path to ~1.6s when the
classifier actually runs), and that its asks reach our canUseTool handler
and surface as the panel's Allow/Deny card.

The CLI already discards a Bash allow rule passed via --allowedTools
("Ignoring dangerous permission Bash(*) from cliArg (bypasses
classifier)"), so dropping it here changes nothing today. It removes the
dependency on that behaviour: nothing in this list can pre-approve a
shell command should a future CLI stop discarding it.

- Bash off allowedTools, with the reasoning recorded beside the list.
- Edit Mode's Bash confirm returns an explicit allow rather than {}, so a
  command the user just approved is not then re-checked by the CLI.
- Log the tool, mode and the SDK's decisionReason whenever the CLI asks,
  so a genuine ask can be told apart from a silent auto-allow.
editorPreferences carried a permission allow rule, so in Auto every call
went straight through, `set` included, without the classifier ever seeing
it. Dropping the rule puts each write in front of the classifier, and a
PreToolUse hook allows `get` and `list` outright so a read never prompts
and never sits through a classifier round-trip either.

The tool reads and writes through one entry point, so it cannot carry a
static readOnlyHint the way getEditorState does; the operation has to be
inspected per call, which is why this needs a hook rather than an
annotation.

Verified in the desktop app against the CLI's debug log
(permissionDecisionMs, and whether a classifier request was made):

  Auto + get       0ms, no card   (was 1957ms through the classifier)
  Auto + set       1386ms, classified: "operation=set id=fontSize ..."
  Plan + get       0ms, no card   (previously raised a card)
  Edit Mode + get  0ms, no card
@sonarqubecloud

sonarqubecloud Bot commented Sep 5, 2026

Copy link
Copy Markdown

@abose
abose merged commit e0fe117 into main Sep 5, 2026
13 of 21 checks passed
@abose
abose deleted the ai branch September 5, 2026 07:11
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.

1 participant