bypassPermissionsKillswitch.ts
utils/permissions/bypassPermissionsKillswitch.ts
156
Lines
4839
Bytes
6
Exports
6
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with shell-safety, permissions. It contains 156 lines, 6 detected imports, and 6 detected exports.
Important relationships
- utils/permissions/PermissionMode.ts
- utils/permissions/PermissionPromptToolResultSchema.ts
- utils/permissions/PermissionResult.ts
- utils/permissions/PermissionRule.ts
- utils/permissions/PermissionUpdate.ts
- utils/permissions/PermissionUpdateSchema.ts
- utils/permissions/autoModeState.ts
- utils/permissions/bashClassifier.ts
Detected exports
checkAndDisableBypassPermissionsIfNeededresetBypassPermissionsCheckuseKickOffCheckAndDisableBypassPermissionsIfNeededcheckAndDisableAutoModeIfNeededresetAutoModeGateCheckuseKickOffCheckAndDisableAutoModeIfNeeded
Keywords
toolpermissioncontextprevvoidsetappstatefastmodeappstateuseappstatechecknewstatebypasspermissionscheckran
Detected imports
bun:bundlereactsrc/state/AppState.jssrc/Tool.js../../bootstrap/state.js./permissionSetup.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
import { feature } from 'bun:bundle'
import { useEffect, useRef } from 'react'
import {
type AppState,
useAppState,
useAppStateStore,
useSetAppState,
} from 'src/state/AppState.js'
import type { ToolPermissionContext } from 'src/Tool.js'
import { getIsRemoteMode } from '../../bootstrap/state.js'
import {
createDisabledBypassPermissionsContext,
shouldDisableBypassPermissions,
verifyAutoModeGateAccess,
} from './permissionSetup.js'
let bypassPermissionsCheckRan = false
export async function checkAndDisableBypassPermissionsIfNeeded(
toolPermissionContext: ToolPermissionContext,
setAppState: (f: (prev: AppState) => AppState) => void,
): Promise<void> {
// Check if bypassPermissions should be disabled based on Statsig gate
// Do this only once, before the first query, to ensure we have the latest gate value
if (bypassPermissionsCheckRan) {
return
}
bypassPermissionsCheckRan = true
if (!toolPermissionContext.isBypassPermissionsModeAvailable) {
return
}
const shouldDisable = await shouldDisableBypassPermissions()
if (!shouldDisable) {
return
}
setAppState(prev => {
return {
...prev,
toolPermissionContext: createDisabledBypassPermissionsContext(
prev.toolPermissionContext,
),
}
})
}
/**
* Reset the run-once flag for checkAndDisableBypassPermissionsIfNeeded.
* Call this after /login so the gate check re-runs with the new org.
*/
export function resetBypassPermissionsCheck(): void {
bypassPermissionsCheckRan = false
}
export function useKickOffCheckAndDisableBypassPermissionsIfNeeded(): void {
const toolPermissionContext = useAppState(s => s.toolPermissionContext)
const setAppState = useSetAppState()
// Run once, when the component mounts
useEffect(() => {
if (getIsRemoteMode()) return
void checkAndDisableBypassPermissionsIfNeeded(
toolPermissionContext,
setAppState,
)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
}
let autoModeCheckRan = false
export async function checkAndDisableAutoModeIfNeeded(
toolPermissionContext: ToolPermissionContext,
setAppState: (f: (prev: AppState) => AppState) => void,
fastMode?: boolean,
): Promise<void> {
if (feature('TRANSCRIPT_CLASSIFIER')) {
if (autoModeCheckRan) {
return
}
autoModeCheckRan = true
const { updateContext, notification } = await verifyAutoModeGateAccess(
toolPermissionContext,
fastMode,
)
setAppState(prev => {
// Apply the transform to CURRENT context, not the stale snapshot we
// passed to verifyAutoModeGateAccess. The async GrowthBook await inside
// can be outrun by a mid-turn shift-tab; spreading a stale context here
// would revert the user's mode change.
const nextCtx = updateContext(prev.toolPermissionContext)
const newState =
nextCtx === prev.toolPermissionContext
? prev
: { ...prev, toolPermissionContext: nextCtx }
if (!notification) return newState
return {
...newState,
notifications: {
...newState.notifications,
queue: [
...newState.notifications.queue,
{
key: 'auto-mode-gate-notification',
text: notification,
color: 'warning' as const,
priority: 'high' as const,
},
],
},
}
})
}
}
/**
* Reset the run-once flag for checkAndDisableAutoModeIfNeeded.
* Call this after /login so the gate check re-runs with the new org.
*/
export function resetAutoModeGateCheck(): void {
autoModeCheckRan = false
}
export function useKickOffCheckAndDisableAutoModeIfNeeded(): void {
const mainLoopModel = useAppState(s => s.mainLoopModel)
const mainLoopModelForSession = useAppState(s => s.mainLoopModelForSession)
const fastMode = useAppState(s => s.fastMode)
const setAppState = useSetAppState()
const store = useAppStateStore()
const isFirstRunRef = useRef(true)
// Runs on mount (startup check) AND whenever the model or fast mode changes
// (kick-out / carousel-restore). Watching both model fields covers /model,
// Cmd+P picker, /config, and bridge onSetModel paths; fastMode covers
// /fast on|off for the tengu_auto_mode_config.disableFastMode circuit
// breaker. The print.ts headless paths are covered by the sync
// isAutoModeGateEnabled() check.
useEffect(() => {
if (getIsRemoteMode()) return
if (isFirstRunRef.current) {
isFirstRunRef.current = false
} else {
resetAutoModeGateCheck()
}
void checkAndDisableAutoModeIfNeeded(
store.getState().toolPermissionContext,
setAppState,
fastMode,
)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mainLoopModel, mainLoopModelForSession, fastMode])
}