dsh-live · 现场驾驶舱
给 DeepSeek Harness 右侧栏加一个「这个会话此刻在干什么」的面板:当前轮次/步骤、正在跑的工具、在等的审批、活着的后台任务、本轮 token 花费。它不是事后回放(那是内置的 Trajectory 账本),而是现在。
面板里有什么
| 区块 | 内容 |
|---|---|
| 状态行 | 思考中 / 正在执行工具 / 等你处理 / 空闲,加 第 N 轮 · 第 M 步 · 已跑 S 秒 |
| 运行中的工具 | 工具名与已运行秒数;空闲时显示“没有工具在运行” |
| 需要你处理 | 待批准请求(有才出现),标题与等待秒数 |
| 后台任务 | 未结束的后台 job(有才出现),名字与已运行秒数;名字取命令首行、压平空白并截到 48 字 |
| Token | 本轮累计 ↑ 输入 / ↓ 输出,有重试时附错误数 |
| 刚完成的 | 最近 5 次工具调用:✓/✕、名字、耗时毫秒 |
耗时每秒自刷一次;没有事件时面板不重渲染。
安装
已装 DSH Desktop 的情况下,用打包运行时的 CLI 装进桌面 profile:
$dsh = 'C:\Program Files\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd' # 换成你的安装路径
& $dsh plugin --profile desktop add dsh-live
本地开发(不改桌面 profile,家目录重定向到工作区):
$env:DSH_HOME = '<你的开发家目录>'
& $dsh --profile dev --from-default-profile web --dump-config
& $dsh plugin --profile dev add '<本包绝对路径>' --store-dir '<pnpm store 绝对路径>'
& $dsh --profile dev --no-open --port 3181
然后打开 CLI 打印的 http://127.0.0.1:3181/?token=...,点右侧栏的 +(Guide)→「现场驾驶舱」。
依赖
- 运行时零依赖:host 半只用 DSH 自己的
ctx.sessionProjections,不引 zod 或任何第三方包(投影注册表只调用 schema 的.parse(),本包自带等价校验)。 - 需要右侧栏席位
@deepseek-ai/dsh-client-ui-sidebar-right(DSH 自带)。缺这个包时 client 半不会挂载。 - 需要的 host 服务:
sessionProjections(由@deepseek-ai/dsh-session-projection提供,随核心加载)。
工作原理
host 半注册一个会话投影:
export const inject = ["sessionProjections"];
export function apply(ctx) {
ctx.sessionProjections.register(liveProjectionDefinition); // key: "live", stateVersion: 1
}
apply 是对已提交事件日志的纯同步折叠:只处理 turn/start|end、step/start、tool/call|result、approval/asked|decided、job/registered|settled、assistant/message(usage)、llm/retry、llm/retry-started,其余事件原样返回同一个 state 引用,所以无关流量在注册表里被 Object.is 抑制、不会往下游发布。
重试的计数是按失败序列而不是按事件:同一次失败会连续发多条共享 retryId 的 llm/retry(retry 从 1 递增,后面是倒计时更新),所以错误数只在 retry <= 1 时 +1;llm/retry-started 只把状态拉回“思考中”,不重复计数。
client 半只用席位给的 useProjection('live') 读取,注册一个 tab 类型(id: "dsh-live", kind: "live")与两个 slot 条目(body + title),不手挂 DOM,样式只用 --dsw-alias-* token。
投影字段
turn、step、phase(idle|llm|tool|waiting)、turnStartedAt、stepStartedAt、tool({callId,name,startedAt})、pending({id,title,since}[])、jobs({jobId,label,startedAt}[])、tokensIn、tokensOut、errors、lastError、recent({name,ok,ms}[],最多 5 条)。
已知限制
- 只有 token 数,没有货币金额:DSH 的 usage 里没有价格字段,别在这里编一个。
- 面板是「本会话」维度,不跨会话汇总。
- 工具结果按
data.message.toolCallId(回落data.message.source.callId)与tool/call配对;配不上的结果不改面板(不会把别人的结果算进「刚完成的」)。 - 后台任务来自
job/registered/job/settled,只覆盖走该事件的任务。
开发与自测
四份 node 测试,全部不需要浏览器:
npm test # 前三份:host 折叠 / client 模块结构 / 迷你 React 渲染
npm i # 下面这份需要 react + jsdom(devDependencies)
npm run test:react # 真实 React 19 + jsdom 挂载、1 秒自刷
test/host-fold.mjs:host 折叠语义 + schema 拒收 + 同引用抑制(用真实事件载荷形状)test/client-module.mjs:client 模块结构(loader id / inject / 4 个注册 / guide 字段)test/component-render.mjs:用迷你 React 渲染 4 种面板状态与标题 chiptest/react-dom.mjs:真实 ReactDOM 挂载到 jsdom 并断言 1 秒自刷
另外两步验证过真实运行时:
- 真事件流回放:把一段真实会话日志(
session.v4.jsonl.zstd,多帧,用node:zlib的zstdDecompressSync逐帧解)逐条喂给apply,1436 条事件被接受、1186 条原样返回,四种 phase 都出现,最后wire.view与 checkpoint 还原后 deepEqual。 - 真插桩启动:把本包装进一个 headless profile 后启动官方运行时,host 半随插件图一起挂载(
ctx.sessionProjections.register通过注册表校验),启动一路走到 LLM 凭据检查才停。
许可
MIT
No comments yet. Be the first to write one.