You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Defer native plan-mode approve so PermissionRequest exits plan mode
On approve, PreToolUse:ExitPlanMode returned permissionDecision "allow". In
native Claude Code that resolves the tool's permission early and skips the
plan-approval dialog, so the PermissionRequest hook — the only documented path
that exits plan mode — never fires. Claude Code then falls back to the TUI
prompt, forcing the user to approve a second time after clicking Approve in the
browser UI.
Gate the PreToolUse approve output on permission_mode: in native plan mode
("plan") emit permissionDecision "ask" instead of "allow". The dialog then
fires, PermissionRequest replays the cached approval as
{decision:{behavior:"allow"}}, and plan mode exits with no terminal prompt.
Conductor (bypassPermissions) and OpenCode/pi (default) have no
PermissionRequest, so they keep the immediate "allow".
- server/types.ts: PreToolUse permissionDecision union += "ask"
- server/runtime/decision.ts: defer approve when permission_mode === "plan"
- server/historyLifecycle.test.ts: plan-mode defer + PermissionRequest replay test
- AGENTS.md: document the deferral (with a don't-revert-to-allow warning)
server/index.ts unchanged: the PreToolUse decision cache write is already
ungated, so PermissionRequest can replay the "ask"-deferred approval.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: AGENTS.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -115,6 +115,8 @@ The deny feedback field (`permissionDecisionReason` or `decision.message`) conta
115
115
116
116
The hook is registered on both `PreToolUse` and `PermissionRequest`. `PreToolUse` fires before the permission flow and regardless of `--permission-mode`, which makes Request Changes (`deny`) work in Conductor. Native Claude Code still needs `PermissionRequest` to apply final plan approval. When both fire for the same tool call, the `PreToolUse` decision is cached briefly and replayed to `PermissionRequest` so the browser UI opens only once.
117
117
118
+
**Native plan-mode approve must be deferred, not allowed.** In native Claude Code the ExitPlanMode `PreToolUse` event arrives with `permission_mode: "plan"`. Returning `permissionDecision: "allow"` there resolves the tool's permission early and skips the plan-approval dialog — which means the `PermissionRequest` hook that actually exits plan mode never runs, and the user is forced to approve a second time in the TUI. So on approve in `permission_mode: "plan"` the binary emits `permissionDecision: "ask"` (defer) instead of `allow`; the dialog then fires, `PermissionRequest` replays the cached approval as `{decision:{behavior:"allow"}}`, and plan mode exits with no terminal prompt. Every other host (Conductor `bypassPermissions`, OpenCode/pi `default`) has no `PermissionRequest`, so those keep the immediate `PreToolUse``allow`. Do not "simplify" plan-mode approve back to `allow` — that reintroduces the double-approve.
119
+
118
120
The OpenCode bridge (`opencode/bridge.js`) constructs the same `HookEvent` format and parses the same `HookOutput` response, so the binary always goes through the same code path.
0 commit comments