From 8a41cd480aa81611f3cb61d8e85bf055dce64832 Mon Sep 17 00:00:00 2001 From: Ramnique Singh <30795890+ramnique@users.noreply.github.com> Date: Fri, 10 Jul 2026 23:55:07 +0530 Subject: [PATCH] fix(x): hide grant-scope buttons where no grant persistence exists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The permission card offered "Allow for Session" and "Always Allow" on the turns path (full-screen chat and side-pane), but the sessions layer never persists grants — the scope rode respondToPermission metadata that nothing consumes, so both buttons behaved exactly like "Approve" once while telling the user otherwise. Grant persistence itself is deferred until there is a sound notion of grant keys (executeCommand parsing is not AST-based), and auto-permission mode covers the fatigue meanwhile. The dropdown now renders only when the caller wires the scope handlers. The legacy code-mode chat keeps them (runs:authorizePermission does persist grants there); the turns-path call sites stop passing them and drop the dead scope plumbing from their handlers. Co-Authored-By: Claude Fable 5 --- apps/x/apps/renderer/src/App.tsx | 4 ---- .../src/components/ai-elements/permission-request.tsx | 11 +++++++++-- apps/x/apps/renderer/src/components/chat-sidebar.tsx | 4 +--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index bb75a1eb..89907284 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -3150,7 +3150,6 @@ function App() { toolCallId: string, subflow: string[], response: 'approve' | 'deny', - scope?: 'once' | 'session' | 'always', ) => { if (!runId) return @@ -3159,7 +3158,6 @@ function App() { await sessionChat.respondToPermission( toolCallId, response === 'approve' ? 'allow' : 'deny', - scope ? { scope } : undefined, ) } catch (error) { console.error('Failed to authorize permission:', error) @@ -6834,8 +6832,6 @@ function App() { toolCall={permRequest.toolCall} permission={permRequest.permission} onApprove={() => handlePermissionResponse(permRequest.toolCall.toolCallId, permRequest.subflow, 'approve')} - onApproveSession={() => handlePermissionResponse(permRequest.toolCall.toolCallId, permRequest.subflow, 'approve', 'session')} - onApproveAlways={() => handlePermissionResponse(permRequest.toolCall.toolCallId, permRequest.subflow, 'approve', 'always')} onDeny={() => handlePermissionResponse(permRequest.toolCall.toolCallId, permRequest.subflow, 'deny')} isProcessing={isActive && activeIsWorking} response={response} diff --git a/apps/x/apps/renderer/src/components/ai-elements/permission-request.tsx b/apps/x/apps/renderer/src/components/ai-elements/permission-request.tsx index 9317bcae..961e85f9 100644 --- a/apps/x/apps/renderer/src/components/ai-elements/permission-request.tsx +++ b/apps/x/apps/renderer/src/components/ai-elements/permission-request.tsx @@ -67,6 +67,13 @@ export const PermissionRequest = ({ const isResponded = response !== null; const isApproved = response === 'approve'; + // Scope actions ("Allow for Session"/"Always Allow") render only when the + // caller wires them: the legacy code-mode path persists grants, but the + // turns path has no grant persistence yet and must not show dead buttons. + const hasScopeActions = + Boolean(onApproveSession || onApproveAlways) && + Boolean(command || filePermission); + // Once a response is chosen, collapse the details to just the header. // Users can click the header to expand them again. const [expanded, setExpanded] = useState(false); @@ -180,12 +187,12 @@ export const PermissionRequest = ({ size="sm" onClick={onApprove} disabled={isProcessing} - className={cn("flex-1", (command || filePermission) && "rounded-r-none")} + className={cn("flex-1", hasScopeActions && "rounded-r-none")} > Approve - {(command || filePermission) && ( + {hasScopeActions && (