How the Kimi CLI Approval System Manages Tool Execution Permissions

The Kimi CLI approval system acts as a gatekeeper that decides whether a tool call may be executed by evaluating permission modes, routing requests through an approval runtime, and caching user decisions for the session.

The MoonshotAI/kimi-cli repository implements a robust permission framework that governs every external action performed by the CLI. This system intercepts tool invocations before execution, presents contextual information to the user, and maintains audit trails through integrated telemetry. Understanding how the approval system manages tool execution permissions is essential for configuring secure automation workflows and unattended operations.

Core Architecture and Entry Points

Every tool invocation in Kimi CLI flows through a centralized approval mechanism defined in src/kimi_cli/soul/approval.py. The architecture separates concerns between permission policy definition, runtime request handling, and result tracking.

The Approval.request Gatekeeper

The primary entry point is Approval.request(), a class method that every tool call must traverse before execution. Located in src/kimi_cli/soul/approval.py, this method receives the tool name, its arguments, and UI display blocks that will be shown to the user.

The method constructs a permission mode based on current session flags before determining whether to auto-approve, prompt the user, or reject the request. This design ensures that no external command executes without explicit policy evaluation.

Permission Modes and Session Flags

The system supports four distinct permission modes that control execution behavior:

  • yolo – Auto-approves every request, useful for "trust everything" automation scripts
  • afk – Auto-rejects every request, designed for unattended runs where no interaction is possible
  • auto_approve – Approves the request without prompting, but only for the current execution step
  • approve_for_session – Prompts the user once and caches the decision for all subsequent identical calls during the session

These flags are manipulated programmatically through the Approval object using set_yolo(), set_afk(), and set_runtime_afk() methods.

The Approval Runtime and User Interaction

When a permission mode requires user confirmation, the system delegates to the approval runtime implemented in src/kimi_cli/approval_runtime/runtime.py.

ApprovalSource and Request Construction

The runtime creates an ApprovalSource record (defined in src/kimi_cli/approval_runtime/models.py) containing the session ID, tool name, and display surface derived from the first DisplayBlock.type. It then constructs an ApprovalRequest message defined in src/kimi_cli/wire/types.py and transmits it over the wire to the UI layer.

The shell visualizer renders this request, presenting the tool operation and arguments to the user for evaluation.

Response Handling and Execution Flow

The UI returns one of three responses: "approve", "approve_for_session", or "reject". The runtime translates this into an ApprovalResult object, where ApprovalResult.__bool__ returns True for approved responses to maintain compatibility with legacy boolean checks.

If the user rejects the request, ApprovalResult.rejection_error() constructs a ToolRejectedError from src/kimi_cli/tools/utils.py that propagates back to abort execution immediately.

Session Caching and Telemetry Integration

The approval system maintains state across long-running sessions and records comprehensive audit trails.

Persistent Session Decisions

When a user selects "approve for session", the approval runtime stores the decision in a session cache. Subsequent identical tool calls bypass the UI entirely and receive automatic approval, reducing friction while maintaining security boundaries for recurring operations.

Audit Trails via _track_permission_result

After every decision, the _track_permission_result method emits a permission_approval_result telemetry event. This records:

  • The tool name and permission mode used
  • The approval result and surface type
  • Request latency and session cache updates
  • Any user-provided feedback

This data flows through the Kimi CLI telemetry system defined in src/kimi_cli/telemetry.py, creating immutable audit trails for compliance and debugging.

Practical Implementation Examples

Respecting the Approval System in Custom Tools


# A tool implementation that respects the approval gate

async def run_git_pull():
    # The toolset automatically calls Approval.request() 

    # before executing the actual git command

    result = await some_tool.call(name="git_pull", args={"repo": "."})
    
    if not result:
        # User rejected the request - ToolRejectedError is raised

        raise result.rejection_error()
    
    return result

Configuring Permission Modes Programmatically

from kimi_cli.soul.approval import Approval

approval = Approval.shared()

# Enable auto-approval for all operations

approval.set_yolo(True)

# Ensure we are not auto-rejecting

approval.set_afk(False)
approval.set_runtime_afk(False)

Manual Approval Requests


# Direct usage (rarely needed - tools invoke this internally)

approval = Approval.shared()
decision = await approval.request(
    tool_name="shell",
    display=[DisplayBlock(type="shell", content="rm -rf /tmp/*")],
)

if decision:
    # Approved - proceed with execution

    pass
else:
    # Rejected - handle gracefully

    raise decision.rejection_error()

Summary

  • Entry Point: Every tool call passes through Approval.request() in src/kimi_cli/soul/approval.py before execution
  • Permission Modes: Four modes (yolo, afk, auto_approve, approve_for_session) control whether requests auto-approve, prompt, or reject
  • Runtime Flow: The approval runtime in src/kimi_cli/approval_runtime/runtime.py creates ApprovalSource records and manages ApprovalRequest messages via src/kimi_cli/wire/types.py
  • Result Handling: ApprovalResult objects provide boolean evaluation and rejection_error() methods that raise ToolRejectedError from src/kimi_cli/tools/utils.py
  • Session Management: "Approve for session" decisions cache permissions to bypass future prompts for identical operations
  • Telemetry: The _track_permission_result function emits permission_approval_result events through src/kimi_cli/telemetry.py for complete audit trails

Frequently Asked Questions

How do I enable automatic approval for all tool executions in Kimi CLI?

Set the yolo permission mode by calling Approval.shared().set_yolo(True) in your script or configuration. This mode auto-approves every request without user interaction, suitable for trusted automation environments. Remember that this disables all safety prompts, so use it only with verified toolsets.

What happens when a tool execution is rejected by the user?

When rejected, the ApprovalResult.rejection_error() method generates a ToolRejectedError defined in src/kimi_cli/tools/utils.py. This exception propagates back through the call stack, aborting the tool execution and allowing the calling code to handle the rejection gracefully or terminate the operation.

How does Kimi CLI remember my approval decisions across multiple commands?

The approval runtime maintains a session cache stored in src/kimi_cli/approval_runtime/runtime.py. When you select "approve for session", the runtime stores a hash of the tool name and arguments. Subsequent identical calls match this cache entry and receive automatic approval without UI interaction until the session ends.

Can I programmatically check the current permission mode before executing a tool?

Yes. Access the shared Approval instance via Approval.shared() and inspect the current flags using the is_yolo(), is_afk(), or is_runtime_afk() methods available in src/kimi_cli/soul/approval.py. This allows conditional logic that adapts behavior based on the current permission context.

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 →