Skip to content

agent-notify-dsh

Verified

agent-notify-dsh · v0.1.0 · MIT

DeepSeek Harness (DSH) plugin that forwards agent lifecycle events to agent-notify for desktop and phone push notifications

Install

dsh plugin add agent-notify-dsh

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

agent-notify-dsh

把 DeepSeek Harness(DSH)的 agent 生命周期事件转发给 agent-notify,从而在授权等待、提问等待、任务完成或任务失败时收到桌面与手机推送。


当前状态:两侧均已完成并已发布,可直接使用

本仓库产出 DSH 插件侧(TypeScript);接收端 handle-dsh-hook 子命令属上游 agent-notify 的 M2 任务,其 Go 侧同样已完成, 已合并进上游 main(#58, 合并提交 421e47b),并已随 v0.18.0 正式发布——Release 说明中列有该 PR。

本插件是独立的一层,需要单独安装:上游 v0.18.0+ 只带来了接收端(handle-dsh-hook), 不会自动把插件注册进 DSH profile——两边都到位才会收到通知。

层 谁提供 是否自动
接收端 handle-dsh-hook 上游 agent-notify v0.18.0+ ✅ 装上游即自带
DSH profile 里的插件 本包 agent-notify-dsh ❌ 需手动 add(见下方安装)

唯一需要留意的版本边界是 v0.18.0:更早的版本(含 v0.17.x)不认识 dsh。 若插件已装而二进制过旧,插件会照常注册监听器并把载荷写到 stdin,但不会产生通知—— 这是「缺少接收端」,不是插件故障;升级上游版本即可。

确认接收端已就绪(v0.18.0+):

agent-notify dsh print-hooks --help   # 列出 DSH 安装计划
agent-notify handle-dsh-hook --help   # 存在即说明接收端已就绪

安装

1. 先装 agent-notify 本体

插件不自带二进制,它会去本机查找 agent-notify 可执行文件(默认落点 ~/.agent-notify/agent-notify,Windows 为 agent-notify.exe):

npx agent-notify        # 交互式向导:下载平台二进制 + 配置渠道
# 或按上游 README 的方式构建/安装,最终二进制需落在 ~/.agent-notify/

2. 再装 DSH 插件

# 从 npm 安装(推荐)
dsh plugin --profile web add agent-notify-dsh

# 本地开发:克隆后以 link: 方式安装(路径换成你自己的克隆位置)
git clone [email protected]:wanfanggreat/agent-notify-dsh.git
dsh plugin --profile web add link:$(pwd)/agent-notify-dsh

为什么优先 npm:从 git 安装时 DSH 会走 pnpm 的 prepare 构建,而 pnpm 默认 拦截构建脚本,用户需手动往 pnpm-workspace.yaml 加 allowBuilds 才能继续。 npm 包已含编译好的 lib/,无需构建。

注意:

  • dsh plugin 内部转发给 pnpm,并按 dsh.profile.bundles 记账;不要手改 profile 的 package.json 或 node_modules。
  • desktop profile 由 Electron 应用独占管理,会被 DSH 直接拒绝(apps/cli/src/args.ts 的 rejectElectronProfile);如需指定 profile,请用 web 或自建 profile。
  • 卸载:dsh plugin --profile web remove agent-notify-dsh。

事件映射

插件把 5 类 DSH 事件转换为一行 JSON 写入二进制的 stdin,字段名沿用 Claude Code 方言 (hook_event_name / session_id / cwd / tool_name / reason / question / error), 以便上游复用既有的 Claude Code 解析器。契约见 docs/CONTRACT.md §1。

hook_event_name 归一化事件 DSH 来源 说明
SessionStart session_start agent/created(仅根 agent) 仅点击聚焦捕获,不发通知
ApprovalRequest permission_required approval/request 等待授权——核心增量
UserQuestion input_required user-questions/request 等待用户回答
Stop run_completed agent/turn-stopping 一轮结束
RunFailed run_failed agent/error 运行失败

只对根 agent 触发:子 agent(subagent)的事件一律被过滤,避免刷屏并污染点击聚焦的窗口缓存 (契约 §4.3)。


为什么需要这个插件:相对 DSH 自带 Claude Code bridge 的增量

DSH 内置 @deepseek-ai/dsh-hooks-claude-code,它能直接跑未修改的 Claude Code 命令式 hook。 但它只认 7 个事件(packages/hooks/hooks-claude-code/src/config.ts:11-19):

const CLAUDE_EVENTS = [
  'SessionStart', 'UserPromptSubmit', 'PreToolUse',
  'PostToolUse', 'Stop', 'SubagentStart', 'SubagentStop',
] as const

而 agent-notify 写给 Claude Code 的事件是 5 个 (agent-notify/internal/claudehooks/settings.go:17-23): SessionStart / PermissionRequest / Notification / Stop / PostToolUseFailure。

交集只有 SessionStart 与 Stop:

agent-notify 写入 CC 的事件 归一化事件 DSH CC bridge 是否支持
SessionStart session_start(仅聚焦捕获) 支持
PermissionRequest permission_required 静默丢弃
Notification input_required 静默丢弃
Stop run_completed 支持
PostToolUseFailure run_failed 静默丢弃(bridge 只有 PostToolUse)

parseClaudeCodeConfig 只遍历该白名单(同文件 :86),表外事件不报错、不告警——用户会以为装好了, 实际上只有 2/4 事件生效,而**「等待授权」恰恰是这类工具最核心的通知场景**。

因此本插件不走向 CC bridge 兼容层,而是走原生 Cordis 插件路线,直接订阅 approval/request 与 user-questions/request 两个 waterfall 扩展点, 把 permission_required / input_required / run_failed 三个事件补齐。

两条 waterfall 的纪律(对 DSH 是安全前提)

approval/request 与 user-questions/request 都是 waterfall,ApprovalOutcome 是 fail-closed 闭集: 监听器如果不调用 next() 就返回,会短路整条审批链,导致工具被拒或授权弹窗永不出现。

本插件的两个监听器一律「先通知、再无条件委托」,且通知包在 try/catch 内、return next() 放在 try 之外:

ctx.on('approval/request', (req, next) => {
  try { forward({ /* ... */ }) } catch { /* swallow */ }  // 只通知
  return next()                                           // 必须:不可省略、不可条件化
})

所有监听器体都包 try/catch(agent/created 是 await 的,抛出会污染会话启动); 投递使用分离式 spawn + unref(),永不阻塞、永不抛出。契约见 §4。


配置

配置由 schemastery 校验,4 个字段都带默认值,因此 {} 即合法。定义见 src/config.ts。

字段 类型 默认值 说明
enabled boolean true 关闭后监听器仍注册,但不投递任何事件(便于单独测量监听器开销)
binaryPath string(可选) 未设置 指定 agent-notify 二进制绝对路径。设置后优先于环境变量(它覆盖下面的定位第 1 级)
sessionStart boolean true 是否转发 agent/created 为 SessionStart。它不发通知,只为让 Go 侧捕获本会话的点击聚焦窗口
debug boolean false 每次投递向插件 logger 打一行诊断;默认静默

写进 profile 的 patch 会整行替换 config 而非合并,所以只写非默认值即可,例如:

- insert:
    - id: agent-notify-dsh
      name: agent-notify-dsh
      config:
        debug: true
        binaryPath: /Users/me/.agent-notify/agent-notify

二进制定位(固定 3 级优先级)

严格按契约 §2 顺序解析,找不到就静默放弃,绝不抛出:

  1. 环境变量 AGENT_NOTIFY_BINARY(非空即用,便于 make local 与测试)
  2. <home>/.agent-notify/agent-notify(Windows 为 agent-notify.exe)——npx launcher 与 make local 的共同落点
  3. 都没有 → 返回 null,该次投递静默丢弃

home 取 os.homedir()。插件配置里的 binaryPath(见上表)会覆盖第 1 级。


兼容性

  • 开发基线:DSH 已发布包 0.2.0-rc.1(@deepseek-ai/dsh-agent / dsh-session / dsh-user-approval / dsh-user-questions 均锁定该版本;peerDependencies 为 @deepseek-ai/cordis ~4.0.4)。
  • DSH 处于 rc/alpha 阶段,事件签名可能漂移。插件对未知事件名静默跳过,不会因此报错。
  • Node.js >=20。

挂载方式

插件是标准 npm 包,manifest 里声明 bundle patch:

"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }

DSH 启动时按 bundle 层栈叠加各插件的 patch,本插件用一条 insert 把自己的 entry 插入 profile 的配置树 (见 cordis.patch.yml):

- insert:
    - id: agent-notify-dsh
      name: agent-notify-dsh

name 与 src/index.ts 导出的 name 一致;inject 刻意声明为空数组, 这样 agents 服务缺失时插件仍能加载(否则整个 profile 起不来),根判定处用 ctx.agents?.roots() ?? [] 做可选访问。


开发

npm install
npm run typecheck   # tsc --noEmit
npm test            # vitest run
npm run build       # 产物到 lib/

CI(.github/workflows/ci.yml)在 push 与 pull_request 上按 Node 20 / 22 矩阵跑 npm ci → npm run typecheck → npm test。


许可

MIT