jlreyes/agent-history-skills · Archived

exploring-codex-sessions

Finds and explores OpenAI Codex CLI conversation history stored locally as JSONL rollout files. Use when the user asks to find, search, read, export, or resume a Codex CLI session, rollout, thread, or transcript.

First seen Jun 11, 2026

Installation

$ npx skills add jlreyes/agent-history-skills --skill exploring-codex-sessions

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from jlreyes/agent-history-skills.

npx skills add jlreyes/agent-history-skills

Browse all from jlreyes/agent-history-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 3
License LICENSE
Default branch main
Open issues 0
Status Archived

Skill metadata

Parsed from SKILL.md frontmatter.

CompatibilityRequires jq; sqlite3 and ripgrep recommended. Paths are macOS/Linux.
Allowed toolsBash(jq *) Bash(sqlite3 *) Bash(rg *) Bash(find *) Bash(zstdcat *) Bash(zstdgrep *) Bash(head *) Bash(sort *) Bash(tail *) Bash(ls *) Bash(cut *) Bash(codex *)
Declared agents codex
More metadata
author
jlreyes

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 11,110 B
  • docs SUMMARY.md 244 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 8 installs

SKILL.md

Exploring Codex Sessions

Set CODEXROOT="${CODEXHOME:-$HOME/.codex}" and CODEXDBROOT="${CODEXSQLITEHOME:-$CODEXROOT}". Codex CLI stores sessions as JSONL rollouts under $CODEXROOT; rollouts are canonical and SQLite is rebuildable. Writers are mixed; persistent exec is the proven paginated-default path.

Storage locations

Path What it holds
$CODEX_ROOT/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl Full session transcripts. Date dirs use local time
$CODEXROOT/archivedsessions/rollout-*.jsonl Archived sessions (flat, no date dirs, via codex archive)
$CODEXDBROOT/state_5.sqlite threads table: metadata index (cwd, preview, firstusermessage, title, name, git info, history_mode, archived flag). Treat as cache — rebuilt from rollouts
$CODEXDBROOT/threadhistory1.sqlite Rebuildable transcript-item projection; contains item JSON but is not canonical
$CODEXDBROOT/queue_1.sqlite Pending user submissions; writing to it changes queue state
$CODEX_ROOT/history.jsonl User-typed prompts: {"session_id", "ts" (unix sec), "text"}. Check its mtime first — it can silently stop being appended (see pitfall)
$CODEXROOT/sessionindex.jsonl Thread names: {"id", "threadname", "updatedat"}, last entry wins
$CODEX_ROOT/config.toml `[history] persistence = "save-all"\ "none"`

Sessions are never auto-deleted (codex archive / codex delete are manual). Rollouts may be zstd-compressed to .jsonl.zst; search them when zstdgrep or zstdcat is available. The SQLite root follows CODEXSQLITEHOME, then CODEXHOME; do not assume CODEXHOME relocates every store. session_index.jsonl is an append-only compatibility index; prefer SQLite where available.

Rollout schema (quick reference)

Every wrapped line is a rollout envelope, optionally carrying sequential ordinal in current paginated files (legacy readers accept no ordinal). The eleven source variants are documented in [data-model.md](data-model.md):

  • sessionmeta — line 1: id/sessionid, cwd, cliversion, source (cli, vscode, exec, mcp, subagent objects), git{branch, commithash, repositoryurl}, historymode, contextwindow, forkedfrom_id
  • responseitem — model-visible conversation: message (roles user/assistant/developer), functioncall(+output), customtoolcall(+output, e.g. applypatch), toolsearchcall/toolsearchoutput, agentmessage (cross-agent delivery), reasoning (encrypted), websearchcall
  • eventmsg — legacy UI events usermessage / agentmessage; paginated itemcompleted carries UserMessage / AgentMessage TurnItems. Exclude commentary.
  • turncontext — per-turn snapshot: model, cwd, approvalpolicy, sandboxpolicy, permissionprofile, collaboration_mode
  • world_state — model-visible world snapshot ({full, state}: skills, environments, permissions); not conversation — skip when building transcripts
  • compacted — compaction marker; replacement_history[] substitutes prior history on replay
  • interagentcommunication / _metadata — multi-agent only

Injected-context pitfall: responseitem user and developer messages carry harness injections. In legacy format prefer eventmsg/usermessage; in paginated format prefer itemcompleted/UserMessage. Do not treat bare-era files as covered by these envelope recipes.

Format and integrity pitfall: current paginated files use itemcompleted; legacy files use usermessage/agent_message. Desktop alpha corpora include malformed split records: raw jq can stop or truncate. Preflight before export and use the current projection/migration or an explicitly documented tolerant repair; do not silently hide failures with fromjson?.

Recipes

List recent sessions (fast path, via the index)

CODEX_ROOT="${CODEX_HOME:-$HOME/.codex}"
CODEX_DB_ROOT="${CODEX_SQLITE_HOME:-$CODEX_ROOT}"
sqlite3 -separator ' | ' "$CODEX_DB_ROOT/state_5.sqlite" \
  "SELECT datetime(updated_at,'unixepoch','localtime'), substr(id,1,13), cwd,
          substr(replace(first_user_message,char(10),' '),1,60)
   FROM threads WHERE archived=0 ORDER BY updated_at DESC LIMIT 20;"

Use 13 id chars, not 8 — UUIDv7 prefixes collide for sessions started in the same instant. Add AND threadsource='user' to drop subagent/guardian threads, whose firstuser_message is an injected block rather than human text.

List recent sessions (filesystem only — works on every version)

CODEX_ROOT="${CODEX_HOME:-$HOME/.codex}"
find "$CODEX_ROOT/sessions" "$CODEX_ROOT/archived_sessions" \( -name 'rollout-*.jsonl' -o -name 'rollout-*.jsonl.zst' \) -print 2>/dev/null |
  while IFS= read -r f; do printf '%s\t%s\n' "${f##*/}" "$f"; done |
  sort | tail -20 | cut -f2-
# Wrapped legacy cwd/prompt probes (use the dump recipe for paginated files; see the era doc for bare files):
# For a compressed FILE, pipe `zstdcat FILE` into the jq probes instead of reading it directly.
head -1 FILE | jq -r '.payload.cwd // .cwd // "?"'
jq -r 'select(.type=="event_msg" and .payload.type=="user_message") | .payload.message' FILE | head -1

Search all sessions for a keyword

CODEX_ROOT="${CODEX_HOME:-$HOME/.codex}"
rg -l --glob 'rollout-*.jsonl' 'KEYWORD' "$CODEX_ROOT/sessions" "$CODEX_ROOT/archived_sessions"
# or search only what the user typed, with session IDs — but check freshness first:
ls -l "$CODEX_ROOT/history.jsonl"
jq -r 'select(.text|test("KEYWORD";"i")) | "\(.session_id)  \(.ts|todate)  \(.text[0:80])"' "$CODEX_ROOT/history.jsonl"

This covers uncompressed active and archived files. For .zst rollouts, require zstd support and use zstdgrep or zstdcat; do not silently omit them.

history.jsonl can lag badly: on the verification host its last append was 2026-07-10 despite persistence = "save-all" and hundreds of later sessions, and codex exec never appends to it. If its mtime is older than the newest rollout, use the rg line or state5.threads.firstuser_message instead.

Resolve a logical session to its current rollout

CODEX_ROOT="${CODEX_HOME:-$HOME/.codex}"
CODEX_DB_ROOT="${CODEX_SQLITE_HOME:-$CODEX_ROOT}"
case "$ID" in (*[!0-9a-fA-F-]*|'') echo 'Invalid ID prefix' >&2; exit 2;; esac
sqlite3 -separator ' | ' "$CODEX_DB_ROOT/state_5.sqlite" \
  "SELECT id, rollout_path FROM threads WHERE id LIKE '${ID}%';"

Dump a transcript as markdown

jq empty FILE >/dev/null || {
  echo 'Malformed rollout: use the current projection/migration or explicitly repair it first.' >&2; exit 1;
}
jq -r '
  if .type=="event_msg" then .payload |
    if .type=="user_message" then "## User\n\n\(.message)\n"
    elif .type=="agent_message" and ((.phase//"final")!="commentary") then "## Codex\n\n\(.message)\n"
    elif .type=="item_completed" and .item.type=="UserMessage" then "## User\n\n" + ([.item.content[]? | select(.type=="text") | .text] | join("\n")) + "\n"
    elif .type=="item_completed" and .item.type=="AgentMessage" and ((.item.phase//"final")!="commentary") then "## Codex\n\n" + ([.item.content[]? | select(.type=="Text") | .text] | join("\n")) + "\n"
    elif .type=="item_completed" and (.item.type=="CollabAgentToolCall" or .item.type=="CommandExecution" or .item.type=="DynamicToolCall" or .item.type=="FileChange" or .item.type=="ImageView" or .item.type=="McpToolCall" or .item.type=="WebSearch") then "  [tool] \(.item.type)\n"
    else empty end
  elif .type=="response_item" and (.payload.type=="function_call" or .payload.type=="custom_tool_call" or .payload.type=="tool_search_call") then
    "  [tool] \(.payload.name // .payload.type)\n"
  else empty end' FILE

Tool activity renders only its name or paginated category; arguments and results are intentionally omitted. This reader is current-first and also handles legacy events; see [data-model.md](data-model.md) for older bare rollouts.

Resume, fork, and manage a found session

codex resume <SESSION_ID>            # interactive; also accepts a thread name
codex resume --last                  # most recent for current directory
codex resume --all                   # picker across all directories
codex exec resume <SESSION_ID> "prompt"   # headless continue (also --last / --all)
codex exec fork <SESSION_ID_OR_NAME> "prompt"
codex fork <SESSION_ID>              # branch into a new thread (also --last / --all)
codex archive <SESSION>              # move to archived_sessions/
codex unarchive <SESSION>            # move back
codex delete <SESSION> --force       # permanently remove (--force requires a UUID)

The codex resume picker filters to the current cwd and interactive sources by default; add --all and --include-non-interactive to see everything (exec/MCP/subagent sessions). codex exec resume --all selects across threads. codex agents lists agent work. Queue commands mutate pending submissions; do not use them merely to inspect history.

Tips

  • Revert can create a new rollout generation whose filename UUID differs from logical sessionmeta.id; state5.threads.rolloutpath resolves the current generation. Paginated forks use historybase lineage/cutoff rather than replaying the parent. Archive/delete/unarchive operate across logical generations and descendants.
  • Filename timestamps are local time; in-file timestamps are UTC — a late-evening session can sit in the "wrong" date directory.
  • Subagent/review/compact threads have object-valued source in sessionmeta; filter on it (or on threadsource) to separate human sessions from automation. On a multi-agent host they can outnumber human sessions.
  • originator is a free-form host string, not an enum: Codex Desktop, codex-tui, codexclirs, codexexec, codexwork_desktop all occur.
  • codex exec --ephemeral runs with no persisted rollout at all — such runs leave nothing under sessions/.
  • codex migrate-rollouts is dry-run by default. It rewrites canonical JSONL only with --apply; there is no --dry-run flag. There is no codex history subcommand.

Verified against codex-cli 0.153.4 on 2026-09-06. A live persistent run wrote 15 ordinalled lines (0–14), including paginated UserMessage/AgentMessage and tokenusagerecord; Studio and laptop corpora were exhaustively checked. See [data-model.md](data-model.md) and the [0.146.0 historical snapshot](references/data-model-0.146.0.md).