source dump of claude code
at main 101 lines 3.3 kB view raw
1import { feature } from 'bun:bundle' 2import type { ToolPermissionContext } from '../../Tool.js' 3import { logForDebugging } from '../debug.js' 4import type { PermissionMode } from './PermissionMode.js' 5import { 6 getAutoModeUnavailableReason, 7 isAutoModeGateEnabled, 8 transitionPermissionMode, 9} from './permissionSetup.js' 10 11// Checks both the cached isAutoModeAvailable (set at startup by 12// verifyAutoModeGateAccess) and the live isAutoModeGateEnabled() — these can 13// diverge if the circuit breaker or settings change mid-session. The 14// live check prevents transitionPermissionMode from throwing 15// (permissionSetup.ts:~559), which would silently crash the shift+tab handler 16// and leave the user stuck at the current mode. 17function canCycleToAuto(ctx: ToolPermissionContext): boolean { 18 if (feature('TRANSCRIPT_CLASSIFIER')) { 19 const gateEnabled = isAutoModeGateEnabled() 20 const can = !!ctx.isAutoModeAvailable && gateEnabled 21 if (!can) { 22 logForDebugging( 23 `[auto-mode] canCycleToAuto=false: ctx.isAutoModeAvailable=${ctx.isAutoModeAvailable} isAutoModeGateEnabled=${gateEnabled} reason=${getAutoModeUnavailableReason()}`, 24 ) 25 } 26 return can 27 } 28 return false 29} 30 31/** 32 * Determines the next permission mode when cycling through modes with Shift+Tab. 33 */ 34export function getNextPermissionMode( 35 toolPermissionContext: ToolPermissionContext, 36 _teamContext?: { leadAgentId: string }, 37): PermissionMode { 38 switch (toolPermissionContext.mode) { 39 case 'default': 40 // Ants skip acceptEdits and plan — auto mode replaces them 41 if (process.env.USER_TYPE === 'ant') { 42 if (toolPermissionContext.isBypassPermissionsModeAvailable) { 43 return 'bypassPermissions' 44 } 45 if (canCycleToAuto(toolPermissionContext)) { 46 return 'auto' 47 } 48 return 'default' 49 } 50 return 'acceptEdits' 51 52 case 'acceptEdits': 53 return 'plan' 54 55 case 'plan': 56 if (toolPermissionContext.isBypassPermissionsModeAvailable) { 57 return 'bypassPermissions' 58 } 59 if (canCycleToAuto(toolPermissionContext)) { 60 return 'auto' 61 } 62 return 'default' 63 64 case 'bypassPermissions': 65 if (canCycleToAuto(toolPermissionContext)) { 66 return 'auto' 67 } 68 return 'default' 69 70 case 'dontAsk': 71 // Not exposed in UI cycle yet, but return default if somehow reached 72 return 'default' 73 74 75 default: 76 // Covers auto (when TRANSCRIPT_CLASSIFIER is enabled) and any future modes — always fall back to default 77 return 'default' 78 } 79} 80 81/** 82 * Computes the next permission mode and prepares the context for it. 83 * Handles any context cleanup needed for the target mode (e.g., stripping 84 * dangerous permissions when entering auto mode). 85 * 86 * @returns The next mode and the context to use (with dangerous permissions stripped if needed) 87 */ 88export function cyclePermissionMode( 89 toolPermissionContext: ToolPermissionContext, 90 teamContext?: { leadAgentId: string }, 91): { nextMode: PermissionMode; context: ToolPermissionContext } { 92 const nextMode = getNextPermissionMode(toolPermissionContext, teamContext) 93 return { 94 nextMode, 95 context: transitionPermissionMode( 96 toolPermissionContext.mode, 97 nextMode, 98 toolPermissionContext, 99 ), 100 } 101}