How Session Summaries Are Generated in ai-memory: A Deep Dive into the Summary.json Lifecycle
Session summaries in ai-memory are generated by the client runtime (Grok, Kiro, Antigravity, etc.) as atomic JSON writes to summary.json, then discovered and parsed by the transcript module at crates/ai-memory-workstream/src/transcript.rs.
The ai-memory project uses a standardized summary.json format to persist metadata about AI chat sessions. When a session terminates, the producing runtime serializes session identifiers, timing data, token usage, and message counts into this file. The core transcript module later reads this file to enable session discovery, indexing, and resumption.
What Contains a Session Summary
A session summary is a JSON file located at the root of a Grok-style session directory. The summary.json format contains a required info object with these fields:
info.id— A UUID uniquely identifying the sessioninfo.cwd— The working directory the session was attached toinfo.start/info.end— ISO 8601 timestamps delimiting the sessioninfo.tokens— Token usage statistics (tokens in, tokens out)info.messageCount— Total chat messages in the sessioninfo.additional— Optional implementation-specific metadata (LLM model, provider, etc.)
Each session producer (Grok, Kiro v2/v3, Antigravity) assembles these fields based on its own runtime observations.
How the Transcript Module Discovers Sessions
The transcript module at crates/ai-memory-workstream/src/transcript.rs handles session discovery by parsing summary.json. The implementation reads the file and extracts the session identifier and checkout path.
From lines 2606–2620 of transcript.rs, the discovery logic:
let Ok(raw) = fs::read_to_string(session_dir.join("summary.json")) else { … };
let Ok(summary) = serde_json::from_str::<Value>(&raw) else { … };
The required fields are extracted from the info object:
let info = summary.get("info").unwrap_or(&Value::Null);
let (Some(id), Some(cwd)) = (
info.get("id").and_then(Value::as_str),
info.get("cwd").and_then(Value::as_str),
) else { … };
This parsing validates that summary.json contains the minimum metadata needed to identify and resume a session.
Atomic Write Pattern for Summary Generation
Session producers follow a repository-wide atomic-write convention to prevent data corruption. The pattern—implemented in ai-memory-wiki/src/wiki.rs and reused across crates—ensures summary.json appears atomically at session termination.
The write sequence:
- Serialize the session metadata to a JSON object
- Write to a temporary file (
summary.json.tmp) - Call
fsyncto flush to disk renamethe temporary file tosummary.json
This guarantees that readers never observe partially written summaries.
Complete Code Example: Reading a Session Summary
The transcript module uses this pattern to load session metadata:
use std::fs;
use std::path::{Path, PathBuf};
use serde_json::Value;
/// Returns the (session-id, cwd) pair from a summary.json file.
fn read_summary(session_dir: &Path) -> std::io::Result<(String, PathBuf)> {
let raw = fs::read_to_string(session_dir.join("summary.json"))?;
let json: Value = serde_json::from_str(&raw)?;
let info = json.get("info").unwrap_or(&Value::Null);
let id = info.get("id").and_then(Value::as_str).ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::InvalidData, "missing id")
})?;
let cwd = info.get("cwd").and_then(Value::as_str).ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::InvalidData, "missing cwd")
})?;
Ok((id.to_string(), PathBuf::from(cwd)))
}
This function returns the session UUID and working directory required to reconstruct the session context.
Complete Code Example: Generating a Session Summary
A session producer implements generation like this simplified illustration:
use std::fs;
use std::io::Write;
use std::path::Path;
use serde_json::json;
use uuid::Uuid;
/// Called when a session ends; writes `summary.json`.
fn write_summary(session_dir: &Path, id: &Uuid, cwd: &Path) -> std::io::Result<()> {
let summary = json!({
"info": {
"id": id.to_string(),
"cwd": cwd.to_string_lossy(),
"start": chrono::Utc::now().to_rfc3339(),
"end": chrono::Utc::now().to_rfc3339(),
"tokens": 12345,
"messageCount": 27
}
});
// Atomic write: write to a temp file then rename
let tmp_path = session_dir.join("summary.json.tmp");
let mut tmp = fs::File::create(&tmp_path)?;
tmp.write_all(summary.to_string().as_bytes())?;
tmp.sync_all()?; // fsync
fs::rename(tmp_path, session_dir.join("summary.json"))?;
Ok(())
}
The sync_all() call ensures durability before the atomic rename makes the file visible to readers.
Key Source Files
| File | Role |
|---|---|
crates/ai-memory-workstream/src/transcript.rs |
Discovers sessions by reading summary.json (lines 2606–2620) |
crates/ai-memory-wiki/src/wiki.rs |
Contains the generic atomic-write helper for persisting summaries |
Session-producer crates (ai-memory-workstream, ai-memory-wiki, etc.) |
Implement concrete logic for assembling and writing JSON at session termination |
Summary
- Session summaries are JSON files named
summary.jsonliving in session directories - Generation occurs at session termination by the runtime that produced the session (Grok, Kiro, Antigravity)
- Atomic writes via temp-file-and-rename prevent corruption and ensure readers see complete data
- Discovery happens in
transcript.rsat lines 2606–2620, whereserde_jsonparses the file and extractsinfo.idandinfo.cwd - The format standardizes session identity, timing, token usage, and message counts across all ai-memory producers
Frequently Asked Questions
What happens if summary.json is missing or corrupted?
The transcript module skips the directory. The read_to_string and serde_json::from_str calls both return Err variants that the caller handles by logging and continuing to the next session directory. No partial session data is exposed to downstream consumers.
Can session producers extend the summary.json format?
Yes, through the info.additional field. The base format requires only id and cwd; producers may attach implementation-specific metadata such as LLM model names, API providers, or custom telemetry without breaking the core discovery logic.
Why does ai-memory use atomic writes for JSON files?
The atomic temp-file-and-rename pattern prevents readers from observing partially written JSON during crashes or power failures. The fsync before rename guarantees that the data reaches stable storage, and rename is atomic on POSIX systems. This same pattern appears throughout ai-memory's persistence layer, including in ai-memory-wiki/src/wiki.rs.
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 →