dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
给每个对话加一道行为围栏:规则存在一个 JSON 文件里,可在设置页或对话面板里编辑,作为一段固定顺序的运行时上下文注入,不重写系统提示词前缀。
给 DSH(DeepSeek Harness)的每一次对话设定边界与围栏:一套随时增删、下一轮即生效的行为约束规则。
全部规则存在一个 JSON 文件里——用任何编辑器直接改 JSON,或者直接在设置页里逐条编辑。
文件放在哪可以自己定,设置页一键「打开文件夹」就能看到它。
不破坏前缀缓存 · 不膨胀上下文 · 不影响其它对话 · 与 billion-context 压缩共存。
两层规则,叠加注入:
fence.json 里的 rules 数组)—— 对每一个对话都生效,包括新开的对话。设置页里主要就是编辑这一层。fence.json 里的 sessions..rules)—— 只叠加到某一个对话,可选,通常在会话右上角的「围栏」浮层里维护。适用形态:`dsh` CLI(web / headless / acp 等 profile) 与 DSH Desktop。
大模型每轮都会忘掉「这次对话的规矩」。常见的补丁方式都有代价:
做法 · 代价 把规矩写进系统提示词 · 内容一变,系统提示词就变 → 前缀缓存整体失效(本机 workbuddy / opencode-go 路由未声明 systemPromptUpdate: 'in-history',会触发 head 重写) 每轮追加一条 user 消息 · 纯追加安全,但永不回收,几十轮后吃掉几千 token 用压缩插件的「规则」功能 · billion-context 0.1.202 的 acp_rule 只存不注(源码里 RULES_USAGE_PROMPT 有定义零引用),plugin 模式 compress.rules 亦不生效
dialog-fence 的做法:把规则注入到 运行时上下文快照(runtime context) 这一段里。
<围栏位置>/fence.json ← 单文件:{ enabled, rules: [...], sessions: { <id>: { rules: [...] } } }
└─ systemPrompt.context({ name:'dialog-fence:rules', order:118 })
└─ 通用规则 + 启用会话的专属规则 → 拼成一段文本,全都没有就返回空串
└─ agent-loop: joinContextSections() → "Current runtime context. ..."
└─ RuntimeContextProjection.project()
├─ 文本与上次相同 → return(一条都不追加,零 token)
└─ 文本变化 → 追加一条新 user 消息(纯 append)三件事因此同时成立:
Current runtime context,被 billion-context 的 dshDurableToolPath 归为 durable/runtime-context,自动进 protectedLatestTools(只保护最新一份,旧快照可被压缩),围栏常驻且不膨胀。隔离性:通用规则为空、且当前会话没有启用专属规则时渲染空字符串,而 renderContextSections() 会 .filter(s => s.text.length > 0) —— 空串 = 零 token、零注入、零副作用。(总开关关掉时,两层一起静音。)
界面 · 位置 · 能力 设置页 · 设置左栏 → 「对话围栏」(与「通知提醒」「模型」同级) · 主入口:总开关 + 通用规则全文的可视化编辑(逐条多行编辑框,可换行)+ 新增/删除/清空 + 某个对话的专属规则(下拉选对话,默认选中当前对话;逐条编辑/新增/清空;可单独停用该对话)+ 打开文件夹 + 更改围栏文件位置 插件详情页配置区 · 左侧「插件」→ 已安装 → dsh-dialog-fence → 详情页(包描述与行之间) · 同一份配置界面 会话按钮 · 会话右上角工具区「围栏」,带规则条数徽标 · 打开本会话规则浮层 规则浮层 · 右下角面板(点击面板外部自动关闭) · 与设置页同一套四张卡片(紧凑版):总开关 + 通用规则编辑 + 任意对话的专属规则(下拉默认停在该对话) + 打开文件夹;另有顶部「关闭」 插件页标签页 · 设置 → 内置插件 → 「对话围栏」一页 · 同一份配置界面(老入口,仍在)
五处界面写的是同一个 JSON 文件:面板改完 → 文件落盘 → 下一轮装配时重新读取 → 生效。没有任何状态分叉。 会话按钮上的数字就是真实注入条数(通用 + 本对话专属),不是只数会话层。
设置页的对话下拉里,选项文字用对话名称(重命名过就用你起的名字,否则用项目目录名; 连这些都没有的空白对话显示「未命名对话 N」)——任何情况下都不会把 `session-` 显示出来。 当前对话会标注「(当前)」并默认选中,所以「给现在这个对话加一条专属规则」不需要先选。
为什么卡片在「已安装」而不是「官方」:plugins.item 槽位的语义被写死为「官方分组里的一张卡片」,改 id / order 都改不到别处;plugins.bundle.config 才属于「组合包自己的配置」,以 npm 包名 为 key 渲染在该 bundle 详情页的描述与行之间。
0.5.0 起本插件按组合包安装(profiledependencies+dsh.profile.bundles+dsh.bundle.patch三件齐备),
因此它出现在「已安装」分组,装与卸都走插件页右上角「添加插件」那套官方流程。
推荐走官方插件安装器(装在「已安装」分组里,可一键卸载)。改完必须完全退出并重启 DSH(宿主半边不走 dsh-hmr 热重载)。
包内已带 dsh.bundle.patch: ./cordis.patch.yml、exports、icon 与 dsh.client 声明,三件套齐备(dependencies + dsh.profile.bundles + bundle patch),因此官方安装器直接认它。
图形界面:左侧「插件」→ 右上角「添加插件」→ 在输入框填本包的绝对路径:
C:\Users\<你>\AppData\Roaming\dsh-desktop\harness\dialog-fence点安装。成功后卡片出现在「已安装」分组(不是「官方」分组),点进去的详情页里就有围栏配置区。 卸载同理:进该包详情页点「卸载」,卡片消失、loader 行一并移除。
命令行:
dsh plugin --profile web add "C:\Users\<你>\AppData\Roaming\dsh-desktop\harness\dialog-fence"dsh plugin add 装完后,dsh-plugin-manager 的 reconcile() 会自动把声明了 dsh.bundle 的新依赖追加进 profile 的 dsh.profile.bundles 并保存 manifest —— 不需要手工改 profiles//package.json。
⚠️ 重要:本地路径安装是「复制」,不是「链接」
DSH Desktop 的安装后端(profileBundlePackageBackend)会把整个源码目录复制到 generation 快照里,再在那儿 pnpm add:
`harness/profiles/.generations/live/dsh-dialog-fence++/node_modules/dsh-dialog-fence/
`所以改完 `harness/dialog-fence/` 里的源码,已安装的那份不会跟着变,
必须在插件页卸载再装一次(或重跑上面那条命令)。
规则数据不在这里:默认数据目录固定为 /dialog-fence/rules,与 generation 快照无关,升级换代不会丢规则(见下面「直接编辑规则文件」)。
想要「改一行就生效」的开发体验,用方式 B。
file:/// 挂载(开发用,不进「已安装」清单)# 装到默认 profile(本机 DSH Desktop 用的 profile 叫 web)
node scripts/install.mjs install
# 指定 profile / DSH_HOME;--dry-run 只查看不落盘
node scripts/install.mjs install --profile headless --home "C:\Users\你\AppData\Roaming\dsh-desktop\harness" --dry-run
# 卸载(只删插入行,规则文件保留)
node scripts/install.mjs uninstall脚本往 /profiles//cordis.patch.yml 末尾追加一段带标记的 insert 块,改写前自动备份成 cordis.patch.yml..bak:
# >>> dsh-dialog-fence (managed by scripts/install.mjs — do not edit by hand) >>>
- insert:
- id: dialog-fence
name: "file:///C:/.../dialog-fence/index.js"
# <<< dsh-dialog-fence <<<若本机没有 node(DSH Desktop 自带的是 Electron),用:$env:ELECTRON_RUN_AS_NODE="1"; & "D:\DSH Desktop\DSH Desktop.exe" scripts\install.mjs status这种方式是引用源码目录,改 client.js 刷新页面即可、改 index.js 重启即可,无需重装。 代价:file:/// 插入行不属于 profile bundle,因此
[desktop] profile inconsistency: the patch layer inserts file:///…, which is not installed——只是提示,不影响加载,同一份日志下一行就是 [dialog-fence] ready。两种方式不要同时用。同时装会有两条 loader 行重复注册槽位。
从方式 B 切到方式 A 之前,先 node scripts/install.mjs uninstall。# 1) 无宿主测试(语法 + 行为,不会碰线上规则目录)
node test/smoke.mjs && node test/route.mjs && node test/client.mjs && node test/render.mjs && node test/opensource.mjs
# 2) 重启后在任意会话里
/fence status重启并开启围栏后,下一轮的运行时上下文里会出现:
Current runtime context. This snapshot supersedes earlier runtime-context snapshots.
...(沙箱策略 / 审批策略)...
Dialog fence (user-set rules binding this conversation; a newer direct user instruction wins on conflict):
1. 你的第一条规则看到这段就说明生效了。
重启后:
Ctrl+Enter 保存 / Esc 取消。底部编辑框新建一条,另有「清空全部」,右侧实时显示 注入 N 字符 ≈ M token。顶部还有 「打开文件夹」 与 「更改位置」。下半部分还有 「某个对话的额外规则」:下拉里按名字选对话(默认已选中当前对话),下面就是那个对话的专属规则列表与新建框 —— 和浮层下半部分是同一份数据、同一套操作(0.10.0 起)。这一块是两行:第一行「对话」+ 下拉,第二行「停用/启用该对话」按钮 + N/40 条专属规则 计数(0.12.1 起,此前挤在一行会把中文压成逐字换行)。三处配置入口(渲染的是同一个组件,内容和行为完全一致):
三处都有:
.json 结尾的目录)即可把围栏文件搬过去;随后原目录留一个 location.json 指针,删掉它就回到默认位置面板与这三处配置页都只在 Web 形态出现(CLI / TUI / headless 没有 connection 服务,插件会自动跳过路由注册,/fence 命令照常可用)。
命令 · 作用 /fence · 查看状态(启用与否、条数、实际注入文本、token 估算、围栏文件路径) /fence on / /fence off · 启用 / 停用本会话的专属规则(不影响通用规则) /fence add · 追加一条本会话专属规则(自动启用;重复文本不会重复添加) /fence rm · 删除本会话第 N 条 /fence clear · 清空本会话规则(保留启用状态) /fence reset · 彻底删除本会话记录(等于从未配置过) /fence dir · 打印围栏文件路径(含位置指针) /fence open · 在资源管理器里打开并选中围栏文件 /fence file · 更改围栏文件位置(现有规则一起搬过去) /fence switch on / switch off · 通用总开关 `/fence all` · 查看通用规则(对每个对话都生效) `/fence all add ` · 追加一条通用规则 `/fence all rm ` · 删除第 N 条通用规则 `/fence all clear` · 清空通用规则 /fence help · 帮助
默认位置 /dialog-fence/fence.json(可用环境变量 DIALOG_FENCE_FILE 直接指定文件,或 DIALOG_FENCE_HOME 指定基目录;也可以在设置页「更改位置」)。同目录下的 location.json 是指针文件,{ "file": "" }。
{
"version": 3,
"enabled": true, // 总开关;false = 所有对话零注入
"rules": [ // 通用规则:对每个对话都生效,数组顺序即注入顺序
"结论必须给出可复现证据(命令 / 文件:行号 / 实测输出三者至少其一);给不出就明说「未验证」。",
"改文件前先读该文件;改配置或数据前先备份,并给出精确回滚路径。"
],
"sessions": { // 会话专属规则:只叠加到该会话
"session-<uuid>": {
"enabled": true,
"rules": ["本对话额外的一条规则"]
}
}
}注入顺序:先 rules 数组 1..N,再把该会话启用的 sessions..rules 接在后面编号。总开关 enabled: false 时两层一起静音。
enabled)。0.3.0–0.6.x 的「一条规则一个 txt」形态(rules/common/*.txt、rules/sessions//*.txt)会在首次启动时自动迁移成单个fence.json,老文件原样保留不删。
为什么围栏文件默认不放在包目录里:0.5.0 起本插件可以作为组合包被官方安装器装进
profiles/.generations/live//node_modules/dsh-dialog-fence/,那个目录每次升级