会话管理器能读哪些来源,读到的又是什么

2026-08-10

「会话管理器」这四个字很容易被理解成:把你在各个 CLI 里聊过的记录集中到一处,然后想接着哪条就接着哪条。CC Switch 的 Session Manager 在代码层面确实做了前半句,但后半句在这一版里是有条件的——而且条件卡得比大多数人预期的紧。

下面全部基于我们本地 clone 的 cc-switch 仓库快照 c39c903(提交日期 2026-08-10),仓库内版本号 3.19.2。我们只读源码与文档文本,没有安装也没有运行过这个桌面应用,所以本文不涉及任何界面与操作层面的描述。

先把「能读哪些」数清楚

入口是 src-tauri/src/session_manager/mod.rs 里的 scan_sessions()。它用 std::thread::scope 并发起 7 个扫描线程,顺序是 codex、claude、opencode、openclaw、gemini、hermes、grokbuild(src-tauri/src/session_manager/mod.rs:58-75)。结果汇总后按 last_active_at 倒序排,这个字段缺失时回落到 created_atmod.rs:84-89)。

顺带说一个容易数错的地方:src-tauri/src/session_manager/providers/mod.rs:1-8 声明了 8 个子模块,但其中 utils 是公共工具模块,真正的 provider 是 7 个。你要是直接数 mod 行数,就会多出一个。

每个 provider 从哪里找会话,写在 provider_roots() 里(mod.rs:196-208):

provider会话根目录
codexcodex::session_roots()
claude<claude_config_dir>/projects
opencodeopencode::get_opencode_data_dir()
openclaw<openclaw_dir>/agents
gemini<gemini_dir>/tmp
grokbuildgrokbuild::session_roots()
hermes<hermes_dir>/sessions

这张表要配着两条补充读:Codex 与 GrokBuild 各有两个根,分别是 <config_dir>/sessions<config_dir>/archived_sessionsproviders/codex.rs:46-47providers/grokbuild.rs:37-38);OpenCode 的数据目录优先取 $XDG_DATA_HOME/opencode,否则落到 ~/.local/share/opencode,其下 SQLite 是 <base>/opencode.db、文件存储是 <base>/storageproviders/opencode.rs:19-33)。

反直觉的那一处:读得到 ≠ 接得上

真正会影响你判断这个功能对自己有没有用的,不是「读了几个来源」,而是恢复这条路。

代码里每条会话元数据带一个恢复命令模板。五个 provider 有:claude 是 claude --resume {id}、codex 是 codex resume {id}、gemini 是 gemini --resume {id}、grokbuild 是 grok --resume {id}、opencode 是 opencode -s {id}(分别在 providers/claude.rs:251codex.rs:414gemini.rs:172grokbuild.rs:192opencode.rs:152)。

另外两个是 None Hermes 的 resume_commandNoneproviders/hermes.rs:146427),OpenClaw 同样为 None,源码注释写的是 “OpenClaw sessions are gateway-managed, no CLI resume”(providers/openclaw.rs:298)。也就是说,这两个来源的会话在这一版里是只读的记录,扫得到、能查看、能删,但代码里没有给出一条命令行恢复入口。

比这更硬的一道门在终端那一侧。session_manager/terminal/ 只有一个文件 terminal/mod.rs(440 行),入口函数是 launch_terminal(target, command, cwd, custom_config)terminal/mod.rs:3-8)。它开头做两件事:命令为空报 “Resume command is empty”(terminal/mod.rs:9-11);紧接着判断,非 macOS 直接返回 “Terminal resume is only supported on macOS”terminal/mod.rs:13-15)。这个判断在 match target 之前,所以不管你选哪个终端目标,Windows 与 Linux 上这条自动拉起终端的路径都在函数入口就被挡住了。

再往下的 match 共 9 类分支:terminaliTerm/itermghosttykittyweztermkakualacrittywarp(仅 #[cfg(unix)])、custom,其余报 “Unsupported terminal target”(terminal/mod.rs:17-29);对应 9 个 launch 函数(terminal/mod.rs:325582116141158207248277)。Terminal.app 与 iTerm 这两支走的是 osascript -e 执行 AppleScript,命令文本先经 escape_osascript 转义(terminal/mod.rs:32-5355-79)——这两支依赖的是 macOS 自带的 AppleScript 能力。至于源码为什么整体只放开 macOS,卡里没有依据,我们不推断。

本站读者以 Windows 居多,这一条得说明白:恢复命令模板本身只是一段字符串,代码里给出了它;把它交给某个终端去执行的这条自动化路径,非 macOS 一律返回错误。 这段命令文本你自己怎么用,本文不做建议,也不推断作者后续会不会补别的平台——源码写到哪,我们就说到哪。

读到的「内容」,其实走两条不同的分支

第二个容易误解的点:七个来源的会话记录并不是同一种东西。

load_messages 的分流逻辑写在 mod.rs:96-101SQLite 型会话用 sqlite: 前缀的 source_path 区分,opencode 与 hermes 命中前缀时分别走各自的 load_messages_sqlite;删除侧同样有独立的 delete_session_sqlite 分支(mod.rs:118-127)。Hermes 的状态库路径是 <hermes_dir>/state.db,同时它还留了一条 jsonl 扫描分支 scan_sessions_jsonlproviders/hermes.rs:1822285)。换句话说,同一个 provider 内部就可能同时存在库读与文件读两条来源。

标题这类元数据的来源也是一家一个样,这里只举三个和「你能不能认出这条会话」直接相关的:

  • Codex:会话标题来源包括 session_index.jsonl 与状态库;状态库文件名常量是 state_5.sqlite,位置可被 config.toml 里的 sqlite_home 或环境变量 CODEX_SQLITE_HOME 改掉(providers/codex.rs:23src-tauri/src/codex_state_db.rs:1-28)。你要是改过这个环境变量,扫描落点也就跟着变了。
  • GrokBuild:每个会话目录里含 summary.jsonchat_history.jsonlproviders/grokbuild.rs:5799)。
  • OpenClaw:会话目录里有 sessions.json 作索引,用于 sessionId → displayName 的查找;标题优先级是 displayName > 首条用户消息 > 目录名(providers/openclaw.rs:156-159275)。

体量上给个参照(以下行数与测试数均为我们 2026-08-10 采集的快照静态计数):src-tauri/src/session_manager/ 全目录用 wc -l 数出来合计 5112 行,其中 providers/opencode.rs 1001 行、providers/codex.rs 997 行、providers/hermes.rs 603 行,mod.rs 359 行、terminal/mod.rs 440 行。单测按 grep -c "#\[test\]" 静态计数:codex.rs 20、claude.rs 10、opencode.rs 8、hermes.rs 7、openclaw.rs 5、grokbuild.rs 4、gemini.rs 3、utils.rs 1、mod.rs 4。这些是文件里写了多少个测试函数,我们没有跑过构建与测试,不知道它们是否通过。

删除会话时那道路径校验

会话记录是本机文件与数据库,删掉就是删掉。代码在这一步做了边界检查:删除前会 canonicalize 会话根与源路径,源路径若不在任何一个 provider 根之下,报 “Session source path is outside provider roots”(mod.rs:158-194)。批量删除的结果结构是 DeleteSessionOutcome,含 success 与 error 两个字段,未删成功时 error 写 “Session was not deleted”(mod.rs:47-56230-238)。

这道校验的存在值得读者知道,但请按它的字面意思理解:它约束的是「删除动作只能落在受管的会话根之内」,不等于任何形式的数据安全保证。会话文件本身属于你本机的敏感数据,是否删、删之前要不要自己留一份,请结合自身情况判断。

手册与代码对不上的五处

这个功能的用户手册是 docs/user-manual/zh/3-extensions/3.4-sessions.md(149 行)。我们把它和代码逐条对了一遍,有五处可核实的差异,只陈述位置与内容,不推断原因:

  1. 会话来源数量3.4-sessions.md:7-20 的表格是 6 行,正文写”覆盖上表六类会话来源”;代码 scan_sessions() 并发的是 7 个 provider,多出 grokbuild(mod.rs:58-75)。
  2. Claude 会话路径:手册写 ~/.cache/claude/projects/*.jsonl3.4-sessions.md:9);代码是 get_claude_config_dir().join("projects"),默认落在 ~/.claude/projectsmod.rs:199src-tauri/src/config.rs:37-43)。
  3. Gemini 会话路径:手册写 ~/.cache/gemini/tmp/<project_hash>/chats/3.4-sessions.md:13);代码是 get_gemini_dir().join("tmp"),默认 ~/.gemini/tmpmod.rs:203src-tauri/src/gemini_config.rs:9-15)。
  4. 终端列表:手册列 7 个(Terminal.app、iTerm2、Ghostty、Kitty、WezTerm、Alacritty、Warp)(3.4-sessions.md:86);代码的 match 分支还有 kakucustom 两项(terminal/mod.rs:17-29)。
  5. 仓库根目录那份 session-manager.md(268 行)是一份 PRD 性质的文档,写的是”对 Codex / Claude Code 的本地会话记录进行可视化管理”、“支持 Provider:Codex、Claude Code(可扩展)“(session-manager.md:365);代码已实现 7 个 provider。同一份文档自述”v1 仅 macOS”(session-manager.md:4),这一句与 terminal/mod.rs:13-15 的 macOS 限制是一致的。

差异说到这里就停。哪一处”才算数”、为什么没同步,本文不推断,也不拿它去评价这个项目。

你自己怎么核一遍

不用装应用,clone 仓库读文本即可,四个动作按顺序做:

# 1. 数并发扫描了几个 provider
sed -n '58,75p' src-tauri/src/session_manager/mod.rs

# 2. 看七个 provider 的会话根目录
sed -n '196,208p' src-tauri/src/session_manager/mod.rs

# 3. 找出哪几个 provider 没有 resume 命令
grep -n "resume_command" src-tauri/src/session_manager/providers/*.rs

# 4. 确认非 macOS 的那道拦截在 match 之前
sed -n '1,30p' src-tauri/src/session_manager/terminal/mod.rs

第 3 步和第 4 步是本篇的重点:前者告诉你哪两个来源只能看不能接,后者告诉你在你自己的操作系统上,自动拉起终端这条路通不通。先跑这两条,再决定这个功能对你有没有价值,比对着手册的表格数来源数量要有用。

最后交代我们没核到的部分,免得你把本文当成完整结论:providers/ 各文件里的消息解析细节(role 映射、内容截断、去重逻辑)我们没有逐个核实;文档提到的”OpenCode JSON 与 SQLite 去重”,我们只看到 mod.rs 里按 sqlite: 前缀分流这一处,没有定位到具体的去重代码;前端组件除预设文件外没读,所以手册描述的批量操作、状态过滤这类行为无法与代码对照。这些地方,本文一律留白。


本文依据 CC Switch 官方仓库(github.com/farion1231/cc-switch)的 README、docs/ 下的用户手册与发布说明、 src/config/ 的预设定义与 src-tauri/src/ 的后端源码整理,核对日 2026-08-10,对应仓库快照 c39c903。 本文内容为仓库源码与文档口径,我们没有安装或运行过这个桌面应用, 因此不涉及界面外观、操作手感与切换速度的任何描述。 文中出现的阈值与默认值均为源码中的默认配置,不构成对实际运行结果的保证。 该项目仍在快速迭代,版本与默认值随时可能变动,请以仓库最新内容为准。 安全相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。