Files
MailUI4Agents/plugins/homeagent-mail-bridge/plugin_test.go
JianFeeeee 1ec88866ac fix(permission): 另外四家桥的"人类说明"缺口 —— 三家修、一家本来就有
承接 453f451(pi 桥)。用户批准后把同款缺口在其余四家逐一核对:
**zcode 本来就有**(`说明:${decision.note}`),homeagent / dsh / opencode 三家缺。

## homeagent(Go,能完整修)

SSE 事件结构里**根本没有 Note 字段**(json 里只有 decision/decided_by)⇒ 备注在
解码那一步就没了。补上字段,并把提示词抽成纯函数 `permissionDecisionPrompt(evt)`,
加了判据(说明必须出现 + 反向对照:无说明/空白说明不得凭空造出说明段)。
构建(`go build -buildmode=plugin`)后 install 到
`/home/newqqagent/plugins/homeagent-mail-bridge/plugin.bin` 并重启,已核验部署件
含新符号(`grep -a`,中文用 strings 查是查不到的)。

## dsh / opencode(平台回执放不下理由 → 分两步)

两家的审批回执都是**三态字符串**:DSH `ApprovalOutcome` 只有
allowed-once / rejected / cancelled / unavailable,openCode 只有 once / always / reject
—— **没有地方放人类的说明**。所以:

1. 提示词("你之前发起的权限请求已有结论:…")统一走 `permissionPrompt(data)`,
   带上 `用户的说明:…`。dsh 原有**三处**内联文案(续谈/新会话/通知投递),
   措辞分叉正是这类信息漏掉的地方 —— 判据直接钉"只有一处拼这句话"。
2. 带说明的决策**另投一趟通知**,让模型在会话里看到理由。代价是多一轮;比悄悄
   丢掉人的指令轻(原缺陷就是丢了指令,模型把同一条命令换写法又问一遍,连问 9 次)。
3. 决策回执不再被当成"新任务"(内容已随 permission_decision 交付),并记下
   `decision_mail_id` 防重复 —— 与 pi 桥同源。

判据:dsh / opencode 各 5 条(含"拿缺陷时的源码形态喂进来必须判红"的自检)。

## 部署与代价

- dsh → 快照 20260914-081456、opencode → 20260914-081516、homeagent → 新 plugin.bin,
  三家的服务 active 且心跳/连接已核。
- 重启 dsh 时它正在"续谈"一封邮件(08:10:45 日志)——事后核对:那一轮**已回完**
  (faad0037 的 parent = 4919aa88),没有丢活。
- 套件:dsh 372、opencode 323、homeagent go test ok、zcode 382 全绿。
2026-09-14 08:17:23 +08:00

31 lines
1.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"strings"
"testing"
)
// 人类的说明必须出现在注入给模型的提示词里。
//
// 缺陷现场2026-09-13人类在权限决策里写「我说了让你拉取仓库到program下你听不懂吗」
// 而注入的提示词只有"结论 + 决策人" —— 模型不知道要改什么,把同一条命令又问了一遍。
func TestPermissionDecisionPromptCarriesNote(t *testing.T) {
const note = "我说了让你拉取仓库到program下你听不懂吗"
got := permissionDecisionPrompt(mailEvent{
Subject: "拒绝", FromName: "jianf", Note: note,
})
if !strings.Contains(got, note) {
t.Fatalf("提示词必须原样带上人类的说明,实际:%q", got)
}
// 反向对照:没写说明时不得凭空造出"用户的说明"
plain := permissionDecisionPrompt(mailEvent{Subject: "同意", FromName: "jianf"})
if strings.Contains(plain, "用户的说明") {
t.Fatalf("无说明却出现了说明段:%q", plain)
}
// 空白说明同样算没有ss TrimSpace 过的)
if s := permissionDecisionPrompt(mailEvent{Subject: "拒绝", FromName: "jianf", Note: " "}); strings.Contains(s, "用户的说明") {
t.Fatalf("空白说明不该算说明:%q", s)
}
}