DSH plugin 钩子插件怎么写?tools/pre-execute 执行门禁的放行与拒绝
DSH插件里的钩子插件,就是「在拦截点上运行的普通 Cordis 插件」——监听 tools/pre-execute 拿到待执行的工具调用,返回 { kind: 'deny', reason } 拒绝、返回 next() 放行。 权限门禁只是它的一个示例;沙箱、权限与 plan-mode 插件都复用同一扩展点。它不需要任何外部协议,官方称之为「原生钩子」。
DSH plugin 钩子插件是什么:在拦截点上运行的普通 Cordis 插件
钩子插件本身不等同于权限门禁——它是在拦截点上运行的普通 Cordis 插件,可以拦截多种扩展点(来源)。 官方给出的最小示例是权限门禁,从 tools/pre-execute 返回类型化决策,决定允许或拒绝一次调用:
import type { Context } from '@deepseek-ai/cordis'
import type { PreToolDecision, ToolExecution } from '@deepseek-ai/dsh-tools'
declare function isAllowed(exec: ToolExecution): Promise<boolean>
export const name = 'permission-gate'
export function apply(ctx: Context) {
ctx.on('tools/pre-execute', async (exec, next): Promise<PreToolDecision> => {
if (!(await isAllowed(exec))) {
return { kind: 'deny', reason: 'Denied by policy.' }
}
return next()
})
}
它用到的正是 waterfall 事件:tools/pre-execute 是可重排的策略层,每个监听器拿到 exec 与 next(),可以返回决策,也可以调用 next() 交给下游。这与 DSH plugin 事件系统 里 waterfall「必须调用 next()」的纪律是同一条规则。
DSH plugin 的门禁怎么写:tools/pre-execute 的放行与拒绝
在 tools/pre-execute 上,返回 { kind: 'deny', reason } 即拒绝该次调用,调用 next() 即放行或继续下传(来源)。 按三步写一个最小门禁:
- 声明钩子插件 — 模块顶层导出
name,例如permission-gate。预期:加载后作为独立插件生效。 - 在
apply内注册监听 —ctx.on('tools/pre-execute', async (exec, next) => { ... })。预期:每次工具执行前都会进入你的 handler。 - 返回决策 — 不允许时返回
{ kind: 'deny', reason: '...' },允许时return next()。预期:被拒绝的调用不会执行,并带上 reason。
需要用户确认时返回 ask,再通过 ctx.approval 应答。 官方的权限系统 / AskUserQuestion 就是这样实现的:从 tools/pre-execute 返回 ask,并注册一个面向模型的独立 ask 工具。想了解审批策略的取值与结果,见 权限预设与审批。
DSH plugin 拦截点怎么选:pre-execute、guard、execute 与 result
四个拦截点的选择标准是「要不要改结果、要不要强制」:策略层用 tools/pre-execute,强不变式用 ctx.tools.guard(),包裹分发用 tools/execute,变换结果用 tools/post-execute,只观察不改动则用 tools/result(来源)。 对照如下:
tools/pre-execute— 可重排的策略层,返回允许 / 拒绝 / 追问。预期:适合权限、沙箱、plan-mode 这类可组合策略。ctx.tools.guard()— 强制不变式、提供单调的最终拒绝。预期:适合硬约束,后续监听器无法翻盘。tools/execute— 包裹分发生命周期,可用于超时 / 重试 / 指标(仅exec.signal可替换)。预期:适合给工具调用加通用包装。tools/post-execute— 显式结果变换;只想观察不可变最终结果则用tools/result。预期:不改结果就别用 post-execute。
一个实战提醒:钩子插件拦截工具调用时不要顺手改动工具自身的实现,工具契约归 ctx.tools.register() 与 defineTool 管,参见 工具插件开发。写完自检三项:handler 是否在所有分支都返回决策或 next();硬约束是否用了 guard();deny 是否带上可诊断的 reason。想找现成的沙箱 / 权限类插件对照,可在 DSH Plugin Hub 检索。
常见问题
DSH plugin 的钩子插件就是在拦截点上运行的普通 Cordis 插件,不需要任何外部协议,官方称之为「原生钩子」。权限门禁只是钩子插件的一个示例,它从 tools/pre-execute 返回类型化决策;钩子插件还可以拦截其他扩展点,本身不等同于权限门禁。
DSH plugin 写门禁只需三步:声明 name、在 apply 里 ctx.on('tools/pre-execute', handler)、让 handler 返回类型化决策或调用 next()。官方示例在 isAllowed(exec) 为假时返回 { kind: 'deny', reason: 'Denied by policy.' },否则 return next() 放行。
DSH plugin 在 tools/pre-execute 上返回 { kind: 'deny', reason } 即拒绝该次调用,返回 next() 或继续调用 next() 则放行。官方也支持在此返回 ask,再通过 ctx.approval 应答,实现需要用户确认的权限流程。
DSH plugin 的 tools/pre-execute 是 waterfall 式策略层,决策可被后续监听器重排;ctx.tools.guard() 用于不变式场景,需要单调、不可翻盘的最终拒绝。前者适合可组合策略,后者适合必须被强制的硬约束。
DSH plugin 拦截工具调用主要有四种:tools/pre-execute 做策略层放行或拒绝、tools/execute 包裹分发生命周期、tools/post-execute 做显式结果变换、tools/result 观察不可变最终结果。按「要不要改结果、要不要强制」来选即可。
相关术语
- 钩子插件(hook plugin)
- 钩子插件是 DSH plugin 里的一种形态,通过监听并拦截工具或其他扩展点来实现策略;它在拦截点上运行,是普通 Cordis 插件,不依赖外部协议,官方称为原生钩子。— https://deepseek-harness.github.io/deepseek-harness/reference/cookbook/extension-cookbook
- tools/pre-execute
- tools/pre-execute 是 DSH plugin 的执行门禁扩展点,钩子监听它会收到待执行的 ToolExecution 与 next(),返回类型化决策即可允许、拒绝或追问一次工具调用;沙箱、权限与 plan-mode 插件都使用它。— https://deepseek-harness.github.io/deepseek-harness/reference/cookbook/extension-cookbook
- PreToolDecision
- PreToolDecision 是 DSH plugin 在 tools/pre-execute 上返回的决策类型,deny 表示拒绝并给出 reason,配合 next() 表示放行或继续下传。— https://deepseek-harness.github.io/deepseek-harness/reference/cookbook/extension-cookbook
- ctx.tools.guard()
- ctx.tools.guard() 是 DSH plugin 用于强制不变式的工具守卫,提供单调的最终拒绝;当约束不允许被后续监听器翻盘时应使用它,而不是可重排的 policy 层。— https://deepseek-harness.github.io/deepseek-harness/reference/cookbook/extension-cookbook
来源
- DeepSeek Harness 官方文档 - 实操手册:扩展插件形态· deepseek-ai
- DeepSeek Harness 官方文档 - 工具参考:执行策略与观察· deepseek-ai