🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

会话健康度 Elari39/dsh-session-insight

DeepSeek Harness
Elari3931 天前有提交已提供中文文档扫描:未发现敏感能力

在输入框上方查看步数、失败数与重复调用率

29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/4 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Elari39/dsh-session-insight
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · chat
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 31 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🟢 扫描:未发现敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。

扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-session-projection
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 查看步数与工具调用
  • 统计失败数与重复调用
  • 展示失败率

只读会话健康条,给 DeepSeek Harness 看步数、工具调用、失败、重复和失败率,以 sessionInsight 投影加 conversation.input.dock 卡片交付。MIT。

README

由 DeepSeek 最新模型翻译生成
dsh-session-insight 一个 DSH 练手插件:走通 sessionProjections 投影 → wire.view → 客户端 useProjection → slot 卡片 这条完整链路。 功能本身刻意做小:在输入框上方显示一条只读的会话健康度状态条 —— 步数、工具调用数、失败数、重复调用数、失败率。它不重复官方 sessionStats(那个回答「多久/多少次」),而是回答「这个会话健康吗」。 安装 本包是一个 dsh bundle(package.json 里的 dsh.bundle.patch 指向 cordis.patch.yml), 从 GitHub 直接安装即可,不需要 npm,也不需要任何构建步骤 —— 仓库里的源码就是最终产物(index.js 是 host 半,client.js 是预先构建好的浏览器半)。
展开完整文档(README 剩余 1 节)
dsh-session-insight

一个 DSH 练手插件:走通 sessionProjections 投影 → wire.view → 客户端 useProjection → slot 卡片 这条完整链路。

功能本身刻意做小:在输入框上方显示一条只读的会话健康度状态条 —— 步数、工具调用数、失败数、重复调用数、失败率。它不重复官方 sessionStats(那个回答「多久/多少次」),而是回答「这个会话健康吗」。

安装

本包是一个 dsh bundle(package.json 里的 dsh.bundle.patch 指向 cordis.patch.yml), 从 GitHub 直接安装即可,不需要 npm,也不需要任何构建步骤 —— 仓库里的源码就是最终产物(index.js 是 host 半,client.js 是预先构建好的浏览器半)。

用 Desktop 自带的 CLI 绝对路径;PATH 上的旧 dsh 管不了新 profile $dsh = "D:\Downloads\software\DSH\resources\runtime\cli\bin\dsh.cmd"

1) 装进目标 profile(pnpm 会克隆仓库,并自动装好 zod 与 peer 依赖) & $dsh plugin --profile web add github:Elari39/dsh-session-insight

2) 让 profile 真的加载它:编辑 ~/.dsh/profiles/web/package.json,在 dsh.profile.bundles 数组里加上 "dsh-session-insight"

生产环境建议钉到固定版本(tag 对应的 tarball 内容不会变):

& $dsh plugin --profile web add https://github.com/Elari39/dsh-session-insight/archive/refs/tags/v0.1.0.tar.gz

这条路径已实测验证:pnpm add github:Elari39/dsh-session-insight 之后,
import('dsh-session-insight') 正常返回 apply, inject, name 三个导出(inject 没有被
unwrapExports 折叠丢掉),zod@4.6.5 经 pnpm 虚拟 store 正确解析,peer 依赖自动落到 @deepseek-ai/cordis@4.0.4 与 @deepseek-ai/dsh-session-projection@0.2.0-rc.2。

npm 渠道目前未发布。若以后要发,publishConfig 已配好,执行 npm publish --otp= 即可
—— 注意 npm 现已强制要求 2FA 才能建包/发包。

Desktop profile(Electron 独占,不能用 dsh plugin)

dsh plugin 会拒绝 desktop profile(profile "desktop" is managed exclusively by the Electron application),只能直接改 patch 文件:

pwsh -File .\install.ps1 # 幂等,可重复跑 pwsh -File .\install.ps1 -Uninstall # 卸载

⚠️ 任何插件操作前都要完全退出 Electron 应用。

装完确认

重启后输入框上方应出现健康度状态条;也可以直接向运行中的 host 请求浏览器半的 bundle: /plugins/??dsh-session-insight/client.js&rev=<rev> 返回 200 才算加载成功, 404 意味着 host 半没挂上(最常见原因是 loader 行指向了目录而不是 index.js)。

文件

| 文件 | 作用 |
| index.js | Host 半:向 ctx.sessionProjections 注册 sessionInsight 投影单元 |
| client.js | Client 半:向 conversation.input.dock 注册一张只读卡片 |
| package.json | dsh.bundle.patch + dsh.client 双声明 |
| cordis.patch.yml | 把插件插进 profile 的 patch 行 |

一、投影单元契约(全部从已安装包源码核实)

ctx.sessionProjections.register({
key: 'sessionInsight',
stateVersion: 1, // 必须是非负安全整数;同 key 不同版本会被拒绝 stateSchema, // zod(不是 schemastery!)
init: (header, inheritedEventCount) => state,
apply: (state, event) => state, // 必须同步;不关心的事件必须返回同一个引用
wire: { viewSchema, view }, // zod;省略 wire 则该单元仅 host 可见
})

来源:@deepseek-ai/dsh-session-projection/README.md(§Define a projection unit、§Register and read)、 dsh-session-stats/lib/index.js、dsh-tool-todo/lib/index.js。

两个 schema 是 zod,不是 schemastery —— 这是最容易搞错的一点。首方包确实同时用两者: Config 用 @deepseek-ai/schemastery,而 stateSchema/viewSchema 用 zod (dsh-session-stats/lib/index.js:1 是 import { z } from "zod", dsh-tool-todo/lib/index.js:1-2 两个都 import 了)。

注册表真实的校验(dsh-session-projection/lib/index.js:68-101):只检查 stateVersion 是非负安全整数、以及同 key 不能跨版本共享。所以版本号写错是最容易踩的运行时错误。

引用相等是契约,不是优化

注册表有两道 Object.is 闸门:

1. apply 返回同一个 state 引用 → 直接跳过 wire.view 计算;
2. wire.view 返回同一个引用 → 抑制发布。

所以本插件把算好的 view 缓存在 state 里(view 字段),只在可见计数器变化时才重算。
否则每次 tool/result 成功、assistant/message 记账这类内部状态变化都会向客户端发布一次它看不见的变化。dsh-session-stats 的 view 每次返回新对象,是接受这种多余发布的写法;本插件做得更严格。

二、折叠的事件(每个字段都核实过,没有猜)

| 事件 | 用到的字段 | 来源 |
| assistant/message | data.message.content[] 里 {type:'tool-call', id, name, arguments} | dsh-llm/lib/types/assembler.js:100-105 |
| tool/call | data.callId | dsh-session/lib/index.js:830、types/invariant.js:87-89 |
| tool/result | data.message.source.callId、data.message.isError | types/invariant.js:101-105、lib/index.js:862 |
| step/end | data.turn | dsh-session-stats/lib/index.js(注释说明它才是 step 生命周期的权威) |
| turn/end | — | 同上;首方也在这里清 pendingCalls |

为什么工具名要从 assistant/message 取:tool/call 事件只带 callId,工具名和参数在 assistant 消息的 tool-call 块上。所以用 callId 把两者配对。
这也顺带解释了为什么重复调用检测能做:签名 = name + "\0" + arguments。

arguments 是原始 JSON 字符串(assembler.js:104),不是解析后的对象 —— 直接拿来做签名最稳。

失败率怎么算

失败率 = 失败的 tool/result 数 ÷ 派发的 tool/call 事件数

两个已知口径边界,都是刻意的:

- 分母是「已派发」的调用。 被 ctx.tools.guard() 拦下、根本没派发的调用不产生 tool/call,所以它既不进分子也不进分母 —— 这个数字反映的是「已派发调用的失败率」,不是「模型意图调用的失败率」。
- 重复调用是「连续」判定,不是累计:read a.txt 中间夹了别的调用再回来不算重复(中间做过别的事,说明不是在死循环)。

显示的精度策略

投影里的 failureRate 保留三位小数,但展示层(client 半)另有一套策略,因为百分比会隐藏自己的分母 —— 8.3% 无论是 1/12 还是 1000/12000 读起来都一样,而会话早期比率摆动极大,一个小数位会诱发过度反应。

| 样本量 | 显示 | 例子 |
| toolCalls === 0 | — | 还没跑过,不假装是 0% |
| ≥ 10 | 一位小数 | 17/198 → 8.6%(旧版整数会显示 9%) |
| < 10 | 原始分数 | 1/3、0/5、2/7 |

小样本用原始分数的好处:精确(不做统计推断)、自带样本量(分母就是证据)、且能让等比的分数保持可区分 —— 1/3 和 1/6 不会都塌缩成 33.3%。

曾考虑过用 Wilson 上界(<43.5%)表示小样本的不确定性,但它需要统计推断、且极端样本会饱和到 100% 而失去信息量。原始分数更简单也更诚实,故改为此方案。

这套策略只改 client 半,failureRate 的投影契约没变,所以不需要 bump stateVersion。

三、Client 半:useProjection 是怎么来的

这是整条链路里最难查、也最容易卡住的一环。useProjection 不是 import 来的,它是 renderer 动态合成后通过 props 发给组件的:

dsh-client-ui-session/lib/client.js:127
keyedHooks: { projection: (key) => binding.session.projections.faceOf(key) }

dsh-client-ui-renderer/lib/client.js:648-654
每个声明的 hook 源 → standardHookPropName(name) 变成 prop ("projection" → "useProjection")

dsh-client-ui-renderer/lib/client.js:703-746, 763-769
standardKit() 把 standard 摊进 kit,ContextualEntry 渲染时 {...kit} 传给组件

关键前提:只有注册进 scope: "session" 的 slot,组件才会拿到 useProjection。 conversation.input.dock 声明为 { kind: 'list', scope: 'session' } (dsh-client-ui-conversation/lib/client.js:18167-18170),所以可用。
官方 goal dock 就是这么用的(dsh-client-ui-goal/lib/client.js:372-373)。
组件收到的相关 props:

function Card({ useProjection, t }) {
const insight = useProjection('sessionInsight'); // 第二参可选 selector }

t 只在注册时声明了 locale 才会注入;本插件声明了 locale: 'sessionInsight',并通过 ctx.locale.register(ns, { zh, en }) 提供字典(字典是扁平的 { key: string })。

一条安全约束

client 半只 require('react')。 官方明确禁止第三方 UI 插件 import 任何 @deepseek-ai/dsh-client-ui-(首方自己可以,goal 就 require 了 primitives)——因为一个抛错的组件会让整块 slot entry 崩掉(slot entry crashed in '')。本插件的 InsightCard 在 useProjection 不可用时返回 null 而不是抛错,就是为了让 slot 作用域万一变化时降级为不显示,而不是拖垮整个 slot。

四、验证结果

不是「应该能跑」,是实际跑过的:

| 验证 | 方法 | 结果 |
| 端到端(真实运行中) | 从正在运行的 Web 服务请求 /plugins/??dsh-session-insight/client.js&rev=<算出的 rev> | 200 + 真实 bundle |
| 界面渲染(真实运行中) | 用户在 desktop profile 的 GUI 里实际看到状态条 | ✅ 「留意 步数 150 工具 176 失败 16 重复 2 失败率 9%」 |
| Host fold 逻辑 | 真实 zod 4.6.5 跑 11 组事件序列,每步校验 stateSchema + viewSchema | 24 项断言通过 |
| 引用闸门契约 | 断言内部状态变化时 state.view 引用不变、可见变化时变 | 通过 |
| Client 半 + 失败率格式 | 打桩 window.__ModuleLoader__ + require,真实执行 client.js 并渲染;formatRate 按渲染输出断言(含真实数字、分数/百分比切换、阈值 10 边界、零调用) | 42 项断言通过 |
| 模块加载 | 从插件目录 import('./index.js'),真实解析 zod | 通过 |
| unwrapExports 陷阱 | 复现 cordis-plugin-loader 的折叠逻辑 | inject 未丢,无 export default |
| 目录 vs 文件 specifier | 实测 import() 目录失败、文件成功,并模拟 nearestPackage 发现链 | 目录 FAIL、文件 OK |
| 真实加载器 | Desktop CLI --dump-config(改动前后回归对比) | 两者都 exit=0、零错误 |
| 对齐宽度 | 代入官方 token 化简,与 TodoPanel 的有效宽度逐字比对 | 一致:min(100% - 64px, 920px) |

⚠️ 注意 --dump-config 那行:它通过不代表插件能用。目录 specifier 的错误版本同样 exit=0、零报错,但浏览器半永远拿不到。真正的判据是上面第一行(bundle 200)与第二行(界面可见)。

复现验证(下面这些脚本在作者的开发工作区里,不在本仓库内,路径按需替换):

node <开发工作区>\_verify_insight\test.mjs
node <开发工作区>\_verify_insight\test-client.mjs
node <开发工作区>\_probe_bundle.mjs # 对运行中的 host 探测 bundle
node <开发工作区>\_probe_discovery.mjs # 模拟 host 的 client 发现链

五、怎么装上跑起来

装进 desktop profile(本机实际采用的方式)

desktop profile 由 Electron 独占,dsh plugin 会直接拒绝:

error: profile "desktop" is managed exclusively by the Electron application

所以只能直接改 patch 文件。用附带脚本(幂等,可重复跑):

pwsh -File F:\WorkSpace\Coding\DSH_plug\demo01\dsh-session-insight\install.ps1
卸载: pwsh -File F:\WorkSpace\Coding\DSH_plug\demo01\dsh-session-insight\install.ps1 -Uninstall

它会往 C:\Users\Elaina\.dsh\profiles\desktop\cordis.patch.yml 追加:

- insert:
- id: session-insight
name: 'file:///F:/WorkSpace/Coding/DSH_plug/demo01/dsh-session-insight/index.js'

⚠️ 指向文件,不要指向目录

name: 必须指向入口文件 index.js。写成包目录会静默失败,且症状极具误导性:

file:///.../dsh-session-insight ← ❌ 目录 → Node ESM: ERR_UNSUPPORTED_DIR_IMPORT → host 半没有 fiber → dsh-client-modules 只扫描「拥有 fiber 的 Loader 行」(lib/index.js:836) → 浏览器半永不被发现 → /plugins/<id>/client.js 返回 404 → 界面上什么都不显示,但配置校验(--dump-config)完全通过

改成 file:///.../dsh-session-insight/index.js 后,locatePkgJson 的路径分支(lib/index.js:748-770)会用 nearestPackage 从文件所在目录向上找到 package.json,读出 dsh.client 与 exports["./client"],浏览器半才被服务。

注:~/.dsh/desktop-overlay/cordis.yml 里官方那个覆盖层插件指向的也是文件(index.mjs),这是同一个道理。

其它装法(开发靶场)

$dsh = "D:\Downloads\software\DSH\resources\runtime\cli\bin\dsh.cmd" # 必须绝对路径

一次性覆盖层,不落盘 & $dsh --profile web --patch "F:\WorkSpace\Coding\DSH_plug\demo01\_overlay-insight.yml"

⚠️ 做插件操作前要完全退出 Electron 应用。

重启后确认真的加载了

不看界面也能验证 —— 直接问正在运行的 Web 服务要 bundle:

node F:\WorkSpace\Coding\DSH_plug\demo01\_probe_bundle.mjs

它按 host 的算法(sha1 over 文件 stat)算出 rev 并请求 /plugins/??<id>/client.js&rev=<rev>,返回 200 才算加载成功。
(脚本里带一个确定已加载的对照包 dsh-plugin:它 200 而你的 404,说明 URL 格式对、插件没加载。)

关于 node_modules/zod

本目录里预置了一份 zod 4.6.5(从 Desktop 运行时 asar 内提取),这样本地路径插件开箱即可解析 import { z } from 'zod',不需要联网安装。
正式发布时应删掉它,让 dependencies 里的 zod: ^4.4.3 由包管理器安装。

六、踩坑记录

| 坑 | 说明 |
| Loader 行必须指向文件,不能指向目录 | file: 目录 → ERR_UNSUPPORTED_DIR_IMPORT → host 半无 fiber → client 半不被扫描 → 404 且无任何报错。详见第五节 |
| dock 里的条目要自己居中 | conversation.input.dock 是整条 composer 栈的全宽容器,裸 div 会贴左边。必须用 composer token 算宽度 + margin: 0 auto,见第八节 |
| 投影 schema 用 zod | Config 才是 schemastery。写错的话 register() 不报错,直到第一次 viewSchema.parse 才炸 |
| stateVersion 必须是非负安全整数 | 同 key 跨版本注册会抛 refusing to share it with stateVersion |
| apply 必须同步且返回同引用 | 异步 view 会返回 Promise,被 viewSchema.parse 拒绝;不同引用会让下游每次都重算 |
| stateSchema 要包含 view 字段 | 从检查点恢复走 stateSchema.parse(row.val)(lib/index.js:297)。若 view 缓存在 state 里却没写进 schema,.strict() 会拒绝恢复 |
| useProjection 只在 session 作用域 slot 可用 | root 作用域 slot 拿不到它,会静默 undefined |
| client 半不能 import @deepseek-ai/dsh-client-ui- | 一个抛错组件会崩掉整块 slot entry |
| 函数插件不能有 export default | Loader 的 unwrapExports 会折叠模块并丢掉 inject,且无任何警告 |
| tool/call 事件不带工具名 | 必须从 assistant/message 的 tool-call 块按 callId 配对 |

七、对齐:让条目和输入框一样宽

conversation.input.dock 渲染在 composerStack 里(dsh-client-ui-conversation/lib/client.js:16309),而 composerStack 的 CSS 只有 flex-direction: column(.Dc7zOa_composerStack)—— 它是全宽的。所以一个只设了 padding 的 div 会贴在左边,而不是对齐输入框。

官方同 slot 的 todo 面板(TodoPanel root,.aSus8q_root)是靠三件事对齐的:

box-sizing: border-box;
flex: none;
width: calc(100% - 2×var(--dsh-composer-side-clearance) - 4×var(--dsh-composer-dock-inset));
max-width: calc(var(--dsh-composer-card-max-width) - 4×var(--dsh-composer-dock-inset));
margin: 0 auto; /* ← 居中 */

代入本机实际值(全部在官方 CSS 里有定义):

| 变量 | 值 |
| --dsh-chat-content-width | min(100% - 32px, 920px) |
| --dsh-composer-card-max-width | calc(chat-content-width + 32px) = min(100%, 952px) |
| --dsh-composer-side-clearance | 16px |
| --dsh-composer-dock-inset | 8px |

化简后:width = 100% - 64px,max-width = min(100% - 32px, 920px) → 有效宽度 min(100% - 64px, 920px),与官方 todo 面板逐字一致。
这些 token 由 composer 作用域提供,不要硬编码 px —— 否则窗口变窄或用户改缩放时又会错位。

八、下一步可以做什么

- 加一个 invariant companion:exports["./invariant"] 可以挂一个校验本包持久化关系的独立插件(见 dsh-tool-todo/lib/invariant.js)。这是报告里确认的另一片空白。
- 把卡片做成投影驱动的更细视图:比如按工具名展开失败排行 —— 状态里已经记了 pending[callId].name,只需扩展 errorByTool 与 viewSchema,记得 bump stateVersion(改语义必须 bump)。
- 加 ctx.storageDomain 持久化:目前只有进程内状态;跨会话的失败率趋势需要落盘。
预览分享卡片长什么样 ↓Elari39/dsh-session-insight
分享 · DeepSeek Harness Hub
Elari39/dsh-session-insight

在输入框上方查看步数、失败数与重复调用率

这是你的仓库?认领条目

回到顶部