你在 Claude Code 里跟一个 bug 缠了一个小时,上下文里塞满了读过的文件、跑过的命令、试错过的三条死路。现在你怀疑换个模型能更快破局,想让 Codex 接着干——然后你发现,除了手动复述一遍,没有任何办法把这一个小时的现场搬过去。
这不是配置问题。AGENTS.md 可以软链,settings 可以转换,官方甚至给了 /import。真正搬不动的,是会话本身。
OpenAI 官方的 import 文档把这件事写得非常清楚:AGENTS.md、settings.json、skills、plugins、memories、MCP server、hooks、subagents 全都列进了导入清单——而关于对话,只有一行:“Codex CLI imports up to 50 chats from the last 30 days.”(learn.chatgpt.com/docs/import,访问 2026-09-16)
换句话说,厂商的导入搬的是你的"设置",不是你的"工作"。而工作才是你花掉的那一个小时。
这篇文章拆一个叫 txcript 的开源项目——它自称 “Pandoc for AI chats”,做的事情就是把会话在不同 harness 之间转换。我不打算给你抄一遍它的命令(README 里都有),而是借它当手术台,把三个真正值得搞清楚的问题摆到台面上:会话在盘上长什么样、为什么必须要有中间表示、以及转换时到底会掉什么。如果你正在为一个团队做 agent 集成,这三个问题的答案决定了你要不要押注这条路。
会话不是文档,是 agent 的私有副作用
要理解"搬会话"为什么比"搬配置文件"难一个数量级,看一眼盘上的真实结构就够了。
Claude Code 把一个 session 存成 ~/.claude/projects/<编码后的cwd>/<uuid>.jsonl——一个会话一个 JSONL 文件,每行一个 JSON 对象,用 type 字段区分。看起来挺朴素,但关键在于:会话里的消息不是按文件顺序组织的,而是按 parentUuid → uuid 串成的一条链。文件里还有 summary、system、attachment、file-history-snapshot 这些书签性质的记录混在中间。其中 system 行尤其麻烦:它大部分时间是"记账"用的,但在较新版本的 CLI 里,subtype: "local_command" 的 system 行又会承载斜杠命令的输出——同一个类型,不同版本,语义不一样。(txcript claude-code.md,访问 2026-09-16)
Codex 那边更绕。rollout 文件按日期分层放在 ~/.codex/sessions/YYYY/MM/DD/,而这个文件里同时交织着两份日志:response_item 是协议日志,记录模型实际交换的内容;event_msg 是显示日志,记录 TUI 渲染的东西。大部分内容两边都有,但 shell 的真实结果(aggregated_output、exit_code)只在 event_msg 里,而 usage 需要在 token_count 加 task_complete 两个状态事件的配合下,才能回填到那一轮最后一条 assistant 文本上。
这不是"每行独立解析"能搞定的事,而是一趟有状态的解析:turn_context 和 task_started 划定当前轮次,assistant 消息继承该轮的模型,工具调用靠 call_id 配对——唯独 web search 例外,因为它的调用常常根本没有 call_id,只能靠序列化后的 action 对象反查。(txcript codex.md,访问 2026-09-16)
看清这里的困境了:同一个仓库、同一份 JSONL,每一家都在里面塞了自己的状态机,而且谁都没打算给外人读。 Claude Code 是闭源的,Anthropic 官方文档只告诉你会话存在哪儿,并且明确声明记录格式属于"内部实现、随版本变化"——txcript 的格式文档因此只能标注自己是 reverse-engineered,观测到的 CLI 版本跨度从 0.144 一直到 2.1.227。
格式是各家内部实现,不是接口。想让它变成接口,就必须引入中间表示。
Common 模型是那条 IR:五类 block,加一个显式留白的 system prompt 槽位
txcript 的抽象很克制:Transcript = Meta + Vec<Message>,而 Message 里只装五类 typed block——Text、Thinking、ToolUse、ToolResult、Image。斜杠命令被建模成 Tool::Command 加一个配对的 ToolResult,于是"用户敲了 /release"和"模型调了 Bash"在数据结构上变成了同一类事件。
这个设计里最值得停一下的,是它没有做什么。
txcript export 写出的交换格式叫 Simple——一份纯 JSON 文档,没有目录、没有 store、txcript list 里也不会出现,你把它拷到另一台机器上就能 continue。而它的文档里明明白白写着:
There is no system-prompt slot, deliberately. No harness transports a system prompt through conversion — each rebuilds its own environment on resume — so a field here would silently die at the hub.
(txcript simple.md,访问 2026-09-16)
“故意没有 system prompt 槽位”——这一句是整个方案里最锋利的工程结论,比任何功能介绍都更重要。
它承认的不是"我们还没做完",而是"这件事在语义上就不该做"。因为没有任何 harness 会在 resume 的时候接受一个外来 system prompt,每一个都坚持用自己的 system instructions 和工具集重建环境。就算你在交换格式里硬塞一个槽位,它也会在写入目标 harness 的那一刻静静死掉。
顺着这个逻辑,文档给了一条很实用的操作建议:会话所依赖、但不由任何工具调用产生的上下文,要内联写在 user 消息里——注入的记忆、预加载的指令,都该放在模型当时看到它们的那个位置上。而目标 harness 自己会在 resume 时重新生成的环境脚手架(目录列表、git status),最好的处理方式是干脆省略。
中间表示的天花板不是技术没做到,是语义上不该做。想清楚这一点,后面所有的"有损"就都好理解了。
保真是分层的:native 往返无损,过一次 Common 就丢东西
很多介绍这类工具的文章会含糊地说"可能会丢失部分内容"。这句话没有信息量。有损是可以被具体化成机制的,而且分成了清晰的三层。
第一层:同一 harness 的 native 往返,是无损的。 这是常有误解的地方。txcript 在 native 层保留每一个 payload 的原始 JSON,包括它不认识的字段——未知行会存成 Record::Other 原样带走。所以"读进来再写回去"这件事,字节级是保真的。
第二层:一旦经过 Common,损失开始发生,而且是按来源分门别类的。
Claude Code 侧的机制是确定性重生成:Common → native 时会用 UUIDv5 重新生成 entry id,于是记账行(attachment、file-history-snapshot、mode 这些)、envelope 上未被建模的 extra key(isSidechain、requestId、userType……)、以及未建模的 block 类型,全部消失。同一个会话如果走的是同 harness 的 native 路径,这些东西会全部保留。
Codex 侧的机制是不透明内容被主动排除:reasoning item 上的 encrypted_content(推理的不透明加密内容)不进 Common,sandbox/approval 上下文、rate limit 信息、所有 display-only 事件同样不进。最值得注意的是 apply_patch 的降级规则——只有"单独一个单 hunk 的更新"才能映射成 Edit,“单独新增一个文件"才能映射成 Write;多文件、多 hunk、删除、移动,一律降级成 raw ApplyPatch,只剩一串受影响的路径。
第三层:写进目标 harness 时,还会被它自己的 schema 强制归一化,而且不可逆。
两个例子足够说明性质。其一,Codex resume 硬性要求 session_meta 里必须写出 model_provider: "openai",因为当前版本会把 null provider 解析成空字符串,然后 resume 直接失败,报 Model provider "" not found。其二,外来的工具名会被规范到 OpenAI 要求的 [A-Za-z0-9_-]+ 字符集:不支持的字符变成 _,空名字变成 tool。这个规范化只在写入端发生,不可逆,而且不同的名字会收敛到一起——a.b 和 a/b 会变成同一个名字。
把三层叠起来看,结论就清楚了:txcript 这一类项目在修的问题,主要是"搬过去能不能跑起来”,而不是"搬过去像不像原来那样"。 翻它近几个版本的处理就知道——sanitize 重放的工具名、把自由格式的工具输入导出成对象、给连续的 assistant 消息生成唯一 entry id,几乎全在"能不能跑"这个区间里。
有损不是 bug,是设计前提。要求"搬过去一模一样",等于要求两个 agent 共享同一套内部实现。
哪些东西不会跟你走:项目文件、目标模型的工具,以及会话的保质期
把边界写成可以逐条对照的清单,比记住"它有损"有用得多。
项目文件必须自己另外带。 这是 README 里明说的:conversion carries conversation history, project files must be available separately。txcript export 出来的 run.json 拷到另一台机器,记录的 cwd 如果存在就沿用,不存在就换成 continue 运行所在的目录——但那个目录里的代码,得你自己同步过去。
目标 harness 自己决定工具集。 源会话里的 tool call 到了那边只是历史记录,不会给目标新增任何能力。Claude Code 里的 Read、Grep,在 Codex 里不会变成它的工具;反过来也一样。这就是那张能力矩阵存在的原因:Claude Code、Codex、OpenCode、Cursor CLI、Cursor desktop、pi、Campfire、Cowork、Grok CLI、fx、Antigravity 标的是可读可写,而 Hermes Agent 和 Amp 只读、不能续写,Claude Chat 和 ChatGPT 更是走未公开的私有 web API、复用你本地 app 的登录,默认根本不出现在 txcript list 里。
所以"支持 17 个 harness"这个数字有大量脚注,不能当卖点直接报。
会话是有保质期的。 这一条最容易被忽略,也最应该促使你今天就动手。Claude Code 官方文档在"本地目录"页写得很直接:Claude Code deletes the files in the paths below once they're older than cleanupPeriodDays,紧接着一句是 “The default is 30 days and the minimum is 1; setting 0 fails with a validation error.”——也就是说 transcript 默认 30 天就被清扫,而且这个清理是"静默"的,不会问你要不要留。(code.claude.com/docs/en/claude-directory,访问 2026-09-16)
也就是说,你三个月前那个把生产事故查清楚了的黄金会话,很可能已经不在盘上了。
这就把"会话资产化"的顺序彻底调过来了:第一步不是转换,是先归档。 要长期留档,就用 txcript export 落成 Simple;要接着干活,就在目标 harness 里重新建立环境。跨工具续写的正确心智模型是交接,不是无损搬迁。
上游格式还在漂移,这才是集成方真正的成本
写到这,得说一件比 txcript 自身成熟度更值得关注的事。
这些格式全都还在动。用 GitHub Releases API 拉出来的时间戳看得很直白(访问 2026-09-16):2026-09-11 一天之内,Codex 发了五个预发布版本(alpha.3 在 02:35、alpha.3.7 在 07:58、alpha.3.8 在 09:18、alpha.3.9 在 11:51、alpha.3.10 在 15:52);2026-09-15 又是一天四个(alpha.5 在 00:31、alpha.6 在 02:00、alpha.7 在 21:09、alpha.8 在 22:26)。而 0.153.4 这个正式版停在 09-04——中间十一天,预发布通道跑了二十多个版本。
Claude Code 侧同理,只是在闭源条件下更难观测:txcript 的格式文档记录到本地命令的 envelope 从 user 行迁移到了 system 行、标签顺序和 <command-args> 的有无在变、每个版本都可能冒出新的行类型(ai-title、file-history-delta、pr-link 都是近期新增的)。
所以做会话转换的一方,实质上在承担上游随时改格式的成本。 这不是 txcript 独有的问题,是所有依赖逆向格式的方案的宿命。
txcript 自己的应对方式值得学:它给每份格式文档标注三档来源(官方文档 / 开源代码 / 逆向工程),并且每份都带一个 Last verified: 日期。三份核心文档现在都停在 2026-08-10,而 Claude Code 的 CLI 版本跨度早已到 2.1.227——这个日期本身就是风险披露。 作者自己的话是,格式会 silently drift,一份没标日期的逆向格式说明比没有更糟。
这就是给集成方的判断:如果要把这类能力接进团队流程,把版本固定和回归测试当正经工程做,而不是直接吃上游的 alpha。上游一天五个 alpha 的时候,“跟最新"不是一个策略。
那这个方案现在能用吗
得泼盆冷水,说清楚什么情况下不该用它。
txcript 是个早期项目,不是成熟基础设施。2026-09-16 实测的 GitHub 数据是 50 star、8 fork、13 个 open issue、0 watcher,2026-06-25 才创建,最近一次 push 是 09-15。更关键的是贡献者结构:首位贡献者一个人提交了 143 次,第二位 11 次——这是一个由单人主导的项目。它的 HN 投稿(item 49719696)只拿到 1 分、0 评论,社区热度基本为零,所以任何"社区正在热捧"的说法都是编的。
格式文档的 Last verified 停在 2026-08-10,而 Codex 两天上好几个 alpha,Claude Code 已经走到 2.1.227。解析器会不会在某个版本后静默失效,作者自己也没法保证。
所以按场景分档:
- 留档、检索、跨机器交接 → 值得用。
export落成 Simple 是设计得最完整的路径,而且"30 天就被清掉"这个默认值,让归档这件事有了时间上的紧迫性。 - 指望会话在另一个 agent 里原样复活 → 不要用。这是设计上就不成立的期待,那个故意留空的 system prompt 槽位已经把话说尽了。
- 高频跨 harness、且涉及大量本地文件改动 → 先小范围验证。尤其是会话里如果充满了
apply_patch这类多文件改动,它们会全部降级成 raw 记录,你得到的是一串路径而不是可复现的操作。
顺便说一句,这条路不是你唯一的选择。市面上已经有后台录制所有 agent 会话、再一键 handoff 的商业产品(比如 Kurrent Capacitor 那种做法),也有不少团队干脆用一个 skill,让源 agent 自己总结出"聊了什么、动了哪些文件、定了什么决策、下一步做什么”,再手动贴过去。后一种做法其实更诚实——它不假装自己无损,而是承认降维是必然的,只把决策要点搬过去。txcript 的价值在于它把这件事做成了确定性的数据结构操作,而不是每次靠模型即兴总结。
明天可以先做这两件事
第一件,查你的会话还剩多少天。 打开 ~/.claude/settings.json,看看有没有 cleanupPeriodDays。没有的话,默认就是 30 天——去找一个你最近做过的、值得留档的长会话,在它被清掉之前先导出来。Claude Code 自己就有 /export。这一步跟用不用 txcript 无关,纯粹是止损。
第二件,如果你在评估这类工具,先跑一条最小的验证。 装法取自 README 原文:
|
|
continue 会写一个新的 native session,然后在记录的 working directory 里启动 Codex——源会话保留,不会被改掉。真正值得你花时间观察的,是搬过去之后的那个 Codex 会话:原来的 apply_patch 还剩多少是结构化的,工具调用的历史还在不在,usage 有没有对上。 这几个观察点,比任何功能介绍都更能告诉你这条路对你们团队值不值。
会话是你花了时间生产出来的资产,而它现在正躺在一个 30 天后会自动删除的目录里。别让"换工具"这件事,把你已经挣到的上下文一起带走。
参考与验证
- txcript 仓库(Apache-2.0,Rust 库 / CLI / WASM;GitHub API 实测 2026-09-16:50 star / 8 fork / 13 open issues / 0 watcher / 创建于 2026-06-25 / 最近 push 2026-09-15;贡献者 NishantJoshi00 143 commits、Narsagna 11 commits):https://github.com/skillsynchq/txcript
- txcript README(能力矩阵、“What carries over”、CLI 与 library 用法原文):https://github.com/skillsynchq/txcript (访问日期 2026-09-16)
- Claude Code 会话格式文档(存储布局、
parentUuid→uuid链、行类型、Caveats:UUIDv5 重生成 id / bookkeeping 行与 extra keys 丢失 / 斜杠命令 envelope 迁移;Last verified: 2026-08-10,观测 CLI 版本 0.144–2.1.227):https://raw.githubusercontent.com/skillsynchq/txcript/main/docs/formats/claude-code.md (访问日期 2026-09-16) - Codex 格式文档(rollout 日期分层、
response_item协议日志与event_msg显示日志双日志交织、有状态解析、call_id配对与 web search 例外;Caveats:encrypted_content/ sandbox / rate limit / display-only 不进 Common,apply_patch降级规则,model_provider: "openai"硬要求,工具名[A-Za-z0-9_-]+不可逆归一化;Last verified: 2026-08-10):https://raw.githubusercontent.com/skillsynchq/txcript/main/docs/formats/codex.md (访问日期 2026-09-16) - Simple 交换格式(无 store、无目录、
export/continue用法、“There is no system-prompt slot, deliberately” 原句、内联user消息的上下文建议):https://raw.githubusercontent.com/skillsynchq/txcript/main/docs/formats/simple.md (访问日期 2026-09-16) - Claude Code 本地目录与保留期(保留清扫规则原句
Claude Code deletes the files in the paths below once they're older than cleanupPeriodDays,以及The default is 30 days and the minimum is 1; setting 0 fails with a validation error.):https://code.claude.com/docs/en/claude-directory (访问日期 2026-09-16) - Claude Code 会话管理(
--continue/--resume//export等入口、transcript 存储位置、resume 会恢复什么):https://code.claude.com/docs/en/sessions (访问日期 2026-09-16) - OpenAI 官方 import 文档(导入清单:instruction files → AGENTS.md、settings.json → config.toml、skills、plugins、projects、memories、MCP、hooks、subagents;以及 “Codex CLI imports up to 50 chats from the last 30 days”):https://learn.chatgpt.com/docs/import (访问日期 2026-09-16)
- openai/codex 发布节奏(GitHub Releases API 实测 2026-09-16:2026-09-11 发布 alpha.3 02:35:52Z / alpha.3.7 07:58:14Z / alpha.3.8 09:18:04Z / alpha.3.9 11:51:30Z / alpha.3.10 15:52:58Z;2026-09-15 发布 alpha.5 00:31:51Z / alpha.6 02:00:26Z / alpha.7 21:09:02Z / alpha.8 22:26:34Z;正式版 rust-v0.153.4 为 2026-09-04;仓库约 124,000 star / 17,000+ open issues):https://github.com/openai/codex/releases
- Kurrent Capacitor(商业方案对照:后台录制多 agent 会话后 handoff):https://www.youtube.com/watch?v=6NCx7LGJbGs (访问日期 2026-09-16)