@@ -26,6 +26,7 @@ import {
2626import { getFilePath } from "@utils/getFilePath" ;
2727import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
2828import { getSessionService } from "../service/service" ;
29+ import { flattenSelectOptions } from "../stores/sessionStore" ;
2930import {
3031 useSessionViewActions ,
3132 useShowRawLogs ,
@@ -70,6 +71,25 @@ interface SessionViewProps {
7071const DEFAULT_ERROR_MESSAGE =
7172 "Failed to resume this session. The working directory may have been deleted. Please start a new session." ;
7273
74+ /**
75+ * When an allow_always permission is granted outside a mode-switch prompt,
76+ * ratchet the session to the closest "auto-accept edits" preset offered by
77+ * this adapter's mode catalog. Claude exposes `acceptEdits`; Codex has no
78+ * exact equivalent, so fall back to `auto`. Returns undefined if neither is
79+ * available (in which case leave the current mode untouched).
80+ */
81+ function resolveAllowAlwaysUpgradeMode (
82+ modeOption : ReturnType < typeof useModeConfigOptionForTask > ,
83+ ) : string | undefined {
84+ if ( modeOption ?. type !== "select" ) return undefined ;
85+ const availableIds = new Set (
86+ flattenSelectOptions ( modeOption . options ) . map ( ( opt ) => opt . value ) ,
87+ ) ;
88+ if ( availableIds . has ( "acceptEdits" ) ) return "acceptEdits" ;
89+ if ( availableIds . has ( "auto" ) ) return "auto" ;
90+ return undefined ;
91+ }
92+
7393export function SessionView ( {
7494 events,
7595 taskId,
@@ -109,6 +129,11 @@ export function SessionView({
109129
110130 useEffect ( ( ) => {
111131 if ( allowBypassPermissions ) return ;
132+ // Cloud runs execute in an isolated sandbox where bypass is safe, and the
133+ // agent's own gate (ALLOW_BYPASS = !IS_ROOT || IS_SANDBOX) already permits
134+ // it regardless of this local preference. Auto-reverting here would clobber
135+ // the user's explicit plan-approval choice and strand them in Plan Mode.
136+ if ( isCloud ) return ;
112137 const isBypass =
113138 currentModeId === "bypassPermissions" || currentModeId === "full-access" ;
114139 if ( isBypass && taskId ) {
@@ -118,7 +143,7 @@ export function SessionView({
118143 "default" ,
119144 ) ;
120145 }
121- } , [ allowBypassPermissions , currentModeId , taskId ] ) ;
146+ } , [ allowBypassPermissions , currentModeId , taskId , isCloud ] ) ;
122147
123148 const handleModeChange = useCallback (
124149 ( nextMode : string ) => {
@@ -227,11 +252,18 @@ export function SessionView({
227252 const isModeSwitch =
228253 firstPendingPermission . toolCall ?. kind === "switch_mode" ;
229254 if ( selectedOption ?. kind === "allow_always" && ! isModeSwitch ) {
230- getSessionService ( ) . setSessionConfigOptionByCategory (
231- taskId ,
232- "mode" ,
233- "acceptEdits" ,
234- ) ;
255+ // Pick the adapter-appropriate "upgrade" mode. Claude exposes
256+ // acceptEdits; Codex does not — its closest analogue is auto. Resolve
257+ // against the session's advertised mode catalog so the footer label
258+ // stays coherent with the dropdown contents.
259+ const upgradeMode = resolveAllowAlwaysUpgradeMode ( modeOption ) ;
260+ if ( upgradeMode ) {
261+ getSessionService ( ) . setSessionConfigOptionByCategory (
262+ taskId ,
263+ "mode" ,
264+ upgradeMode ,
265+ ) ;
266+ }
235267 }
236268
237269 if ( customInput ) {
@@ -268,7 +300,14 @@ export function SessionView({
268300
269301 requestFocus ( sessionId ) ;
270302 } ,
271- [ firstPendingPermission , taskId , onSendPrompt , requestFocus , sessionId ] ,
303+ [
304+ firstPendingPermission ,
305+ taskId ,
306+ onSendPrompt ,
307+ requestFocus ,
308+ sessionId ,
309+ modeOption ,
310+ ] ,
272311 ) ;
273312
274313 const handlePermissionCancel = useCallback ( async ( ) => {
0 commit comments