# 统一 Codex 会话历史:功能介绍与使用攻略(CC Switch) > 适用版本:CC Switch v3.16.x 及以上。本文根据当前代码整理,命令与路径均可亲手验证;示例使用去敏数据,不包含真实会话内容或 API Key。 ## 这个功能是什么 「统一 Codex 会话历史」是 CC Switch v3.16.x 为 Codex 新增的一个开关。它的位置在 **设置 → 通用 → 「Codex 应用增强」分组**里("Codex 应用增强"是这个分组的标题,开关本身叫"统一 Codex 会话历史")。开启后,**官方订阅(ChatGPT 登录 / OpenAI API Key)的会话,会和 CC Switch 管理的所有第三方供应商会话,出现在同一个历史 / 续聊列表里**——不再被分隔在两个互相看不见的列表中。 ## 它解决什么问题 Codex 自己按"供应商标签"(一个叫 `model_provider` 的字段)给会话分类,而且**续聊 / 历史列表只显示和你当前激活的供应商同标签的会话**。于是会话天然被分进两个"抽屉": - 官方订阅的会话,归在 Codex 内建的 **`openai`** 标签下; - CC Switch 管理的所有第三方供应商,归在 **`custom`** 标签下。 两个抽屉互相看不见。如果你**经常在官方与第三方之间切换**,就会遇到这种割裂:"刚才用官方聊的会话,切到第三方后在历史列表里找不到了"——它其实没丢,只是被分到了另一个抽屉。这种割裂既容易让人误以为会话丢失,也不方便把所有会话放在一处统一回顾、续聊。 **这个开关就是为了消除这种割裂**:让官方订阅也以 `custom` 标签运行,于是官方与第三方会话合并进同一个列表,找起来、续起来都在一处。 > ✅ **一个贯穿全文的重要前提,请先记住**:这个功能(统一 / 迁移 / 还原)**全程只改写会话记录里那一个归类标签 `model_provider`,而且每次改写前都会自动把原文件备份一份**。它不会删除、清空或覆盖你的任何一句对话。所以本文后面若提到"某些会话看不到了",几乎都是"被分到了另一个抽屉",而不是"数据没了"——真担心时,直接看 [症状对照表](#我感觉会话丢了症状对照表) 与 [亲手验证文件还在](#亲手验证你的会话文件还在硬盘上最重要的一节)。 ## 工作原理(一句话版) 把它想成 **两个抽屉 + 自动备份**: - 默认时,官方会话在 `openai` 抽屉、第三方会话在 `custom` 抽屉,互不可见; - 开关让**官方也改用 `custom` 抽屉**,于是两个抽屉合并成一个共享列表; - 你可以选择把**现有的官方老会话**也一并"搬"进共享抽屉(这一步叫**迁移**,可选、需主动勾选),而**任何搬动前都会先复制一份备份**,所以整个过程**可逆**; - **认证完全不受影响**——官方订阅照常用你的 ChatGPT 登录、照常走官方后端,变的只是会话的归类标签。 完整机制(注入了什么、为什么可逆、迁移/还原如何保证不丢数据)见下文 [核心心智模型](#核心心智模型两个抽屉--自动备份) 与文末 [进阶原理附录](#进阶原理附录给想真正搞懂机制的用户)。 ## 如何使用(速览) 1. **开启**:设置 → 通用 → Codex 应用增强 → 打开「统一 Codex 会话历史」→ 在弹窗里决定是否勾选"同时迁入现有官方会话历史"(想让**以前**的官方会话也并进统一列表,就勾上;只想从现在起统一,就不勾)→ 确认。详见 [开启时会发生什么](#开启时会发生什么分步说明)。 2. **关闭**:关掉同一开关 → 弹窗里保持勾选"按备份精确还原"(默认就勾着)→ 确认,即可把当初迁入的官方会话精确翻回官方列表。详见 [关闭时会发生什么](#关闭时会发生什么分步说明)。 3. **感觉会话丢了?** 别慌,跳到 [症状对照表](#我感觉会话丢了症状对照表) 按症状定位,并用 [亲手验证](#亲手验证你的会话文件还在硬盘上最重要的一节) 一节的命令亲眼确认文件都在。 --- ## 核心心智模型:两个抽屉 + 自动备份 要理解这个功能,你只需要记住两件事:**抽屉**和**备份**。 ### 抽屉:Codex 怎么给会话分类 你每开一个 Codex 会话,Codex 会在会话文件头部记一个标签 `model_provider`,标记"这条会话是用哪个供应商聊的"。Codex 的**续聊 / 历史列表是按当前激活的这个标签精确过滤的**——只显示和"你现在这个供应商"同标签的会话。 - 官方订阅(ChatGPT 登录 / OpenAI API Key)的会话,标签是内建的 **`openai`**。 - CC Switch 管理的所有第三方供应商,统一用标签 **`custom`**。 所以默认情况下,官方会话和第三方会话天生互相看不见——它们在两个不同的抽屉里。这是 **Codex 自身的设计**,不是 CC Switch 弄丢了什么。 ```text 默认状态(没开统一开关): ┌─────────────────┐ ┌─────────────────┐ │ openai 抽屉 │ │ custom 抽屉 │ │ (官方订阅会话) │ │ (第三方供应商会话)│ └─────────────────┘ └─────────────────┘ ▲ ▲ 用官方时只看到这边 用第三方时只看到这边 (两个抽屉互相看不见) ``` **「统一 Codex 会话历史」开关做的事,就是让官方订阅也以 `custom` 标签运行,把两个抽屉合并成一个**,于是官方会话和第三方会话出现在同一个续聊列表里。注意:**认证没变**——你的官方订阅照常用你的 ChatGPT 登录、照常走官方后端,只是会话的"归类标签"从 `openai` 变成了 `custom`。 ```text 开启统一开关后: ┌─────────────────────────────────────────┐ │ custom 共享抽屉 │ │ 官方订阅会话 + 第三方供应商会话 │ │ (出现在同一个历史 / 续聊列表里) │ └─────────────────────────────────────────┘ ``` ### 备份:每次改标签前都先复制一份 "合并抽屉"需要把一部分官方会话的标签从 `openai` 改成 `custom`(这一步叫**迁移**,且是**可选的、需要你主动勾选**)。而**任何一次改写之前,CC Switch 都会先把原文件原封不动地复制一份**到这里: ```text ~/.cc-switch/backups/codex-official-history-unify-v1/<时间戳>/ ``` 这份备份,就是日后"按备份精确还原"的唯一依据。它让整个过程变得**可逆**:你随时可以关掉开关,把当初迁进来的官方会话精确地翻回 `openai` 抽屉。 记住这两个词——**抽屉**(会话只是换了归类)、**备份**(改前必先复制)——后面所有内容你都能轻松理解。 --- ## 开启时会发生什么:分步说明 ### 第 1 步:找到开关 ```text 设置 → 通用 → Codex 应用增强 ``` 在"Codex 应用增强"这个区块里有两行开关,**第二行**(蓝色历史图标)就是本攻略的主角: > **统一 Codex 会话历史** 它下方有一段说明文字(逐字): > 开启后,官方订阅将以共享的 custom 供应商标识运行,官方与第三方会话出现在同一历史列表中,并可选择把现有官方会话一并迁入(迁移前自动备份)。关闭开关时可按备份恢复迁入的会话。注意:跨供应商继续旧会话时,对方后端可能无法解密会话中的 encrypted_content 推理内容,导致继续失败 > **注意**:这一句说明里已经预告了三件事——会出现在同一列表、可选迁入并自动备份、跨供应商续聊"可能继续失败"。这里的"继续失败"指的是**续不上、生成不了新回合**,不是"记录丢失"。这正是后面要重点拆解的核心误解。 ### 第 2 步:把开关从关拨到开 → 弹出确认窗 一旦你把开关拨到开,CC Switch **不会立刻保存**,而是先弹出一个确认窗口。窗口文案如下(逐字): - **标题**:统一 Codex 会话历史 - **正文**: > 开启后,官方订阅与第三方将共用同一个会话历史列表。注意:跨供应商继续旧会话时,可能因对方后端无法解密 encrypted_content 推理内容而失败。 > > 可选择同时把现有官方会话历史迁入共享列表(迁移前自动备份到 ~/.cc-switch/backups,关闭开关时可选择恢复)。 - **复选框**:同时迁入现有官方会话历史 - **确认按钮**:我已了解,继续开启 - **取消按钮**:取消 **这个复选框默认是不勾选的。** 这是一个重要的分岔点: | 你的选择 | 效果 | 此刻你的数据在哪 | |---|---|---| | **不勾**(默认) | 只切换标识。**只有开启之后新建的官方会话**才会落进 `custom` 共享抽屉 | 你**开启前**的官方老会话,标签仍是 `openai`,原地未动,仍在 `~/.codex/sessions/` | | **勾上** | 除了切换标识,还会把**现有的官方老会话**也从 `openai` 抽屉迁进 `custom` 抽屉 | 老会话被**复制备份**后,标签改写为 `custom`;原始数据有备份兜底 | > **如果你希望"以前的官方会话也出现在统一列表里",必须主动勾选这个复选框。** 否则你会遇到下面对照表里的"场景 A"——老会话看起来"不见了",其实只是留在原抽屉里。 点"取消"或点窗口外面:开关直接弹回关闭状态,什么都没发生。 点"我已了解,继续开启":开关保存为开启,CC Switch 在后台落盘配置(如果勾了迁移,就执行迁移)。 ### 第 3 步(仅当勾了迁移):迁移如何执行 + 数据安全 如果你勾了"同时迁入现有官方会话历史",CC Switch 会对你的官方老会话做这套流程: ```text 对每个官方(openai 标签)会话文件: ① 先把原文件原样复制一份到备份目录 ← 数据有了第一道保险 ② 用「写临时文件 → 整体替换」的原子方式, 只把头部那行 session_meta 里的 model_provider 从 "openai" 改成 "custom" ← 对话正文一个字节都不动 ③ 索引数据库 state_5.sqlite 同步在一个事务里把标签改过来 ``` - **备份位置**:`~/.cc-switch/backups/codex-official-history-unify-v1/<时间戳>/`,每次迁移生成一个带时间戳的"代际目录",内含 `jsonl/`(会话副本)、`state/`(索引库副本)、`meta.json`(记录这次迁移属于哪个 Codex 目录)。 - **改的是什么**:只有 `model_provider` 这一个字段值。你的对话内容、推理内容、所有正文**原样保留**。 - **删的是什么**:**什么都没删**。备份是"复制",改写是"原子替换同一个文件",全程没有任何删除会话或索引的动作。文件在任何时刻都是完整的(要么是旧内容、要么是新内容,绝不会是空或半截)。 迁移成功后,这些官方老会话就出现在统一列表里了。**此刻你的数据**:① 原始副本在备份目录;② 活动文件里只有归类标签变了,内容完好。 > **注意**:开启与迁移本身**不会弹成功提示**。迁移是后端在保存时顺带跑的,UI 上你只会看到开关变成了打开状态。所以"没看到迁移成功的弹窗"是正常的,不代表失败。 --- ## 关闭时会发生什么:分步说明 ### 第 1 步:把开关从开拨到关 → 探测备份 → 弹出确认窗 关闭时,CC Switch 会**先花一瞬间探测有没有迁移备份**,然后弹出确认窗口(所以关闭弹窗会有一点点延迟,属正常)。文案如下(逐字): - **标题**:关闭统一会话历史 - **正文**: > 关闭后,官方订阅与第三方将恢复各自独立的会话历史列表。开启期间产生的会话因无法区分来源,将留在第三方历史中,官方订阅将看不到它们。 - **复选框**(条件显示):把开启时迁入的官方会话还原回官方历史(按备份精确还原) - **确认按钮**:关闭 - **取消按钮**:取消 > **划重点**:正文说的是"官方订阅**将看不到它们**"——是**看不到**,不是**删除**。开启期间你新聊的会话仍然完整地在 `custom` 抽屉里,只是关闭后官方那一侧看不到而已。 **这个还原复选框默认是勾选的。** 也就是说,默认行为就是"关闭的同时,把当初迁入的官方会话精确还原回官方历史"。你只要保持勾选、点"关闭"即可。 如果复选框**没有出现**,说明系统判断当前没有需要还原的备份(要么你从没勾过迁移、要么探测不到备份)——这种情况下你的官方老会话从没被改动过,关掉开关它们自己就回到 `openai` 抽屉了。 ### 第 2 步:还原如何执行(按备份账本精确翻回) 如果你保持勾选并点"关闭",CC Switch 的还原流程是这样的: ```text ① 先把当前现场再复制一份到独立的还原备份目录 ~/.cc-switch/backups/codex-official-history-unify-restore-v1/<时间戳>/ (还原本身也先备份,所以还原也不会丢数据) ② 翻遍所有迁移备份代际,找出"当初标签是 openai"的会话 id,组成一份"账本" ③ 只对【既在账本里、当前又仍是 custom】的会话,把标签改回 "openai" ``` 注意第 ③ 步的**双重条件**——既要在账本里(证明它当初确实是官方迁来的),又要当前仍是 `custom`(说明你没手动改过它)。两个条件都满足才翻回。这保证了还原既精确又不会误伤。 **此刻你的数据**:被迁回的官方会话标签改回 `openai`,重新出现在官方列表;同时迁移备份和还原备份两份副本都还在硬盘上。 ### 第 3 步:看提示,确认结果 只有"关闭 + 勾选还原"这条路径会弹结果提示。可能看到的提示(逐字): | 你看到的提示 | 含义 | |---|---| | **已按备份还原官方会话历史({{files}} 个会话文件、{{rows}} 条索引记录)** | 还原成功。`{{files}}` / `{{rows}}` 处会显示实际数字 | | **当前 Codex 目录没有可恢复的迁移备份** | 没有可还原的内容(**不等于数据丢了**,详见对照表场景 E) | | **统一会话历史开关已重新开启,已跳过还原** | 还原排队期间你又把开关打开了,系统主动放弃还原(详见对照表场景 F) | | **还原官方会话历史失败,请重试** | 还原过程报错,重试即可,数据未被破坏 | | **保存失败,请重试** | 关闭这一步保存本身就失败了;此时**绝不会触发还原**,开关弹回原位 | > **一个贴心的安全设计**:如果"关闭开关"这一步保存失败,CC Switch **绝不会去执行还原**。否则就会出现"开关还开着、会话却被翻回 openai 桶"的撕裂状态。保存失败时开关会**自动弹回原来的位置**,你不会停留在一个"看起来已关、实则没保存"的假状态里。 --- ## "我感觉会话丢了?"症状对照表 下面六个场景,是用户最容易误以为"会话丢了"的情形。**每一个的真相都是:数据完好,只是换了抽屉或暂时看不到。** 先用这张表按症状定位,再看下面的详细说明。 | 场景 | 你看到的 | 数据真相 | 一句话解法 | |---|---|---|---| | **A** 没勾迁移 | 官方老会话不在统一列表 | 全在,仍带 `openai` 标签 | 重开并勾迁移,或关开关 | | **B** 跨供应商续聊失败 | 续不上 / 报错 | 文件完好,只是密文跨后端解不开 | 回原供应商续;只看内容直接读 jsonl | | **C** 代理接管 / 注入被拒 | 没迁也没还原 | 迁移被安全跳过,文件没动 | 退出接管 → 重启重试;或直接关开关 | | **D** 还原后新会话没回官方 | 开启期间新会话不在官方 | 在 `custom` 抽屉,设计上不动 | 切第三方供应商即可见 | | **E** 提示"没有可恢复备份" | 还原"失败" | 通常压根没迁移过,会话在原抽屉 | 关开关官方会话自动复现 | | **F** 提示"开关已重新开启,跳过还原" | 还原被拒 | 防数据撕裂,啥也没改 | 先彻底关开关再还原 | ### 场景 A:开了开关但没勾迁移 → 官方老会话"不见了" **现象**:你开了统一开关,但开启弹窗里那个"同时迁入现有官方会话历史"没勾(它默认就不勾)。开启后一看,以前的官方老会话好像都不在列表里了。 **真相**:数据 100% 都在,一行都没动。开关只对"开启之后新建"的官方会话生效,你**开启前**的官方老会话标签仍是 `openai`,原封不动地躺在 `~/.codex/sessions/` 里。你现在激活的是 `custom` 抽屉,自然看不到留在 `openai` 抽屉里的老会话——这就是"看起来消失"的全部原因。 **怎么办**(任选其一): 1. **重新开启开关时勾上"同时迁入现有官方会话历史"**,把老会话换到 `custom` 抽屉,它们立刻出现在统一列表(改写前自动备份)。 2. **或者干脆关掉统一开关**,官方重新以 `openai` 抽屉运行,老会话原地复现。 ### 场景 B:跨供应商续聊旧会话失败 → 以为"这条会话坏了 / 没了" **现象**:统一之后列表里能看到一条用"另一家供应商"聊出来的旧会话,你切到现在的供应商点"继续",结果报错或接不上。 **真相**:会话文件完好无损,丢的不是数据,是"跨后端解密能力"。Codex 会话里保存了一段加密的推理内容 `encrypted_content`,**这段密文只有当初生成它的那个后端能解密**。你用 B 供应商去续 A 供应商生成的会话,B 解不开 A 的密文 → 续聊失败。这是**上游 Codex 的设计限制(by design)**,与 CC Switch 是否动过文件无关。会话里的文字内容你随时能读到。 > 这是整篇攻略里**唯一一个"看起来真出了问题"的真实例外**——但请注意:它只是**无法续聊(生成不了新回合)**,**原始文件依然完整存在**,对话文字随时可读。 **怎么办**: - **用"当初创建这条会话的那个供应商"去续聊**,就能正常解密、接上。 - 只想看历史内容、不必继续?直接读那条会话的 `.jsonl` 文件(文末有命令)。 - 经验法则:**跨供应商更适合"开新会话",老会话尽量回原供应商续。** ### 场景 C:开了开关也勾了迁移,但迁移被静默跳过 → 以为"迁移把会话弄丢了" **现象**:你开启并勾了迁移,但官方老会话既没进统一列表、关开关想还原也提示没东西可还原(或者关闭弹窗里压根没出现还原复选框,参见场景 E)。你怀疑迁移过程中把会话搞丢了。 **真相**:迁移根本**没执行**,所以也不可能弄丢——你的会话一个字都没被改。CC Switch 在迁移前有一道安全闸门:它会检查 Codex 的 live 配置(`~/.codex/config.toml`)此刻是否**真的**路由到了共享 `custom` 抽屉,只有真路由过去了才迁移。以下两种情况会判定"还没统一"(内部原因码 `live_not_unified`),于是**主动跳过迁移、保留你的开关和迁移意愿、等条件满足后再迁**: - **代理接管期间**:CC Switch 的代理接管了 live 配置,接管期的 live 不带统一路由标记。 - **注入被拒**:你的 `config.toml` 已有手工指定的 `model_provider`,或已存在一张形态不同的 `[model_providers.custom]` 表(可能带第三方地址)。为避免把官方流量错误路由到第三方后端,CC Switch 宁可不注入、不迁移。 跳过迁移 = 不碰任何会话文件。**没迁,等于没动,谈不上丢。** 这是"安全延后",不是"失败丢数据"。 **怎么办**: - 退出代理接管 → **重启 CC Switch**:启动时会自动重试迁移(你的迁移意愿一直保留着)。 - 检查 `~/.codex/config.toml`:若有你手工写的冲突路由,整理掉冲突后再开开关。 - 实在不想折腾:直接关开关,官方会话仍以 `openai` 抽屉正常显示,毫发无损。 ### 场景 D:关了开关并还原,但"开启期间新聊的会话"没回官方 → 以为"新会话丢了" **现象**:你开启统一期间,用官方又聊了几条新会话。后来关开关、勾了还原,还原完发现那几条新会话没回到官方抽屉。 **真相**:这是**有意为之**的设计,新会话好端端在 `custom` 抽屉里,能看见、能续。还原的依据是"迁移时的备份账本"——**只有当初从 `openai` 抽屉迁进来的会话**,备份里有据可查,才会被精确翻回 `openai`。你**开启期间新建**的会话不在任何备份账本里;而且统一之后官方和第三方都用 `custom` 标签,**CC Switch 无法分辨这条新会话到底是官方聊的还是第三方聊的**。为了不把第三方会话误塞进官方历史,产品决策是:这些新会话一律留在 `custom`(第三方)历史里,绝不自动搬动。关闭弹窗的文案也明示了这一点——"开启期间产生的会话因无法区分来源,将留在第三方历史中"。 **怎么办**: - 切到任意一个第三方供应商(`custom` 抽屉),就能在历史列表里看到这些会话。 - 想看内容直接读 `.jsonl`;想续聊遵循场景 B 的规则(回到当初生成它的后端)。 - 如果你确实想把**某一条**手动归回官方:目前没有自动按钮(刻意不做,避免误判方向)。进阶用户可在**先备份**该文件后,手动把它 `.jsonl` 第一行 `session_meta` 里的 `model_provider` 从 `custom` 改回 `openai`(属高阶操作,改前务必复制一份)。 ### 场景 E:还原提示"当前 Codex 目录没有可恢复的迁移备份" → 以为"还原失败 = 数据没了" **现象**:关开关时勾了还原,结果弹出提示"当前 Codex 目录没有可恢复的迁移备份"。你慌了:还原都失败了,是不是数据彻底没了? **真相**:"没有可还原的东西"≠"数据丢了"。恰恰相反,通常是因为**根本没有需要还原的迁移**。常见原因: - **你当初没勾过"迁入现有官方会话"**:既然没迁移,自然没有迁移备份、也没有需要翻回去的会话。你的官方老会话一直在 `openai` 抽屉,关开关后直接复现(同场景 A)。(这种情况下,关闭弹窗甚至可能**根本不显示还原复选框**——因为系统探测不到任何备份。) - **已经还原过一遍了**:会话标签已全部翻回 `openai`,再点一次自然"没有仍是 custom 的目标可还原"——这是**幂等保护,不是失败**。 - **切换过 Codex 目录**:还原只认属于**当前**目录的备份账本,换了目录就找不到旧目录的账本,把目录切回去即可。 这三种情况下,没有任何会话被删除。 **怎么办**:用文末命令统计 `~/.codex/sessions/` 里的会话文件总数,确认文件都在;再看 `~/.cc-switch/backups/` 里有没有 `codex-official-history-unify-v1` 目录——如果连这个目录都没有,说明你从没触发过迁移,会话一直在原抽屉。 ### 场景 F:还原被拒,提示"统一会话历史开关已重新开启,已跳过还原" **现象**:关开关 → 勾还原 → 你手很快,紧接着又把开关重新打开了,然后看到提示"统一会话历史开关已重新开启,已跳过还原"。 **真相**:这是一道防护,防止把数据弄成"撕裂"状态,会话同样没丢。还原的动作是"把会话标签从 `custom` 翻回 `openai`",但如果此刻开关又开着,live 配置正路由到 `custom`——一边把历史翻回 `openai`、一边新会话往 `custom` 落,会话会被人为撕成两半。所以 CC Switch 检测到"开关又开了",**主动放弃这次还原、什么都不改**。会话维持现状,没有任何删除或破坏。 **怎么办**:想真正还原,就**先把开关稳定地关掉**(别再立刻打开),再执行关闭 + 勾还原;想保持统一,就别还原,让会话留在 `custom` 共享抽屉正常使用。 **总原则:CC Switch 的统一 / 迁移 / 还原全程只改会话的一个标签字段,并且每次改写前都自动备份。它不会删你的对话。看不见 ≠ 丢了——换个抽屉看,或用下面的命令亲眼确认。** --- ## 亲手验证:你的会话文件还在硬盘上(最重要的一节) 文字再多,不如亲眼看见。下面给出**真实路径**(取自 CC Switch 源码)和在不同系统下查看会话文件、备份目录的方法。**全程只读不改,强烈建议你亲手试一遍。** ### 最简单的方式:用文件管理器直接打开(完全不用命令行) - **macOS(Finder)**:按 `Cmd + Shift + G`,粘贴 `~/.codex/sessions` 回车,就能看到一堆 `.jsonl` 会话文件和它们的修改时间;备份目录粘贴 `~/.cc-switch/backups`。 - **Windows(文件资源管理器)**:在地址栏粘贴 `%USERPROFILE%\.codex\sessions` 回车,就能看到会话文件夹和里面的 `.jsonl`;备份目录粘贴 `%USERPROFILE%\.cc-switch\backups`。 **只要你能在这里看到一批 `.jsonl` 文件,就证明会话数据完好无损地在硬盘上。** 文件数量、修改时间,比任何文字都直观。 ### 你的会话 / 历史文件到底在哪 | 内容 | 真实路径 | 说明 | |---|---|---| | **会话正文(核心)** | `~/.codex/sessions/`(含按日期分的子目录,递归) | 每个会话一个 `.jsonl` 文本文件,**这就是你的对话内容** | | **归档会话** | `~/.codex/archived_sessions/` | 同为 `.jsonl` | | **会话索引数据库** | `~/.codex/state_5.sqlite` | `threads` 表的 `model_provider` 列就是"抽屉标签",**它才是续聊列表真正读取的归类来源** | | **迁移备份**(开启迁移时自动产生) | `~/.cc-switch/backups/codex-official-history-unify-v1/<时间戳>/` | 内含 `jsonl/`、`state/`、`meta.json` | | **还原备份**(点还原时自动产生) | `~/.cc-switch/backups/codex-official-history-unify-restore-v1/<时间戳>/` | 还原前的安全副本 | > **注意**:如果你在 CC Switch 里改过 Codex 目录,或在 `config.toml` 里设了 `sqlite_home`,请把上面的 `~/.codex` 换成你的实际目录。下文 `~` = 你的用户主目录。 ### macOS / Linux 命令 **1. 数会话文件总数(这才是"没丢"的硬证据)** ```bash # 统计会话文件总数 —— 只要这个数字符合你的预期,数据就都在 find ~/.codex/sessions ~/.codex/archived_sessions -name '*.jsonl' 2>/dev/null | wc -l # 看最近修改的 10 个会话文件 find ~/.codex/sessions -name '*.jsonl' 2>/dev/null -print0 \ | xargs -0 ls -lt 2>/dev/null | head -10 ``` **2. (辅助)看每个"抽屉"各有多少会话** ```bash # 官方抽屉(openai)会话文件数 grep -rlE '"model_provider"[[:space:]]*:[[:space:]]*"openai"' ~/.codex/sessions 2>/dev/null | wc -l # 统一抽屉(custom)会话文件数 grep -rlE '"model_provider"[[:space:]]*:[[:space:]]*"custom"' ~/.codex/sessions 2>/dev/null | wc -l # 看各标签分布一目了然 grep -rhoE '"model_provider"[[:space:]]*:[[:space:]]*"[^"]*"' ~/.codex/sessions 2>/dev/null | sort | uniq -c ``` > **重要提示,别被这一步吓到**:**早期版本的 Codex 不在 `.jsonl` 里写 `model_provider` 字段**,这些旧官方会话用上面的 grep 是**数不到**的,但它们在索引库 `state_5.sqlite` 里仍然归类为 `openai`、续聊列表照样能看到。所以**判断"会话没丢"请以第 1 步的文件总数为准**——分桶 grep 只是帮你理解归类,数出来比文件总数少**完全正常**,绝不代表"丢了一批"。 **3. (进阶)查索引库 `state_5.sqlite`——续聊列表真正读的归类** ```bash # 需要已安装 sqlite3;没装可跳过 sqlite3 ~/.codex/state_5.sqlite \ "SELECT COALESCE(model_provider,'<空>'), COUNT(*) FROM threads GROUP BY 1;" ``` > 这张 `threads` 表才是 Codex 续聊列表真正读取的归类来源,`openai` 行数 ≈ 你官方抽屉里能看到的会话数。它和第 2 步的 jsonl grep 可能对不上数——原因就是上面说的"旧会话不写 jsonl 字段,但索引库里仍是 openai"。两边对不上不是异常。 **4. 直接读某条会话的内容(确认对话文字还在)** ```bash # 把 <文件名> 换成上面 ls 列出的某个 .jsonl 路径 python3 -m json.tool < "<文件名>.jsonl" 2>/dev/null | head -50 # 或者直接用编辑器打开看(纯文本) open -e "<文件名>.jsonl" # macOS ``` **5. 看 CC Switch 的备份目录(证明迁移 / 还原前都留了副本)** ```bash ls -la ~/.cc-switch/backups/codex-official-history-unify-v1/ 2>/dev/null ls -la ~/.cc-switch/backups/codex-official-history-unify-restore-v1/ 2>/dev/null ``` ### Windows 命令(PowerShell) 会话目录通常在 `C:\Users\<你的用户名>\.codex\`,备份在 `C:\Users\<你的用户名>\.cc-switch\backups\`。 ```powershell # 1. 会话文件总数("没丢"的硬证据) (Get-ChildItem "$env:USERPROFILE\.codex\sessions","$env:USERPROFILE\.codex\archived_sessions" -Recurse -Filter *.jsonl -ErrorAction SilentlyContinue).Count # 2. 最近修改的 10 个会话 Get-ChildItem "$env:USERPROFILE\.codex\sessions" -Recurse -Filter *.jsonl | Sort-Object LastWriteTime -Descending | Select-Object -First 10 FullName,LastWriteTime # 3. (辅助)官方(openai) / 统一(custom) 抽屉各多少会话文件 (Get-ChildItem "$env:USERPROFILE\.codex\sessions" -Recurse -Filter *.jsonl | Select-String -Pattern 'model_provider"\s*:\s*"openai"' -List).Count (Get-ChildItem "$env:USERPROFILE\.codex\sessions" -Recurse -Filter *.jsonl | Select-String -Pattern 'model_provider"\s*:\s*"custom"' -List).Count # 4. 看备份目录 Get-ChildItem "$env:USERPROFILE\.cc-switch\backups\codex-official-history-unify-v1" -ErrorAction SilentlyContinue Get-ChildItem "$env:USERPROFILE\.cc-switch\backups\codex-official-history-unify-restore-v1" -ErrorAction SilentlyContinue ``` > 同样提醒:第 3 步的 grep 数会**少于**文件总数属正常(旧会话不写该字段),请以第 1 步的**文件总数**作为"会话没丢"的判断依据。 --- ## 进阶原理附录(给想真正搞懂机制的用户) ### 1. 分桶机制(抽屉的本质) Codex 的续聊 / 历史列表按当前激活的 `model_provider` id **精确字符串过滤**。会话文件 `.jsonl` 的**第一行**是一条 `type:"session_meta"` 记录,其 `payload.model_provider` 即该会话所属抽屉(`grep -rl` 只要文件里出现一次该标签就计入该文件,因此无需逐行解析;旧版本未写该字段的会话则数不到)。真正驱动续聊列表的是索引库 `state_5.sqlite` 的 `threads.model_provider` 列。官方订阅在 `config.toml` 没有显式 `model_provider` 时落进内建默认 id `openai`;CC Switch 的所有第三方供应商统一用 `custom`。 ### 2. 开关做的事(注入,只活在 live) 开启后,CC Switch 对官方 live `config.toml` 注入如下内容: ```toml model_provider = "custom" [model_providers.custom] name = "OpenAI" requires_openai_auth = true supports_websockets = true wire_api = "responses" ``` 每个字段都有作用:`requires_openai_auth = true` 让认证继续走 `auth.json` 里的 ChatGPT 登录、base_url 缺省回落官方 Codex 后端;`name = "OpenAI"` 让 Codex 的官方特性门控(web search、远程压缩等)继续命中;`supports_websockets = true` 补回 custom 条目默认丢失的能力;`wire_api = "responses"` 用官方 responses 协议。**净效果是:认证没变,只是桶名变了。** **关键不变量:这段注入只能存在于 live `config.toml`,绝不写进数据库的存储配置。** 切换离开官方供应商、把 live 回写数据库时,CC Switch 会把这段注入精确剥离(只在形态与注入产物完全一致时才剥,第三方自定义的 `custom` 表原样保留)。正因如此,"关掉开关 + 切换一次"就能彻底还原 live,数据库里始终是你原本干净的官方配置——这是整个开关可逆性的基石。 ### 3. 注入的两道拒绝闸(对应场景 C) - `config.toml` 已有显式 `model_provider` → 不覆盖用户路由; - 已存在形态不同的 `[model_providers.custom]` 表(可能带第三方 `base_url`)→ 拒绝注入,否则会把 ChatGPT OAuth 流量路由到错误后端。 拒绝注入时 live 不统一,迁移闸门(检查 live 的 `model_provider` 是否 trim 后等于 `custom`)判定 `live_not_unified` → 跳过迁移、保留意愿、等下次启动重试时再做。这是"安全延后",不是"失败丢数据"。 ### 4. 会话三分类(决定迁移 / 还原边界) - **A 类**:开启时迁入的存量官方会话——备份即账本,可精确还原回 `openai`; - **B 类**:开启期间新建——不在任何备份、官方 / 第三方不可分,**永不自动搬动**(留 `custom`); - **C 类**:开启前的纯第三方历史——绝不触碰。 ### 5. 迁移 / 还原的安全性(数据不会被真正删除,保障来自哪里) 四层设计共同保证:在**正常与异常的所有路径**下,原始会话数据都不会被真正删除。 - **只改字段,不动正文**:迁移 / 还原只把会话元数据里的 `model_provider` 值在 `openai` 与 `custom` 之间切换,对话内容、`response_item`、`encrypted_content` 一律原样保留。 - **改写前必先复制备份**:jsonl 用文件复制、state DB 用 SQLite 完整副本,存进时间戳代际目录。迁移备份在 `codex-official-history-unify-v1/`,还原备份在独立的 `codex-official-history-unify-restore-v1/`,两者分开以保持账本纯净。 - **只移不删 + 原子写**:所有 jsonl 改写走"临时文件 + 整体替换",state DB 走事务化 `UPDATE`,全程没有任何删除会话或索引的动作。文件在任一时刻都是完整的。 - **悲观跳过 + 幂等可重试**:桶不一致时(`live_not_unified`)宁可不迁;一把进程锁串行化迁移与还原,避免"启动重试 / 保存后台任务 / 关闭还原"并发对同批文件双向改写;完成标记按 Codex 目录绑定、条件写入,防漏迁;还原用"在账本 + 当前仍 custom"双重条件,防误改。还原扫描全部备份代际取并集,多次开关循环后仍能还原早期迁入的会话;重复还原返回 `nothing_to_restore`,是幂等保护而非失败。 ### 6. 跨后端 encrypted_content(对应场景 B) 会话内的推理密文只能被生成它的后端解密,上游 Codex by design 不支持跨后端解密。这是"续聊失败"的根因,与文件完整性无关——会话 `.jsonl` 完整躺在磁盘上、`encrypted_content` 也完好无损。换回原供应商续聊,或开新会话,都正常。 --- ## 参考链接 - [使用第三方 API 时保留 Codex 远程操作和官方插件:CC Switch 配置攻略](./codex-official-auth-preservation-guide-zh.md) - [在 Codex 中使用 DeepSeek 这类 Chat 格式 API:CC Switch 路由攻略](./codex-deepseek-routing-guide-zh.md) - CC Switch 用户手册中「Codex 应用增强」相关章节 --- **给你的最后一句话**:你看到的"会话不见了 / 续聊失败",本质是**会话被换到了另一个历史列表(抽屉)里、或对方后端无法解密旧推理内容**,文件始终原封不动地躺在 `~/.codex/sessions/`(及 `state_5.sqlite`)里。关闭开关时勾选"按备份还原"即可把当初迁入的官方会话精确翻回官方列表;即便不还原,原始 `.jsonl` 文件和 `~/.cc-switch/backups/codex-official-history-unify-*/` 下的备份副本也都在——**数据绝不会真正丢失。**