关于 read-codex-history
面向需要核查 Codex 历史记录的 Agent 工作流,提供按请求目标选择证据来源和脚本的指南。强调区分提示词、会话状态、完整事件流与运行时日志,避免把读取误作继续执行。
主要功能
- 01按目标路由历史查询
- 02区分四类 Codex 数据源
- 03定位并核验原始对话
- 04读取工具调用与结果
- 05检查运行时错误日志
适用场景
- 01列出最近的 Codex 会话
- 02按引文定位原始会话
- 03统计并核对用户输入
- 04分析超时与重连错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ daymade/ claude-code-skills --skill read-codex-history安装后在 Claude Code 中输入/read-codex-history确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/codex_rollout_format.mdlegacy_multi_provider_inventory.mdstorage_and_portability.mduser_input_reconciliation.mdscripts/analyze_sessions.pylist_codex_user_inputs.pylist_local_history.pyprompt_cursor.pyread_codex_session.pyreconcile_codex_inputs.pyscripts/_core/__init__.pyclaude.pycodex.pyhomes.pykimi.pymodel.pynative_output.pyparse.pysources.pytext.pytests/test_analyze_sessions.pytest_kimi_provider.pytest_list_codex_user_inputs.pytest_list_local_history.pytest_native_output.pytest_original_record_evidence.pytest_paginated_lineage.pytest_pre_envelope_rollout.pytest_prompt_cursor.py详细介绍
读取 Codex 历史记录
只读取 Codex 证据。不要继续旧任务或更改其项目。如果用户希望在读取完成后执行操作,请将已核实的证据传递给 daymade-claude-code:continue-codex-work。
Codex 历史记录来源
| 来源 | 权威性 | 用途 |
|---|---|---|
<codex-home>/history.jsonl | 用户提交的内容,按会话 ID 和内部纪元时间戳索引 | 精确的近期用户输入表 |
state_*.sqlite | 清单元数据,例如 cwd、标题、更新时间和 rollout 路径 | 快速列出条目并发现候选项 |
sessions/**/rollout-*.jsonl 和 archived_sessions/** | 完整的用户/助手/工具/压缩/分叉事件流 | 在索引或物理预选之后,提供精确会话证据并验证关键词 |
<codex-home>/logs_*.sqlite(按修改时间选择最新文件) | 运行时事件:每个事件的 ts、level、target、feedback_log_body、process_uuid(包含 OS 进程 ID),以及可选的 thread_id | 确定哪个实时进程出现故障及其故障方式——按错误特征分类并分析时间分布 |
不要用一种来源替代另一种。提示词账本中的一行只能证明提交了什么,不能证明 Agent 回答了什么。状态数据库中的路径只是候选项,必须等 rollout 中的 session_meta.id 匹配后才能确认。即使没有提示词账本行,rollout 也可能存在;即使有 /fork 提示词,子 rollout 也可能不存在。运行时日志中的线程在经 state_*.sqlite 确认之前,不是可恢复的会话(见下文)。
运行时错误日志(logs_*.sqlite)
对于“Codex 很慢/一直在重新连接/我的哪个会话出错了”这类问题,应先读取此来源,再归咎于网络或服务。按错误特征和小时分组:错误集中在特定时间段,表示传输出现阶段性不稳定;错误率持续稳定,表示存在长期问题;如果一个 process_uuid 出错而其他进程正常,原因可能出在该进程本身(例如内存中的令牌过期),而不是服务中断。
-- error shapes in the last 24h
SELECT CASE WHEN feedback_log_body LIKE '%timed out%' THEN 'timeout'
WHEN feedback_log_body LIKE '%could not be refreshed%' THEN 'token-refresh'
WHEN feedback_log_body LIKE '%error sending request%' THEN 'send-failure'
ELSE 'other' END AS shape, COUNT(*)
FROM logs WHERE ts >= strftime('%s','now','-24 hours') AND level IN ('ERROR','WARN')
GROUP BY shape ORDER BY 2 DESC;
-- the same buckets per hour: clustered windows or a steady rate?
SELECT strftime('%m-%d %H', ts, 'unixepoch', 'localtime') AS hr,
SUM(feedback_log_body LIKE '%timed out%') AS timeouts,
SUM(feedback_log_body LIKE '%could not be refreshed%') AS token_refresh
FROM logs WHERE ts >= strftime('%s','now','-72 hours') AND level IN ('ERROR','WARN')
GROUP BY hr ORDER BY hr;
-- which OS processes are failing, and since when
SELECT process_uuid, COUNT(*), datetime(MIN(ts),'unixepoch','localtime') first_seen
FROM logs WHERE feedback_log_body LIKE '%could not be refreshed%'
GROUP BY process_uuid ORDER BY 2 DESC;
2026-10-03 测得的陷阱:
- 此处找到的
thread_id是 app-server 的线程句柄,并不能证明存在可恢复的 rollout。提供codex resume <id>之前,请在state_*.sqlite的threads.id中确认其存在;仅在日志中出现的 ID 会报错:“No saved session found with ID”。 strftime('%s', '<local wall time>', 'localtime')会重复转换,并可能将边界推到未来,导致返回 0 行,看起来像是“没有数据”。请将边界写成 UTC 字面量(即本地墙上时间减去时区偏移量)。
请阅读 references/storage_and_portability.md,了解来源发现、时间戳、写入锁语义、旧版 Kimi 兼容性和存储故障。解读分叉快照、压缩、事件流或结束原因之前,请阅读 references/codex_rollout_format.md。
按所需结果选择路径
| 用户想要 | 使用 |
|---|---|
| 最近的 Codex 会话、标题、ID,或确认存在写入者锁的证据 | scripts/list_local_history.py --source codex --index-only;如果索引不可用,则清单状态未知 |
| 根据已知项目、日期或标题线索,查找包含所粘贴引文的 Session | 参见下方的 查找引文对应的对话:先盘点候选项,再核实原始消息 |
| 按从新到旧的顺序,获取各 Session 中的确切近期用户输入 | scripts/list_codex_user_inputs.py |
| 包含继承历史在内的完整对话原始输入计数和引文 | scripts/reconcile_codex_inputs.py --session <ID> |
| 根据内部身份定位某个确切 rollout | scripts/analyze_sessions.py locate-codex <ID> |
| 重建某个 Session 及其声明的父级快照 | scripts/read_codex_session.py --session <ID> |
| 验证导出器捕获的 rollout 前缀,而不采集对话内容 | 导出器的已捕获 rollout 验证 |
| 在已知 Session 中验证工具返回值、评论 ID 或已读取的外部消息 | 使用下方的 原始工具和记录证据 命令;简报预览并非完整的工具证据 |
| 查找包含某个主题或短语的 rollout | read-claude-code-history/scripts/history_index.py recall --provider codex,然后核实确切的 rollout |
| 哪些运行中的进程正在报错、错误形态如何随时间变化、重连/超时历史 | 使用上方的 运行时错误日志 部分(对 logs_*.sqlite 进行分组);不要使用 rollout 事件流 |
| 记得内容但具体措辞已模糊 | 使用相同的混合模式索引检索,前提是向量数据完整 |
所需输出优先于查询动机。“显示我最近的原始输入”意味着按时间排序的原始输入表,而不是反馈分类、主题挖掘、交互式应用或所有历史 Session。
若要查看“我在这次对话中发了多少条消息/反馈;逐字列出”,请阅读 references/user_input_reconciliation.md。
使用 reconciler 组合现有台账和严格的谱系读取器。
它会保留出现次数、原始字符串和来源坐标。将退出码 2 或 complete: false 视为结果不完整:scope_input_count: null 不等于零,已核实的输入也不构成完整总数。提供任何绑定哈希的注入排除项之前,请根据实际来源核查未匹配记录。
说明计数单位和截止范围;不要把消息数量称为不同批评的数量。普通的近期输入请求应使用仅查台账的路径。
命令
脚本路径相对于此 SKILL.md 解析。不要使用临时 SQLite、Node、jq 或递归 grep 来重建联接。
近期清单
<skill-dir>/scripts/list_local_history.py \
--source codex --index-only --cwd <workspace> --limit 20 --language zh
写入者锁输出仅包含正向结果:锁被持有,证明快照期间持有了该确切的 advisory lock。它无法确定进程身份,也无法证明进程仍在运行;未标记的行不能证明该 Session 已停止。
定位引用的对话
如果你想知道“这是哪个 Session?”,并提供了项目、日期或标题线索,请先使用现有清单。根据标题和范围选出最有可能的候选项;标题匹配只是线索。若这些线索已限定了查找范围,就不要从全库扫描或跨提供方索引入手。
<skill-dir>/scripts/list_local_history.py \
--source codex --index-only --cwd <workspace> --include-archived \
--from-date <YYYY-MM-DD> --to-date <YYYY-MM-DD> --limit 20
<skill-dir>/scripts/read_codex_session.py --session <CANDIDATE_ID> --full
省略未知的日期范围;如果工作区未知,请将 --cwd 替换为 --all-projects。日期指的是引用的对话发生时间,而不是 Session 的创建时间。清单会检查创建/更新时间段是否重叠,因此当天恢复的较早 Session 仍是候选项。绝不要将 rollout 目录限定为当天的创建目录。资源日期不是消息日期。
从候选项中排除当前 Session。必须确认读者的身份,并在原始发言者的时间线条目中找到引用文本(若引用的是粘贴的助手回复,则需在 ASSISTANT 条目中找到,并提供记录坐标)。仅凭用户引用该回复、工具结果或压缩摘要,无法证明该回复最初出现的位置。对于大型简报,请使用下方的私有文件路径,并检查匹配条目及其角色和记录标题;查找 Session 不需要阅读无关历史记录,也不需要继续旧任务。
确认引用内容和身份后立即停止;返回 ID 和来源坐标。如果某个候选项不匹配,请尝试其余合理的候选项。列表被截断、时间戳缺失、清单不可用或没有匹配候选项,都不代表内容不存在:请细化索引召回并说明其覆盖范围。不要退而搜索原始语料库。
原始输入
# Global recent window, then group by Session
<skill-dir>/scripts/list_codex_user_inputs.py --recent 200 --language zh
# Expand exact Sessions already shown, preserving their order
<skill-dir>/scripts/list_codex_user_inputs.py \
--session-id <ID-1> --session-id <ID-2> \
--per-session 100 --language zh
Markdown 是供人阅读的形式;JSON 则保留存储的字符串值,供取证或机器处理使用。保留重复项、行顺序、时间戳、措辞和 Session 边界。不要臆造标题,也不要将一个 Session 拆分为语义类别。
经协调的完整对话输入
<skill-dir>/scripts/reconcile_codex_inputs.py --session <EXACT_ID> --format json
使用 --through-record 在所选 Session 中指定明确的包含式截止位置;仅在用户确实要求排除时,才使用 --omit-first/--omit-last。这两个选项都不能决定某条消息是开场指令还是反馈。解决缺漏后,使用 --format markdown 输出带字面编号的引文。请阅读链接的协调参考文档,了解结果字段、已审核的排除项、部分结果以及仅针对确定性测试样例的验证。
精确的 Session 证据与沿革
<skill-dir>/scripts/read_codex_session.py --session <SESSION_ID> --full
预期输出:# Codex Session Evidence Briefing、经验证的所选身份、从根节点到子节点的分叉沿革、精确的父级字节边界、按时间顺序排列的交接、压缩后的上下文、最新计划、工具调用、文件、错误、结束原因和工作区状态。请遵循rollout 身份与分叉约定,了解逻辑/物理选择及继承的边界。若状态数据库中的候选项有误,应予以拒绝,并使用精确的身份定位符;标题或文件名相似,并不代表可以从该候选项继续。如果简报因安全限制而失败,该简报仍属于失败;只有在用户要求验证结果时,才使用下方的导出器路径。
如果完整简报过大,无法放入单个模型上下文,请将其一次性写入私有临时文件,并在读取前记录其 SHA-256 值和行数。该不可变文件仍是唯一的一份简报;“一份简报”并不意味着只能有一份 stdout 输出,也不意味着必须将其作为单体一次性载入上下文。使用现有标题或精确记录坐标,读取范围有限且互不重叠的内容,并根据记录的行数跟踪覆盖范围;将每个未读取范围报告为缺口。不要以不同的截断设置重新运行读取器,再将输出拼接成看似完整的时间线。
导出器的已捕获 rollout 验证
当导出器需要验证明确选定的字节前缀,且不累积对话内容时,请使用随附的 validate_codex_rollout 可调用对象。
在导入它或遍历祖先记录之前,请阅读API、就绪状态与恢复契约。
使用其实际元数据和已验证的边;复制或验证历史记录并不能确立执行归属。
原始工具和记录证据
按身份或索引召回选定一个 Session 后,请使用随附的读取器读取存储的工具结果,而不要另写 JSONL 解析器:
<skill-dir>/scripts/read_codex_session.py --session <ID> --tools --contains '<literal clue>' --format json
<skill-dir>/scripts/read_codex_session.py --session <ID> --record <ORDINAL> --format json
不带 --record 的 --tools 会按从根记录到所选记录的顺序展开完整的已声明逻辑历史,并使用精确的祖先前缀。其 scope 为 logical_history;sources 列出每个物理来源及其截止位置。每个结果都会保留其来源路径、物理 rollout ID、物理 record 序号以及 logical_record 位置。paired_call 可能指向较早的分段。不会展开单独的子 Agent 线程和附件字节。
对所选物理 rollout 中多个从 1 开始计数的非空记录序号,重复指定 --record。此选择器会保留 scope: selected_rollout_only;将其与 --tools 结合使用时,会取这些选定物理记录的交集。--contains 是在报告范围内进行的字面子字符串筛选。这两种模式都会保留原始存储记录,包括每个输出字符串和字段,不做内容脱敏或预览截断。对于结果,paired_call 会定位之前的调用;在归属外部消息之前,请检查这两个坐标。角色标签不能证明内容由人撰写。
预期输出会报告 identity、records_examined、matched_records、来源坐标以及 truncated: false。缺少请求的序号时会明确报错。零匹配仅适用于报告的范围。--full 仍用于控制文字简报的截断;它不会将简报变成完整的工具证据。
疑似终止证据
要调查记录中的批次或控制器是否已终止,应沿着该次运行自身的证据链追查,而不是只搜索进程:
- 按身份选定确切的 Session,然后使用
--tools读取原始配对工具记录,并将每条结果与paired_call配对。字面搜索--contains kill返回零条匹配,不能证明未尝试终止:--contains只会在所报告的范围内筛选字面子串,而记录的命令可能会使用不含“kill”一词的信号名称。路径、引号内的源码或代码编辑中出现匹配,也不能证明信号已发送;请检查实际操作及其配对结果。 - 声明的逻辑历史只会展开从根记录到所选记录的祖先记录。单独的子 Agent/子线程和附件字节不会展开,因此子线程的终止证据位于子线程自身的记录或其自身的运行回执中,而非父线程的逻辑历史中。
- 区分三项互不构成彼此证明的事实:控制器会话中记录的信号尝试、子线程实际退出(其自身的返回码或进程证据),以及之后的 UI 或业务清理。证实其中一项,绝不能据此证实另一项。
- 沿着记录中的运行器追查:读取其分派记录中的运行器/脚本指纹,以及该次运行保留的摘要(超时、取消和退出字段),再查看该记录版本对应的清理实现。如果当前源码不同,请保留原始脚本版本;仅搜索进程名称或
kill无法重建这条证据链。 - 在每项结论旁记录搜索的时间范围。观察到症状的时间(或“大约那天下午”这样的估算时间段)限定的是症状,而非其原因:导致症状的控制器操作可能发生在读者从未搜索过的更早时间段。从选定的截止时间开始筛选,会悄然排除此前的所有内容;因此,筛选窗口内零条匹配只是范围内未命中,绝不代表全局不存在,也不是遍历原始语料库的理由。如果证据指向更早的时间段,只扩大已选定任务或候选记录的窗口,说明修正后的时间范围,并在该范围内重新读取同一批记录。
索引内容搜索
索引会从存储的文本中返回候选结果。请阅读确切的 rollout,以检查工具输出、思考内容、压缩内容以及引用行的发言者。
<read-claude-code-history-dir>/scripts/history_index.py status
<read-claude-code-history-dir>/scripts/history_index.py recall '<keyword>' \
--mode bm25 --provider codex --exclude-session <CURRENT_ID>
从确切 ID、已知日期或索引内容线索入手。通过候选 rollout 的 session_meta.id 验证。超时和旧版 --from-date 筛选器都不会阻止原始命令读取每个 rollout;该命令现在会拒绝实时来源。
通过备用索引进行全文检索
单独的 ~/.claude-flow-viewer/search.sqlite FTS 索引也可以定位候选会话。使用前请检查其提供方覆盖范围和新鲜度,然后通过精确会话读取器核实每条命中记录。命中只是线索,并非证据。
sqlite3 ~/.claude-flow-viewer/search.sqlite "
SELECT s.source, s.session_id, s.project_encoded, substr(c.text, 1, 600)
FROM search_chunks_fts fts
JOIN search_chunks c ON c.rowid = fts.rowid
JOIN indexed_sessions s ON s.session_id = c.session_id
AND s.source = c.source AND s.project_encoded = c.project_encoded
WHERE fts.search_chunks_fts MATCH '\"keyword-one\" OR \"keyword-two\"'
AND c.kind IN ('prompt','ai-text')
LIMIT 15;"
根据此索引测得的限制如下,每一项都会影响零结果的含义:
- 先查看新鲜度边界。
sqlite3 ~/.claude-flow-viewer/search.sqlite "SELECT * FROM search_meta;"会打印last_indexed_at;比该时间戳更新的会话根本不在索引中,因此查询无法检索到它们。报告覆盖范围时,请一并说明这个边界。 - 通过
search_chunks_fts匹配,切勿直接查询search_chunks。 直接查询会对约 9GB 的表进行未索引的全表扫描——实测耗时 26 秒,而索引路径仅需 0.03 秒。 - 此查询使用全文匹配。 它检索的是语料中的字面文本,而非向量相似度。实测快照中,尽管已有 sqlite-vec 分片表,
search_embeddings仍有 0 行;声称支持向量检索前,请检查当前数据量。若要检索释义,请使用read-claude-code-history中的混合召回索引。 - 此版本使用 FTS5 语法。 将带引号的短语与显式
OR组合。未加引号的多词查询会执行隐式 AND:以空格直接连接的两个词,只会匹配同时包含两者的行,因此原本应扩大搜索范围的查询反而会返回空结果。请写成"term-a" OR "term-b"。NEAR/n斜杠语法会报语法错误;标准的双操作数NEAR(A B, k)形式可以正常解析(某个测试查询返回了 0 行——这是真实的无共现结果,而非语法错误,因此不要把 0 理解为“NEAR 已损坏”)。 unicode61分词器会将连续的 CJK 字符串视作一个词元,因此中文短语只有在与完整字符串一致时才能匹配。在技术选型上实测:短语查询返回 10 行,而子串出现次数为 163 次;在闭门造车上则为 10 行,对应 790 次。证明单个字符不会单独成为词元的关键证据是:闭返回 2 行,而闭门返回 17 行——如果闭被单独分词,结果集不可能更小。同一词族中更长的字符串查询效果要好得多(不要闭门造车→ 314 行,对应 790 次),因此搜索中文时,请使用你能猜到的最长连续字符串。ASCII 和以标点分隔的词语不存在此问题(technology-selection匹配 35 行,对应 34 次)。绝不要把中文查询返回 0 行或较少行理解为内容不存在——在此路径中,未命中的表现与“我们从未讨论过此事”无法区分,而这正是使用此路径要避免的问题。- 筛选
c.kind以获取用户自己的话语。prompt是用户输入,ai-text是助手内容;thinking、tool-call和tool-result也会被索引,因此不加筛选的查询大多会把工具输出读回给你。 - 检查
s.source,再归属命中记录。 在此处找到命中记录,本身并不能证明它是 Codex 的发现。 - 排除当前会话。 当前会话记录了这个问题本身以及搜索它的命令,因此几乎任何关于当前会话的查询都会匹配。
身份与溯源门槛
在对具名 Session 作出任何行为判断前:
- 如果引用用户输入,请核实提示词账本中的 Session ID。
- 使用精确会话读取器,或在使用导出器时使用捕获前缀 API,并遵循其身份/祖先关系约定,再归属事件。
- 明确报告仅有提示词或仅有 rollout 的缺口。
观察到的故障包括:提示词账本中的某个 Session,其状态数据库却指向另一个 rollout;以及一个没有子 rollout 的 /fork 输入。
读取结果约定
每个回答都必须说明:
- 读取的来源 — 提示词账本、状态数据库、实时/归档 rollout。
- 覆盖范围 — Session ID、项目、内部时间范围。
- 结果 — 请求的原始表格、时间线或匹配项。
- 身份/沿袭状态 — 已验证、仅提示词、仅 rollout 或不匹配。
- 缺口 — 格式错误/无法读取的来源、缺失的父项、未包含的附件字节、 超时或未搜索的范围。
“未找到”仅适用于此覆盖范围。不得将超时或不完整扫描称为否定结果。
防护措施
- 保持此技能只读;不得恢复、归档、重命名、删除或修复。
- 不要运行
codex resume、codex --continue或新的实现实验。 - 不要将数 MB 的 rollout 直接载入上下文;请使用随附的读取器。
- 不要根据进程名称、cwd 或不存在的写入锁推断 Session 状态或所有权。
- 除非用户明确要求分享,否则请将原始历史记录保留在本地。
- 不要将任一索引中的零行命中视为“对话从未发生”:请检查提供方范围和新鲜度,并将未编入索引的记录报告为未知。通过精确读取 rollout 验证正向线索。
路由器与旧版兼容性
当前的 local-conversation-history 是跨提供方路由器;它会将特定提供方的 Codex 读取请求路由至此处,且不会取代此技能的身份、沿袭或证据约定。旧版组合命令约定仍保留在 references/legacy_multi_provider_inventory.md 中,以免其 Kimi 分支和历史标志被悄然删除。特定提供方的 Claude 请求会路由至 daymade-claude-code:read-claude-code-history。
有界的当前线程直接输入游标
当某项操作需要在当前线程请求之后追加原始直接用户输入时,请使用 prompt_cursor.py。在呈现请求之前运行 snapshot,然后运行 tail --cursor <snapshot.json>;适配器只会从账户所有的规范提示词账本中读取新增字节,并在解码文本前选取当前运行时线程。保留 inode/device/offset,并拒绝轮替、截断、不完整记录、线程变更、非默认来源或不受支持的主机。不接受调用方提供的账本/根目录覆盖。账本架构无法证明作者为人类;缺少已知对端记录只能作为有界证据。适配器会返回作者身份尚未确立的来源记录,绝不返回批准信息;使用该适配器的操作负责内容绑定、确认语法、失效处理和单次尝试的消耗。检索历史输入时请使用现有读取器。