# How Session Management and Resumption Work in Kimi CLI

> Discover how Kimi CLI’s session management and resumption works. Learn how chat history and local state are saved and restored for seamless continuity with WorkDirMeta and SessionState.

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

---

**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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/state.json) within each session directory, including automatic migration of legacy [`metadata.json`](https://github.com/MoonshotAI/kimi-cli/blob/main/metadata.json) files via `_migrate_legacy_metadata`.

### Session: Runtime Orchestration

The `Session` class in [`src/kimi_cli/session.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/state.json) via `atomic_json_write`.

```python
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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/session.py)). The resulting title is cached in `Session.title` and synchronized to `WorkDirMeta.last_session_id`.

```python

# 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:

```python
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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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

```python

# 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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/src/kimi_cli/session_state.py) handles atomic saves and legacy migrations
- **Runtime management** via the `Session` class in [`src/kimi_cli/session.py`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/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`](https://github.com/MoonshotAI/kimi-cli/blob/main/state.json) file becomes corrupted?

When `load_session_state()` encounters a corrupted or unreadable [`state.json`](https://github.com/MoonshotAI/kimi-cli/blob/main/state.json) file, it falls back to default values for all session parameters. Additionally, if a legacy [`metadata.json`](https://github.com/MoonshotAI/kimi-cli/blob/main/metadata.json) file exists from older CLI versions, the system automatically migrates its contents into the new [`state.json`](https://github.com/MoonshotAI/kimi-cli/blob/main/state.json) format before loading.