DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Contract Check — DeepSeek Harness 插件(DSH Plugin)
← Plugins
C

dsh-contract-check

Contract Check

DeepSeek Harness 插件的契约检查:静态验证已注册工具的 output.render() 返回 ContentBlock[],并在会话事件类型超出内核词汇表时发出警告。仅警告。契约体检插件(不检测恶意)。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:imtokenxinluo/dsh-contract-check#da00daa063f022d7ccf61d2148bb7cc15ccb9d17
README兼容性版本

兼容性与来源证明

Contract Check 以 dsh-contract-check 发布,当前版本为 0.1.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/9/19

版本

0.1.0stable
2026/9/19

相关插件

正在加载相关插件…

最新版
0.1.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/9/19
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录

相关插件

继续浏览 developer-tools 分类下经过校验的插件。

Web App@deepseek-ai/dsh-web-appdsh 浏览器界面捆绑包:位于 dsh-base 之上的 Web 补丁层,加上运行时粘合插件(提供前端 dist、Web 界面提示符、bash 运行时变量和 URL 行)Sdk Minimal@deepseek-ai/dsh-sdk-minimal独立的最小 SDK 配置包:JSON-RPC、一个 DeepSeek 适配器、持久化 Shell 和 JSONL 会话Sdk App@deepseek-ai/dsh-sdk-appdsh SDK 配置包:基于 dsh-base 提供 stdio JSON-RPC 服务和进程生命周期管理Subagent Codex@deepseek-ai/dsh-subagent-codex基于官方 app-server 协议的一次性 Codex 子代理提供程序

README

dsh-contract-check

它检查「契约合规」,不检测恶意,也不拦截执行。

本工具回答一个问题:当前装着的插件,有没有在违反 DSH 的内核契约—— 写坏会话历史的那个雷,现在是不是还埋着。

  • ✅ 它能发现:render() 返回字符串(会让整条会话历史无法加载)、插件写入词汇表外的会话事件类型
  • ❌ 它不能发现:蓄意投毒、数据窃取、后门。进程内的检查器和恶意插件权限相同,挡不住对方
  • ❌ 它不阻止任何操作:只告警(这是刻意的设计选择)。发现违规后由你决定怎么办

如果你要的是"防投毒",这个工具帮不了你;如果答案是"是"的那类事故——它真能挡住。


与社区同类工具的差异(先说清楚,别重复造轮子)

DSH 社区已有成熟的插件审计工具,本工具不是首创。诚实的定位如下:

omdsh-dev/dsh-plugin-check本工具
扫描对象插件仓库目录(发布前)运行中的工具注册表(安装后)
检查内容清单协议 / patch 格式 / 构建陷阱 / hub 收录 / 生态合规,33 项只两条:render 契约、会话事件类型词汇表
覆盖范围更广(打包、命名、patch、hub 状态)更窄(但更深)
压缩/无源码的插件看不到(扫源码树)照样看得见(读的是注册表里的定义)
GUI 状态徽章❌✅
token 成本⚠️ 注册了模型工具 plugin_check(每请求付 schema 税)0

同组织还有:dsh-session-health(会话文件帧级健康)、dsh-security-audit(凭据/暴露面)。

因此:

  • 发布前查打包/清单/命名 → 用 dsh-plugin-check(更全)。 本仓库的 package 子命令只是轻量版(只查"声明要用的文件会不会被漏发"这一类), 够用就好,不够请用他们那个。
  • 装进来之后查"有没有在埋雷" → 用本工具:它是唯一一个看实时注册表、 盯让会话打不开的那两个契约、并把状态做成零 token 的 GUI 角标的。

两者互补而非竞争:他们管"发出去之前对不对",本工具管"装进来之后有没有在埋雷"。


硬约束:token 中立

本插件不注册模型工具、不注入提示词、不产生模型可见输出。

它唯一的对外输出是 ctx.logger.warn(...)——只进控制台/日志,不进模型上下文。 所以它对你每轮对话的 token 消耗零影响。

这条是硬约束,不是巧合:一个模型工具的定义(名字+描述+参数 schema)会进入每一次请求的 上下文,几十轮对话就是上万 token,不管你用没用它。任何"要让 agent 知道"的功能都要付这笔税, 因此本插件永远不加模型工具。

GUI 状态页/徽章不受此约束:它是浏览器侧代码,不进模型上下文,所以 token 成本仍然是零。


GUI 状态标识(绿/黄/灰)

装进 profile 并重启后,会话头部会多出一个小按钮:一个颜色圆点 + 「体检」二字。 鼠标移上去展开完整状态条:

🟢 无耗 · 检查 21 个插件 · 违规 0 · 不可判定 1
  • 无耗 放在最前:这个检查不花一个 token(浏览器侧代码不进模型上下文)。
  • 违规 0 与 不可判定 1 即使为 0 也照常显示 —— 数字消失会被误读成"这一项没查"。

界面上就只有这一个按钮,没有 Tab、没有面板。理由很直接: 体检是后台该做的事,不该要求用户主动去翻页签。检查由插件自己跑 (加载时 + 工具注册表变化时),按钮只是"想立刻确认一下"的入口。

点击后的反馈(0.5 秒):圆点变成转圈、文字变「体检中…」、按钮禁用防连点。 即使体检只花几十毫秒,动画也会转完这半秒——否则就是"点一下闪一下",比不动还难看。

另外设置页会多出一栏「无耗检查」,里面是自愿打赏(装了 dsh-tip-jar 才显示)。 打赏放在设置页而不是日常视野里,是因为它跟体检本身没关系,不该天天占地方。

颜色含义
🟢 绿体检过,0 违规,且确实验证到了东西 —— 健康
🟡 黄体检过,有违规 —— 有插件在写坏会话的风险
⚪ 灰尚未体检 / 无法枚举 / 全部判不准 —— 诚实显示"不知道",绝不假装健康

不可判定(unknown)不影响颜色:那是"判不准",不是"有问题"。 把它算成风险会让你天天看到黄色,然后就再也不看这个颜色了。

但有一个例外(实机教训):如果 21 个插件全部判不准,那不是"健康",是"什么都没验" —— 这时显示灰,状态条会写明"全部判不准,等于没有验证"。 绿色必须意味着"真验过了"。

准确率靠什么:静态 + 实测

手段做法局限
静态读 render 源码,看 return 的是不是数组对 defineTool 写的工具永远失效(见下)
实测按 output.schema 造假数据,真调一次 render,看返回值假数据不合形状时会抛异常 → 归 unknown

内核的 defineTool 会把作者的 render 包一层:

// 我们读到的是这个壳 —— 函数体只是一句"调用别人",静态永远看不透
render(args, value) { return userRender(args, value); }

所以静态检查对绝大多数正经插件都只能报 unknown(实机实测:21/21 全 unknown = 等于没检查)。 实测是必需品,不是可选项,因此默认开启(probeRender: false 可关)。

实测的边界(重要):调用失败绝不等于违规 —— 假数据不合形状会让 render 抛异常, 那是检查器自己的问题,所以一律归 unknown,绝不误报成 violation。

实机效果:21 个插件 → 实测判定 20,剩 1 个如实说"判不准"。

实现方式(刻意从简):

  • 宿主侧用 ctx.webServer 注册两条同源 HTTP 路由:
    • GET /contract-check/status —— 最近一次体检结果(JSON,含 diag 诊断)
    • POST /contract-check/audit —— 立刻体检一次
  • 客户端(src/client.js → lib/client.js)只做两件事:fetch 上面两条路由 + 渲染
    • 不走 typert/remote RPC:同源 fetch 就够,少一层协议
    • 体检进行中重复点击返回 409(枚举注册表是 CPU 活,不排队堆叠)
    • 体检失败不清空上一次结果(否则界面会从"有风险"跳回"未知",丢掉信息)
  • 服务依赖一律用 ctx.inject([...], cb) 延迟注入: tools 与 webServer 都可能在 apply 之后才就绪(实机踩到过两次: 直接读 ctx.tools / ctx.webServer 会静默失败,界面只能是灰点)。 /status 的 diag 字段会告诉你注入是否触发、用了哪个访问器、view() 返回什么形态。

⚠️ 客户端产物需要构建:npm run build(用本机已有的 esbuild,不联网安装)。 构建产物 lib/client.js 必须存在,否则 DSH 启动会报 "client bundles not found"。 files 白名单已含 lib,避免打包时漏发(这正是本工具自己检查的那类错误)。


背景:为什么这值得一个插件

DSH 的写路径宽松、读路径严格,而失败模式是整条会话而不是"那一条记录":

契约违反的后果真实事故
output.render() 必须返回 ContentBlock[]加载器报 SessionPersistenceCorruptionError,整条历史打不开2026-09:dsh-ssh-ops 的 sftp_*/tunnel_* 返回裸字符串,多个会话失联
会话事件类型必须在 KNOWN_SESSION_EVENT_TYPES 内加载器拒绝解释整条日志("unknown to this harness and not marked ignorable")社区多次报告(dsh-agent-teams 等)

第二条有个容易忽略的硬事实(本工具核验过内核源码):

  • 加载器判定只有一行:if (KNOWN_SESSION_EVENT_TYPES.has(event.type) || event.ignorable === true) continue;
  • 词汇表在 0.1.0-rc.6 上共 44 类
  • Session.append(type, data, ...opts) 的 opts 只接受 sourceEventSeqs / surfaceOp —— 插件没有设置 ignorable 的入口;而且整个内核里不存在任何 ignorable: true 的赋值

结论:插件写一个自定义事件名 = 使用者的会话打不开,且作者无法自救。


两种用法

1) 装进 profile(进程内体检)

把包加进 profile 依赖与 bundle 列表:

// profiles/<profile>/package.json
{
  "dependencies": { "dsh-contract-check": "^0.1.0" },
  "dsh": { "profile": { "bundles": ["...", "dsh-contract-check"] } }
}

本地开发可以写成 "dsh-contract-check": "link:D:/tool/claude_code/dsh-contract-check" ——改代码即时生效,但改完客户端要重跑 npm run build 再重启 DSH。

它会做三件事:

  • 加载时:枚举 ctx.tools.view() 里可见的工具定义,逐个静态检查 output.render
  • 运行期:订阅 session/event,发现表外事件类型立刻告警
  • GUI:注册上面那两条状态路由,供头部那个「体检」按钮读取(界面只有它一个元素)

插件注册顺序不可控,所以本插件也会监听 tools/change:先加载时注册表还是空的就保持安静, 等注册表变化后再体检(同一问题只告警一次,不刷屏)。

2) 离线检查(不需要 DSH)

dsh-contract-check scan <文件或目录>     # 递归扫源码里的 render 定义
dsh-contract-check package <包目录>      # 检查 npm 打包会不会漏发声明要用的文件
dsh-contract-check vocab                 # 打印内嵌词汇表版本与条目数

package 检查的是"整树起不来"那类事故:package.json 声明要用的文件 (dsh.bundle.patch / exports 目标 / types)有没有被 files 白名单漏在包外。

⚠️ 这是轻量版:只覆盖"声明要用的文件会不会被漏发"这一小类。 完整的插件仓库体检(清单协议 / patch 格式 / 构建陷阱 / 命名 / hub 收录,共 33 项) 请用社区成熟的 omdsh-dev/dsh-plugin-check。 本工具的重点始终是运行中的注册表与会让会话打不开的契约,不是发布前的仓库检查。

它是按 npm 的真实语义判定的(用一次性实验实测,不是猜):

npm 行为文件
总是包含(不会漏)package.json、README*、LICENSE*、CHANGELOG*、main 指向的文件、bin 指向的文件
不自动包含(必须在 files 里)exports 的目标、types、任何自定义字段——含 dsh.bundle.patch
files 无此字段时默认全打包,无遗漏风险

它不运行 npm、不执行被检查包的脚本——npm pack 会跑 prepack/prepare, 对不受信任的包是危险的,所以白名单判定是本工具自己实现的。

发现 violation 时退出码为 1,便于串进脚本或 CI。


三态判定:为什么会出现 unknown

判定含义
ok返回值形态正确(静态看出是数组,或实测返回数组)
violation返回字符串 / 非数组 / undefined(静态命中,或实测证实)
unknown判不准(静态看不透 + 实测也失败)——如实说不知道

宁 unknown,不 violation 是这个工具的硬原则:误报会让人卸载好插件、并从此不信任检查器。 所以:实测调用抛异常一律归 unknown(那是假数据不合形状,是检查器自己的问题); 全不可判定时状态显示灰而不是绿(什么都没验 ≠ 健康)。

几个由此得来的实现细节(都有测试锁定):

  • 链式表达式由链尾决定类型,但只在括号深度 0 处认标记: [{ text: lines.join("\n") }] 是数组(.join 在数组内部),而 [...].join("") 是字符串
  • 剔除嵌套函数体后再收集 return:render 内部的箭头函数/局部函数里的 return "x" 不属于 render 的返回值
  • 只认属于函数自己的箭头:render(a, v) { ...map((i) => ...) } 是方法简写, 不能因为体内有 => 就当成箭头函数
  • 实测按 output.schema 造形状合法的假数据(含 required 字段、数组、嵌套对象,有深度上限); 自引用的病态 schema 不会卡死

实测证据(零误报)

npm run verify:real 对真实已装代码跑一遍:

真实代码合计:render=52,误报 violation=0
合成样本:violation=2(应 > 0,证明检测有效)
✓ 通过:真实代码零误报,且对已知违规有效
  • 覆盖 19 个内核工具包(内核自己的工具必然合规 → 任何 violation 都是本工具的误报)
  • 覆盖真实第三方插件:dsh-ssh-ops@0.2.1(15 个 render,0 violation——该版本已修好)。 作为对照:修复前的同款代码会返回裸字符串,正是本项目要防的那类事故

打包检查的验证:抓出真实历史 bug

把本仓库要防的那次事故原样复现(dsh-session-doctor 修复前的提交 896f6da^):

$ dsh-contract-check package <修复前的包>
VIOLATION  ...(1 项会被漏发)
  · cordis.patch.yml
      在 files 白名单之外,npm 不会打包它:dsh.bundle.patch(漏发 = 插件树拒启)

——这正是当初让整个插件树拒绝启动的那个 bug。8 个真实已装包中 7 个判 ok (零误报),唯一命中的 @opendsh/dsh-plugin-scheduled-tasks 是真实瑕疵: 它用 "./src/*": "./src/*" 导出子路径,但 src 不在 files 里 → 安装后该路径 import 会失败。

  • 另配合成"旧式违规"样本,确认检测确实有效(不是什么都判 ok)

词汇表漂移检查:npm run verify:vocab → 内嵌 44 条 vs 内核 44 条,一致。


局限(如实)

  1. 静态分析有盲区:运行时拼出来的返回值、深度混淆的代码 → 判为 unknown,不是"没问题"
  2. 不检测恶意:一个满足全部契约的插件照样能偷数据。这需要进程外的隔离与权限手段,不是本工具的职责
  3. 不拦截:发现违规只告警。要"拦住"需要 tools.guard() 注册执行前守卫——那会影响正常工作, 是另一个决策(见下方"设计取舍")
  4. 词汇表有版本性:内嵌表来自 @deepseek-ai/dsh-session@0.1.0-rc.6。 升级 DSH 后请跑 npm run verify:vocab,不一致就 npm run gen:vocab 重新生成并复核
  5. 实测 render(默认开启,可关):真调用一次 render() 能大幅提高判定率 (实机:静态 21/21 判不准 → 加了实测后 20 个能判定)。 代价是它会真的执行一次插件代码;假数据不合形状时一律归 unknown,绝不误报成违规。 要关:probeRender: false
  6. 未覆盖的契约:以下两类不在本工具能力范围——
    • 打包白名单(files 是否漏了 dsh.bundle.patch 指向的文件):那是「包在磁盘上的属性」, 用 npm pack --dry-run 核对,注册表里看不到
    • 是否杀宿主进程 / 自杀式重启:那是运行时行为,不是工具定义的静态属性
  7. 实机状态(2026-09-19):已在真实 DSH 中挂载运行——web profile 以 link: 接入后, 会话头部出现状态按钮,实机读数 无耗 · 检查 21 个插件 · 违规 0 · 不可判定 1 (其中实测判定 20)。两点如实说明:
    • 黄灯路径已实机走通(2026-09-20):临时装入一个故意违规的测试夹具后, 状态变为 无耗 · 检查 23 个插件 · 违规 2 · 不可判定 1,且点名准确。 夹具验证完即移除。顺带验证了实测探针的价值:一个静态判不准的 render, 被实测抓成了真实违规 —— 静态看不透的,跑一次就清楚。
    • 首次体检的日志行未经核对(DSH 输出在启动它的控制台窗口,未落盘); tools/change 的作用域是否到达本插件也未验证,因此插件额外做了兜底: 首次见到会话流量时补做一次体检。

设计取舍

  • 为什么只告警不拦截:拦截意味着误报会直接阻断你的正常工作。本工具的首要目标是"零误报", 而零误报是动态目标;在两者冲突时选择不干预。要拦可以后续加一个显式开关。
  • 为什么不做成"防投毒":进程内的检查器与恶意代码同权限,对抗不了;把它宣传成安全工具 会给出虚假的安心,比不做更糟。
  • 为什么合并进这个包而不是并进 session-doctor:两者的信任域不同——doctor 处理"你自己的会话", 本工具审"别人写的包"。分开更清楚。

测试

npm run build         # 构建客户端产物 lib/client.js(改了 src/client.js 后必须先跑)
npm test              # 168 个用例:判定三态 / 实测 / 审计 / 词汇表 / 插件壳 / 源码扫描 / 打包检查 / 状态与路由 / 客户端产物 / CLI
npm run verify:real   # 真实代码零误报验收
npm run verify:vocab  # 词汇表漂移检查

npm test 会校验产物里盖的源码指纹:改了 src/client.js 却忘了 npm run build 会直接测试失败(否则用户拿到的是旧界面)。

License

MIT