diff --git a/docs/PLAN.md b/docs/PLAN.md index b95790e..b400a1d 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -1561,6 +1561,21 @@ plan/workspace/full **一一对应** —— 不是巧合,是同一个问题的 + 心跳报 `native`(0c98fab) - [x] homeagent:`permission_mode.go` + advisory 提示词 + 心跳报 `advisory`(ed37032) +#### L5 实测结果(2026-09-06) + +12 格矩阵已完成 8 格实测。关键发现: + +| 平台 | plan | workspace | full | enforcement | +|---|---|---|---|---| +| **pi** | ✅ bash block(无权限询问) | ✅ bash → 权限询问邮件 | ✅ bash 直接执行 | native | +| **opencode** | ⚠️ 规则已传但 bash 仍执行 | ✅ bash 执行(权限询问转邮件) | ✅ bash 执行 | native* | +| **dsh** | ⚠️ read-only 但 Landlock partial | ✅ workspace-write + ask | ✅ danger-full-access | native* | +| **homeagent** | ⚠️ 只在提示词告知 | ⚠️ 只在提示词告知 | ⚠️ 只在提示词告知 | advisory | + +*opencode `session.create` 的 permission 参数被 API 静默忽略(不报错也不生效),实际权限由 opencode.jsonc 全局配置决定。dsh Landlock ABI 版本旧导致 partial enforcement(echo 等无害命令通过,但写文件应被拦)。 + +结论:**只有 pi 实现了 100% 档位强制**。其余三台都存在平台级限制。这是 PLAN 7.11 已记录的已知取舍,不阻塞上线。 + #### P5:前端与文档 - [ ] types / API / 卡片档位徽标 / 对话页档位选择器 / 新建邮件档位选择 diff --git a/plugins/opencode-mail-bridge/index.js b/plugins/opencode-mail-bridge/index.js index 0d03614..f991a2d 100644 --- a/plugins/opencode-mail-bridge/index.js +++ b/plugins/opencode-mail-bridge/index.js @@ -682,11 +682,15 @@ async function resolveSessionForMail(client, directory, data, kind) { // 故意不传 title:opencode 只在标题缺省时才让模型按首轮对话生成摘要标题, // 传了占位标题就等于掐掉平台自己的命名机制。标题稍后由 session.updated 事件回写。 - const permMode = normalizeMode(data.permission_mode); - const created = await client.session.create({ - query: wantDir ? { directory: wantDir } : undefined, - permission: opencodePermissions(permMode), - }); + let created; + try { + created = await client.session.create({ + query: wantDir ? { directory: wantDir } : undefined, + }); + } catch (e) { + console.error(`[mail-bridge] session.create 失败: ${e?.message || e}`); + throw e; + } const session = created?.data ?? created; const sessionID = session?.id; if (!sessionID) throw new Error("session.create 未返回 id"); @@ -901,6 +905,9 @@ async function deliverMail(client, directory, data, kind) { } const fromHuman = data.from_human === true; + // opencode 1.18+ 不支持 session.create permission 参数, + // 只能在提示词里告知模型档位约束(advisory 路径)。 + const permBriefing = modeBriefing({ mode: normalizeMode(data.permission_mode), enforcement: 'advisory', workspace: directory || '' }); const text = kind === "permission" ? `你之前发起的权限请求已有结论:${data.decision}(决策人:${data.decided_by || "用户"})。请据此继续后续工作。` : [ @@ -921,6 +928,8 @@ async function deliverMail(client, directory, data, kind) { `请先调用 read_inbox 读取完整正文(附带附件清单,如有附件可用 download_attachment 取回),然后处理其中的请求。`, ``, ...replyInstruction({ fromHuman, replyAddress: data.reply_address }), + ``, + permBriefing, ].join("\n"); // 按管理员划定的范围逐个尝试,全部失败才回一封说明失败原因的邮件。