# How the Kimi CLI Approval System Manages Tool Execution Permissions

> Discover how the Kimi CLI approval system manages tool execution permissions by evaluating modes, routing requests, and caching decisions. Ensure secure and controlled tool access.

- Repository: [Moonshot AI/kimi-cli](https://github.com/MoonshotAI/kimi-cli)
- Tags: internals
- Published: 2026-07-24

---

**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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/telemetry.py), creating immutable audit trails for compliance and debugging.

## Practical Implementation Examples

### Respecting the Approval System in Custom Tools

```python

# 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

```python
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

```python

# 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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/approval_runtime/runtime.py) creates `ApprovalSource` records and manages `ApprovalRequest` messages via [`src/kimi_cli/wire/types.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/soul/approval.py). This allows conditional logic that adapts behavior based on the current permission context.