Commit Graph

2 Commits

Author SHA1 Message Date
1816a837ec fix(agent)★★: 会话作用域拒绝文案不再拼出「你当前在会话 。」
2026-10-05,比对 MCP 与插件接入能力时实测发现。

## 症状

独立 MCP 宿主读自己刚收到的那封信,得到:

    这封信不在你当前所在的那条会话里:…你当前在会话 。要读它,…

`你当前在会话 。` 中间是空的。

## 根因:一个 reason 值管三种拒绝,文案却只有一句

`AgentMayReadSession` 用**同一个 `reason`** 覆盖三种情形:

    ① 没声明 session_id(scope == nil)
    ② 声明了别人的会话
    ③ 声明了但**不是参与方**(session_id 由请求方提供,自述不是事实)

而 `canReadSession` 无条件拼 `"你当前在会话 " + self + "。"`,
于是 ① 拼出那句残缺文案。

★ 为什么这句残缺文案比「不说」更糟:模型读到「你当前在会话 。」
  会以为自己在一个**无名会话**里,于是转去猜/去搜 ——
  而正确做法(从 new_mail 事件取 session_id)就在同一句话的后半段,
  它却读不到前半段。

## 修法

按 `scope` 有无分句,**不给空值留位置**:

    ① → 「你当前**没有**声明自己所在的那条会话,所以无从判断这封信是不是你的。」
    ② → 「你当前在会话 00000000-…」(仍只报自己声明的那条)

②③ 故意不拆开:`session_workspace.go:171` 已经警告过「新增 reason 还要同步改
handler 的 switch,漏改就是 500」,而两者给模型的指引**完全相同**
(都是"去那条会话读"),拆开只增加分支不增加信息。

## 实测(真实请求,部署后)

    ① 不声明  → 「你当前**没有**声明自己所在的那条会话…」  无残缺 ✓
    ② 声明他人 → 「你当前在会话 00000000-…」(自己声明的那条)✓
    ★ 「会话 。」残缺句已消失

## 判据

`TestSessionScopeHintNeverLeavesEmptySessionSlot` —— 盯**结构**而非文案本身:

- 禁止再出现 `"你当前在会话 "+self+"。` 这种无条件拼接
- 要求按 `scope != nil` 分句
- 要求存在「未声明会话」那一句

两个变异都转红:退回无条件拼接 / 删掉「未声明」那句。

## 顺带确认:MCP 与插件的工具面已对齐

实测 `tools/list`(11 个)与 4 个插件的真实注册项做集合比对:

    MCP 有、插件没有: 无
    插件有、MCP 没有: 无

⚠️ 中途差点误判:grep 到插件里有 `permission_decision` 等 3 个词,
以为 MCP 缺 3 个工具。查下去 `permission_decision` 是 **mail_type**
(收到授权请求邮件后转给 worker),另两个只存在于 `src/pool.mjs`
和测试里,**从不暴露给模型**。
2026-10-05 10:22:46 +08:00
6e5ea42138 fix(agent): 会话作用域拒绝时说明 session_id 从哪来 —— 独立 MCP 宿主的实测卡点
## 触发它的实测

跑「独立 MCP 宿主 E2E」时,第一版客户端卡在这里:
它收到了 new_mail 事件、**知道该读那封信**,却不知道该传什么 session_id,
于是一封信都读不到,报的是「这封信不在你当前所在的那条会话里」。

## 原文案缺的那半句

原文案说「要读它,就在它那条会话里读」—— 只说了**去哪读**,
没说**怎么去读**(id 从哪来)。缺的那句正是接入端唯一缺的信息。

实测依据:new_mail 事件的 payload 有 16 个键,含
`session_id` / `session_alias` / `mail_id`;桥侧另有
`GET /api/v1/agent/session/default` 可查默认会话。

## 安全边界未动

★ 只报**调用方自己所在的会话**(`self`),绝不把目标会话 id 拼进文案 ——
  那是 2026-09-15 用户订正过的语义(原文案刻意不提目标会话,避免泄露别人的会话)。
  判据第③条专门盯这一点。

## 判据自己踩了两个坑(与「变异必须确认真的应用」同族)

1. **变异一度删掉了 490 行** —— 循环没写终止条件,从提示那行一路删到文件末尾。
   备份救回(608 行完好)。
2. **判据窗口取太宽导致假绿** —— 初版按「文案起点 + 2200 字符」截取,而
   **同一文件别处的文档注释**里也写了 `/agent/session/default`(agentScope 上方的说明)
   ⇒ 删掉提示后窗口里仍有它 ⇒ 判据绿。
   修法:按**单条 Error(...) 调用**取段,不按固定窗口。

★ 这两个坑的共同教训:**「变异应用了」≠「判据看到了正确的范围」**。
  变异一次"没生效"(我以为代码没问题)、一次"生效了但判据看错范围"(我以为判据有效)
  —— 两种都表现为 0 红。
  **「0 红」的第一反应必须是「变异或判据有一方错了」。**

14 包全绿。
2026-10-04 18:57:18 +08:00