dsh-workspace-gate
Per-workspace gate that hides the tool schemas and system prompt sections of selected plugins from the model in directories that do not need them.
Install
dsh plugin --profile web add dsh-workspace-gate
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
<p align="right">
<b>简体中文</b> | <a href="./README.en.md">English</a>
</p>
dsh-workspace-gate · 工作区插件门控
插件照常加载,但按工作区把它不该占的提示词前缀摘掉——工具 schema 与提示词段都不再进模型上下文。



一个 DeepSeek Harness(dsh)插件。
它让"要不要让模型看见某个插件"从全局下沉到工作区:插件依然全部加载、功能随时可用,
但在你指定的目录下,它的工具 JSON schema 和系统提示词段不再出现在每一次请求里。
省下的是每轮请求的输入 token,而不是插件的加载开销。
目录
- [它解决什么问题](#它解决什么问题)
- [它做什么 / 不做什么](#它做什么--不做什么)
- [主要特性](#主要特性)
- [界面](#界面)
- [安装](#安装)
- [使用](#使用)
- [归属是怎么判断的](#归属是怎么判断的)
- [数据存放](#数据存放)
- [为什么不是"真·停用插件"](#为什么不是真停用插件)
- [实测数据](#实测数据)
- [开发](#开发)
- [已知边界](#已知边界)
- [许可证](#许可证)
它解决什么问题
DSH 自带的插件管理只有 全局 开关:一个插件要么对所有工作区都开,要么都关。
但插件对 prompt 的成本是每轮都付的——只要它启用了,它的工具 JSON schema 和
系统提示词段就会出现在每一次请求里,哪怕这次对话完全用不到它。
典型场景:
- 装了「小说写作」插件,但你 90% 的时间在写代码;
- 装了「网页搜索」全家桶,但某个离线项目根本不该联网;
- 学生工作区只需要讲解/脑图,教师工作区只需要备课/教案。
本插件把"要不要让模型看见"从全局下沉到工作区,不动全局开关、不重启、不重载插件。
它做什么 / 不做什么
做 · 不做
按目录(含子目录继承)决定哪些工具/段不进 prompt · ❌ 不安装/卸载/升级插件
组级开关 + 展开到单个工具名/段名 · ❌ 不改变插件的全局启用状态
按来源分档:第三方 / 自定义 / 官方 / 官方可选包 · ❌ 不能把全局已停用的插件"变出来"(只减不加)
自动归属推断 + 静态扫包 + 手工归属 · ❌ 不回溯已开会话(前缀已经发出去了)
fail-open:任何异常都当作"没配置" · ❌ 不碰 sandbox / approval 策略
主要特性
- 工作区级收窄:配置单位是目录,子目录自动继承最近祖先(给
D:\Projects配一次,
D:\Projects\api 就继承)。
- 只减不加:用的是 DSH 最弱的收窄原语
ctx.tools.restrict(),它只能移除,不会新增能力。 - 两类可见面都管:工具 schema + 系统提示词段(后者用"同名空文本段遮蔽"实现)。
- 按来源分档:靠 bundle 的
availability判断"随 dsh 安装"还是"用户自装",
第三方档默认展开,官方档默认收起。
- 归属可手工矫正:每个工具/段右侧有归属下拉,可以把未归属的项并入已有分组或新建分组。
- 模型可调:
workspace_gate工具支持status/list/set。 - 可控的噪声:不写 DOM、不加载宿主客户端包、颜色只用
--dsw-alias-*主题 token。 - 可测试:核心逻辑抽在
lib/policy.mjs,24 条单测,不启动 DSH 也能跑。
界面
插件管理页会多出一个 「按工作区」 页签:
插件照常加载;勾掉的插件,它的工具与提示词段不再进入这个目录的请求。 存储: ~/.dsh/data/workspace-gate.json [重新扫描] 工作区 (default-workspace) (my-project ●) (+ 添加目录) C:\work\my-project · 跟随全局 · 已关 1 组 · 共 9 组 插件组 [筛选…] › 第三方 2 ≈ 2.7k [网] web-search-free [9 工具][1 段] ≈2.7k › ● › 自定义分组 1 › 官方 7 ≈ 17.8k › 官方可选包 1 › 未归属 27
- 行尾开关 = 这一组在这个目录下是否可见;
›展开后可以只勾单个工具/段,右侧下拉可以改它的归属;- 顶部显示当前目录、是否已单独配置、关了几组、大概省多少 token/轮。
安装
本插件是标准 DSH bundle(package.json 里声明 dsh.bundle.patch)。
三种装法,推荐顺序从上往下;装好后都会在插件管理页出现「工作区插件门控」这一行。
版本要求:engines.dsh = ^0.2.1-rc.2。比这更老的 DSH 会被插件管理器直接拒绝安装,
而不是装上以后在运行期报错。
方式 1 · 让 AI 帮你装(最省事,推荐新手)
新开一个会话,把这句话发给助手:
帮我安装这个 DSH 插件:github:M1r0er/dsh-workspace-gate
助手会调用 plugin_manager 完成「pnpm 安装 → 选中 bundle → 激活」整条链路,最后告诉你
application: applied。然后刷新页面即可。
- 前提:当前会话的预设启用了
plugin_manager工具。DSH 默认只在 Creator 模式下启用它,
其它预设默认关闭;如果助手说没有这个工具,切到 Creator 再试,或改用方式 2。
- 安装 Host 代码属于高权限操作,会请求一次性授权。
方式 2 · Web 插件页安装(不碰命令行)
1. 打开 DSH 的 插件 页;
2. 点安装入口,在 spec 里填 github:M1r0er/dsh-workspace-gate
(也可以填你本地 clone 的绝对路径);
3. 若弹出 “Allow these scripts and retry”,确认后重试一次;
4. 列表里出现 dsh-workspace-gate 即装好,刷新页面;
5. 确认「工作区插件门控」这一行是开启状态。
方式 3 · 命令行(知道自己在做什么的人)
# A. 用 dsh CLI(profile 不是 Desktop 时) dsh plugin --profile <your-profile> add github:M1r0er/dsh-workspace-gate # B. 直接进 profile 目录用 pnpm cd ~/.dsh/profiles/<your-profile> pnpm add github:M1r0er/dsh-workspace-gate # C. 本地目录(开发/调试) git clone https://github.com/M1r0er/dsh-workspace-gate.git pnpm add link:<克隆下来的绝对路径>
--profile <name>决定改哪个 profile 目录:--profile web改的是~/.dsh/profiles/web,
而桌面版 GUI 跑的是desktopprofile —— 装进web,桌面窗口里看不到,两个 profile 的
依赖和 dsh.profile.bundles 互相独立。要装进桌面版,请用它自己的插件页
(npm 版 CLI 会拒绝管理 desktop)。
⚠️ pnpm 只装依赖,不会把包写进 dsh.profile.bundles。
少这一步插件不会加载。装完必须再做一件事:
- 打开 Web 插件页,把 dsh-workspace-gate 这个 bundle 打开;或
- 手工在~/.dsh/profiles/<profile>/package.json的dsh.profile.bundles
数组末尾加 "dsh-workspace-gate"。
然后重启 DSH(没有 HMR 时)才会加载。
装好后:关掉插件管理页里「工作区插件门控」这一行 = 门控整体不生效,行为回到未安装状态(fail-open)。
常见坑
现象 · 原因 / 处理
装完插件页没有这一行 · bundle 没被选中:按方式 3 的提示把它加进 dsh.profile.bundles
返回 already-installed · 同 spec 已装过;先在插件页 Remove 再装
桌面版提示 CLI 不接管 · Desktop profile 由 Electron 托管,npm CLI 会拒绝管理它;请用 Web 插件页
安装卡在 build scripts · pnpm 11 默认拦截安装脚本;在弹窗里批准后重试
想在插件市场里搜到 · 仓库已打 dsh-plugin topic,但要在市场出现还需被 awesome-dsh-plugin 索引收录(提交 PR)
使用
给人
插件管理 → 「按工作区」→ 选目录 → 关掉不需要的组 → 保存。对新会话生效。
给模型
workspace_gate(action:"status") # 最近哪些会话命中了哪个目录、摘掉了什么
workspace_gate(action:"list") # 可门控的插件 → 工具名/段名
workspace_gate(action:"set", dir:"D:\\Projects\\api",
hide:["plugin:r18-novel"]) # 需要显式给 dir
workspace_gate(action:"set", dir:"D:\\Projects",
show:["tool:read"]) # 从隐藏清单移除
这个工具没有用 deferLoading:实测打开后子会话里它直接不可见,模型想调也调不到。
两种写法等价(dir 必须是绝对路径,子目录会继承):
- 隐藏整组:
plugin:<分组 id> - 隐藏单项:
tool:<工具名>/section:<段名>
归属是怎么判断的
DSH 运行时不记录某个工具是哪个插件注册的(tools.register() 只按名字存),
所以"按插件勾选"只能推断。本插件按强度依次尝试:
顺序 · 手段 · 说明 · 可靠度
1 · 段名命名空间 · r18-novel:protocol → r18-novel,与插件 id/包名归一后比较 · 高(约定)
2 · 工具名前缀 · 工具名 token 与插件 id 或包名 token 的前导全等匹配 · 中高
3 · 静态扫包 · 读包入口(第三方递归扫 .js)里的 name: 'x' 定义位,与未归属工具名求交,只在唯一命中时采纳 · 较高
4 · 手工归属 · 界面里自己指定,赢过以上全部 · 最高
静态扫包解决的是这类情况:dsh-free-search 的 profile 行 id 是 web-search-free,
而它注册的 platform_search / video_search / weather 等名字里根本不含包名——
只靠前缀只能认出 1/9,扫包之后 9 个全部归位。
硬边界:官方内置包在 app.asar 里,磁盘上没有目录,扫不到;运行时动态注册的工具
(没有对应文件插件)也没有包可扫。这些会留在「未归属」,用手工归属分类即
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::2,最后更新 2026-10-18