Codex CLI 的会话历史:在哪里、怎么读、怎么搜

每个会话都在磁盘上;Codex 只是把它们列出来,并不搜索

Codex 记下你跑的每一个会话。它缺的是:没法问你哪一次会话里有过那个修复,也没法在 resume 选择器之外看它们。文件本身足够简单,jq 和 sqlite3 就能读出大半;可跑上一周,有一个细节会让最顺手的 grep 失灵。

Codex 把它放在哪里

一切都在 ${CODEX_HOME:-~/.codex} 之下:

  • sessions/YYYY/MM/DD/rollout-<time>-<id>.jsonl:一个会话一个文件,整段对话随运行不断追加。第一行是 session_meta,里面是会话 id 和工作目录。
  • 七天之后,Codex 会在后台把 rollout 压缩成 rollout-….jsonl.zst。两种形式 Codex 都读。而凡是在找 *.jsonl 的东西,从此再也看不到一周之前的会话。
  • archived_sessions/:你归档过的会话,格式相同。
  • history.jsonl:只有你敲过的提示词,一行一条,带着会话 id。没有回复,也没有命令。
  • state_5.sqlite:resume 选择器读取的会话列表,含标题、工作目录、git 分支、token 数和归档标记。名字里的数字会跟着 Codex 的版本走。

Codex 自带的东西

codex resume                 # 选择器,列出在这个目录里开过的会话
codex resume --all           # 所有目录
codex resume --last          # 直接进入最近的一个
codex resume --include-non-interactive   # 连 codex exec 跑的也算
codex fork                   # 复制一个旧会话继续
codex archive <id>           # 藏起来;codex unarchive 可以恢复
codex delete <id>            # 永久删除

选择器显示标题和第一条消息,不往对话里面看,也只认 Codex。某个会话在它里面不见了,或者 resume 报出 no rollout found,见Codex resume。

不用任何工具去读

最近几次会话,以及它们是在哪里跑的:

for f in $(ls -t ~/.codex/sessions/*/*/*/rollout-*.jsonl | head -10); do
  jq -r 'select(.type=="session_meta") | .payload | "\(.timestamp[:16])  \(.cwd)  \(.id)"' "$f"
done

同一份列表,改成只读地读选择器的数据库:

sqlite3 -readonly ~/.codex/state_5.sqlite "
SELECT datetime(updated_at, 'unixepoch', 'localtime'), substr(title, 1, 50), cwd
FROM threads WHERE archived = 0 ORDER BY updated_at DESC LIMIT 20;"

一次会话里你敲过什么,从提示词日志里读。它只记交互式会话,交给 codex exec 的提示词不在里面:

jq -r 'select(.session_id=="<id>") | .text' ~/.codex/history.jsonl

哪些会话提到过某个词,压缩过的那批也算:

grep -l "pgbouncer" ~/.codex/sessions/*/*/*/rollout-*.jsonl
zstdgrep -l "pgbouncer" ~/.codex/sessions/*/*/*/rollout-*.jsonl.zst

这对一个你记得分毫不差的词管用。放到几个月的历史上就慢,没有排序,还会漏掉那次你换了种说法说起它的会话。

搜索它

deja-vu 会给这些文件建索引——压缩过的、归档过的都算在内——和这台机器上其他每个智能体的历史放在一起。它对它们只读不写。

deja index
deja --harness codex "pgbouncer prepared statement"   # 带排序,并附会话 id
deja last 20 --harness codex                           # 最近的会话,不限目录
deja show <id>                                         # 那段对话
deja resume <id>                                       # 能在 Codex 里重新打开它的命令
deja resume <id> --app                                 # 或者在 ChatGPT 应用里打开
deja view                                              # 全部内容合成一个本地 HTML 页面

不带参数的 deja 打开的是同一个搜索,只是变成一屏边打字边更新的界面:a 把范围收窄到 Codex,↵ 在命中的地方读那次会话,r 在 Codex 里重新打开它。

去掉 --harness codex,同一个搜索就覆盖 Claude Code、Cursor、opencode 以及其余的——答案往往就落在它们那边。凭据在建索引时就被脱敏。deja 索引过的会话,在 codex delete 或一次清理之后仍然搜得到;只有 deja forget 会有意把它丢掉。

想让 Codex 在会话开始时自己去查,而不是由你手动搜索,见给 Codex CLI 加记忆。

在多台机器上

Codex 把历史留在本机,codex resume 也只列出这块磁盘上有的东西。deja 可以把自己的索引通过 SSH 在机器之间搬运,中间不需要任何服务:

deja sync ssh devbox        # 把本机的会话推到那边
deja sync ssh devbox --both # 也把它的会话拉过来

送过去的东西,在那边用 deja 就能搜。它不会把会话加进那台机器的 resume 选择器。跨机器同步有更多细节。

删除 Codex 的会话 · 每个智能体把历史存在哪里 · 接着一次会话继续