diff --git a/src/cli/App.tsx b/src/cli/App.tsx index 77d2c6d..27d6568 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -932,14 +932,17 @@ export default function App({ const [networkPhase, setNetworkPhase] = useState< "upload" | "download" | "error" | null >(null); - // Track permission mode changes for UI updates - const [uiPermissionMode, setUiPermissionMode] = useState( + // Track permission mode changes for UI updates. + // Keep a ref in sync *synchronously* so async approval classification never + // reads a stale mode during the render/effect window. + const [uiPermissionMode, _setUiPermissionMode] = useState( permissionMode.getMode(), ); - const uiPermissionModeRef = useRef(uiPermissionMode); - useEffect(() => { - uiPermissionModeRef.current = uiPermissionMode; - }, [uiPermissionMode]); + const uiPermissionModeRef = useRef(uiPermissionMode); + const setUiPermissionMode = useCallback((mode: PermissionMode) => { + uiPermissionModeRef.current = mode; + _setUiPermissionMode(mode); + }, []); const statusLineTriggerVersionRef = useRef(0); const [statusLineTriggerVersion, setStatusLineTriggerVersion] = useState(0); @@ -4884,6 +4887,7 @@ export default function App({ syncTrajectoryElapsedBase, closeTrajectorySegment, resetTrajectoryBases, + setUiPermissionMode, ], ); @@ -10672,7 +10676,7 @@ ${SYSTEM_REMINDER_CLOSE} setUiPermissionMode("default"); } } - }, []); + }, [setUiPermissionMode]); // Handle permission mode changes from the Input component (e.g., shift+tab cycling) const handlePermissionModeChange = useCallback( @@ -10686,7 +10690,7 @@ ${SYSTEM_REMINDER_CLOSE} setUiPermissionMode(mode); triggerStatusLineRefresh(); }, - [triggerStatusLineRefresh], + [triggerStatusLineRefresh, setUiPermissionMode], ); // Reasoning tier cycling (Tab hotkey in InputRich.tsx) @@ -10924,6 +10928,7 @@ ${SYSTEM_REMINDER_CLOSE} appendError, refreshDerived, setStreaming, + setUiPermissionMode, ], ); @@ -11146,7 +11151,13 @@ Plan file path: ${planFilePath}`; } else { setApprovalResults((prev) => [...prev, decision]); } - }, [pendingApprovals, approvalResults, sendAllResults, refreshDerived]); + }, [ + pendingApprovals, + approvalResults, + sendAllResults, + refreshDerived, + setUiPermissionMode, + ]); const handleEnterPlanModeReject = useCallback(async () => { const currentIndex = approvalResults.length;