DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

Aily-DSH /

Aily-DSH/dsh-approval-inbox

Verified

Approval inbox for DeepSeek Harness: blocking approvals become a background queue so the agent keeps working, with project sections, row badges, importable rules and peak-hour holding

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@297be29f

dsh-approval-inbox

dsh-plugin plugin market license dsh

把「等你点头」变成「排队等你点头」:Agent 遇到要授权的步骤不再卡住,而是把请求丢进收件箱、自己继续做别的;你什么时候有空什么时候批,批完它自己接着跑。

English · 安装 · 快速上手 · 规则 · 已知问题 · 工作原理


背景

DeepSeek Harness 里有两件事会白白吃掉时间:

  1. 审批阻塞。Agent 跑到一个需要你确认的调用(删目录、强推、写系统路径),会停下来等你。你不在屏幕前,整条流水线就闲置——哪怕它手上有五个不需要授权的活可以干。
  2. 算力错峰。DeepSeek 按峰谷价计费(工作日 09:00–12:00、14:00–18:00 北京时间约为低谷价 2 倍,对应 UTC 01:00–04:00、06:00–10:00)。峰时停下来最省钱的假设只在"所有活都吃 token"时成立;本地测试、构建、数值验证这些占时间但不烧 token 的活,峰时照做才对。

这个插件同时处理这两件:

  • 用 tools/pre-execute 把待授权调用拦下入队并拒绝当前 step,模型收到「已排队、继续别的」后接着干下一个 step;
  • 把队列摊在左侧一个常驻面板里,按项目 / 会话分区,会话行上挂数字角标;
  • 你批准后经 schedule.create 投递重试消息,并留一张一次性凭据,它重试同一调用时放行一次;
  • 峰时批准会把投递排到谷时边界,吃算力的调用(task / web_search / image_gen …)在峰时自动压后,谷时自己执行。

功能

不阻塞

  • 🚦 命中规则的调用返回 deny 而不是 ask:当前 step 立刻结束,不产生任何等待中的 Promise
  • 📥 入队后立刻可见,模型在同一回合里继续做别的事,不需要你介入
  • 🔁 批准 → 投递重试 → 凭据放行一次 → 再调再拦,闭环有测试守着

收件箱 UI

  • 📌 左侧导航行「决策收件箱」:常驻、不可关闭,关掉右侧标签也不会失去入口
  • 🗂 全局面板:项目 → 会话两级分区,逐条批准 / 拒绝 / 总是允许 / 批量批准,可批注
  • 📄 右侧栏「待批阅」标签:只看当前会话,已注册到侧栏引导页
  • 🔢 会话行角标:在转圈的同时显示黄色数字(1–99,超过显示 99+),颜色继承官方 --dsw-alias-state-warn-*
  • 🈶 中英双语,跟随界面语言

规则

  • 🎚 三种动作:allow 放行、defer 入队、deny 直接拒绝(并告诉模型别再试)
  • 🧩 十一个条件:tool / kind / scope / project / session / contains / notContains / regex / notRegex / path / minRisk
  • 📦 规则集是普通 JSON:面板内导出下载、粘贴导入(合并或替换)、单条试跑告诉你哪条会赢
  • 🛡 出厂 7 条只拦不可逆或离开本机的事,只读与本地构建一律放行

峰谷联动(配 dsh-peak-guard 或独立工作)

  • 🌗 读 peak-guard 的阶段快照;没装也能用内置日历(chinese-days + 官方峰时窗口)
  • ⏳ 峰时批准 → 投递排到谷时边界,重试那一轮按低谷价
  • 🤖 峰时对吃算力的工具族自动压后,谷时自动批准投递,不占你一次点击

外部接入

  • 🧾 POST /raise 与文件投递箱 ~/.dsh/dsh-approval-inbox/inbox/:任何进程都能登记一条待决
  • 🤝 与 dsh-superagent 联动:工单进入 blocked 或向人提问时,事件自动进收件箱

环境要求

  • DeepSeek Harness >= 0.2.1-rc.2(桌面客户端或带宿主服务的 web 部署)
  • Node.js ^22.19.0 || >=24.0.0
  • 宿主进程保持运行(插件跑在宿主里;关掉 DSH 就没人拦、没人投递)
  • 可选:dsh-peak-guard >= 0.3.2(只为拿到"手动停过"这个状态;没有则自算)

安装

从 GitHub 安装(推荐)

dsh plugin --profile <profile> add "git+https://github.com/Aily-DSH/dsh-approval-inbox.git"

dsh 不在 PATH 时用客户端自带的 CLI:

cd "/d/Program Files/DeepSeek Harness/resources/runtime/cli/bin"
./dsh.cmd plugin --profile desktop add "git+https://github.com/Aily-DSH/dsh-approval-inbox.git"

desktop profile 的特殊情况

桌面客户端的 desktop profile 由 Electron 独占管理,CLI 会直接拒绝写入。两种可行方式:

  1. 客户端内安装:设置 → 插件 → 从 Git 地址安装(走 GUI 的同一套逻辑)。
  2. 手动装(本仓库开发时就是这么装的,已验证):
cd ~/.dsh/profiles/desktop
node -e "
const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));
p.dependencies['dsh-approval-inbox']='file:D:/DSH/plugins/dsh-approval-inbox';
const b=p.dsh.profile.bundles;
if(!b.includes('dsh-approval-inbox')){const i=b.indexOf('dsh-peak-guard');b.splice(i>=0?i+1:b.length,0,'dsh-approval-inbox');}
fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\n');"
npx --yes pnpm@11 install

file: 依赖在版本号没变时不会刷新副本。改过源码就把 node_modules/dsh-approval-inbox 删掉再 install,或者 bump 版本号。

本地开发安装

dsh plugin --profile <profile> add "file:D:/path/to/dsh-approval-inbox"

装完必须重启

宿主半(lib/index.js)只在启动时挂载,客户端半的注入清单也只在启动时计算。不重启不会生效。

快速上手

  1. 重启后看左侧导航:多了一行 决策收件箱。点开,面板顶部会显示当前是峰时还是谷时、下次切换的时间。

  2. 发一个会命中规则的请求:

    请执行 rm -rf ./dsh-selftest-tmp,然后告诉我结果。
    
  3. 期望:没有弹窗;面板里多出一条 bash 待批;会话没有卡住,模型在拒绝理由之后继续说话。

  4. 点 批准。模型收到一条「可以重试」的消息,重试同一个调用时放行一次;再跑一次又会重新排队。

  5. 想静音某一类操作:点 总是允许,它会提升成一条 allow 规则(在「规则」区能看到并随时删掉)。

界面

位置 作用 会不会消失
左侧导航行「决策收件箱」 常驻入口,点击打开全局面板;有待批时图标带数字 不会,这是 sidebar.panellist 席位,没有关闭按钮
全局面板 项目 → 会话分区;批准 / 拒绝 / 总是允许 / 批量;规则编辑与导入导出 不会,由左侧行唤起
右侧栏「待批阅」标签 只看当前会话 会:关掉标签后从面板「去会话」或侧栏引导页重新打开
会话行角标 该会话的待批数量,转圈时也显示 跟随会话列表渲染

角标的数字含义:这个会话里有几件事在等你。它和「需要决策」的黄色标记同一套色板,但位置在行尾(官方 session.row.leading 席位只在 idle 时渲染,满足不了"转圈同时显示")。

规则

规则是一个有序列表,先匹配先生效。每条规则一个动作:

动作 结果 用在哪
allow 直接放行 只读、本地构建、你信得过的常规操作
defer 入队,当前 step 被拒绝,模型被要求继续别的 不可逆、离开本机、需要你判断的操作
deny 直接拒绝并告诉模型不要重试 明确禁止的事(比如往生产库写数据)

条件字段:

字段 类型 说明
tool string[] 工具名,按 glob 匹配(* 不跨分隔符),如 bash、task*
kind string[] tool / question / blocked
scope string any(默认)/ main(你直接对话的会话)/ worker(子 Agent)
project string 项目(工作区)名,glob
session string 会话 id,glob
contains string[] 参数里出现任一子串才匹配(大小写不敏感)
notContains string[] 出现任一子串就排除(用来给 allow 规则开例外)
regex string 对「工具名 + 全部参数字符串」做正则匹配
notRegex string 命中就排除
path string[] 参数里的路径做 glob 匹配,* 不过分隔符、** 穿过;正反斜杠等价
minRisk number 风险分下限(0–3,按命令内容估算)

出厂规则:

id 名称 动作 拦什么
allow-workspace-writes 工作区内读写放行 allow 读文件、写文件、列目录、glob、grep、改 todo
allow-local-build 本地构建与测试放行 allow pnpm / npm / node / tsc / vitest / cargo / go / python / git status,add,commit 等
defer-force-push 强推与改写历史 defer git push --force、git reset --hard、git clean -f
defer-destructive 删除与格式化 defer rm -r、rmdir /s、del /s、format、mkfs、Remove-Item -Recurse
defer-outside-workspace 写系统目录或用户目录 defer C:\Windows\**、C:\Program Files*\**、**/AppData/Roaming/**、**/.dsh/**
defer-publish 发布与对外动作 defer npm publish、gh release、docker push、vercel --prod
defer-questions 需要人回答的问题 defer kind = question / blocked 的任何调用

导出与导入都是 JSON:

{ "format": 1, "rules": [ { "id": "my-rule", "name": "写生产库", "action": "deny", "order": 5,
  "match": { "tool": ["bash"], "regex": "prod\\.db" }, "reason": "生产库禁止直接操作" } ] }

面板内「规则与导入导出」可以下载、粘贴导入(merge 按 id 覆盖 / replace 整体替换)、并对一条假想调用试跑,告诉你哪条规则会赢。

完整说明见 docs/RULES.md。

配置

配置文件 ~/.dsh/dsh-approval-inbox/config.json(首次启动自动生成;面板里能改大部分)。

字段 默认 说明
enabled true 总开关;关掉后 UI 仍在,但不拦任何调用
nativeApproval rule 原生审批怎么答:rule 按规则、allow 全放、defer 全入队
grantTtlMinutes 30 一次性凭据的有效期(分钟)
grantScope exact exact 要求参数完全相同;tool 放宽到同会话同工具
maxQueue 400 队列容量(50–5000),超出先丢最旧的已关闭条目
expireHours 168 待决条目超过多少小时自动过期
deferRiskAt 0 没有规则命中但风险分达到此值时也入队;0 关闭
questionTools 内置 6 个 哪些工具名算「Agent 在问你」
peak.hold true 峰时是否把批准压到谷时
peak.deferTools task subagent web_search image_gen video_gen 峰时自动压后的工具族
peak.maxHoldHours 22 单次压后最多表达多少小时
rules 7 条出厂规则 规则列表

HTTP 接口

宿主只监听回环地址。token 与 GUI 相同(?token=…)。

方法 路径 作用
GET /api/dsh-approval-inbox/state 全量状态(配置、队列、计数、阶段)
GET /api/dsh-approval-inbox/counts 只取角标计数,供 UI 轮询
POST /api/dsh-approval-inbox/decide { id, decision: approve|deny, note, always, now }
POST /api/dsh-approval-inbox/decide-batch { ids, decision }
POST /api/dsh-approval-inbox/raise 外部登记:{ sessionId, kind, title, detail }
GET /api/dsh-approval-inbox/rules/export 下载规则 JSON
POST /api/dsh-approval-inbox/rules/import { json, mode: merge|replace }
POST /api/dsh-approval-inbox/rules/test 试跑一条匹配
POST /api/dsh-approval-inbox/config 局部配置更新

不想猜端口就往文件投递箱写:~/.dsh/dsh-approval-inbox/inbox/*.json,tick(30 秒)消费后删除。字段同名,另有 dedupeKey 去重。

工作原理

一次工具调用
  │
  ├─ rules allow ───────────────────────────────▶ 正常执行
  │
  └─ rules defer
        ├─ 写入 queue.json(原子替换 + 上一代 .bak)
        ├─ 返回 { kind:'deny', reason:'已提交人工审批 #xxxx;先继续别的,稍后会通知' }
        │     └─ 当前 step 结束 → 模型读完理由 → 下一 step 继续干别的
        │
        └─ 你点「批准」
              ├─ schedule.create(sessionId, { prompt:'可以重试…', after_seconds })
              │     └─ 峰时:after_seconds = 距谷时边界的秒数
              └─ 记一张一次性凭据(session + tool + argsHash,TTL 30 分钟)
                    └─ 模型重试同一调用 → 凭据命中 → 放行并消耗

每 30 秒一次 tick 负责:过期清理、凭据清扫、峰时压后条目在谷时自动批准、消费文件投递箱。

为什么是 deny 而不是 ask:内核的 approval/request 只在未结束的回合内有效,且只支持 allowed-once,跨回合挂起会被当成崩溃尾巴。也就是说"挂起一个审批等你两小时后点"在内核层不存在;ask 会把等待塞进工具调用里,整个 step 卡死——正是要消除的症状。详见 docs/ARCHITECTURE.md。

峰谷联动

装了 dsh-peak-guard 0.3.2+ 时它会写 ~/.dsh/dsh-peak-guard/state.json,本插件按这个优先级取阶段:

  1. 该文件(5 分钟内有效)——唯一知道「你手动停过」的来源;
  2. 自己的同名规则(官方峰时窗口 + chinese-days 节假日 + peak-guard 的按日覆盖);
  3. 都没有 → 视为谷时,不干预。

峰时的两个动作:

  • 压后批准:投递 after_seconds = 距谷时边界的秒数,重试那一轮按低谷价计费;
  • 压后算力调用:task / subagent / web_search / image_gen / video_gen 直接入队并标 autoRelease,谷时 tick 自动批准投递,不用你点。

配套改动:dsh-peak-guard 0.3.2 起每次 tick 写那份快照(纯投影,写失败即吞);不装也不影响本插件。

状态文件

~/.dsh/dsh-approval-inbox/
  config.json        配置 + 规则
  queue.json         队列 + 一次性凭据
  queue.json.bak     上一次写入的前一代(损坏时自动回退)
  inbox/*.json       外部投递箱,消费后删除

DSH_HOME 优先,所以隔离实例有自己的一套状态,不会碰到真机。

已知问题

诚实分成两栏:缺陷(还没修) 和 限制(设计使然)。带复现路径、初步定位和修复方向的详版在 docs/KNOWN-ISSUES.md。

缺陷 — 还没修

# 问题 影响 现在怎么规避
K1 多实例并发写会互相覆盖。写盘已经是「先备份、再临时文件、最后 rename」的原子替换(不会写出半个 JSON),但没有跨进程锁:两个宿主(desktop + dsh web)同时运行时,后写的一方会用自己内存里的队列覆盖对方新增的条目 同时开两个实例时,新入队的某一两条可能丢;不会损坏文件 同一时间只开一个宿主;需要并行就用不同的 DSH_HOME
K2 角标依赖官方 DOM 结构。它靠 [data-row-key^="session:"] 找会话行,官方改版或换属性名就失效 表现是角标不显示;拦截、面板、投递全都不受影响 无(等官方提供能显示在 running 状态上的席位)
K3 原生审批看不到参数。approval/request 只带工具名,没有参数 普通会话的原生审批只有「按工具名」的规则能生效;其余交还原生应答方,因此仍会弹窗 想全部接管就把 nativeApproval 设成 allow 或 defer
K4 被拒绝后模型可能不重试而直接报 blocked。拒绝理由写了「先继续别的、稍后会通知」,但模型行为不完全可控 任务可能提前停下等你 实测调规则粒度;必要时把 deferRiskAt 打开放宽命中
K5 grantScope: tool 会放宽。凭据按「会话 + 工具名」放行一次,不校验参数 同会话里另一个同工具的调用可能吃掉这张凭据 保持默认 exact
K6 客户端 UI 没有逐屏人工验收。已验的是:bundle 进了 application 阶段、假 DOM 下真实 apply() 的注册与角标逻辑、43 个自动化用例 布局细节、极端窗口宽度、主题切换下可能有瑕疵 按 docs/SELFTEST.md C1–C6 走一遍
K7 会话被删后投递失败。条目会停在 approved,stats.failed 加一 那条决定不会真的到达任何会话 面板里能看到,手动再点一次或删掉
K8 dsh plugin --profile desktop add 会被拒。desktop profile 由 Electron 独占 命令行装不上 用客户端插件页,或按安装里的手动方式

限制 — 设计使然

  • 只能排到调用粒度。一个回合里「先设计、再测试」的顺序由模型自己决定,插件插不进去。峰时能做的是「压后吃算力的调用 + 放行本地活」,做不到"这个任务的设计步骤放谷时、测试步骤放峰时"。
  • 批准是一次性的。想长期放行就用「总是允许」生成规则,或者调 grantTtlMinutes。
  • 峰时压后依赖那次投递还在。会话被清空、进程重启都可能让 after_seconds 的那条消息落空。
  • 队列容量 400。超出先丢最旧的已关闭条目,历史会变短;待批条目不受影响。
  • enabled=false 不是卸载。UI 还在,只是不拦;要彻底移除见卸载。
  • 投递至少 1 秒延迟(schedule.create 的下限)。

卸载

dsh plugin --profile <profile> remove dsh-approval-inbox

再删状态目录(可选,会丢掉待批与规则):

rm -rf ~/.dsh/dsh-approval-inbox

配套的上游改动不回滚也不会坏:peak-guard 多写一个没人看的小文件,superagent 往一个没人读的目录写 JSON。回滚步骤见 docs/ROLLBACK-AND-UPSTREAM.md。

目录结构

lib/index.js            宿主半:规则评估、拦截、队列、HTTP 路由、投递、tick
lib/client.js           客户端半:左侧常驻行、全局面板、右侧标签、会话行角标
lib/core/rules.js       规则规格化、评估、导入导出
lib/core/match.js       glob / 正则 / 参数摘要 / 风险分
lib/core/queue.js       队列与凭据(纯内存 + persist 回调)
lib/core/peak.js        峰谷日历(读 peak-guard 快照,退化为自算)
lib/core/sessions.js    会话目录(标题、项目、worker 判定)
lib/core/config.js      配置默认值、补丁校验
cordis.patch.yml        把插件行插入 profile 的 cordis 配置
docs/                   架构、规则、验收、回滚、已知问题
test/                   43 个用例(纯逻辑 16 / 宿主 19 / 客户端 8)
scripts/publish.ps1     版本校验 → 测试 → 打包 → 提交 → 打 tag

开发

node --test test/core.test.mjs test/host.test.mjs test/client.test.mjs
  • 纯逻辑:规则、队列、日历、配置边界,零依赖。
  • 宿主半:假 Cordis 上下文 + 真实 tools/pre-execute 处理器 + 真实队列文件 + 假 schedule。覆盖完整闭环:拦下 → 批准 → 投递 → 凭据命中一次 → 再次拦下;以及写盘回退(把 queue.json 写坏,重挂载后从 .bak 恢复)。
  • 客户端半:假 __ModuleLoader__ / React / DOM,跑真实 apply(),验证席位名、选项形状、角标增删。

隔离运行(不碰真机状态):

DSH_HOME=D:\DSH\test-home dsh web --port 14399 --no-open

发布:

pwsh -File scripts/publish.ps1 -Version 0.1.1 -Push

赞赏

如果这个插件帮你省了时间,欢迎扫码请作者喝一杯 ☕(纯属自愿,不影响使用和更新)

赞赏码

生态与收录

  • GitHub topic:dsh-plugin(DSH 社区约定的插件标记),本仓库另带 dsh / deepseek-harness / plugin 等 topics。

  • Aily-DSH 插件市场索引:本插件登记在 Aily-DSH/dsh-plugin-index(format 1,每个版本 pin 一个 commit)。把索引地址加进 profile 就能在客户端内看到:

    # ~/.dsh/profiles/desktop/cordis.patch.yml
    - id: plugin-market
      name: "@deepseek-ai/dsh-plugin-market"
      config:
        indexes:
          - https://cdn.jsdelivr.net/gh/Aily-DSH/dsh-plugin-index@main/index.json
    

    分类 agents-workflows,关键词 approval / inbox / permissions / queue / superagent / peak-hours。

  • 社区目录:cccakeee/awesome-dsh-plugins 收录后可被 deepseekharnessplugins.com 等站点搜到。

  • 版本可溯源:每个 release 打 vX.Y.Z tag,tag 信息里记着当时依赖的上游版本;CHANGELOG.md 按版本记录行为变化。

  • 查不到时:npm / GitHub 搜索有索引延迟(1–3 天),直接用 dsh plugin add "git+https://github.com/Aily-DSH/dsh-approval-inbox.git" 装。

English

dsh-approval-inbox turns blocking approvals into a background queue for DeepSeek Harness.

When the agent reaches a call that needs your approval, most setups stop the whole turn and wait. This plugin intercepts the call at tools/pre-execute, answers it with a deny carrying "already queued, keep going", and parks the request in an inbox. The agent continues with everything that does not need you; you approve whenever you get back, and a one-shot grant lets the retried call through exactly once.

The inbox is a permanent row in the left rail (not closable), a panel grouped by project → conversation, a right-sidebar tab scoped to the current conversation, and a numeric badge on each conversation row that shows up even while that conversation is busy. Rules are an ordered list with allow / defer / deny and nine match conditions; rule sets export and import as plain JSON.

Paired with dsh-peak-guard, approvals made during DeepSeek's peak-priced hours are delivered at the off-peak boundary, and compute-heavy tool families (task, web_search, image_gen, …) are held automatically until the price drops.

dsh plugin --profile <profile> add "git+https://github.com/Aily-DSH/dsh-approval-inbox.git"
# restart the DeepSeek Harness client, then look for 决策收件箱 in the left rail

Requires DeepSeek Harness >= 0.2.1-rc.2 and Node ^22.19.0 || >=24.0.0.

Known issues are listed in 已知问题: no cross-process lock on the queue (run one host at a time), the row badge depends on the client's DOM shape, native approval/request carries no arguments, and the UI has not been reviewed screen by screen in a real client.

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 297be29f13be

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout