G1en-114/dsh-codex-import

Import OpenAI Codex CLI conversations into DeepSeek Harness as sessions — /codex-import command + standalone CLI · 把 OpenAI Codex CLI 的对话历史导入 DeepSeek Harness 成为可浏览的会话

项目介绍Project Overview

dsh-codex-import 是把 OpenAI Codex CLI 历史会话导入 DSH 的插件/CLI,将 rollout JSONL 转为标准 session.jsonl.zstd,保留消息、工具调用与 turn 结构,可在侧边栏浏览、搜索并继续。适合迁移旧 codex 对话。注意:超大历史会触发模型压缩并产生一次性 token 费用,建议先 --dry-run 或用 --max-turns 截断。

dsh-codex-import is a DSH plugin and standalone CLI that imports OpenAI Codex CLI conversation history into DSH. It converts rollout JSONL into standard session.jsonl.zstd files, preserving messages, tool calls, outputs, titles, and turn structure so sessions become browsable, searchable, and resumable. Use it when migrating Codex sessions into DSH. Caveat: very large histories may trigger model-based compaction with real token costs; use --dry-run or --max-turns first.

或使用命令行安装(适合开发者)Or use CLI install (for developers)

命令行安装CLI Install

dsh plugin --profile web add git+https://github.com/G1en-114/dsh-codex-import.git

G1en-114/dsh-codex-import 加入你的 DSH 配置(web profile)即可启用。

READMEREADME

dsh-codex-import

OpenAI Codex CLI 的历史对话导入 DeepSeek Harness (DSH),成为可浏览、可搜索、可继续的 DSH 会话。 Import OpenAI Codex CLI conversation history into DeepSeek Harness (DSH) as browsable, searchable, resumable sessions.

License: MIT Node zero dependencies


特性 / Features

  • 🗂️ 一键导入/codex-import <session-id> 把 codex CLI 的任意历史会话变成 DSH 会话,出现在侧边栏对应 workspace 下

  • 🔁 完整保真:用户消息、助手回复(commentary + final_answer)、工具调用与输出(exec_command / write_stdin / apply_patch 等)、turn/step 结构、会话标题

  • 🧩 两种形态:宿主插件命令(在 GUI 里用)+ 独立 CLI(无需启动 DSH,直接写会话文件)

  • 零运行时依赖:纯 Node,用 Node ≥ 22.20 内置的 node:zlib zstd 支持写 DSH 标准会话文件

  • 🛡️ 写入自校验:产物与 DSH 持久化层字节级一致(双帧 zstd、header 单独一帧、seq 连续、带 checksum),写完即验证

  • Source: ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl — the raw event stream codex resume <session-id> replays

  • Target: ~/.dsh/sessions/<workspace>/<session-id>/session.jsonl.zstd — the standard DSH session artifact


安装 / Install

作为 DSH 插件(推荐 / recommended)

dsh plugin --profile web add git+https://github.com/G1en-114/dsh-codex-import.git

⚠️ git 托管的安装:仓库的 prepare 脚本会被 pnpm 拦截,需要先按 pnpm 报错提示,把包名加进 ~/.dsh/profiles/web/pnpm-workspace.yamlallowBuilds,然后重跑上面的命令。

⚠️ For git installs, pnpm blocks the prepare script until you add the exact package key it prints to allowBuilds in ~/.dsh/profiles/web/pnpm-workspace.yaml, then re-run the command.

因为是 bundle 插件dsh.bundle.patch),安装后自动注册到 profile,重启 dsh web(或刷新页面)后即可使用。

Being a bundle plugin, it self-registers into the profile — just restart dsh web (or refresh the page).

作为独立 CLI / standalone CLI

# 无需安装,直接从仓库运行(Node >= 22.20)
node bin/dsh-codex-import.mjs 019feec0-f565-7900-b985-1d6ba3b63a56

# 或全局安装
npm install -g dsh-codex-import
dsh-codex-import <https://github.com/G1en-114/dsh-codex-import/blob/HEAD/codex-session-id | rollout.jsonl> [options]

使用 / Usage

在 DSH 会话里 / in a DSH session

/codex-import 019feec0-f565-7900-b985-1d6ba3b63a56
/codex-import ~/rollout.jsonl --session-id session-my-import --cwd /mnt/e/cell
/codex-import 019feec0-... --max-turns 80
/codex-import 019feec0-... --title "cell 比赛(已压缩·可继续)" --no-compact

导入完成后会返回新的 session id,刷新侧边栏即可看到(标题会在首次打开会话后固化到投影缓存)。

超大会话自动压缩:codex 长会话的 rollout 保留的是完整未压缩历史,全量导入可能超过模型的上下文窗口(如 100 万 token)。命令默认自动处理:先估算模型可见历史的 token 数,超过上下文预算(窗口 − 输出预算 − 余量)时,用当前模型把最老的 turn 压缩成 <compacted-summary> 检查点(DSH 压缩的同一格式),最近的 turn 保留原文——早期上下文不丢失,只是变成摘要。--no-compact 可关闭自动压缩,--max-turns <n> 仍是确定性的纯截断方式。

⚠️ 超大对话的风险与成本

  • token 消耗:导入是纯文件转换、不消耗 token;但之后每一次对话都按完整历史计费输入 token,历史越大每次请求越贵。自动压缩也不是免费的:一次压缩调用会把被压缩部分的历史全部作为输入发给模型(几十万 token 很常见),是一笔真实计费的一次性成本。(作者的惨痛教训)
  • 建议:导入前先 dsh-codex-import <id> --dry-run 看估算规模;若只需保留最近可继续的部分,用 --max-turns <n> 纯截断(零 token 成本);确实需要早期上下文时再接受自动压缩的一次性成本;压缩后在已压缩的会话里继续,不要回到未压缩的旧会话。

CLI

dsh-codex-import <https://github.com/G1en-114/dsh-codex-import/blob/HEAD/codex-session-id | rollout.jsonl> [options]

Options:
  --session-id <id>   指定导入后的会话 id(默认自动生成 session-<uuid>)
  --cwd <dir>         会话所属 workspace(默认取 codex 会话自己的 cwd)
  --max-turns <n>     只保留最近的 n 个 turn(丢弃更早内容;标题/创建时间不变)
  --title <text>      覆盖会话标题(例如标记"可继续"以区别于完整导入)
  --compact           超预算时自动压缩最老的 turn 为摘要检查点(需 API key:
                      DEEPSEEK_API_KEY 环境变量或 ~/.dsh/.credentials.yaml)
  --model <id>        摘要模型(默认 deepseek-v4-flash)
  --context-window <n>  模型上下文窗口,用于预算(默认 1000000)
  --max-tokens <n>    输出预算,用于预算(默认 256000)
  --root <dir>        DSH 会话根目录(默认 ~/.dsh/sessions)
  --dry-run           只解析、构建、打印摘要,不写入
  -h, --help          帮助

示例 / examples:

dsh-codex-import 019feec0-f565-7900-b985-1d6ba3b63a56                    # 导入到 ~/.dsh/sessions
dsh-codex-import --root /tmp/test-sessions 019feec0-...                  # 写入自定义根目录
dsh-codex-import --dry-run 019feec0-...                                  # 试跑
dsh-codex-import --max-turns 80 019feec0-...                             # 只导入最近 80 个 turn
dsh-codex-import --compact 019feec0-...                                  # 超预算时自动压缩为摘要检查点
dsh-codex-import ~/backup/rollout-2026-08-11.jsonl --cwd /mnt/e/cell     # 直接给 rollout 文件

它是怎么工作的 / How it works

codex rollout 是两类事件流的 JSONL:event_msg(UI 层消息与 turn 生命周期)和 response_item(模型 API 条目,含工具调用与输出)。导入器按 codex 的 turn_id 分组,每个 codex turn 对应一个 DSH turn(含单个 step),消息与工具按时间戳排序合并:

codex 事件 转换后 DSH 事件
event_msg/task_started turn/start + step/start
event_msg/user_message user/message
event_msg/agent_message(commentary / final_answer) assistant/message
response_item/function_callcustom_tool_call tool/call
response_item/function_call_outputcustom_tool_call_output tool/result
event_msg/task_complete / turn_aborted step/end + turn/end
首条用户消息 session/title(fallback 标题,自动剥离 URL)
  • web_search_call 因 codex 不落盘搜索结果而省略;工具调用保留 codex 原生名称与参数。
  • 工具调用同时以两种形式出现:独立 tool/call 事件(供 UI 轨迹与不变式检查),以及挂到最近一条 assistant 消息上的 tool-call 内容块(供 LLM 历史——DSH 的模型可见历史只由 user/message/assistant/message/tool/result 派生,工具调用必须由 assistant 消息声明)。
  • 被中断、没有落盘输出的调用(turn 被 abort)会在 step 结束前补一条合成的中断 tool/resultisError: true,文案与 DSH 自身的 interruptedTurnClosers 一致),保证每个声明的 tool_calls id 都有应答。
  • 自动压缩(/codex-import 默认开启、CLI 加 --compact)把最老的 turn 用模型压成 <compacted-summary>…</compacted-summary> 检查点消息(含 DSH 压缩的前言与 {kind:"plugin", plugin:"compact"} source,格式与 dsh-compaction-basic 一致),最近的 turn 保留原文;token 预算 = 上下文窗口 − 输出预算 − 30k 余量。
  • 写入的 session.jsonl.zstd 与 DSH 持久化层完全一致:第一帧只有 header 行,第二帧为全部事件行,seq 从 0 连续递增,压缩带 checksum。CLI 写入后自校验(字节级比对 + seq 检查)。
  • 产物已用 DSH 真实读取器 JsonlSessionPersistence.loadStored 验证通过(无 torn marker)。

开发 / Development

npm test                                    # 单元测试(纯 Node,无依赖)
node bin/dsh-codex-import.mjs --dry-run <session-id>   # 用真实 rollout 试跑
node scripts/live.mjs /tmp/test-sessions <session-id>  # 在真实 CommandRuntime 里跑 /codex-import(需 @deepseek-ai 依赖)
node scripts/wire-verify.mjs <sessions-root> <session-id>  # 用真实 foldSurface + 序列化规则校验会话 LLM 历史(需 @deepseek-ai 依赖)

仓库结构 / layout

lib/core.js              # 纯转换核心:parseRollout / buildSession / projectKey / deriveTitle
lib/index.js             # 宿主插件:/codex-import 命令(commands + sessionPersistence 服务)
bin/dsh-codex-import.mjs # 独立 CLI:解析 → 构建 → 双帧 zstd 写入 → 自校验
test/                    # 单元测试 + 合成样例 rollout
scripts/live.mjs         # 真实 DSH 服务装配验证脚本
cordis.patch.yml         # bundle 插件行(自动注册)

常见问题 / FAQ

导入后侧边栏看不到? 重启 dsh web 后刷新;冷会话第一次打开后标题才固化到投影缓存。

能重复导入同一个 codex 会话吗? 可以,每次生成新 session id;指定相同 --session-id 会因 id 已存在而报错。

为什么没有 web 搜索结果? codex 的 rollout 不保存 web_search_call 的输出,无法还原,故省略。

Node 版本要求? ≥ 22.20(node:zlib 的 zstd API)。插件命令形态运行在 DSH 进程内,无此限制。

为什么导入后发消息报 "maximum context length exceeded"? 会话历史超过了模型的上下文窗口(导入本身不会报错)。用 --max-turns <n> 截断后重新导入,或让自动压缩把最老的部分压成摘要;并确保之后在压缩后的会话里继续,而不是回到未压缩的旧会话。


License

MIT

上一个 Prev dsh-cue-bank 下一个 Next dsh-composer-history