Welcome back! In the previous chapters, we set the stage:
ls").Now, we need a judge to interpret these inputs and make a final verdict. That judge is the Permission Enforcement Engine.
Imagine the following scenario:
npm test".npm test.What happens? Does the "Allow" rule win? or does the "Read-Only" mode win?
We need a centralized logic pipeline to resolve these conflicts. We need an Enforcement Engine.
Think of the Permission Engine as a security checkpoint at an airport. It follows a strict sequence of checks before letting you board the plane (execute a tool).
This decision process happens inside the function hasPermissionsToUseTool. Let's visualize the flow:
Let's look at how this is implemented in permissions.ts. We will break the massive hasPermissionsToUseTool function into small, understandable chunks.
The very first thing the engine does is check for Deny rules. Safety comes first. If a user explicitly said "Never run rm", we stop immediately.
// File: permissions.ts (Simplified)
// 1. Check if the tool is listed in the Deny Rules
const denyRule = getDenyRuleForTool(context, tool)
if (denyRule) {
return {
behavior: 'deny',
message: `Permission to use ${tool.name} has been denied.`
}
}
Next, we ask the tool itself: "Do you see any problems with this input?"
For example, the Bash tool might see that you are trying to run a command that is syntactically invalid or inherently dangerous inside a specific environment.
// 2. Ask the tool implementation
// (Some tools have built-in safety checks)
const toolResult = await tool.checkPermissions(input, context)
if (toolResult.behavior === 'deny') {
return toolResult // The tool itself said "No"
}
If the tool is not denied, we check the Permission Mode (from Chapter 1). If the user is in Bypass (YOLO) mode, we skip the rest of the checks and allow the action.
Note: Even in Bypass mode, some "Safety Checks" (like overwriting specific config files) can still force a prompt.
// 3. Check if we are in Bypass Mode
const isBypass = context.mode === 'bypassPermissions'
if (isBypass) {
return {
behavior: 'allow',
decisionReason: { type: 'mode', mode: 'bypassPermissions' }
}
}
If we aren't in Bypass mode, we check the Rule System (from Chapter 2). Did the user write a rule like Bash(ls)?
// 4. Check if there is an Always Allow rule
const allowRule = toolAlwaysAllowedRule(context, tool)
if (allowRule) {
return {
behavior: 'allow',
decisionReason: { type: 'rule', rule: allowRule }
}
}
This is the most advanced part of the engine. If the user is in Auto Mode, we don't just "Allow everything." We delegate the decision to a smart AI Classifier.
The engine sends the tool use context to a specialized AI model. The AI decides if the action is "Safe" or "Dangerous."
// 5. Auto Mode Logic
if (context.mode === 'auto') {
// Call the AI Classifier (Detailed in Chapter 4)
const classifierResult = await classifyYoloAction(
context.messages,
toolName,
input
)
if (classifierResult.shouldBlock) {
return { behavior: 'deny', message: 'AI flagged as dangerous' }
}
// If AI says it's safe:
return { behavior: 'allow' }
}
If none of the above checks resulted in an "Allow" or "Deny," the default behavior is to Ask the user.
// 6. Default Fallback
return {
behavior: 'ask',
message: 'Claude requested permissions...'
}
Sometimes the agent runs in a "Headless" environment (like a background server) where there is no human to click "Approve."
In these cases, shouldAvoidPermissionPrompts is set to true. If the engine reaches Step 6 (Ask User), it detects that no user is present and automatically Denies the action to prevent the agent from hanging forever.
if (context.shouldAvoidPermissionPrompts) {
return {
behavior: 'deny',
message: 'Permission prompts are not available in this context'
}
}
In this chapter, we learned that the Permission Enforcement Engine is a pipeline that filters every action:
We briefly touched on the "AI Classifier" in Step 5. This is a fascinating system that uses a Large Language Model (LLM) to judge safety in real-time.
Next Chapter: Auto Mode Classifier (YOLO)
Generated by Code IQ