版本升级与迁移指南
一句话版:保留旧环境,先在新的 DSH home 和测试工作区验收源码版,再切换日常入口;回退时同时恢复旧运行时与旧数据副本。
审计基线 0.1.5-alpha.1 @ 5dda764ed3(2026-09-09 核验)。 本文处理升级到 0.1.5-alpha.1 的迁移:第 3 节汇总 0.1.2 以来的接口变更,第 4 节是 0.1.5 新增的会话存储格式 v2 → v3 迁移;不构成对未来版本的兼容性承诺。shell 示例适用于 macOS / Linux。
1. 先选渠道,而非直接换版本号
| 渠道 | 本次核验结果 | 适合 |
|---|---|---|
npm latest / next | 都是 0.1.2-rc.1 | 继续使用已发布包 |
| 官方源码 / tag | 0.1.5-alpha.1,tag 为 dsh-v0.1.5-alpha.1 | 验证本文的新接口 |
npm alpha 通道 | 0.1.5-alpha.1 | 想直接用已发布包时选它 |
node --version
npm view @deepseek-ai/dsh dist-tags --json
源码 Node 要求为 ^22.19.0 || >=24.0.0。默认 npx @deepseek-ai/dsh web 仍启动 npm 版;Git tag 存在不等于同名 npm 包已发布。后续渠道变化以 状态页 和现场查询为准。
2. 升级流程
记录与备份
先记录当前使用的是 npm 还是源码、精确版本 / commit、DSH_HOME、profile、额外 patch 与外部插件路径。停止 Web、SDK、ACP 等使用同一 home 的进程,等日志写入结束;只关闭浏览器标签页并不等于停掉 Host。
export DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
STAMP="$(date +%Y%m%d-%H%M%S)"
BACKUP="${DSH_HOME%/}.backup-$STAMP"
umask 077
test -d "$DSH_HOME" && test ! -e "$BACKUP" && \
cp -a "$DSH_HOME" "$BACKUP" && printf 'Backup: %s\n' "$BACKUP"
备份包含凭据、对话与插件配置,应留在私有目录。它不覆盖项目工作区,也不一定覆盖外部插件源码或 MCP 记忆库;这些需按各自位置另存。不要一边运行一边复制数据库或会话日志。
独立构建与试运行
下面的源码目录与试用 home 均应为新路径:
git clone --branch dsh-v0.1.5-alpha.1 --depth 1 \
https://github.com/deepseek-ai/deepseek-harness.git \
"$HOME/deepseek-harness-alpha-trial"
cd "$HOME/deepseek-harness-alpha-trial"
git rev-parse HEAD
pnpm install
pnpm run build
export TRIAL_HOME="$HOME/.dsh-alpha-trial-$STAMP"
DSH_HOME="$TRIAL_HOME" pnpm dsh web
首次试运行配置独立的模型凭据,并选择不含敏感信息的测试目录。新 home 不会自动继承旧 home 的登录、设置或会话;单独换 home 也不构成工作区隔离。
先确认出厂组合正常,再逐项迁入旧 profile patch 和自定义插件。不要用整个旧 profile 目录覆盖新模板,否则旧依赖清单及安装产物也会一起进入新环境。外部绝对路径和插件版本尤其要复核。
3. 必查迁移表
| 旧写法 / 习惯 | 新基线的处理 | 深入说明 |
|---|---|---|
DSH_TOOLS_MODE=code、mode: code | 改用 ptc;纯 PTC 下具名工具直调返回 UNKNOWN_TOOL,通过 run_code 的 SDK 绑定调用 | 工具执行 |
导入 dsh-host-apiproxy | 旧包已移除,按 Session / Settings / Workspace 等领域消费 typed Remote | Remote API |
导入 dsh-client-runtime | 按职责迁至 store、Session controller 或 React 适配层,别机械替换包名 | Web UI |
TS SDK launch: { command, args } | 使用 dshBin、profile、patches;客户端与运行时版本匹配 | SDK |
| SDK / ACP demo bin | 使用 dsh --profile sdk / sdk-minimal / acp | ACP |
| 直接访问干净的 Web 根地址 | 首次打开 CLI 打印的 token 启动链接,换取浏览器会话 cookie | 快速上手 |
用 --host 0.0.0.0 开放 Web UI | 当前 Web 启动拒绝该值;远程使用 SSH 转发,Host 信任仍与登录分开 | Remote API |
| 把遥测视为单一开关 | OTel 默认 FEEDBACK_ONLY;插件包元数据默认开,额外 Session 日志元数据默认关,分别检查 | 隐私 |
旧日志中的 tool/code-dispatch* 事件名保持有效,不要因为模式改名而批量改写历史日志。sdk-minimal 是独立组合并固定 full-access 策略,不是“更受限”的 SDK。
4. 会话存储格式迁移:V2 → V3
没有命令,打开就迁移。 session-persistence-jsonl 的 open(id, 'read' | 'write') 选择会话目录里版本号最高的规范 generation;若它低于 SESSION_FORMAT_VERSION(当前 3,packages/core/session/src/types.ts),catalog 就把该版本到当前版本之间的相邻迁移边串成一次流式还原——0.1.5-alpha.1 新增的是最后一条边 session-format-v2-to-v3(0.1.2 写的是 v0,所以要连跑 v0→v1→v2→v3)。apps/cli 里没有迁移命令,JSONL 后端也没有迁移开关——它的配置只有 root(必填)与 compression(可选)。
| 打开方式 | 行为 |
|---|---|
read | 单遍解码并迁移、校验当前逻辑结果,不发布后继,直接把结果返回给读者 |
write | 复用同一份按 revision 记忆的 preparation;同目录临时文件分块编码 → Worker Thread 校验 → 重查源 revision → 无覆盖发布当前 generation;源文件保持字节不变 |
磁盘上会多出一个当前 generation,源文件原名保留(中间的 v1/v2 不会落地——catalog 把相邻边串成一次流式还原,只发布最终结果):
<root>/--<归一化 cwd>--/<encoded-id>/
session.jsonl.zstd ← 迁移前的 generation,原名保留(0.1.2 写的是 v0)
session.v3.jsonl.zstd ← 写打开后发布的当前 generation
(compression: 'none' 时后缀是 .jsonl;文件名由 packages/session/session-format/src/filename.ts 的 sessionFormatLogFilename 决定:版本 0 仍叫 session.jsonl,之后是 session.vN.jsonl。)
迁移改了什么(packages/session/session-format-v2-to-v3):
| 变化 | 内容 |
|---|---|
| 逻辑头 | version: 2 → 3;保留 id、createdAt、isSeeded、delegationDepth 与可选的 cwd / parentSession / origin |
| 系统提示词 | request/header.data.header.system 提升为 surface 的 system/message 头(首个 step/start 之后),随后从每个 request header 移除 |
| preset | 精确 id code → ptc(header.agentPreset 与所有 agent-preset/selected) |
| PTC 词汇 | tool/code-dispatch-start / tool/code-dispatch → tool/ptc-dispatch-start / tool/ptc-dispatch;插件归属 tools-code-mode → tools-ptc |
| 引用重映射 | surface 的 sourceEventSeqs、compaction 的 shadowedRange / shadowedSeqs、command/done.sourceEventSeq、标题事件的 messageSeqs 按插入后的位置重算 |
| 信封规范化 | { op: 'replace', start, end } → { op: 'replace', startSeq, endSeq };省略 tools: [] 与 adapterDefaults: {} |
兼容性与拒绝:
- 迁移保持历史请求的含义,但会插入系统事件,所以事件数、密集 seq 与局部引用都会变;不要把 v3 文件当成 v2 的逐字副本。
- 源文件字节不变;迁移边拒绝时不发布后继,报
SessionFormatUnsupportedError(底层是SessionFormatUnsupportedMigrationError),原始日志仍在原地,可用来排查。 - 已标记 V3 的输入不走这条边(原生 V3 admission 另有一套更严的校验)。
- catalog 是构建期静态清单(
session-format-catalog):profile 不能挂插件来增删或重排迁移边。
回退:持久化层明确不为保留的旧 generation 提供降级支持,唯一受支持的回退是第 2 节那份升级前的 home 备份。手动删除或改名 session.v3.jsonl* 只能算取证手段,不是回退方案:v3 里已经追加了迁移之后的历史,旧版读不到这些事件;而且旧运行时按"版本号最高的规范 generation"选择文件,看到 v3 会因版本高于自己支持的版本直接 SessionFormatUnsupportedError。
验证:
# 当前格式版本
grep -n "SESSION_FORMAT_VERSION" packages/core/session/src/types.ts
# 一个会话目录里同时存在哪些 generation(迁移前后对比)
ls -1 ~/.dsh/sessions/*/*/session*.jsonl*
# 迁移边是构建期静态清单,不是运行时插件
grep -rn "sessionFormatMigration" packages/session/session-format-v2-to-v3/package.json
5. 分层验收,避免把启动成功当作升级完成
在源码目录、使用试用 home 检查:
DSH_HOME="$TRIAL_HOME" pnpm dsh web --dump-config
DSH_HOME="$TRIAL_HOME" pnpm dsh --profile sdk --help
DSH_HOME="$TRIAL_HOME" pnpm dsh --profile acp --help
| 层 | 应看到的证据 |
|---|---|
| 版本与组合 | 目标 commit 正确;旧包引用已迁移;profile / patch 来源符合预期 |
| Web 登录 | 完整启动链接完成登录;页面没有连续 401/403 或 WebSocket 重连 |
| 基础执行 | 在测试目录发一次读文件 / 简单工具请求,有真实结果而非只看到工具 schema |
| PTC(若使用) | run_code 的工具绑定正常,结果进入会话 |
| SDK / ACP(若使用) | 完成实际 prompt、接收结果、关闭子进程;help 只验证入口 |
| 数据与插件 | 新会话保存并可再次打开;逐个启用的外部插件行为正常 |
| 隐私 | 三类数据出口、模型路由及所选 permission preset 与预期一致 |
若要检验旧会话兼容性,用备份另建一个迁移测试副本,保留原备份不动。不要让新旧运行时同时读写同一 home。
6. 切换与回退
验收后,更新日常启动脚本中的运行时路径、DSH_HOME 和 profile,避免终端里的旧全局 dsh 与源码入口混用。保留旧备份,直到常用任务完成验证。
需要回退时,先停止新进程,保留失败环境,再从旧备份创建一个恢复 home:
RESTORE_HOME="${DSH_HOME%/}.restored-$STAMP"
test -d "$BACKUP" && test ! -e "$RESTORE_HOME" && \
cp -a "$BACKUP" "$RESTORE_HOME" && \
DSH_HOME="$RESTORE_HOME" npx @deepseek-ai/dsh@0.1.2-rc.1 web
该命令对应旧环境原本使用 npm 0.1.2-rc.1 的情况;旧环境若来自源码,应启动事先记录的旧 checkout。回退 runtime 不会撤销 Agent 对项目文件的修改;项目文件使用自己的 Git / 备份策略恢复。不要把 alpha 新写的数据直接当作旧版兼容数据。会话日志的格式迁移(第 4 节)同理:它不提供降级,回退只能靠升级前的 home 备份。
源码依据与下一步
- 本次官方发布
- V2 → V3 会话格式迁移规范
- 启动入口与 profile
- SDK 启动契约
- GitHub PR 自动审查、MCP 长期记忆:升级验收后的可选实战。