opencode: 修正权限档位接线——session.create 不支持 permission,改用提示词 advisory
根因:opencode 1.18.29 的 session.create API 只接受 {parentID, title} + query.directory,
permission 字段被静默丢弃(SDK types.gen.d.ts 证实 SessionCreateData 无此字段)。
之前传入的规则不报错也不生效,plan 档下 bash 仍执行。
修复:
- 移除 session.create({permission: ...}) 调用(已被 API 忽略)
- 在 deliverMail 的 prompt 构造里注入 permBriefing(modeBriefing advisory 路径)
- permBriefing 与 homeagent 同理:如实说「这个平台无法强制这一档」
- 修正 catch (e: any) 语法错误(.js 文件不支持 TS 类型注解)
- 补充 import modeBriefing
L5 实测:opencode plan 档回信「由于当前权限档位为 plan(只读),我无法直接执行 bash 命令」
模型自愿遵守 advisory 约束(行为正确,但非平台强制)
This commit is contained in:
15
docs/PLAN.md
15
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 / 卡片档位徽标 / 对话页档位选择器 / 新建邮件档位选择
|
||||
|
||||
@ -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({
|
||||
let created;
|
||||
try {
|
||||
created = await client.session.create({
|
||||
query: wantDir ? { directory: wantDir } : undefined,
|
||||
permission: opencodePermissions(permMode),
|
||||
});
|
||||
} 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");
|
||||
|
||||
// 按管理员划定的范围逐个尝试,全部失败才回一封说明失败原因的邮件。
|
||||
|
||||
Reference in New Issue
Block a user