dsh 桥同一处缺陷:409 带 full 档时也当场拒绝(并且把会话降级了)
pi 报的是它自己的桥,但**同一处缺陷 dsh 桥也有**(`src/index.ts` 的 409 分支无条件 `return 'rejected'`),而且后果多一层 —— dsh 的档位是通过 `applyPermissionMode()` 写进会话的(沙箱 + 审批策略)。补投漏传档位时 `applyPermissionMode(session, '')` 把会话**降级**成 workspace:`danger-full-access → workspace-write`、 `never → ask`,于是每个受守卫的工具调用都去问一次,再被 409 拒绝 —— 一条 full 档会话只要有一封补投邮件,这一轮的工具调用全被自己人拦死, **顺带把自己的权限也降了**。 修法同 pi 桥:409 分支先认回包里的 `permission_mode`,是 full 就 `return 'allowed-once'`(DSH 的 ApprovalOutcome 只认 allowed-once/rejected/cancelled/unavailable); plan 档与"链上没有人类"照旧 `return 'rejected'`。放行分支排在普通 409 之前,否则不可达。 判据 `test/permission-409-full.test.mjs`(含自检:拿掉放行分支必须红)。 自检那步发现我第一版判据又踩了同一个坑:「普通 409 分支里不许出现 allowed-once」 读的是**含注释**的正文,而那段的注释正好写着 "ApprovalOutcome 只认 allowed-once / rejected / …" → 误报。改成读剥注释的源码(规范里那条: 判"代码里有什么"读剥离版,判"理由写清了没"读原文)。 zcode / opencode 不转发权限询问(没有 409 分支),无需改。 验证:dsh 套件 383 通过(+2);变异(拿掉放行分支)→ 自检红。
This commit is contained in:
@ -1814,6 +1814,31 @@ export function apply(ctx: any, config: PluginConfig): void {
|
||||
//
|
||||
// 直接 denied 并把服务端的建议原文写进日志:模型从工具报错里看到
|
||||
// 拒绝后会自己换方式,而挂死时它连重试的机会都没有。
|
||||
/*
|
||||
* 409 有**两种**含义,正确反应相反(2026-09-14 修,与 pi 桥同一处缺陷):
|
||||
*
|
||||
* - 「这条链上没有人类」→ 当场拒绝(上面的理由);
|
||||
* - 「**本档根本不该问**」→ 服务端会在回包里带 `permission_mode`。
|
||||
* 若它说这条会话是 full 档,那这次询问本就不该发生(full 档工具调用无需审批),
|
||||
* 正确反应是**放行**,不是拒绝。
|
||||
*
|
||||
* 什么时候会走到第二种:补投路径漏传档位(`lib/catchup.js`,同日已修),
|
||||
* 于是 `applyPermissionMode(session, '')` 把会话**降级**成 workspace:
|
||||
* 沙箱从 danger-full-access 变 workspace-write、审批从 never 变 ask,
|
||||
* 每个受守卫的工具调用都去问一次,服务端按真实档位回 409 ——
|
||||
* 旧代码把它当「无人可问」拒绝,一条 full 档会话只要有一封补投邮件,
|
||||
* 这一轮的工具调用全被自己人拦死。
|
||||
*
|
||||
* **只认服务端明说的 full**:plan 档(该档语义是"不动手",拒绝是对的)
|
||||
* 与"链上没有人类"照旧拒绝 —— 猜宽了就是提权。
|
||||
*/
|
||||
if (e?.status === 409 && String(e?.body?.permission_mode || '') === 'full') {
|
||||
console.error(
|
||||
`[dsh-mail-bridge] 服务端判定本会话为 full 档,放行本次询问(${relayKey}):无需审批`
|
||||
);
|
||||
return 'allowed-once';
|
||||
}
|
||||
|
||||
if (e?.status === 409) {
|
||||
const hint = [e?.body?.error, e?.body?.detail, e?.body?.suggestion]
|
||||
.filter(Boolean).join(' ');
|
||||
|
||||
Reference in New Issue
Block a user