DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

Hao0311 /

Hao0311/dsh-deferred-approvals

Verified

Non-blocking approvals and timed questions for DeepSeek Harness: permission requests park as pending items while the model keeps working, and ask_user_question runs in timed mode.

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

dsh-deferred-approvals(非阻塞审批)

审批不再阻塞模型:需要用户授权的工具调用会立即得到“已挂起、未执行”的结果并记为一个待办 (编号如 #A1),模型同一回合继续做其它工作;你随时回来在输入框位置(原生审批样式)的 「待处理审批」卡片里允许 / 拒绝。允许过的调用重试时由宿主自动放行(一次性授权), 也可以选择「本对话允许同类」不再反复问。会话列表里对应对话旁会出现原生审批那样的橙色小点。

权限边界(关键)

审批是宿主(sandbox / approval 层)的权限,不是对话内容:

  • 模型在 workspace-write 沙箱下拿不到更宽的权限,只能发起 sandbox_permissions 升级请求;
  • 插件只接管原生本来就会发生的审批:是否要审批完全由原生策略/沙箱决定。会话策略是 never 或原生不需要审批时,插件不介入、不制造审批(forceAskPolicy 默认 false,绝不擅自改会话策略);
  • 插件拦截该请求并把它挂起,给工具返回“未执行”,同时记录待办;
  • 唯一的授权来源是用户在界面上点击(允许一次 / 本对话允许同类)。宿主据此记录 “工具 + 参数指纹”的一次性授权;授权与那次调用绑定、用掉即销、默认 5 分钟过期;
  • 模型收到的只是一条宿主事件通知(user/message 但 source.kind 不是 user,在 Chat 里 渲染为上下文行,不是用户气泡)。它只是让模型知道“可以重试了”,不是授权凭据;
  • 模型自己声明已获批准、或直接重试时,宿主没有匹配的授权就仍然把它重新挂起—— 对话内容无法授予权限。重试时参数指纹必须匹配(command 等实际参数一致; justification / description / timeoutMs 不参与指纹)。

它解决的问题

  • 以前:审批请求一出现,整个 turn 停住,模型必须等你点了才继续;你离开几分钟,任务就卡几分钟。
  • 现在:审批变成后台待办,模型继续跑其它子任务,或改用不需要提权的做法;你回来再批。
  • 你批过的操作:宿主事件通知模型重试原调用,宿主按一次性授权放行(不再弹框)。

工作方式

  1. 拦截:在 approval/request 瀑布最外层(prepend)拿到请求;台账记下工具名、参数指纹、 权限级别与理由,立刻给这次调用返回 rejected(工具层显示“未执行”,下面第 2 步改写文案)。
  2. 改写结果:tools/post-execute 把结果文案换成“已挂起为待办 #A1,不要等待也不要反复重试; 没有别的事就结束本回合等宿主事件;只有已用其它方式完成才调用 pending(action="cancel") 撤销”。
  3. 模型继续:同一个 assistant 回合继续下一步,做其它工作。
  4. 用户决定(输入框位置的原生式卡片,会话列表橙色小点提示):
    • 允许一次:宿主记一次性授权(指纹匹配、5 分钟内有效、用掉即销);通知模型重试;
    • 本对话允许同类:宿主记一条会话规则(工具 + 权限级别),之后同类请求直接放行,本次已排队的同类待办一并批准;
    • 拒绝 / 忽略:关掉待办;拒绝会带备注告诉模型换方式,忽略静默作废。
  5. 送达(含中断):宿主事件消息(source.kind = 'deferred-approval')。模型空闲就 agent.followup (开新一轮);模型在跑时——若本会话没有工具正在执行(正在长考),先 agent.cancel({keepInbox}) 中断当前思考再投递,决定一做出立刻可见;有工具在跑则退回 agent.steer(下一步边界送达),避免杀掉运行中的工具。会话不在线则排队到下次恢复。
  6. 决定台账:每次批准/拒绝/忽略/过期/撤销都会记一条“最近决定”;pending 的 cancel/list 据此给出确定答复(“已被用户批准——不要撤销,直接重试”),不再返回含糊的“可能已批准”。 待办刚被撤销/过期时用户又点了卡片上的按钮(迟到点击),决定仍会被尊重(迟到放行/拒绝)。 面板同时展示「最近已处理」(/state 的 decided 字段),你刚点的决定如何处置一目了然。 重复点击同一按钮是幂等的:第二次起直接返回 ok,不重复授权、不重复打扰模型。 decide 请求带 sessionId 时校验待办归属,跨会话的误点会被拒绝(旧客户端不带则放行,平滑过渡)。
  7. 过期:待办默认 15 分钟过期,静默作废、不唤醒模型;pending 工具可查待办、最近决定和已放行规则。

安装

在工作区目录 plugins/dsh-deferred-approvals 下,通过 plugin_manager 的 install_bundle 以该绝对目录为 target 安装。安装后 Client 半会自动加载;Host 半的源码修改需要重启 DSH 才生效 (进程内已 import 的模块不会热替换,除非开启 HMR 源码监听)。

依赖:无。只用 node:crypto 与 ctx 服务(tools / approval / agents / systemPrompt, 可选 connection、typertGateway);Client 用 slots / locale / uiSession。

配置(写在 profile patch 的这一行 config 里,全部可选)

键 默认 含义
ttlMinutes 15 待办有效期(分钟),到期自动作废
grantMinutes 5 「允许一次」的一次性授权有效分钟数
softCapAway 20 没有在线客户端时的待办上限;有客户端时不设限
forceAskPolicy false 是否把显式设为 never 的根会话策略切回 ask。默认不动会话策略:原生不要求审批时插件也不介入
requireLiveClient true 没有在线 Web 客户端时交回原生审批,不做挂起
excludeTools [] 不接管这些工具的审批。默认空:连 plugin_manager(装/禁插件)也走挂起,批准后重试一次即可
interruptOnDecision true 用户做出决定时,若模型正在长考(本会话无工具在执行),中断当前思考立即送达通知;否则决定要等到当前这步 LLM 流结束才可见,慢模型会长时间以为“没人批准”并误撤销待办。有工具在跑时不中断(退回下一步边界送达),设 false 可整体关闭
sweepMs 20000 过期清理间隔
- id: deferred-approvals
  config:
    ttlMinutes: 15
    softCapAway: 20

未导出 Config schema,所以配置不做加载期校验;缺省值见上表。

已知限制

  • 待办只在 Host 内存里:DSH 重启后丢失(审批本来就是一次性的;已执行的结果仍在会话日志里)。
  • 需要 ask 策略 + 在线 Web 客户端:策略为 never 时审批服务在瀑布之前就拒绝,插件收不到, 也就是原生不要求审批时插件不介入;插件默认不修改会话策略,不改子代理。
  • 重试匹配依赖参数指纹:command 等实际参数一致才算同一次调用;模型改了命令会重新挂起(这正是 权限边界的体现,不是缺陷)。
  • 批准后不自动执行:由模型核对后重试原调用(保证工具调用仍出现在会话日志与 UI 里);若模型已用其它 方式完成,它会按提示撤销授权、不重复执行。
  • 中断只发生在“纯长考”时:本会话有工具在执行时不中断(避免杀掉运行中的命令),决定退回到 下一步边界送达;被中断的思考会以 interrupted 消息保留在历史里。
  • 不接管的问题:ask_user_question 仍是原生的定时模式(见 DSH 的 tool-ask-user 配置)。
  • 原生审批审计只为“挂起”记一条 rejected;真正的批准记录在插件内存(v1 未落盘)。

验证状态

  • Host:语法、插件行激活、pending 工具注册、/api/deferred-approvals/* 路由注册;挂起→批准→一次性放行 的链路用 Node mock 全覆盖测试通过(含“换 description 重试不会放行”的负例)。
  • 回归测试在仓库里:node test/run.mjs(零依赖,Node ≥18,退出码 0 = 全过)。当前 38 项断言: 挂起改写、决定送达三分支(中断/steer/followup)、指纹放行、决定台账确定答复、迟到放行与迟到拒绝、 双击幂等、跨会话 sessionId 拒绝、/state 暴露 decided、同类规则批量批准与自动放行。
  • 2026-11-05 增量:长考时批准→中断思考立即送达(interruptOnDecision,默认开)、有工具执行时不中断 退回 steer、pending cancel 对已批准待办返回“不要撤销”、撤销/过期后的迟到批准与迟到拒绝(含撤销 既有放行)、idle 直接 followup、list 展示最近决定。
  • Client:conversation.composer 插槽已注册(priority 2)、旧浮层已移除、会话状态点复用原生 approval 状态;视觉与交互以实际页面为准(本机无浏览器自动化)。
  • 待重启验证:宿主事件通知渲染为上下文行(而非用户气泡)、侧边栏橙色小点、输入框位置卡片。

定时提问(原 dsh-timed-questions,已合并)

同一个 bundle 还把 standard 预设里的 ask_user_question 切成 timed 模式:

  • 模型提问后最多等 timeout 秒(默认 20);超时返回 {pending:true, callId},模型继续干活;
  • 问题卡片保留,随时可答;回答作为用户消息送回模型(在跑就下一步收到,空闲就开新一轮);
  • 单次调用可用 timeout 覆盖默认值,-1 表示这次必须等到回答;
  • 这是 DSH 内置能力(@deepseek-ai/dsh-tool-ask-user 的 mode: timed),本包只负责配置到预设。

生效范围:新会话。已存在的会话沿用启动时的预设组合,需要新开一个对话。

升级 DSH 后

cordis.patch.yml 是对 standard 预设的整份覆盖(patch 的 config 是整块替换),DSH 升级后这份 副本可能过时。插件启动时会自动对比官方预设与本地副本,发现漂移就重新生成(保留 mode: timed 与 timeout),并提示需要重启后新会话才生效;也可手动:

node check-preset-drift.mjs --regenerate

卸载

plugin_manager 的 remove_bundle(target 为 dsh-deferred-approvals):审批与定时提问一起恢复原生行为。

—/ 5

No ratings yet

Verified DSH bundle

Commit cd9b2fa140e1

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