DeepSeek Harness 推送通知插件

前言

在使用 DeepSeek Harness(DSH)跑任务时,很容易忘记 Agent 在后台做了什么、什么时候卡住、什么时候需要你介入。这个插件会在 DSH 的 Agent 跑完任务、卡住、需要你批准某个操作、模型出错停下时,推一条通知给你。

它只支持两种接口:ntfy(推到手机)和你自己的 webhook。其他服务需要自己用 webhook 模板对接。

它是什么

这是 DeepSeek Harness 的一个插件,由 ztllll 维护。安装后,DSH 会在特定场景下向 ntfy.sh 或你自建的 webhook 发送 JSON 通知。它不依赖任何第三方登录,只做 HTTP 请求。

核心功能

通知场景

插件会在以下情况发送通知:

  • done:一个任务真正做完(连续几轮算一次);done:goal 表示目标完成
  • human:Agent 在等待你批准操作(approval)、问你问题(question)、让你审阅计划(plan)、自己声明需要人(wait)、目标被阻塞(goal-blocked)
  • error:模型请求失败导致中断(认证失效/额度用完/限流/超时等,附一句可操作的提示);输出被长度上限截断(max-tokens)
  • stuck:模型请求已连续重试 4 次(还没失败,但很可能要失败);工具连续失败;原地打转(同一调用重复);很久没任何动静;某个工具调用迟迟不返回

通知内容

所有通知以高优先级发送:ntfy 收到 priority: 4(等同 X-Priority: high);webhook 的 JSON 里 priority 字段也是 4。

通知以 JSON 格式发送,包含以下字段:

字段 含义
kind done / human / error / stuck
sub 细分类型,如 approval、goal、retry;没有就是空串
title / text 现成的标题和正文
priority 固定为 4
project 项目名
cwd 工作目录
session 会话 ID 前 12 位
duration 用时(如 3分12秒)
durationSec 用时秒数
time ISO 时间
label 类别名

隐私设置

通知内容受 detail 配置控制:

  • minimal:只发固定短语、项目名、用时、错误码。没有任何对话内容
  • normal(默认):以上 + Agent 的最后回复(完成时)、Agent 提的问题、目标描述、工具名
  • full:以上 + 审批的命令原文、服务端返回的错误信息

安装与启用

在 DSH 侧栏 插件 → 添加插件,填:

github:ztllll/dsh-push-notify

装好后刷新页面。没配置任何接口时,插件完全空转,什么都不会发。

更新:请在插件页(插件管理器)里操作。命令行 dsh plugin add 只改磁盘,不会通知正在运行的 DSH 重新加载这个插件。

配置接口

方式一:插件面板(最简单)

侧栏 插件 → 已安装 → 推送通知,在「通知地址」里填一个 http:// 或 https:// 地址,点保存。下一条通知起生效,不用重启。

填 https://ntfy.sh/主题 就按 ntfy 发到手机;填其他地址就按 webhook 把 JSON POST 过去(格式见上文)。留空则不发。

面板只管这一个地址;多个接口、事件开关、阈值请用方式二或方式三。

方式二:对话里用 /notify(推荐,立刻生效)

/notify                                   查看状态
/notify add <url> [name=手机] [preset=ntfy|webhook] [token=…] [events=done,human]
/notify test [名字|#序号]                  发一条测试通知
/notify rm <名字|#序号>                    删除
/notify on | off                          总开关
  • 接口保存在 ~/.dsh/dsh-push-notify/endpoints.json,权限 0600(里面有 webhook 密钥)
  • /notify 命令本身不会被记入会话日志;回显、状态和日志里只显示地址的域名(如 ntfy.sh/…),路径和参数一律不显示
  • ntfy.sh 的地址自动按 ntfy 发送;自建 ntfy 服务器请加 preset=ntfy。其他地址都按 webhook 发送
  • token=:ntfy 开了访问控制时填,插件会带上 Authorization: Bearer <token>

方式三:写在插件配置里(可以多个、可以每个接口选不同事件)

dsh 的 profile 里(cordis.patch.yml):

- id: push-notify
  config:
    detail: normal            # normal | minimal | full
    lang: auto                # auto | zh | en
    endpoints:
      - name: 手机
        url: https://ntfy.sh/一个只有你知道的长随机主题
      - name: 我的服务
        url: https://example.com/dsh-hook
        headers: { Authorization: "Bearer 你的密钥" }
        events: [human, error]   # 这个接口只收 需要人 和 出错

ntfy 配置

情况 怎么填
用 ntfy.sh https://ntfy.sh/一个只有你知道的长随机主题。手机装 ntfy App,订阅同一个主题
自建 ntfy /notify add https://你的域名/主题 preset=ntfy
开了访问控制 再加 token=tk_…

主题名就是口令,请用长随机串。插件把通知以 JSON 发布到服务器根地址,中文标题不会乱码。

Webhook 收到什么

不写 body 时,每条通知是一个 POST,Content-Type: application/json,正文如下:

{
  "title": "✅ 任务完成 · demo",
  "text": "已完成:修复了登录页的 bug。\n用时 3分12秒",
  "kind": "done",
  "priority": 4,
  "tags": ["white_check_mark"],
  "project": "demo",
  "cwd": "/w/demo",
  "session": "s1",
  "sub": "",
  "duration": "3分12秒",
  "durationSec": 192,
  "time": "2026-10-19T07:00:00.000Z",
  "label": "任务完成"
}

发送成功判定

你的服务返回任意 2xx 就算成功,响应体不看。408、425、429 和 5xx 会重试,连接失败或超时也会重试:默认间隔 1 秒、3 秒,最多重试 2 次;对方给了 Retry-After 响应头或 JSON 正文里的 retry_after 秒数就按它等,但单次最多等 30 秒。其他状态码不重试,记一行日志。不跟随重定向,请直接填最终地址。

接入其他服务

飞书、钉钉、企业微信、Slack、Telegram 等都能收 HTTP 请求,但各家要的 JSON 格式不同、鉴权方式也不同。插件不内置它们,你可以用 webhook 的 url / headers / body 模板把请求改成对方要的样子:

- id: push-notify
  config:
    endpoints:
      - name: 某个机器人
        method: POST                              # 默认 POST;也可以 GET
        url: https://example.com/hook?t={{title}}  # url 里的变量会被 URL 编码
        headers: { X-Token: 你的密钥 }
        body:
          msg_type: text
          content: { text: "{{title}}\n{{text}}" }
          level: "{{priority}}"                   # 整个值恰好是一个变量时保留原类型:这里是数字 4

可用变量就是 JSON 里的全部字段名,写成 {{字段名}}。先解析、再替换:变量是逐个替换到每个字符串里的,标题里有引号、换行也不会弄坏 JSON。写错的变量名会原样留在通知里,方便发现。对方要表单就加 headers: { Content-Type: application/x-www-form-urlencoded },body 的每个字段会变成一个表单项。body 也可以写成一段 JSON 字符串,效果相同。

对接前请自己确认:1. 对方的 JSON 格式(以它的官方文档为准);2. 是否需要签名——签名一般要用当前时间算哈希,模板做不到,需要你自己写个小转发服务,或者 fork 本插件;3. 对方「HTTP 200 但正文里报错」时,插件仍会当成功。

验证方法:配好后发 /notify test 名字,看对方有没有收到。

可靠性设计

  • 发送失败不拖累 Agent:超时 10 秒、最多重试 2 次(尊重 Retry-After)、永不抛错;失败只写一行日志,且日志里抹掉密钥
  • 不跟随重定向:POST 被 301/302 变成 GET 会悄悄丢消息,所以直接报错并提示你用最终的 https 地址
  • 回复不会张冠李戴:只取根会话最近一次有文字的回复;新任务从空开始,不会带出上一个任务的回答;子代理写的文字不算
  • 去重 + 限流:同一类错误 60 秒内不重复;retry 模型请求每会话每小时最多 1 次;每接口每分钟最多 20 条,超出的会在下一条里写明「被抑制了 N 条」
  • 子代理不单独响;根会话要等整棵树都停了才算完成
  • 配置写错不会让 DSH 起不来:坏值回落默认并在 /notify 状态里点名
  • 损坏的 endpoints.json 绝不覆盖

已知限制

  • dsh 进程崩溃或机器断电无法通知——插件和 DSH 同进程。需要这个请另配外部看门狗
  • 通知不排队落盘:重启 DSH 时还没发出去的会丢
  • 成功 = 服务端接受了请求,不保证手机上一定弹出(那取决于推送服务和你的设备)
  • 只对 ntfy 做过真实推送验证;webhook 在本地 HTTP 服务上验证过。其他服务需要你按「接入其他服务」自己对接和验证
  • ntfy.sh 上的主题名等同口令:请用长随机串,不要用 Project 这类好猜的名字

调参建议

主要阈值(均有范围保护):settleSec(8) 停多久算收工 · minDurationSec(60) 短于此的不算任务 · humanDelaySec(5) 给你多久在屏幕前答复 · remindAfterSec(0=不催) · stallSec(900) · toolStallSec(7200) · retryAt(4) · toolFailStreak(5) · loopRepeat(8) · cooldownSec(60) · quietSec。默认值来自对真实会话日志的实测。

可以先在历史会话上试跑:

node scripts/replay.mjs --limit 80 --detail          # 用你的真实会话日志回放,看会推多少条(不联网、不打印对话内容)
node scripts/replay.mjs --set stallSec=600           # 改一个阈值再看
node scripts/live-ntfy.mjs                           # 往一个随机 ntfy 主题发每一类样例,并回读确认

适用场景与注意

适合需要实时掌握 Agent 状态、希望减少人工盯屏的开发者。插件以当前 DSH 进程的权限运行,安装前应检查源码与许可证。

插件不保证通知的送达率,也不提供离线存储。如果你需要这些能力,需要自己实现或另配外部看门狗。

结尾

这个插件为 DSH 提供了一种轻量级的通知机制,只在 Agent 完成任务、卡住、需要人工介入或模型出错时才发通知,避免刷屏。它通过 ntfy 或 webhook 将通知发送到你的设备或服务,配合模板可以对接多种第三方服务。

目录页 | GitHub

羽毛球分组比赛记分
小程序二维码

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

小夜