Back to Cc Switch

统一 Codex 会话历史:功能介绍与使用攻略(CC Switch)

docs/guides/codex-unified-session-history-guide-zh.md

3.16.435.6 KB
Original Source

统一 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_providercustom 改回 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.sqlitethreads 表的 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.sqlitethreads.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 值在 openaicustom 之间切换,对话内容、response_itemencrypted_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 也完好无损。换回原供应商续聊,或开新会话,都正常。


参考链接


给你的最后一句话:你看到的"会话不见了 / 续聊失败",本质是会话被换到了另一个历史列表(抽屉)里、或对方后端无法解密旧推理内容,文件始终原封不动地躺在 ~/.codex/sessions/(及 state_5.sqlite)里。关闭开关时勾选"按备份还原"即可把当初迁入的官方会话精确翻回官方列表;即便不还原,原始 .jsonl 文件和 ~/.cc-switch/backups/codex-official-history-unify-*/ 下的备份副本也都在——数据绝不会真正丢失。