How the Kimi CLI Approval System Controls Tool Execution Authorization

The Kimi CLI approval system intercepts every tool call through the Approval class in src/kimi_cli/soul/approval.py, validating execution context and routing requests through auto-approval logic or interactive user consent before allowing potentially destructive operations to proceed.

Kimi CLI implements a robust permission gate to prevent unauthorized tool execution. According to the MoonshotAI/kimi-cli source code, the approval system centered in src/kimi_cli/soul/approval.py provides both automatic and manual authorization paths while maintaining comprehensive audit trails for every decision.

Architecture of the Approval System

The approval workflow is orchestrated by the Approval class, which acts as a mandatory checkpoint between the AI agent and tool execution.

Core Entry Point and Context Validation

Tool implementations initiate authorization by invoking await approval.request(...). This method accepts four critical parameters:

  • sender: The tool name (e.g., "shell", "file_editor")
  • action: A machine-readable identifier for the operation
  • description: Human-readable context for the UI
  • display: Optional UI blocks (diffs, shell commands, etc.)

Before processing, the system validates that the call originates from an active tool execution context using get_current_tool_call_or_none. If validation fails, the method aborts immediately with a RuntimeError (lines 223-226).

Auto-Approval Modes for Unattended Operation

The approval system supports bypass mechanisms for automated workflows while preserving telemetry records.

Yolo and AFK Flags

When self.is_auto_approve() returns True (indicating Yolo or AFK mode), the system bypasses user interaction entirely. It emits a tool_approved telemetry event (lines 42-50) and returns ApprovalResult(approved=True) immediately.

Session-Wide Auto-Approval

Users can opt to approve specific actions for the entire session. The system maintains _state.auto_approve_actions as a registry of pre-authorized operations. When a request matches an entry in this cache, it receives automatic approval without UI interruption (lines 62-70).

Manual Authorization Flow

When no auto-approval rule applies, the system enters an interactive blocking flow:

  1. Request Registration: Creates a unique request ID and registers it with ApprovalRuntime
  2. UI Blocking: Suspends tool execution via await self._runtime.wait_for_response(request_id)
  3. User Response Processing: Handles three possible responses from the UI layer:
    • "approve": Executes the tool immediately
    • "approve_for_session": Executes the tool and adds the action to auto_approve_actions (lines 70-78)
    • "reject": Aborts execution and optionally raises ToolRejectedError

Session Cache Persistence

When a user selects "approve for session", the system resolves pending requests automatically. The resolve method updates all queued instances of the same action with approved_via_session_cache=True (lines 72-79), ensuring the UI reflects the updated authorization state without requiring individual confirmations.

Telemetry and Audit Trails

Every authorization decision records detailed metrics through _track_permission_result (lines 36-48). The telemetry captures:

  • Step number and tool name
  • Permission mode: auto, yolo, or manual
  • Result status: approved, rejected, cancelled, or error
  • Approval surface: Derived from the first DisplayBlock type
  • Duration and session cache writes
  • User feedback content

This implementation mirrors the TypeScript permissionGateService contract (as noted in comments at line 47), ensuring compatibility with existing observability infrastructure.

Implementation Example

Tool developers integrate approval checks using the Approval class:

from kimi_cli.soul.approval import Approval

async def run_command(tool_input: str) -> str:
    approval = Approval()
    result = await approval.request(
        sender="shell",
        action="run_shell_command",
        description=f"Execute: {tool_input}",
        display=[DisplayBlock(type="shell", content=tool_input)],
    )
    if not result:
        raise result.rejection_error()
    return await execute_shell(tool_input)

The manual approval flow triggers automatically when the UI presents the request:


# When user selects "Approve for this session":

# 1. Action added to auto_approve_actions

# 2. Pending identical requests resolved via runtime.resolve()

# 3. Tool receives ApprovalResult(approved=True)

# 4. Telemetry logs tool_approved with session_cache_write=True

Summary

  • Entry Point: The Approval.request method validates context and initiates authorization checks
  • Auto-Approval: Yolo/AFK flags and session caches bypass UI for trusted workflows
  • Manual Flow: Blocking wait for user input with support for one-time or session-wide approval
  • Persistence: Session caches automatically resolve pending identical requests
  • Observability: Comprehensive telemetry tracks permission modes, results, and user feedback

Frequently Asked Questions

How does Kimi CLI prevent unauthorized tool execution?

The system validates every tool call originates from an active execution context using get_current_tool_call_or_none (lines 223-226). Without this validation, the request aborts immediately. Valid requests then route through the approval decision tree requiring explicit authorization unless auto-approved.

What is the difference between Yolo mode and session-wide approval?

Yolo mode (and AFK flags) bypasses all authorization checks immediately, automatically approving every request without UI interaction or persistence. Session-wide approval requires an initial manual approval with the "approve for session" option, which then persists that specific action to auto_approve_actions for the duration of the session while still logging telemetry for each execution.

How does the approval system track user decisions?

The _track_permission_result method (lines 36-48) records step numbers, tool identifiers, permission modes (auto/yolo/manual), results (approved/rejected/cancelled/error), approval surfaces, durations, and session cache writes. This data emits as tool_approved or tool_rejected events for audit trails.

Can tool executions be approved automatically without user interaction?

Yes. Two mechanisms enable automatic approval: global flags like Yolo/AFK mode that bypass all checks, or session-wide auto-approval where specific actions are pre-authorized after initial user consent. Both paths emit telemetry events and return ApprovalResult(approved=True) without blocking for UI input.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →