How Session Management and Resumption Work in Kimi CLI

Kimi CLI persists chat history and configuration in per-directory session folders using WorkDirMeta for global tracking and SessionState for local state, enabling resumption via the last_session_id reference and Session.continue_() method.

MoonshotAI/kimi-cli implements a layered session architecture that treats every interaction with a work directory as a resumable conversation. The system stores wire messages, approval settings, and custom titles in isolated session directories, allowing developers to pause AI-assisted workflows and return to them later. Understanding session management and resumption in Kimi CLI requires examining three core components that handle metadata, persistence, and runtime orchestration.

Core Components of the Session Architecture

WorkDirMeta: Global Directory Tracking

The WorkDirMeta class in src/kimi_cli/metadata.py maintains the global registry of all work directories known to the CLI. It stores the last_session_id for each directory and provides the sessions_dir path where individual session folders reside. When you initiate a conversation, load_metadata() reads the global metadata file at ~/.kimi/kimi.json to locate or create a WorkDirMeta entry for your current directory.

SessionState: Persistent Configuration Storage

Per-session persistence is handled by the SessionState model defined in src/kimi_cli/session_state.py. This JSON-serialized object contains approval flags, custom titles, archive status, and todo lists. The helper functions save_session_state() (line 31) and load_session_state() (lines 99‑127) manage atomic writes to state.json within each session directory, including automatic migration of legacy metadata.json files via _migrate_legacy_metadata.

Session: Runtime Orchestration

The Session class in src/kimi_cli/session.py serves as the runtime container, binding together the work directory, context files, and state. It provides static factory methods including create(), find(), list(), and continue_() that abstract the complexity of directory hashing, UUID generation, and file I/O.

Creating a New Session

When initializing a conversation via Session.create() (lines 30‑80 in session.py), the system executes a four-step persistence workflow:

  1. Directory Registration – load_metadata() verifies the work directory exists in the global registry, creating a new WorkDirMeta entry if necessary.
  2. Session ID Generation – A UUID is generated (or supplied by the caller) to identify the session uniquely.
  3. Directory Structure Creation – The system creates sessions/<hash>/<session_id>/ under the work directory's metadata root.
  4. Initial File Population – Empty context.jsonl and wire.jsonl files are created for message storage, and a default SessionState() instance is serialized to state.json via atomic_json_write.
from kimi_cli.session import Session
from kaos.path import KaosPath
from pathlib import Path

work_dir = KaosPath.unsafe_from_local_path(Path("/home/alice/project"))
session = await Session.create(work_dir)
print(session.id, session.title)

Storing and Loading Session State

Each session directory contains the critical state.json file produced by save_session_state(state, session_dir). When resuming work, load_session_state(session_dir) reads this file, applying default values if corruption is detected and automatically migrating legacy metadata.json data when present.

The system maintains two distinct log files:

  • wire.jsonl – Stores raw LLM-tool messages for history replay
  • context.jsonl – Contains line-delimited JSON messages representing the conversation context

Deriving Session Titles

The Session.refresh method rebuilds human-readable titles by inspecting the wire.jsonl log. If state.custom_title is set, it uses that value; otherwise, it extracts the first user utterance from the initial TurnBegin message and truncates it via the shorten function (lines 14‑23 in session.py). The resulting title is cached in Session.title and synchronized to WorkDirMeta.last_session_id.


# Update session state with a custom title

session.state.custom_title = "My Feature Development"
session.save_state()

Resuming Previous Sessions

Session resumption relies on the Session.continue_() method (lines 88‑107), which implements the kimi continue command logic:

session = await Session.continue_(work_dir)
if session:
    print("Resuming:", session.id, session.title)

This method retrieves the last_session_id from the work directory's metadata and invokes Session.find() to reconstruct the full runtime object, reloading state.json and preparing the wire log for append operations.

Session Enumeration and Cleanup

The CLI provides utilities for managing session lifecycles beyond simple resumption:

  • Session.list(work_dir) – Enumerates subdirectories in the session store, filters empty sessions, and returns results sorted by updated_at
  • Session.list_all() – Aggregates sessions across every known work directory via load_metadata().work_dirs
  • Session.delete() – Performs async removal of the entire session directory including both JSON state files

# List all sessions for a work directory

sessions = await Session.list(work_dir)
for s in sessions:
    print(f"{s.id[:8]} – {s.title} (updated: {s.updated_at})")

Summary

Kimi CLI's session management system combines global metadata tracking with per-directory isolation to create a robust persistence layer:

  • Global tracking via WorkDirMeta in src/kimi_cli/metadata.py maintains the last_session_id reference for quick resumption
  • State persistence through SessionState in src/kimi_cli/session_state.py handles atomic saves and legacy migrations
  • Runtime management via the Session class in src/kimi_cli/session.py provides factory methods for creation, lookup, and continuation
  • History storage uses wire.jsonl for raw messages and context.jsonl for conversation context
  • Resumption workflow leverages Session.continue_() to reload the most recent session using stored metadata references

Frequently Asked Questions

How does Kimi CLI determine which session to resume when using kimi continue?

The CLI queries the WorkDirMeta entry for the current directory to retrieve the last_session_id field. This UUID is passed to Session.find(), which locates the corresponding session directory and reconstructs the runtime object by loading state.json and preparing the wire log for the next interaction.

Where are session states physically stored on disk?

Session data resides in ~/.kimi/kimi.json for global metadata and in sessions/<hash>/<session_id>/ subdirectories within each work directory's metadata folder. Each session contains state.json for configuration, wire.jsonl for message history, and context.jsonl for conversation context.

How does Kimi CLI derive the display title for a session?

If the user has set a custom_title in the SessionState object, that value is used. Otherwise, Session.refresh parses the first TurnBegin message from wire.jsonl, extracts the initial user utterance, and applies the shorten function to generate a concise display title automatically.

What happens if the state.json file becomes corrupted?

When load_session_state() encounters a corrupted or unreadable state.json file, it falls back to default values for all session parameters. Additionally, if a legacy metadata.json file exists from older CLI versions, the system automatically migrates its contents into the new state.json format before loading.

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 →