KimiSoul Architecture and Main Agent Loop: Inside MoonshotAI/kimi-cli

KimiSoul is the core orchestrator in MoonshotAI/kimi-cli that wraps an Agent, Runtime, Conversation Context, dynamic injections, Toolset, and HookEngine to drive the main agent loop for every conversational turn.

The KimiSoul class serves as the execution backbone of the Kimi CLI application, transforming raw user input into fully orchestrated agent turns. Understanding the KimiSoul architecture and how it manages the main agent loop is essential for anyone customizing or debugging the MoonshotAI/kimi-cli codebase. At its core, KimiSoul glues LLM inference, tool execution, context compaction, and user-defined hooks into a single extensible session.

Core Components of the KimiSoul Architecture

KimiSoul Class and State Management

The KimiSoul class is defined in src/kimi_cli/soul/kimisoul.py at lines 28–31. It wraps an Agent instance and its associated Runtime, while maintaining state for plan mode, dynamic injections, and the hook engine. During initialization at KimiSoul.__init__ (lines 32–45), the class binds these subsystems together and prepares the conversation context.

Agent and Runtime

The Agent provides the system prompt, toolset, and underlying LLM configuration, and it is instantiated inside KimiSoul.__init__. The Runtime dataclass, defined in src/kimi_cli/soul/agent.py at lines 71–95, bundles the LLM client, OAuth manager, session state, and notification manager. Together, these objects supply the execution environment that KimiSoul orchestrates.

Conversation Context

The Context object holds the message history, provides checkpointing, token counting, and automatic compaction. It is initialized as self._context in KimiSoul.__init__ at lines 48–49 within src/kimi_cli/soul/kimisoul.py. This component ensures the conversation stays within token budgets and can persist state across steps.

Dynamic Injections and HookEngine

Dynamic injections are plugins that prepend synthetic system-reminder messages—such as AFK or plan-mode reminders—into the conversation. They are collected via _collect_injections at lines 65–78. The HookEngine, created at lines 80–82 of the same file, executes user-configurable hooks at well-defined lifecycle points including UserPromptSubmit, Stop, and Notification events.

KimiToolset

The Toolset (KimiToolset) loads built-in tools, binds plan-mode state, and runs tool calls. Setup occurs in _bind_plan_mode_tools at lines 109–156 of src/kimi_cli/soul/kimisoul.py. This module bridges the agent's reasoning with concrete tool execution.

How the Main Agent Loop Executes a Turn

Turn Initialization with run

Every turn begins with the run method in src/kimi_cli/soul/kimisoul.py (lines 60–78). This method prepares the turn, refreshes OAuth tokens, triggers the UserPromptSubmit hook, and determines whether the input is a slash command or a standard user message. It acts as the entry point that delegates into the deeper agent loop.

The _agent_loop Method

The main agent loop itself resides in _agent_loop at lines 137–165 of src/kimi_cli/soul/kimisoul.py. This method drives a single turn through the following lifecycle:

  • Turn initialization: clears stale steers and loads deferred MCP tools.
  • Step guard: enforces max_steps_per_turn.
  • Step begin: emits a StepBegin wire event.
  • Context compaction: automatically compacts the conversation when the token budget is exceeded (see the compaction check around lines 164–172).
  • Checkpoint: persists the current state.
  • Step execution: invokes _step to perform LLM calls, injection delivery, and tool execution.
  • Error handling: catches BackToTheFuture for reverts or fatal errors.
  • Outcome resolution: processes steers and decides whether to continue or finish the turn.

Step Execution via _step

Each iteration of the loop calls _step at lines 111–128 of src/kimi_cli/soul/kimisoul.py. This method handles:

  • Notification delivery for root-only contexts (detailed around lines 133–165).
  • Dynamic injection collection and insertion as a synthetic user message.
  • History normalization via normalize_history.
  • LLM call with retry using kosong.step and tenacity.
  • Toolset preparation via begin_step.
  • Appending assistant and tool messages back into the context.

Turn Termination and Cleanup

After _agent_loop returns, the run method emits a TurnEnd event, tracks telemetry, and cleans up approval sources. This completes the conversational turn and returns control to the caller.

Working with KimiSoul Programmatically

Running a Turn

from kimi_cli.agentspec import load_agent_spec
from kimi_cli.soul.agent import Agent, Runtime
from kimi_cli.soul.context import Context
from kimi_cli.soul.kimisoul import KimiSoul

# Load an agent spec (e.g., the default "code" agent)

agent_spec = load_agent_spec("code")
agent = Agent(spec=agent_spec)               # creates the Agent + its toolset

runtime = Runtime(...)                        # fill with Config, OAuth, Session, etc.

context = Context()                           # empty conversation context

soul = KimiSoul(agent, context=context)      # construct the soul

await soul.run("Write a Python script that prints Hello World")

Adding Dynamic Injections

from kimi_cli.soul.dynamic_injection import DynamicInjectionProvider

class MyReminderProvider(DynamicInjectionProvider):
    async def get_injections(self, history, soul):
        return [DynamicInjection(content="⚡ Remember to save your work!")]
        
soul.add_injection_provider(MyReminderProvider())

Executing Slash Commands

await soul.run("/skill:format_python", skip_user_prompt_hook=True)

Key Source Files

File Description
src/kimi_cli/soul/kimisoul.py Central orchestration class (KimiSoul) and the main agent loop.
src/kimi_cli/soul/agent.py Definition of Agent and the Runtime dataclass.
src/kimi_cli/soul/context.py Message history, checkpointing, token counting, and compaction logic.
src/kimi_cli/soul/toolset.py Tool loading, plan-mode binding, and tool call execution (KimiToolset).
src/kimi_cli/soul/slash.py Registry of built-in slash commands.
src/kimi_cli/hooks/engine.py Hook engine implementation for user-configurable lifecycle hooks.
src/kimi_cli/notifications/* Notification manager and wire events consumed by _step.

Summary

  • KimiSoul is the central orchestrator in src/kimi_cli/soul/kimisoul.py that manages a full conversational turn.
  • The main agent loop inside _agent_loop enforces step limits, compacts context, checkpoints state, and delegates execution to _step.
  • _step handles notification delivery, dynamic injections, LLM calls with retry, and tool message appending.
  • Extension points include DynamicInjectionProvider, the HookEngine, and the KimiToolset, all wired together during KimiSoul.__init__.

Frequently Asked Questions

What is KimiSoul in the kimi-cli project?

KimiSoul is the core orchestrator class defined in src/kimi_cli/soul/kimisoul.py that drives a Kimi CLI session by wrapping an Agent, its Runtime, and a HookEngine. It manages the full lifecycle of a conversational turn, from receiving user input to executing the main agent loop and emitting turn-level events.

How does the main agent loop handle token budget limits?

During each iteration of _agent_loop, the loop checks whether the conversation exceeds its token budget and triggers automatic context compaction around lines 164–172. This compaction is managed by the Context object in src/kimi_cli/soul/context.py, which preserves checkpoint state while reducing history size.

What happens during a single step in the KimiSoul agent loop?

A single step is handled by the _step method at lines 111–128 of src/kimi_cli/soul/kimisoul.py, which delivers notifications, collects dynamic injections, normalizes history, performs an LLM call with retry logic via kosong.step, and appends assistant and tool messages back into the context. Tool execution is coordinated through the KimiToolset after the LLM response is received.

How can developers extend KimiSoul behavior?

Developers can extend KimiSoul by registering custom DynamicInjectionProvider instances for synthetic reminders, configuring hooks in the HookEngine for lifecycle events like UserPromptSubmit, or binding new tools via the toolset setup in _bind_plan_mode_tools. These extension points are initialized in KimiSoul.__init__ and invoked within the main agent loop.

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 →