Expand description
Read-side access to the CLI’s on-disk session logs.
Read-only. Mutation goes through the CLI, via
ArchiveCommand and
DeleteCommand.
§On-disk layout
$CODEX_HOME/sessions/<YYYY>/<MM>/<DD>/rollout-<ISO8601>-<uuid>.jsonlThe date partitioning is why SessionQuery::after and
SessionQuery::before are cheap: they filter directories, without opening
a file.
§Two envelope generations
A real machine holds sessions written by many CLI versions, and the line format changed. Both were found on one machine while writing this, 205 files spanning both:
Modern, from around 0.47 onward. Every line is an envelope:
{"timestamp":"...","type":"session_meta","payload":{"id":"...","cwd":"..."}}
{"timestamp":"...","type":"response_item","payload":{"type":"message","role":"user"}}Legacy, older files. No envelope at all: the first line is the metadata, and later lines are bare records.
{"id":"...","timestamp":"...","git":{},"instructions":null}
{"id":"...","type":"message","role":"user","content":[]}A parser written against only the modern shape returns nothing at all for
the older half of a real history, silently. SessionEntry::entry_type is
None for a legacy line, and its payload is the whole line.
Field-level drift is handled the same way: every metadata field is
optional, because older files carry no cli_version, no cwd, and a
different instructions key. SessionMeta::raw keeps whatever this
crate does not name.
§Example
use codex_wrapper::history::{self, SessionQuery};
for session in history::list(&SessionQuery::new().after(2026, 8, 1))? {
let log = history::read(&session.path)?;
println!("{} in {:?}", session.id, log.meta.and_then(|m| m.cwd));
}Structs§
- GitMeta
- Git metadata recorded with a session.
- Session
Entry - One line of a session log.
- Session
File - A rollout file on disk, identified without opening it.
- Session
Log - A parsed session log.
- Session
Meta - The session’s opening metadata.
- Session
Query - Which sessions to list.