How the Permission System Works in agent-core-v2: Understanding PermissionManager
The permission system in agent-core-v2 centers around the PermissionManager class, which uses configurable modes, rules, and policy objects to decide whether AI tool calls execute automatically, require user approval, or are blocked entirely.
In the MoonshotAI/kimi-code repository, the agent-core-v2 package implements a sophisticated security layer that governs how AI agents interact with external tools. This system ensures that potentially dangerous operations—such as file deletions or network requests—are either automatically vetted, explicitly approved by users, or prevented based on customizable security policies.
Core Architecture of the Permission System
The permission system is built around four fundamental concepts that work together to create a flexible, hierarchical security model.
PermissionManager Class
At the heart of the system lies the PermissionManager class defined in src/agent/permission/index.ts. This class serves as the central authority for all permission decisions within an agent session. When initialized, it accepts an optional parent manager (enabling permission inheritance for sub-agents) and an array of initial rules【PermissionManager constructor (lines 35‑41)】.
The manager maintains an internal list of permission policies created via createPermissionDecisionPolicies(this.agent), which are evaluated sequentially whenever a tool call is intercepted.
Permission Modes: manual, auto, and yolo
The system supports three distinct PermissionMode values that determine default behavior:
manual– Requires explicit user approval for every tool callauto– Automatically approves calls that policies deem safeyolo– Bypasses most security checks (use with caution)
The effective mode is resolved through a hierarchy: modeOverride ?? parent?.mode ?? 'manual', allowing child agents to override parent settings while falling back to safe defaults【mode getter (lines 44‑46)】.
Rules and Policies
PermissionRule objects define pattern-based matching against tool names or arguments, while PermissionPolicy objects implement the actual decision logic through an evaluate(context) method. When a tool call occurs, the manager constructs a PermissionPolicyContext containing the tool name, arguments, execution metadata, and trace ID, then consults policies until one returns a definitive decision.
How Permission Evaluation Works
The permission flow follows a strict six-step pipeline that intercepts tool execution before any side effects occur.
1. Initialization and Policy Setup
When an agent spawns, it instantiates a PermissionManager with optional initial rules:
const manager = new PermissionManager(agent, {
initialRules: [{ pattern: 'git/*', mode: 'deny' }],
});
This setup creates the policy chain via createPermissionDecisionPolicies, which typically includes guards for plan-mode restrictions and dangerous operation blocks.
2. Tool Call Interception
Before any tool executes, the agent invokes PermissionManager.beforeToolCall(context)【beforeToolCall (lines 96‑110)】. This method acts as the gatekeeper, evaluating policies and converting their decisions into a PrepareToolExecutionResult that either allows execution, blocks it, or triggers the approval workflow.
3. Sequential Policy Evaluation
The evaluatePolicies method iterates through registered policies until one returns a non-undefined result【evaluatePolicies (lines 52‑60)】. Each policy can return:
approve– Execute the tool immediatelydeny– Block execution with a formatted message【formatPolicyDenyMessage(lines 13‑18)】ask– Initiate user approval flowresult– A customPrepareToolExecutionResultfor specialized handling
4. User Approval Workflow
When a policy returns ask, the system invokes requestToolApproval, which builds a display payload and fires a PermissionRequest hook to the client-side RPC【requestToolApproval (lines 16‑57, 59‑86, 100‑122)】. The method calls rpc.requestApproval and awaits one of three responses:
Approved– The tool proceeds to executionRejectedorCancelled– Execution halts with a user-friendly block message【formatApprovalRejectionMessage(lines 92‑110)】
5. Telemetry and Audit Logging
Every decision—whether allow, deny, or ask—is tracked via agent.telemetry.track, providing production visibility into permission behavior and helping developers identify patterns in tool usage or security violations.
Implementing Custom Permission Rules
Developers can customize the permission system at runtime or during initialization:
// Change mode dynamically based on UI preferences
manager.setMode('auto');
// Intercept and handle tool calls manually
async function runTool(context: PermissionPolicyContext) {
const prepare = await manager.beforeToolCall(context);
if (prepare?.block) {
console.log('Blocked:', prepare.reason);
return;
}
// Proceed with actual tool execution
}
The test suite demonstrates these patterns in action. For instance, enter-plan-mode.test.ts verifies that setting the permission mode correctly propagates to the RPC layer【line 8】, while plan-mode-hard-block.test.ts validates that denial policies generate appropriate block messages【lines 10‑15】.
Key Source Files in agent-core-v2
Understanding the permission system requires familiarity with these specific files in the MoonshotAI/kimi-code repository:
src/agent/permission/index.ts– CorePermissionManagerimplementation with the constructor, mode resolution, and approval workflowssrc/agent/permission/types.ts– TypeScript definitions forPermissionMode,PermissionRule,PermissionPolicyContext, and related interfacessrc/agent/permission/policies/*– Built-in policy implementations including plan-mode guards and safety checkspackages/agent-core/test/tools/planning/– Test suite covering permission flows including plan-mode transitions and hard blocks
Summary
- The PermissionManager class in
src/agent/permission/index.tsserves as the central authority for tool execution decisions, supporting hierarchical permission inheritance. - Three PermissionMode values (
manual,auto,yolo) control default behavior, resolved viamodeOverride ?? parent?.mode ?? 'manual'【lines 44‑46】. - Policies evaluate tool calls sequentially via
evaluatePolicies, returning decisions that either approve, deny, request user input, or provide custom results【lines 52‑60】. - The approval flow leverages
requestToolApprovalto communicate with client-side RPC, handlingApproved,Rejected, andCancelledstates with appropriate user messaging【lines 16‑122】. - Comprehensive telemetry tracks every permission decision through
agent.telemetry.track, enabling production monitoring of security boundaries.
Frequently Asked Questions
What is the difference between permission rules and permission policies in agent-core-v2?
Permission rules are pattern-based configurations (typically matching tool names like git/*) that define static constraints, while permission policies are executable objects with an evaluate(context) method that implement dynamic decision logic. Rules are stored in the manager and inherited from parents, whereas policies are instantiated via createPermissionDecisionPolicies and consulted at runtime to determine whether to approve, deny, or ask for each tool call.
How does the permission system handle sub-agents or nested agent contexts?
The PermissionManager supports hierarchical permission through its constructor's optional parent parameter【lines 35‑41】. Child managers inherit rules and mode settings from their parents unless explicitly overridden. The mode resolution logic checks modeOverride first, then falls back to parent?.mode, and finally defaults to 'manual', ensuring that sub-agents maintain security boundaries while allowing specialized configurations【lines 44‑46】.
Can the permission mode be changed dynamically during agent execution?
Yes, the permission mode can be modified at runtime using manager.setMode(), allowing the system to respond to UI changes or contextual shifts. For example, an agent might start in manual mode for safety, then switch to auto mode once it enters a sandboxed environment. However, mode changes only affect subsequent tool calls; they do not retroactively alter the status of operations already in flight.
What happens when a user rejects a tool approval request?
When a user rejects or cancels an approval request, the requestToolApproval method records the result via recordApprovalResult and returns a blocked execution result containing a formatted rejection message【formatApprovalRejectionMessage (lines 92‑110)】. The tool call is prevented from executing, and the agent receives a descriptive message explaining that the operation was blocked due to user rejection or cancellation, allowing the agent to potentially suggest alternatives or halt the current task flow.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →