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.ReplaceFilerenames the temporary file to the final destination, ensuring that a system crash cannot leave a partially-written session file - Path Generation:
NewSessionPathgenerates unique filenames for new sessions, whileContinueSessionPathcreates 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:
- Open the JSON-L file and decode each message using a streaming
json.Decoder - Run
NormalizeSessionto repair any legacy format inconsistencies or missing fields - Return a populated
*agent.Sessionstruct 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.sessionPathfor future autosaves - Re-binding checkpoint metadata from the previous conversation
- Clearing the planner's internal state to prevent stale context contamination
- Executing
maybeColdResumePruneto 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:
ListSessionsbuilds lightweightSessionOrderInfometadata for each.jsonlfile- Reads cached previews from side-car files rather than parsing entire JSON-L documents
- Returns
SessionInfostructs 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) ininternal/agent/save.goto 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.Messageslices throughController.History()andSession.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_sessionsandread_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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →