DeepSeek Harness 新建会话出现旧内容与子 Agent 设置失效:空壳会话清理
点「新建会话」,界面是空白输入页,看起来一切正常;输入「哈哈」发出去,屏幕上却先冒出一条 10 月 20 日用 /image 生成的图片卡片,你的新消息被追加在它后面。 这不是界面渲染错乱——新建会话根本没新建,它把一条旧会话当成「空白会话」复用并 seed 了(#10889)。先分清三种空壳会话,再决定怎么清。
DeepSeek Harness 判据一:新会话里出现旧内容
只要旧事件出现在 session/end-seed 之前,就是复用;真正的空白会话不会有这个事件落在中间。
- 找到会话日志
~/.dsh/sessions/<workspace>/<session-id>/session.v4.jsonl.zstd。 - 解压后看事件序:社区案例里 seq 3 是 10-20 的
command/run,seq 4 是command/done。 - seq 5 是 10-24 20:33 的
session/end-seed——这就是「把旧日志当 seed 收尾」。 - seq 6 才是新消息
agent/inbox/spliced "哈哈",seq 7 起才是turn/start。 - 事件时间跨度就是证据:同一份日志里出现两个相隔四天的时间段。
DSH插件 判据二:创建时间不对
会话头的 createdAt 保留旧值,所以「新会话」显示的是几天前的创建时间;这是复用最直观的一条线索。
- 看会话列表里的创建时间,和你刚才点「新建」的时刻比。
- 社区另一个案例里,新会话显示的是 10-17 的时间,实际当天是 10-24。
- 这类会话的日志通常只有三条 seed 事件:
permission/preset、sandbox/mode、approval/policy,没有turn/start,也没有end-seed。 - 也就是说,上一次创建过程没走完,留下一条半初始化记录,被判成 blank 后又被复用。
DeepSeek Harness 三类空壳会话:为什么会永久算 blank
blank 的定义是「折叠后不含任何 turn」,与「有没有内容」无关,所以下面三类都会长期留在候选池里。
| 类型 | 日志里有什么 | 怎么来的 |
|---|---|---|
| 命令型 | command/run + command/done | 只跑斜杠命令(如 /image),没有形成 turn |
| 半初始化型 | 只有 permission/preset、sandbox/mode、approval/policy | 创建过程没有收尾,缺 end-seed |
| 单条消息型 | 只有一条 user/message | 首条消息被打断或失败 |
社区实测:/image 会话与「只有一条 user/message」的会话,在 sessionListMetadata 里都是 blank: true。判定逻辑就写在 schema 里:「Whether the folded prefix contains no turn.」
DeepSeek插件 判据三:子 Agent 模型选择静默失效
这类会话里没有 subagent/model-selection-policy 事件,subagent 工具不带 provider/model,设置页却还写着「仅影响新会话」,用户无从察觉。
- 打开工作区配置,确认
subagent-model-selection-settings是enabled: true且有allowedModels。 - 在被复用的会话里搜
subagent/model-selection-policy,没有这条事件。 - 看
request/header的 tools 数组:subagent只有cwd、description、prompt,没有provider、model、reasoning_effort,也没有list_subagent_models。 - 对照同工作区里真正新建的会话:seq 3 就有该策略事件,
subagent带三个参数,list_subagent_models也在。 - 根因是
tool-subagent用firstLiveSeq === 0判新会话;复用会话从磁盘恢复后首个存活序号是 3(补完 end-seed 后是 4),永远不等于 0,策略就不会被读取和写入。 - 这种策略是每会话一次性快照:那条正常会话快照的是当时的 2 条路由,而当前配置已是 4 条。
DeepSeek Harness 清理:找出空壳会话并移走
判断标准只有一个:日志里没有 turn/start,但有命令或用户消息。
- 完全退出 DSH,避免边清边写。
- 备份整个
~/.dsh/sessions目录。 - 逐个会话目录解压
session.v4.jsonl.zstd(zstd 多帧格式,需要逐帧解)。 - 统计
turn/start出现次数:0 次且存在command/run或user/message的,就是空壳。 - 把这些目录移到 sessions 根之外(不要直接删,先移出观察)。
- 重启后新建会话,确认输入页是真正空白、创建时间是当前时间。
还有一点必须一起查:如果被复用的 blank 会话日志校验不过,新建会直接失败而不是跳过。社区遇到的报错是 SessionQueryError: stored session ... is corrupt ... session event at seq 4 lacks an identified message,原因是旧版本的 user/message 事件没有 data.id,而当前版本要求每条 message 必须带 id。这时只能手工修日志或删掉那条会话。
DSH插件 修复方向与今天的规避
核心要改的是 blank 的判定:只把「header 加默认初始化事件」算空白,并在复用前重新读一遍日志。
- blank 改成「日志为空」:只有 header 与
permission/preset、sandbox/mode、approval/policy时才算。 - 或者至少把含
user/message、command/run的会话排除在候选之外。 - 复用前重新读日志再判(
refreshProjections()目前只在restoreBlank分支调用,普通新建用的是可能过期的缓存提示)。 - 命中「有内容的 blank」时改为新建,而不是 seed 旧日志。
- create 路径对单条坏会话容错:跳过并提示,不要让一条坏日志卡死整个新建流程。
- 子 Agent 侧把新鲜度判据改成「没有
turn/start」,或在 blank 复用路径上补写一次策略事件。
一分钟自检清单
每次新建会话后花一分钟核对三项,能在串台变严重之前发现空壳会话。
- 列表里的创建时间是不是刚才点新建的那一刻。
- 输入页是不是完全空白,没有历史卡片、没有残留的滚动位置。
- 发出第一条消息后,日志里不应出现「
session/end-seed排在你的消息之前」这种顺序。
三项里有任意一项不对,就按上面的清理步骤把候选会话移出去,再新建一次。
注意事项
- 别以为「界面空白」就是新会话。真正的判断在日志里,不在 UI。
- 别只删最后一条。空壳会话可能有多个,清一个之后下次会复用另一个。
- 别忽略创建时间。它是零成本的自检信号。
- 清理前先备份。会话日志里有历史消息,误删不可恢复。
- 升级后复查。rc.3 上社区仍能复现,别以为换版本就好了。
如果你的新会话内容正常、只是偶尔某天出现旧图片,那也属于这一类,只是候选池里恰好只有一条空壳。相关排查见 DeepSeek Harness 新会话草稿丢失:handOffDraft、localStorage 与复用。
常见问题
因为那条旧会话被 DeepSeek Harness 判定成 blank 并复用了。新建会话时会找同工作区里一条 blank 会话,把它的日志当成 seed 继续写,于是 session/end-seed 落在旧事件之后,旧内容就出现在「新」会话里。
DeepSeek Harness 的判定是「折叠后没有 turn」,不是「没有任何内容」。所以斜杠命令会话(只有 command/run 和 command/done)、被打断的首条消息、命令失败留下的记录,都会永久算 blank。
不正常,这同样是被 DeepSeek Harness 复用旧会话导致的。会话头里的 createdAt 保留旧值,所以「新会话」显示的是旧会话的创建时间;只要工作区里还有一条空壳会话,就可能反复出现。
因为 DeepSeek Harness 的 tool-subagent 判「新会话」用的是 firstLiveSeq === 0。被复用的 blank 从磁盘恢复,首个存活序号等于已有事件数,永远不等于 0,于是策略事件不写入,subagent 工具也就不带 provider/model 参数。
在 DeepSeek Harness 里解压 session.v4.jsonl.zstd 后看有没有 turn/start:没有但存在 command 或 user/message 的会话就是空壳。把它们归档或删除,新建会话就能恢复真正的空白。
相关术语
- blank 会话
- blank 会话是 DeepSeek Harness 里折叠后不含任何 turn 的会话。判定只数 turn,不看是否存在 command 或 user/message,因此半初始化的记录也会被当成空白会话复用。— https://github.com/deepseek-ai/deepseek-harness/discussions/10889
- session/end-seed
- session/end-seed 是会话日志中的种子收尾事件,标记某份已有日志被当作新会话的初始内容。它出现在旧事件之后,就是「旧会话被复用」的直接证据。— https://github.com/deepseek-ai/deepseek-harness/discussions/10889
- firstLiveSeq
- firstLiveSeq 是会话中第一条存活事件的序号。tool-subagent 用它判断是否新会话:等于 0 才算新,因此复用过的会话永远拿不到新的子 Agent 策略。— https://github.com/deepseek-ai/deepseek-harness/discussions/10889
- subagent/model-selection-policy
- subagent/model-selection-policy 是会话内一次性快照子 Agent 可用模型设置的事件。它只在新建会话时写入,缺这条事件时 subagent 工具不会带 provider/model 参数。— https://github.com/deepseek-ai/deepseek-harness/discussions/10889
来源
- #10889 — 新建会话会复用「只有命令、没有 turn」的旧会话· deepseek-ai(GitHub Discussions)
- deepseek-ai/deepseek-harness(源码仓库)· GitHub