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
StepBeginwire 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
_stepto perform LLM calls, injection delivery, and tool execution. - Error handling: catches
BackToTheFuturefor 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.stepandtenacity. - 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.pythat manages a full conversational turn. - The main agent loop inside
_agent_loopenforces step limits, compacts context, checkpoints state, and delegates execution to_step. _stephandles notification delivery, dynamic injections, LLM calls with retry, and tool message appending.- Extension points include
DynamicInjectionProvider, theHookEngine, and theKimiToolset, all wired together duringKimiSoul.__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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →