fix: a guardrail with no objection was disabling the user's permission prompts - #62
Merged
Merged
Conversation
…n prompts
On claude_code and vscode_copilot a bare Decision.allow() at pre_tool emitted
hookSpecificOutput.permissionDecision:"allow". That is not the same statement as "the
handler has no objection", and the difference is the user's confirmation dialog.
For VS Code this is proven from the vendor's source rather than inferred:
languageModelToolsService returns autoConfirmed {ConfirmationNotNeeded, "Allowed by
hook"} on exactly that value. For Claude Code the vendor documents what SILENCE does,
verbatim -- "Exit code 0 with no output means the hook has no decision to report, so the
tool call continues through the normal permission flow" -- and says nothing about an
explicit allow. One option has recorded, safe semantics; the other is a guess, and it is
not a guess worth making with the user's protection.
A handler returns allow for everything its policy does not match, which on a normal
session is every tool call. So installing agentseam turned the user's confirmation
prompts off for the whole session -- the exact inverse of what a guardrail is for.
A bare allow is now silence. deny, ask and rewrite are unchanged, and rewrite keeps the
explicit allow because updatedInput is the only way to express one and approving the
substituted call is what the handler asked for. codex_cli already behaved this way, for
a different vendor reason: it rejects permissionDecision:allow without updatedInput.
This is a behaviour change for anyone relying on allow to auto-approve. That reliance
was on undocumented semantics on Claude Code, and on prompt-suppression on VS Code.
Signed-off-by: Claude <noreply@anthropic.com>
jothimani-rajendran
marked this pull request as ready for review
August 28, 2026 15:35
jothimani-rajendran
pushed a commit
that referenced
this pull request
Aug 28, 2026
Only CHANGELOG.md conflicted -- both sides prepend to the same '### Fixed' list -- and both entries are kept, #62's first as the older one. The two adapters #62 also touched, claude_code.py and vscode_copilot.py, auto-merged cleanly. Verified after resolving that both changes survive: a bare allow is still silence (#62), and no adapter crashes on a non-dict tool_input (#63). Signed-off-by: Claude <noreply@anthropic.com>
This was referenced Aug 28, 2026
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The most serious thing found in the un-itemised remainder, and a behaviour change — flagging that explicitly.
What was happening
On
claude_codeandvscode_copilot, a bareDecision.allow()atpre_toolemitted:{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "allow"}}"The handler has no objection" and "approve this without asking the user" are different statements. The difference is the user's confirmation dialog.
A handler returns allow for everything its policy does not match — which on a normal session is every tool call. So installing agentseam turned the user's confirmation prompts off for the whole session. The exact inverse of what a guardrail is for.
The evidence, which differs by vendor
VS Code — proven from source, not inferred.
languageModelToolsService.ts:Claude Code — the vendor documents what silence does, verbatim:
and says nothing about what an explicit allow does. So one option has recorded, safe semantics and the other is a guess. That is not a guess worth making with the user's protection — the same reasoning that kept
tabnine's vocabulary unchanged rather than swapped for another unrecorded word.The change
A bare allow is now silence. Unchanged:
allowpermissionDecision: "allow"""denypermissionDecision: "deny"askpermissionDecision: "ask"rewriteallow+updatedInputRewrite deliberately keeps the explicit allow:
updatedInputis the only way to express a rewrite, and approving the substituted call is precisely what the handler asked for.codex_clialready behaved this way, for an unrelated vendor reason — it rejectspermissionDecision: allowwithoutupdatedInput. So the repo was inconsistent, and the odd one out was the safe one.Behaviour change
Anyone relying on
allowto auto-approve loses that. On Claude Code that reliance was on undocumented semantics; on VS Code it was on prompt-suppression. Neither is something a guardrail library should do by default, and neither was ever stated as a feature.Verification
ruff check,ruff format --check, full suite: 857 passed. The test that pinned the old behaviour now pins the new one, with the reasoning in its docstring.🤖 Generated with Claude Code
https://claude.ai/code/session_01Y9DXWmRN9A2q5BXAiexapu
Generated by Claude Code