dsh-hooks:为 DeepSeek Harness 声明式接入生命周期钩子

前言

在 DSH 的插件化工作方式里,很多自动化需求并不复杂:一轮对话结束后发通知、某个工具被调用时写日志、审批请求出现时弹桌面提示。这些动作通常需要监听事件、解析上下文、再执行命令或通知。dsh-hooks 把这类逻辑放在 profile 配置里声明:在 cordis.patch.yml 中写清楚事件、匹配条件和要执行的 runnotify,不需要额外写插件代码。

这是什么

dsh-hooks 是一个由 PeterBon 维护、采用 MIT 许可的 DeepSeek Harness 插件。它的定位是 config-driven lifecycle hooks:用配置声明 event -> command/notify 的钩子。

它包含两部分:

  1. 钩子引擎:根据配置监听 DSH 事件,并执行命令或内置通知。
  2. Web GUI 设置页:安装后,dsh web 的设置面板会增加 Hooks 区域,提供历史时间线、手动测试、notify 测试、hook editor 和 Feishu connect。

核心能力

下面列出 dsh-hooks 的主要能力:

  1. 在 profile 的 cordis.patch.yml 中声明 event -> commandevent -> notification 钩子。
  2. 不需要编写插件代码,配置即可触发命令或通知。
  3. 一个包同时提供钩子引擎和 Web GUI 设置页。
  4. Web GUI 设置面板增加 Hooks 区域,包括 history timeline、manual tester、notify tests、hook editor 和 Feishu connect。
  5. 内置通知渠道:
    - desktop:平台气泡/提示。
    - webhook:向 HTTP endpoint 发送 JSON,可配置 slack: true 生成一行摘要。
  6. 支持以下事件:
    - turn/start
    - turn/end
    - tree/settled
    - step/end
    - tool/call
    - tool/result
    - user/message
    - approval/asked
    - approval/decided
    - session/title
    - session/created
    - session/disposed
    - agent/created
    - agent/disposed
    - agent/error
    - agent/status
    - hook/failed
  7. 上下文通过 DSH_HOOK_* 环境变量传递;如果配置 input: stdin,完整 context JSON 会写入命令 stdin。

安装与启用

dsh-hooks 要求 Node.js:

node >=22

安装插件可以从 npm 添加:

dsh plugin --profile web add dsh-hooks

也可以直接从 GitHub 仓库添加:

dsh plugin --profile web add github:PeterBon/dsh-hooks

安装后,需要重启 dsh web。重启完成后,设置面板会增加 Hooks 区域,用于查看和配置钩子。

基本配置

配置写在 profile 的 cordis.patch.yml 中。下面是几个典型条目:

- id: dsh-hooks
  name: dsh-hooks
  config:
    hooks:
      - on: 'turn/end'
        when: 'completed'
        run: 'node examples/notify-feishu.mjs'
        timeoutMs: 10000

      - on: 'approval/asked'
        notify:
          channel: 'desktop'

      - on: 'turn/end'
        when: 'completed'
        notify:
          channel: 'webhook'
          url: 'https://hooks.slack.com/services/…'
          slack: true

      - on: 'tool/call'
        match:
          tool: '^(rm|git|ssh)'
        run: 'node examples/notify-webhook.mjs --slack'

上面的配置做了四件事:

  1. turn/endcompleted 结束时,执行 node examples/notify-feishu.mjs
  2. 当出现 approval/asked 时,发送桌面通知。
  3. turn/end 完成时,通过 webhook 发送通知,并使用 Slack 风格的一行摘要。
  4. 当模型请求匹配 rmgitssh 的工具调用时,执行 webhook 通知脚本。

配置字段

dsh-hooks 的 hook 字段覆盖事件触发、条件过滤、执行方式、通知、重试、并发和防抖。常见字段如下:

  1. on:触发事件名,例如 turn/endtool/callapproval/asked
  2. when:过滤 turn/end 的结束原因,例如 completed
  3. match:对上下文字段做条件过滤,例如按工具名正则匹配。
  4. run:要执行的命令。runnotify 二选一。
  5. notify:内置通知,支持 channel: desktopchannel: webhook
  6. input:控制上下文传递方式。使用 stdin 时,会把完整 context JSON 写入命令 stdin。
  7. timeoutMs:单条命令的超时时间,单位是毫秒。
  8. retries:对非零退出码的重试次数。
  9. retryDelayMs:重试间隔,单位是毫秒。
  10. enabled:设为 false 时,配置保留但不触发。
  11. cwd:命令工作目录,可以是 session 或绝对路径;仅用于 run
  12. maxConcurrent:同一 hook 的最大并发进程数。
  13. debounceMs:高频事件的防抖窗口,单位是毫秒。

典型用法

1. 执行命令

当某个事件发生时,执行本地命令:

- on: 'turn/end'
  when: 'completed'
  run: 'node examples/notify-feishu.mjs'

这条配置在 turn/end 且结束原因为 completed 时,启动 node examples/notify-feishu.mjs

2. 使用桌面通知

当审批请求出现时,直接发送平台气泡/提示:

- on: 'approval/asked'
  notify:
    channel: 'desktop'

这条配置不需要额外脚本,直接由 dsh-hooks 的内置通知处理。

3. 使用 webhook 通知

将事件信息 POST 到 HTTP endpoint:

- on: 'turn/end'
  when: 'completed'
  notify:
    channel: 'webhook'
    url: 'https://hooks.slack.com/services/…'
    slack: true

如果目标是 Slack,可以配置 slack: true,发送一行摘要。

4. 按工具名过滤

只监听匹配特定工具名的 tool/call

- on: 'tool/call'
  match:
    tool: '^(rm|git|ssh)'
  run: 'node examples/notify-webhook.mjs --slack'

这里的 match.tool 是一个正则表达式,用于过滤模型请求调用的工具名。

5. 通过 stdin 传递完整上下文

默认情况下,上下文通过 DSH_HOOK_* 环境变量传递。如果脚本需要完整 JSON,可以使用:

- on: 'turn/end'
  input: 'stdin'
  run: 'node my-hook.mjs'

配置后,完整 context JSON 会写入 node my-hook.mjs 的 stdin。

6. 控制重试

对非零退出码进行有限次后台重试:

- on: 'turn/end'
  when: 'completed'
  run: 'node examples/notify-feishu.mjs'
  retries: 2
  retryDelayMs: 1000

这里的重试只针对非零退出码。spawn 失败和超时不会进入重试。

7. 控制并发与防抖

对高频事件,可以限制并发并做防抖:

- on: 'step/end'
  run: 'node examples/log-step.mjs'
  debounceMs: 500
  maxConcurrent: 2

debounceMs: 500 会在 500 毫秒窗口内合并高频触发;maxConcurrent: 2 会限制同一 hook 的并发进程数。超出上限的触发会被丢弃,并记录为 skipped。

8. 临时禁用某条 hook

不想删除配置时,可以保留声明并关闭它:

- on: 'turn/end'
  enabled: false
  cwd: 'session'
  run: 'node examples/log-turn.mjs'

enabled: false 表示这条 hook 不触发,但配置仍然保留。cwd: session 表示命令在 session 的工作目录中运行。

行为说明

dsh-hooks 的执行模型偏向“触发外部动作,但不阻塞主流程”:

  1. 命令执行是 fire-and-forget。
  2. 命令失败只会 console.warn,默认不会阻塞 DSH 的 agent loop。
  3. runnotify 必须二选一。
  4. retries 只用于非零退出码的后台重试;spawn 失败和超时不重试。
  5. maxConcurrent 限制同一 hook 的并发进程数;超出上限的触发会被丢弃并记录为 skipped。
  6. cwd 仅用于 run,可以设为 session 或绝对路径。

依赖与许可证

dsh-hooks 的 package 信息中包含以下 peer dependencies:

@deepseek-ai/cordis
@deepseek-ai/dsh-session
@deepseek-ai/schemastery
react

运行时要求:

node >=22

许可证为 MIT。

适用场景

dsh-hooks 适合这些场景:

  1. 希望把 DSH 生命周期事件接到本地脚本或通知系统。
  2. 需要在 turn/endtool/callapproval/asked 等事件发生时执行轻量自动化。
  3. 希望发送桌面通知、Slack 风格 webhook 通知,或调用自研脚本。
  4. 不想为一次事件监听额外维护插件代码,希望直接在 profile 配置里管理。

需要注意:插件中的 run 命令会以当前 DSH 进程拥有的权限执行。安装前建议检查源码、示例脚本和许可证,并确认要执行的命令不会执行危险操作。

结尾

dsh-hooks 的价值在于把 DSH 生命周期事件转成低代码的自动化入口:配置 cordis.patch.yml 后,就能在 turn、step、tool、approval、session、agent 等事件上执行命令或发送通知,同时通过 dsh web 的 Hooks 设置页进行管理和测试。

相关链接:

  1. GitHub 仓库:https://github.com/PeterBon/dsh-hooks
  2. 插件目录页:当前资料未提供独立 URL;安装后可在 dsh web 的设置面板中查看 Hooks 区域,或在当前 DSH 插件目录入口中检索 dsh-hooks
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye