π File detail
utils/permissions/getNextPermissionMode.ts
π― Use case
This file lives under βutils/β, which covers cross-cutting helpers (shell, tempfiles, settings, messages, process input, β¦). On the API surface it exposes getNextPermissionMode and cyclePermissionMode β mainly functions, hooks, or classes. Dependencies touch bun:bundle. It composes internal code from Tool, debug, PermissionMode, and permissionSetup (relative imports). What the file header says: Checks both the cached isAutoModeAvailable (set at startup by verifyAutoModeGateAccess) and the live isAutoModeGateEnabled() β these can.
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
Checks both the cached isAutoModeAvailable (set at startup by verifyAutoModeGateAccess) and the live isAutoModeGateEnabled() β these can
π€ Exports (heuristic)
getNextPermissionModecyclePermissionMode
π External import roots
Package roots from from "β¦" (relative paths omitted).
bun:bundle
π₯οΈ Source preview
import { feature } from 'bun:bundle'
import type { ToolPermissionContext } from '../../Tool.js'
import { logForDebugging } from '../debug.js'
import type { PermissionMode } from './PermissionMode.js'
import {
getAutoModeUnavailableReason,
isAutoModeGateEnabled,
transitionPermissionMode,
} from './permissionSetup.js'
// Checks both the cached isAutoModeAvailable (set at startup by
// verifyAutoModeGateAccess) and the live isAutoModeGateEnabled() β these can
// diverge if the circuit breaker or settings change mid-session. The
// live check prevents transitionPermissionMode from throwing
// (permissionSetup.ts:~559), which would silently crash the shift+tab handler
// and leave the user stuck at the current mode.
function canCycleToAuto(ctx: ToolPermissionContext): boolean {
if (feature('TRANSCRIPT_CLASSIFIER')) {
const gateEnabled = isAutoModeGateEnabled()
const can = !!ctx.isAutoModeAvailable && gateEnabled
if (!can) {
logForDebugging(
`[auto-mode] canCycleToAuto=false: ctx.isAutoModeAvailable=${ctx.isAutoModeAvailable} isAutoModeGateEnabled=${gateEnabled} reason=${getAutoModeUnavailableReason()}`,
)
}
return can
}
return false
}
/**
* Determines the next permission mode when cycling through modes with Shift+Tab.
*/
export function getNextPermissionMode(
toolPermissionContext: ToolPermissionContext,
_teamContext?: { leadAgentId: string },
): PermissionMode {
switch (toolPermissionContext.mode) {
case 'default':
// Ants skip acceptEdits and plan β auto mode replaces them
if (process.env.USER_TYPE === 'ant') {
if (toolPermissionContext.isBypassPermissionsModeAvailable) {
return 'bypassPermissions'
}
if (canCycleToAuto(toolPermissionContext)) {
return 'auto'
}
return 'default'
}
return 'acceptEdits'
case 'acceptEdits':
return 'plan'
case 'plan':
if (toolPermissionContext.isBypassPermissionsModeAvailable) {
return 'bypassPermissions'
}
if (canCycleToAuto(toolPermissionContext)) {
return 'auto'
}
return 'default'
case 'bypassPermissions':
if (canCycleToAuto(toolPermissionContext)) {
return 'auto'
}
return 'default'
case 'dontAsk':
// Not exposed in UI cycle yet, but return default if somehow reached
return 'default'
default:
// Covers auto (when TRANSCRIPT_CLASSIFIER is enabled) and any future modes β always fall back to default
return 'default'
}
}
/**
* Computes the next permission mode and prepares the context for it.
* Handles any context cleanup needed for the target mode (e.g., stripping
* dangerous permissions when entering auto mode).
*
* @returns The next mode and the context to use (with dangerous permissions stripped if needed)
*/
export function cyclePermissionMode(
toolPermissionContext: ToolPermissionContext,
teamContext?: { leadAgentId: string },
): { nextMode: PermissionMode; context: ToolPermissionContext } {
const nextMode = getNextPermissionMode(toolPermissionContext, teamContext)
return {
nextMode,
context: transitionPermissionMode(
toolPermissionContext.mode,
nextMode,
toolPermissionContext,
),
}
}