feat: 权限档位体系(三档 plan/workspace/full + 四桥 from_session_id)
L2 核心改动:sessions 表补 permission_mode / permission_enforcement 两列 (sqlite + pg 同步),三桥 lib/permission-mode.js 翻译档位到平台原生配置, homeagent advisory 模式提示词告知模型实际强制力。四桥全部携带 from_session_id 供 relay 去重与会话回溯。 FromHuman / ToHuman 判据已加入心跳 payload 与 notify/mail.go。
This commit is contained in:
@ -121,6 +121,21 @@ func Recipients(ctx context.Context, m Mail) {
|
||||
// 生产实测 pi 与 dsh 互相客套 6 轮直到撞上 hop 上限。
|
||||
fromHuman, _ := repo.IsHumanUser(ctx, m.From)
|
||||
|
||||
// 会话级权限档位与强制力。
|
||||
//
|
||||
// 为什么跑在 payload 外:一封邮件可能推给十几个参与方(收件人 + 拄送),
|
||||
// 而档位是**会话**的属性,每个人都一样 —— 放进闭包里就是每个参与方
|
||||
// 查一次库。platformFor 那个坑(会话级的值推给所有人)教过的是反面:
|
||||
// 会话级与参与方级的字段必须分清楚。档位确实是会话级的。
|
||||
perm, permErr := repo.GetSessionPermission(ctx, m.SessionID)
|
||||
if permErr != nil {
|
||||
// 查不到时给默认档 + advisory:不能因为一次查询失败就让插件以为自己拿到了全权。
|
||||
perm = repo.SessionPermission{
|
||||
Mode: models.DefaultPermissionMode,
|
||||
Enforcement: models.EnforcementAdvisory,
|
||||
}
|
||||
}
|
||||
|
||||
payload := func(role, workspace, forName string) map[string]interface{} {
|
||||
p := map[string]interface{}{
|
||||
"mail_id": m.MailID.String(),
|
||||
@ -160,6 +175,18 @@ func Recipients(ctx context.Context, m Mail) {
|
||||
// 插件据此不再对 Agent → Agent 的信说「回信不用你自己发」:
|
||||
// 那句话在那种情形下是假的,而它让模型以为自己只需要「把话说完」。
|
||||
"from_human": fromHuman,
|
||||
// permission_mode 声明本任务允许动手到什么程度:plan / workspace / full。
|
||||
//
|
||||
// 插件必须把它**翻译成平台原生的沙箱/审批配置**,而不是自己按工具名猜着拦:
|
||||
// 那会同时违反 I-1(平台原生信号是唯一真相来源)与 I-4(插件只搬运不决策),
|
||||
// 而且四个插件对「workspace 到底管什么」必然各猜一套。
|
||||
"permission_mode": perm.Mode,
|
||||
// permission_enforcement 是建会话时快照的**事实**:native / advisory。
|
||||
//
|
||||
// 与 permission_mode 成对下发:前者是要求,后者是对方平台实际做得到。
|
||||
// 只给前者会让人以为 plan 档把 homeagent 管住了 —— 它的核心没有
|
||||
// 工具调用拦截点,档位在那里只能写进提示词。
|
||||
"permission_enforcement": perm.Enforcement,
|
||||
}
|
||||
if m.Origin != "" {
|
||||
p["origin"] = m.Origin
|
||||
|
||||
Reference in New Issue
Block a user