How Session Save, Resume, and History Retrieval Work in DeepSeek-Reasonix

DeepSeek-Reasonix persists every conversation as an atomic JSON-L file and provides both CLI and programmatic interfaces for resuming sessions and retrieving history with crash-safe guarantees.

The session management system in the DeepSeek-Reasonix repository handles conversation lifecycle through a combination of atomic file operations, in-memory snapshots, and specialized tools for AI agents. Understanding these mechanisms is essential for building reliable integrations or debugging conversation state issues.

Session Persistence: Atomic JSON-L Serialization

Every conversation turn triggers an automatic save to a *.jsonl file containing a sequence of provider.Message structs. The Session.Save method in internal/agent/save.go implements crash-safe persistence using atomic file replacement.

The save process works as follows:

  • Temporary File Creation: The full transcript writes to a temporary file first
  • Atomic Rename: fileutil.ReplaceFile renames the temporary file to the final destination, ensuring that a system crash cannot leave a partially-written session file
  • Path Generation: NewSessionPath generates unique filenames for new sessions, while ContinueSessionPath creates continuation paths when model switches occur
func (c *Controller) snapshot(markActivity bool) error {
    // ...
    if err := s.Save(path); err != nil {        // <-- writes JSONL
        return err
    }
    preview, turns := agent.SessionPreviewFromMessages(s.Snapshot())
    return agent.UpdateSessionMeta(path, modelRef, preview, turns, markActivity)
}

The controller stores the active path in its sessionPath field, which is used for subsequent autosaves.

Loading and Normalizing Sessions

When resuming a conversation, LoadSession in internal/agent/save.go handles deserialization and data integrity checks.

The loading sequence:

  1. Open the JSON-L file and decode each message using a streaming json.Decoder
  2. Run NormalizeSession to repair any legacy format inconsistencies or missing fields
  3. Return a populated *agent.Session struct ready for the controller

This normalization step ensures backward compatibility with older session formats without manual migration.

Resuming Sessions in the Controller

The Controller.Resume method in internal/control/controller.go (lines 53-66) binds a loaded session to the active runtime environment.

Resume operations include:

  • Pinning the session file path to c.sessionPath for future autosaves
  • Re-binding checkpoint metadata from the previous conversation
  • Clearing the planner's internal state to prevent stale context contamination
  • Executing maybeColdResumePrune to remove stale tool results if the session exceeded the provider cache duration

After resuming, the controller's History() method immediately returns the restored transcript, making the conversation available to the UI layer.

Retrieving Conversation History

Front-end interfaces access conversation history through Controller.History() in internal/control/controller.go (lines 87-94). This method returns a defensive copy of the executor's Session().Snapshot()—a slice of provider.Message structs that represents the current conversation state.

The chatTUI layer consumes this snapshot to render scroll-back buffers, while the underlying Session struct in internal/agent/session.go manages the in-memory representation through methods like Snapshot() and HasContent().

Listing and Selecting Sessions

The /resume command leverages ListSessions in internal/agent/save.go to display available conversations without loading full message histories.

Performance optimization through side-cars:

  • ListSessions builds lightweight SessionOrderInfo metadata for each .jsonl file
  • Reads cached previews from side-car files rather than parsing entire JSON-L documents
  • Returns SessionInfo structs containing timestamp, turn count, preview text, and file path

The CLI implementation in internal/cli/resume.go provides two interaction modes:


# Interactive mode: shows numbered list with metadata

reasonix /resume

# Direct mode: resume specific session by index

reasonix /resume 3

When invoked without arguments, showSessions renders the picker interface; with an index argument, it loads the selected file via agent.LoadSession and invokes Controller.Resume.

AI-Accessible Session Tools

The sessiontool package in internal/tool/sessiontool/sessiontool.go exposes session data to AI agents through two registered tools:

list_sessions: Returns available sessions with metadata (equivalent to the CLI picker data)

read_session: Validates filenames, checks for cleanup-pending markers, and returns a privacy-safe view of messages truncated to 2000 runes per message:

{
  "session": "20231101-120000.000000000-gpt-4.jsonl"
}

By default, read_session omits system messages and tool results unless show_tool_results is set to true, preventing context window pollution while allowing optional deep inspection.

Summary

  • DeepSeek-Reasonix uses atomic file replacement (fileutil.ReplaceFile) in internal/agent/save.go to prevent corruption during session saves
  • Resume operations bind loaded sessions to the controller, clear stale planner state, and optionally prune old tool results via maybeColdResumePrune
  • History retrieval returns defensive copies of provider.Message slices through Controller.History() and Session.Snapshot()
  • Session listing uses lightweight side-car metadata (SessionOrderInfo) to avoid parsing large JSON-L files during UI rendering
  • AI tools provide controlled access via list_sessions and read_session, with content truncation at 2000 runes for privacy protection

Frequently Asked Questions

How does DeepSeek-Reasonix prevent data loss when saving sessions?

The system writes to a temporary file first, then uses fileutil.ReplaceFile for an atomic rename operation in internal/agent/save.go. This ensures that the original session file remains intact until the new content is fully written to disk, eliminating the risk of partial writes during crashes or power failures.

What is the difference between NewSessionPath and ContinueSessionPath?

NewSessionPath generates unique filenames for fresh conversations using timestamps and model identifiers, while ContinueSessionPath creates paths for model-switched continuations that maintain conversation lineage. The controller stores the active path in sessionPath and uses it for all subsequent autosave operations.

How does the read_session tool protect sensitive information?

The tool truncates each message to 2000 runes and filters out system messages and tool results by default. Only when show_tool_results is explicitly set to true does it return full tool execution data, preventing accidental exposure of intermediate computation or private API responses to the AI context.

Can sessions be resumed programmatically without the CLI?

Yes, developers can load sessions directly using agent.LoadSession and pass the result to Controller.Resume. This pattern is useful for testing, custom UI implementations, or automated workflows that need to restore conversation state without invoking the /resume slash command:

sess, err := agent.LoadSession("/home/user/.reasonix/sessions/20231101-120000.000000000-gpt-4.jsonl")
if err != nil { log.Fatal(err) }
ctrl.Resume(sess, "/home/user/.reasonix/sessions/20231101-120000.000000000-gpt-4.jsonl")

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 →