DeepSeek Harness Plugin Hub

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

探索

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

社区

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

相关链接

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

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

Preset Tool Guard — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
P

dsh-preset-tool-guard

Preset Tool Guard

DeepSeek Harness 的按预设工具可见性:在 agent/created 时对代理自身的作用域应用 ctx.tools.restrict(),使预设仅公开应使用的工具,同时保持重量级提供程序处于挂载状态。

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

npx -y @deepseek-ai/dsh plugin --profile web add github:KouzakiUmi/dsh-preset-tool-guard#6908b27df87f832be652ff868d09278f788ff146
README兼容性版本

兼容性与来源证明

Preset Tool Guard 以 dsh-preset-tool-guard 发布,当前版本为 0.1.2。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

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

版本

0.1.2stable
2026/10/19

相关插件

正在加载相关插件…

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

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

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

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

认领这个 Plugin →
报告问题

相关插件

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

Experimental Auto Review@deepseek-ai/dsh-experimental-auto-review针对每个工具的 LLM 授权审查,适用于 DeepSeek Harness Auto 权限预设Cc Safety Netcc-safety-net编码代理 CLI 钩子——阻止破坏性命令和访问机密文件Codex Subscriptiondsh-codex-subscription在 DeepSeek Harness 中通过 OAuth 使用 ChatGPT 和 Codex 订阅,支持配额、安全重置、网络搜索、图像和快速模式,并可通过 ChatGPT 移动应用(Beta)控制 DSHDs Harness Remoteds-harness-remote通过端到端加密,从桌面端、网页端和 Android 远程访问 DeepSeek Harness 及实验性 Codex 工作区,并支持 dsh-TUI Host。

README

dsh-preset-tool-guard

Per-preset tool visibility for DeepSeek Harness (dsh).

Mount it once at the host level. It watches agent/created, reads the preset that agent actually composed, and applies the official ctx.tools.restrict() seam on that agent's own scope — so each preset exposes only the tools it should, while the heavy providers stay mounted and untouched.

简体中文说明见 README.zh.md


The problem

dsh resolves tools along agent → preset → global. A preset composition mounts its own rows; it does not restrict what is inherited from the global layer. So any plugin mounted at the host level — a computer-use driver, browser MCP servers, image providers — shows up in the model's tool catalog of every preset, including minimal ones that were designed to expose two or three tools.

Disabling those providers is not the answer: you lose the capabilities entirely, and it affects every preset and session. The upstream discussion for the preset half of this is deepseek-ai/deepseek-harness#5786.

ctx.tools.restrict({ allow, deny }) is the public, agent-scoped answer: a restriction masks what the scope inherits (the global layer and every ancestor scope layer) and never what the scope registers itself. This plugin wires that seam into the preset lifecycle.

What it changes

Effect
Model-facing tool schemas (the per-request prefix)the masked tools are gone
PTC mode SDK surfacegone as well (both projections read the same view)
Dispatchmasked calls return UNKNOWN_TOOL — a real mask, not a presentation filter
Other agents / presetsunaffected; a restriction is per scope
The provider itselfstill mounted; every capability you kept keeps working

Install

Requirements: Node ^22.19 or >=24 (see engines), and a harness that exposes ToolRuntime.restrict() plus the agent/created event.

The package ships its own cordis.patch.yml, so installing it mounts the plugin row.

# any dsh CLI (TUI, web profile, …)
dsh plugin --profile <profile> add dsh-preset-tool-guard

# from GitHub
dsh plugin --profile <profile> add github:KouzakiUmi/dsh-preset-tool-guard

# from a local checkout
dsh plugin --profile <profile> add /path/to/dsh-preset-tool-guard

DSH Desktop has no dsh on PATH. The GUI executable is the Electron shell, not the CLI — passing plugin … to it does not run a plugin command. Use the terminal shim that the desktop ships (verified on DSH Desktop 2.0.17-next, core 0.2.0-rc.2):

$env:ELECTRON_RUN_AS_NODE = '1'
& "C:\Program Files\DSH NEXT\DSH NEXT.exe" --expose-internals `
  "C:\Program Files\DSH NEXT\resources\app\lib\desktop-cli.js" `
  plugin --profile desktop add dsh-preset-tool-guard

Both forms end up in pnpm inside the profile directory (plugin forwards its arguments to pnpm), so add / remove / install all behave as you expect. Note that --profile is mandatory, and that the bundled dsh refuses to boot a profile literally named desktop outside the Electron path.

Then restart DSH: the plugin is a host-side bundle, so the running process keeps its old tree. On load, the plugin also sweeps existing agents returned by agents.list() and applies their rules.

To remove it, run the same command with remove, and delete the preset-tool-guard entry from your profile patch (see below). Removing the package row alone leaves a dangling config block.

The npm package is dsh-preset-tool-guard; use dsh-preset-tool-guard@0.1.2 to pin this release.

Configure

The plugin's own patch ships a conservative default (dryRun: true, no allowlists). Put the real configuration in your profile's cordis.patch.yml. Loader config for the same entry id is replaced wholesale, so restate every field you care about — including dryRun, which otherwise falls back to the code default false (masking enabled).

- id: preset-tool-guard
  config:
    allowlists:
      minimal: [bash, pwsh, str_replace_editor]
      standard: [read, write, edit, glob, grep, pwsh, skill, todo_write, web_search, memory_recall]
      text: [read, write, edit, glob, grep, pwsh, grok_imagine_edit]
    deny: []
    denyPrefixes: []
    groups: {}
    disableGroups: []
    skipUncomposed: true
    report: true
    # reportFile may be omitted (defaults to $DSH_HOME/logs/preset-tool-guard.log).
    # A leading `~` is expanded; an absolute path is used as-is.
    reportFile: C:/Users/you/.dsh/logs/preset-tool-guard.log
    dryRun: false
FieldDefault in codeMeaning
allowlists{}{ presetId: [toolName, …] }. A matching preset keeps only the listed inherited tools; every other inherited tool is masked (fail-closed: a tool registered later on an inherited layer is not admitted, because the filter is a snapshot). An empty array or a list with no restrictable matches skips only the allowlist with a warning; the inherited tool surface is not cleared, and bottom rules still apply. Presets absent from this map get no allowlist, but still receive the deny / group rules below.
deny[]Exact tool names removed from every agent, composed preset or not.
denyPrefixes[]Plain startsWith prefixes removed from every agent (e.g. cua_driver_native__).
groups{}Named groups: { groupId: { names: […], prefix: "…" } }.
disableGroups[]Group ids whose members are removed from every agent.
skipUncomposedtrueSkip the allowlist for agents with no composed preset. The deny / group half still applies to them.
reporttrueOne report line per (re)application.
reportFile$DSH_HOME/logs/preset-tool-guard.logAppend reports here. false or "" disables file output. ~ is expanded; omit the field to use the default.
dryRunfalseMeasure and report only; change nothing. Start here when tuning.

How it works

  • The restriction is applied on agent.ctx — the agent's own scope. restrict() derives the scope from the calling context and refuses to run from a plain (unscoped) one.
  • The filter is snapshotted at registration. allow therefore keeps admitting only the names it listed at that moment; a name that appears later on an inherited layer is not admitted. If you need a late-registering provider to be visible, create a new agent after it registers.
  • For that reason an allowlist match installs its mask even when nothing is currently excluded — that is what keeps future inherited tools out. Skipping the install would silently turn a fail-closed policy into a fail-open one.
  • Names are resolved against the scope's restrictable set before the call: a name the agent cannot see is skipped (never thrown), so a provider upgrade that renames a tool degrades to a smaller saving plus a report note, not to a startable-but-broken deployment.
  • ToolRuntime.view() is not part of the documented Service contract. The plugin probes it and, if it is unavailable, skips masking and says so rather than falling back to schemas() — the two sets differ (schemas include the agent's own layer and the reserved transport name), and feeding those names to restrict() would make the whole call fail.
  • The reserved PTC transport run_code is never written into a filter.
  • An allowlist with no restrictable matches is skipped with a warning; deny, prefixes and disabled groups still apply. An empty allowlist therefore does not clear the inherited tool surface.
  • Late registrations receive bottom rules. The plugin listens to tools/change and adds denials for newly matching inherited names, including providers connected after agent creation. It does not rebuild the allowlist snapshot or admit late allowlisted names. Repeated events add no duplicate denials. Dry run reports these names without installing restrictions.
  • Every failure path logs and returns; agent creation is never blocked.
  • Preset switches are handled. Upstream recompose (used when a blank session changes preset) only rebinds the mount — it does not re-emit agent/created. The plugin listens for agent-preset/selected, releases the previous restriction and re-applies, because restrictions intersect and cannot be widened in place.
  • The mask has plugin ownership too. restrict()'s disposer is kept per agent and released when the agent is disposed and when the plugin itself unloads, so a config reload cannot leave a stale mask behind on a live agent.

Reporting

2026-10-04T12:34:56.789Z preset-tool-guard: excluded 114/168 inherited names (155810 of 199433 utf8 schema bytes, 78.1%, ~43281 tokens) agent="session-…" preset="standard" mode=allowlist
  • excluded / would exclude (the latter under dryRun), and mode=allowlist|bottom.
  • Counts and sizes describe the inherited surface: names the restriction excludes out of the names the scope could restrict. Visible-tool counts can differ (see the two exceptions below).
  • Sizes are UTF-8 bytes of the serialized schema, converted to a rough token estimate at 3.6 bytes per token — a magnitude, not a tokenizer measurement.
  • On a no-op it prints 无需掩码 / no mask needed instead.
  • The same line is appended to reportFile with an ISO timestamp. The host logger's output channel is not necessarily a readable log file, so the file is the reliable record.

Measured on a desktop profile (core 0.2.0-rc.2): standard went from roughly 60K tokens of tool prefix to about 10.5K of schema, with a computer-use driver (62 tools), two browser MCP servers (55 tools) and image providers excluded from the everyday presets — and still fully available in a full preset that simply declares no allowlist.

Two ways a name stays visible

restrict masks only what a scope inherits. Two consequences are worth knowing before you file a bug:

  1. Self-layer registrations. Tools a plugin registers on the agent's own layer are outside the filter. On one machine subagent, list_subagent_models, schedule_* and cordis_inspect_* behave this way: they stay visible no matter what the allowlist says. An allowlist cannot remove them — turn such a tool off in its own plugin's configuration.
  2. Not registered at that moment. A name whose provider is not connected, or not yet mounted when the agent is created, is skipped. Keeping it in the allowlist does not reserve visibility for later either (see the snapshot rule above); it only avoids a name-collision error.

The report line separates the two cases so you can tell them apart without guessing.

Development

npm test
node scripts/check-manifest.mjs
node scripts/check-patch.cjs [profileDir] [toolsJson] [--strict]

Defaults: profileDir = $DSH_HOME/profiles/desktop, toolsJson = $DSH_HOME/tmp-guard-tools.json. It parses your profile patch with the !!js tag supported, prints every preset-* declaration and the per-preset allowlist sizes, and — when the tool catalog file exists — reports allowlist names the catalog does not contain.

The catalog file accepts either shape, so a raw cordis_inspect_query result can be written to disk as-is:

  • { "tools": [ { "name": "read", … }, … ] } (host / Tool / listTools output)
  • [ "read", "write", … ]

Exit code is 0 on success and 1 on a parse failure, a structurally invalid patch, an unrecognizable catalog shape, or (with --strict) a missing tool name. Without --strict, missing names are warnings — names can legitimately be absent on another platform (the shipped bash row is Windows-disabled).

Compatibility

Verified on @deepseek-ai/dsh-* 0.2.0-rc.2 (Node ^22.19 || >=24). The plugin reads tools, agentPresets and agents through ctx.get() at runtime and imports nothing at load time except Node builtins, so the peer surface is small: @deepseek-ai/cordis (~4.x) plus a harness exposing ToolRuntime.restrict(), agent/created, agent-preset/selected and tools/change. Hosts without tools/change cannot update bottom rules for late registrations; existing rules still apply when an agent is created or its preset changes. This is a visibility mask for inherited tools, not a sandbox or a way to block tools registered directly on the agent's own layer.

License

MIT — see LICENSE.