Claude Code - Skip run_tool HITL elicitation in bypassPermissions mode#35
Merged
Merged
Conversation
When Claude Code runs with --dangerously-skip-permissions, the user has opted out of every approval prompt, so the plugin's own HITL elicitation popup is a redundant gate. The PreToolUse hook now records Claude Code's live permission_mode to a per-session marker under the plugin data dir (keyed by session id, written on each run_tool call). handleRunTool reads that marker and, only when the mode is bypassPermissions, skips elicitation and executes directly. Fail-safe: a missing/malformed marker or a session-id mismatch keeps the gate, and session-keying prevents a bypass in one session from suppressing prompts in another. Scoped to run_tool and to bypassPermissions alone.
eshwar-sundar-glean
marked this pull request as ready for review
July 1, 2026 10:12
eshwar-sundar-glean
requested review from
garvit-scio,
mohit-gupta-glean and
swarup-padhi-glean
as code owners
July 1, 2026 10:12
swarup-padhi-glean
approved these changes
Jul 1, 2026
swarup-padhi-glean
left a comment
Contributor
There was a problem hiding this comment.
Curious why we store a timestamp field in the marker
Address the case where a --dangerously-skip-permissions session is later resumed WITHOUT the flag (same session id): its leftover bypassPermissions marker must not keep suppressing the gate. The PreToolUse hook already rewrites the per-session marker with the current permission_mode on every run_tool call, and PreToolUse runs before the tool, so the resumed session overwrites the stale marker with its live mode before the server reads it. Added a hook test asserting the overwrite, and documented the resume-safety guarantee on the server read.
permissionModeMarkerPath()'s fallback used os.tmpdir() while the PreToolUse hook writes to ~/.glean when PLUGIN_DATA_DIR and CLAUDE_PLUGIN_DATA are both unset. start.sh always sets PLUGIN_DATA_DIR so this was masked in production, but a direct node/tsx launch (no start.sh) would read a different path than the hook wrote. Fall back to ~/.glean so the read and write paths stay in lockstep, matching the documented behavior.
mohit-gupta-glean
approved these changes
Jul 1, 2026
Per review: the PreToolUse hook (write path) cannot see the server-only PLUGIN_DATA_DIR that start.sh derives, so it keys off CLAUDE_PLUGIN_DATA || ~/.glean. The server's read path led with PLUGIN_DATA_DIR, which was an asymmetry even though start.sh makes them resolve identically. Drop PLUGIN_DATA_DIR from the read path so both sides use the exact same expression. Tests stub CLAUDE_PLUGIN_DATA accordingly.
| process.env.PLUGIN_DATA_DIR || | ||
| process.env.CLAUDE_PLUGIN_DATA || | ||
| path.join(os.homedir(), ".glean"); | ||
| process.env.CLAUDE_PLUGIN_DATA || path.join(os.homedir(), ".glean"); |
Collaborator
Author
There was a problem hiding this comment.
For now this is clean because this is only applicable to claude and cursor is not regressed because of this
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.
What
When Claude Code is launched with
--dangerously-skip-permissions(permission_mode: "bypassPermissions"), the user has opted out of every approval prompt for the session. The plugin's own HITL elicitation popup is then a redundant second gate — and, worse, when that elicitation times out it currently blocks therun_toolcall entirely.This change makes the plugin skip its elicitation gate and execute directly when — and only when — the session is in
bypassPermissionsmode. Scoped deliberately torun_tooland tobypassPermissionsalone.Why it needs a hook → server handoff
The elicitation popup is fired by the MCP server (
handleRunTool→mcpServer.elicitInput), but the live permission mode is only visible to the PreToolUse hook (via its stdinpermission_modefield). There is no env var or MCPinitializefield that carries the permission mode to the server, and the mode can change mid-session — so the hook is the only channel.How
plugins/glean/hooks/auto-approve-run-tool.mjs— on every gleanrun_toolcall (whileENABLE_HITLis on), writes{ permission_mode, ts }to<CLAUDE_PLUGIN_DATA|~/.glean>/glean-hitl-mode/<sessionId>.json. Best-effort; a write failure never affects the existing auto-approve decision.src/tools/run-tool.ts— before eliciting, reads that per-session marker; if the mode isbypassPermissions, skips the popup and calls the tool directly.The base dir + session-id sanitization are kept in lockstep across the hook,
run-tool.ts, andstart.sh'sPLUGIN_DATA_DIRexport.Safety
--dangerously-skip-permissionsand later resumed without it (same session id) has its leftoverbypassPermissionsmarker overwritten with the resumed mode by the hook on the resumed session's firstrun_toolcall — before the server reads it — so the gate re-engages. No stale-marker leak across resume.Testing
npm run typecheck— cleannpm test— 179 pass, incl. server cases (bypass skips elicit;default/ absent / other-session all still elicit) and hook cases (marker written / session-keyed / sanitized; overwrites a leftover bypass marker on resume; nothing written when HITL off, non-glean, or mode absent).npm run build—dist/index.jsrebuilt.Verification still worth doing end-to-end
Two assumptions the implementation is safe against but that confirm the feature actually fires:
--dangerously-skip-permissions(strongly implied by the docs;bypassPermissionsis a documented hookpermission_modevalue).session_idequals theCLAUDE_CODE_SESSION_IDthatstart.shfeeds the server asGLEAN_SESSION_ID.Both are confirmed by running once with the flag and checking that a marker lands under
~/.glean/glean-hitl-mode/.