DeepSeek Harness 新会话斜杠命令执行成功却没输出?/balance 无响应排查

故障排查发布于 2026-10-20作者: DeepSeek Plugin 插件市场
DeepSeek Harness斜杠命令会话快照空白会话command/done
DeepSeek Harness 全新会话里执行 /balance、/versions 等斜杠命令,日志已有 command/run 与 command/done,界面却一片空白:根因是快照的 active 判定不把命令节点算作内容。给出改活动判定、插入占位消息、前端单独渲染命令结果三条路径。

DeepSeek Harness 的命令确实执行了,只是界面按「空白会话」把它整页跳过了:全新会话里 command/run 和 command/done 都写进了日志,kind 是 success,但快照的 active 判定不把命令节点算作内容,渲染停在 blank 相位。 这不是渲染函数坏了,而是「什么算内容」在两处各有一套定义。据讨论,报告者在 Web profile 上复现,社区成员核对了 ui-conversation 的 snapshot 契约(讨论原文)。

DeepSeek Harness 新会话斜杠命令:先确认命令有没有执行

判断顺序应该是先看日志、再做对照,最后才怀疑前端,因为「执行成功」和「显示成功」在这条链上是两件事。 步骤:

  1. 打开 ~/.dsh/sessions/.../session.v4.jsonl.zstd,搜索 command/run,预期看到类似 {"type":"command/run","seq":3,"data":{"name":"balance","source":{"kind":"user"}}}。
  2. 紧接着搜索 command/done,预期看到 seq 为 4、kind 为 success,并带有同一条 commandId。
  3. 在同一个工作区里打开一个已经有消息的会话,执行同一条 /balance,预期这次能正常显示。
  4. 对比两次日志里命令事件的落点:已有消息的会话中,命令前面还有消息事件;全新会话里,命令就是第一条可渲染内容。

根因:DeepSeek Harness 有两套「算不算内容」的判断

snapshot.ts 的 active 只看 activeTargets 和 blank / awaitingFirstTurn,命令节点不参与任何一边。 这条链路上没有任何 DSH插件 参与,问题在宿主前端。会话空白还有别的原因,见 会话空白与侧栏隐藏排查。社区核对到的判定是:

ts
const active = conversation.activeTargets.size > 0
  || (!session.blank && !session.awaitingFirstTurn)
return active ? 'active' : session.promptAttempted ? 'engaging' : 'blank'

在这个场景里两个条件同时不成立:

  1. 命令节点不会进入任何视图的 isActive,所以 activeTargets 是空集。
  2. 会话从未发过消息,session.blank 为真,awaitingFirstTurn 也对,第二项同样接不住。
  3. 相位落到 blank,界面按「没有内容」处理,命令结果被跳过。
  4. 命令结果本身已经持久化,所以刷新不会让它出现,重开也不会。

把三种会话形态并排看,差别只有相位这一列:

会话形态activeTargetsblank / awaitingFirstTurn结果相位
全新会话只有命令空两者都为真blank,不渲染
全新会话命令加占位消息空至少一项为假active,渲染
已有消息的会话执行命令可能为空两者都为假active,渲染

只改 isActive 不够,只改会话相位也不够:两处判断互相独立,必须一起动,或者干脆合并成一个「会话有没有可渲染内容」的函数。

这也解释了为什么日志里的 seq 3/4 与屏幕空白能同时成立:存档走事件,渲染走快照,两条路没有共享同一个「内容」定义。

改判定:让 DeepSeek Harness 把命令节点计入 active

改法是让命令节点成为活动内容的来源,同时删掉重复的会话相位判断,避免两套逻辑再次分叉。 步骤:

  1. 找到产出快照的投影处,确认命令事件投影成了哪种节点,预期能在节点里读到 commandId 与结果文本。
  2. 把命令节点计入 activeTargets,或直接在 active 表达式里加一条命令判定(示意):
ts
const active = hasVisibleCommand(conversation)
  || conversation.activeTargets.size > 0
  || (!session.blank && !session.awaitingFirstTurn)
  1. 编译后新建会话执行 /balance,预期界面立即出现命令结果。
  2. 刷新页面再执行一次,预期结果仍在;切换到一个空会话再切回来,预期不重复渲染。
  3. 用一个只有命令、没有任何消息的会话与一个纯空会话对照,预期两者相位不同:前者 active,后者 blank。

补消息:DeepSeek Harness 命令执行后插入占位消息

如果不想动前端的相位判断,最稳的绕法是让命令自己产生内容,把「空白会话」变成「有内容的会话」。 步骤:

  1. 在命令处理器返回结果后,追加一条普通消息节点,内容可以是结果摘要加一句来源说明。
  2. 确认这条消息先于或同时于 command/done 持久化,预期日志里能按 seq 顺序读到它。
  3. 新建会话执行 /versions,预期界面因这条消息进入正常渲染,命令结果一并显示。
  4. 检查刷新、导出与会话列表标题,预期占位消息不会把标题改成命令名。
  5. 回放一份只有 command/done、没有占位消息的旧会话,预期它仍然是 blank——这条路径不追溯历史。

代价要说清:转录里会多出一条消息,纯命令会话不再「只有命令」。

独立渲染:DeepSeek Harness 的命令结果不依赖会话相位

把命令结果当成独立区块挂载,绕开会话相位的门禁,是三条里改动面最集中、也最不依赖后端的做法。 步骤:

  1. 在会话视图里找到命令结果的渲染入口,确认它现在挂在 active 分支内部。
  2. 把命令区块移到相位判断之外,直接按快照里的命令节点列表渲染。
  3. 给区块补空态与折叠:没有命令时不占位,有命令时按时间顺序展示结果文本。
  4. 新建会话分别执行 /balance、/model-sync,预期两条结果都出现;再执行一条会报错的命令,预期错误文本同样可见。
  5. 把窗口收窄到移动端宽度,预期命令区块不会挤掉输入框,也不会被折叠成零高度。
  6. 关掉网络再刷新,预期命令结果从本地快照恢复,不出现空态闪烁。

DeepSeek Harness 改完怎么验,以及什么时候该回退

验收要覆盖「能显示」「不重复」「旧会话不回归」三件事,缺一项就可能在别的会话形态里翻车。 步骤:

  1. 全新会话执行 /balance,预期有输出;按 Ctrl+F5 硬刷新,预期输出仍在且只有一份。
  2. 有消息的会话执行同一命令,预期位置与样式和改动前一致。
  3. 连续输入两次同一命令,预期两条结果各自独立,不被去重。
  4. 若出现命令区块和转录消息重复展示,回退到只改一处判定的版本,先确认哪条路径在维护,再决定是否合并判断。
  5. 用两个浏览器标签同时打开同一会话,预期命令结果只在自己发起的那一侧出现一次,不因并发刷新重复插入。
  6. 把复现步骤写进回归用例:新建会话、只执行一条命令、断言存在可见输出节点。

常见问题

全新会话里输入 /balance 回车后界面没反应,命令到底执行了吗?

DeepSeek Harness 确实执行了这条命令:会话日志里能看到 command/run 与 command/done 两条事件,kind 为 success,说明命令处理器跑完并返回了结果。界面空白是渲染阶段的判定问题,不是命令没跑。

为什么同一个 /balance 在已有消息的会话里能显示,新会话里不行?

因为 DeepSeek Harness 的会话相位判定依赖「是否已经有内容」这一条件。已有消息的会话不满足 blank 与 awaitingFirstTurn,被判为活跃,命令结果照常渲染;全新会话两个标志都为真,直接落进 blank 分支。

只把命令节点加进 chat 视图的 isActive 判定,能修好这个空白问题吗?

只改一处不能修好:DeepSeek Harness 的 activeTargets 汇总的是各视图的 isActive,而会话相位另外只看 blank / awaitingFirstTurn。命令节点在两边都不算内容,只改一处仍会被另一处拦住,两处必须一起改或收敛成一处判断。

不想改前端判定,能不能让 DeepSeek Harness 的斜杠命令产生可见消息?

可以让 DeepSeek Harness 的斜杠命令自己产生一条可见消息:在 command/done 之后补一条普通消息节点写入会话,新会话就不再是 blank,现有的渲染路径会把它画出来。代价是命令结果在转录里多一条消息,刷新与导出都要能接受这个形状。

相关术语

session snapshot(会话快照)
session snapshot 是前端渲染会话时使用的一份只读投影,包含会话相位、活动目标与已渲染节点。渲染决策基于快照,而不是直接读持久化日志。— https://github.com/deepseek-ai/deepseek-harness/discussions/10266
activeTargets
activeTargets 是快照里汇总各视图「是否认为自己有活动内容」的集合,集合非空即把会话判为 active。它的成员来自各视图的 isActive 判定。— https://github.com/deepseek-ai/deepseek-harness/discussions/10266
command/done
command/done 是斜杠命令执行完成时写入会话的事件,携带 commandId、kind 与输出文本。它是命令结果的持久载体,但是否被渲染取决于快照相位。— https://github.com/deepseek-ai/deepseek-harness/discussions/10266

来源