feat(mcp)★★: 自动转发通道 —— relay/relay_key,并修掉一个从未生效的幂等文案
用户 2026-10-04:「不只是 Agent 主动回,还有自动转发 Agent 的最后一条消息」。
## 为什么 MCP 侧需要显式上报,而插件侧不用
插件(pi / dsh / homeagent)有 `session.idle` 钩子:
宿主进程在自己会话 idle 时读到最后一条回复 → 自动 send_mail(relay=…)
MCP 宿主(Codex / Claude Desktop)**没有这个概念**:
不知道「这一轮处理完了」,也不会主动调 send_mail。
⇒ 宿主必须显式上报,服务端才可能自动转发。
所以新增 `send_mail(relay="summary", relay_key="<宿主消息 id>")`:
relay=summary 免配额(服务端既有逻辑),relay_key 提供幂等。
## 顺带修掉一个从未生效的幂等文案(既有缺陷)
MCP 侧读:str(payload, "duplicate_relay")
服务端返:{"status": "duplicate_relay", "detail": "该上游消息已转发过…"}
**字段名对不上 ⇒ 那段逻辑从来没生效过。**
后果对 MCP 接入端尤其致命:它的自动转发正是靠 relay_key 幂等兜底,
而宿主重试时看到的是「已发送」(无 mail_id、无任何异常),
于是继续重试 —— 每一轮都在消耗一次往返。
现在文案是:
该上游消息(relay_key: xxx)已转发过,本次调用未产生新邮件 —— 不需要重试。
## 判据(3 格,2 个变异都转红)
- send_mail 暴露 relay / relay_key(去掉透传 → 转红)
- relay 非空而 relay_key 为空必须被拒
- 同一 relay_key 第二次转发报重复,且**只有一封真邮件**
## 判据自己错了两次
1. `TestRelayRequiresKey` 红着但**原因不对**:没建收件人,400 发生在
「收件人不存在」那一步,掩盖了 parseRelay 的校验。
2. 幂等那格被我自己 `Skip` 了 —— `.relay-idem` 会话不存在,
于是「幂等没被击穿」的假绿。改为 `.new` 并**禁止 Skip**:
幂等是自动转发不重投的唯一保证,它绿着才敢让宿主上报。
(判据不可达 = 描述的状态不存在。)
## 双向对齐判据立刻证明了价值
加了 relay/relay_key 却没登记,它当场红:
★ send_mail 的参数 "relay" 既不在插件版、也没登记为 MCP 额外
若它不存在,加了新参数就会静默通过,而「这个能力是有意加的还是漏登记的」
正是以后没人能回答的问题。relay 登记为「额外」而非「缺失」——
插件侧走 session.idle,不需要这条参数。
14 包全绿。
This commit is contained in:
@ -210,8 +210,25 @@ func summarizeSend(payload map[string]any) string {
|
||||
if sid != "" {
|
||||
fmt.Fprintf(&b, ",落在会话 %s", sid)
|
||||
}
|
||||
if relay := str(payload, "duplicate_relay"); relay != "" {
|
||||
fmt.Fprintf(&b, "。注意:%s", relay)
|
||||
// ★ 幂等重放(2026-10-04 修)。
|
||||
//
|
||||
// 这里原本读 `payload["duplicate_relay"]` —— 而服务端(handler/mail.go
|
||||
// 的 ClaimRelay 分支)返回的是 **`status: "duplicate_relay"`**。
|
||||
// 字段名对不上 ⇒ 这段逻辑从来没生效过 ⇒ 宿主重试拿到的是
|
||||
// 「已发送」而没有 mail_id,看起来像投递成功,于是继续重试。
|
||||
//
|
||||
// 对 MCP 接入端这条尤其致命:它的「自动转发」正是靠 relay_key 幂等
|
||||
// 兜底的,而宿主看到「已发送」就不会改参数 —— 于是同一条上游消息
|
||||
// 被反复转发(服务端不会真的重复建信,但每一轮都在消耗一次往返)。
|
||||
if st := str(payload, "status"); st == "duplicate_relay" {
|
||||
b.Reset()
|
||||
key := str(payload, "relay_key")
|
||||
if key != "" {
|
||||
fmt.Fprintf(&b, "该上游消息(relay_key: %s)已转发过,本次调用未产生新邮件 —— ", key)
|
||||
} else {
|
||||
b.WriteString("该上游消息已转发过,本次调用未产生新邮件 —— ")
|
||||
}
|
||||
b.WriteString("不需要重试。")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user