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 包全绿。
This commit is contained in:
@ -105,7 +105,18 @@ func canReadSession(w http.ResponseWriter, r *http.Request, agentName string, sc
|
||||
Error(w, http.StatusForbidden,
|
||||
"这封信不在你当前所在的那条会话里:每个会话各有各的收件箱(会话之间的记忆是隔离的)。"+
|
||||
"你当前在会话 "+self+"。要读它,就在它那条会话里读 —— 每封来信都会把 worker 唤醒到"+
|
||||
"它自己那条会话上,那时你看到的就是那条会话的收件箱。")
|
||||
"它自己那条会话上,那时你看到的就是那条会话的收件箱。"+
|
||||
// ★ 2026-10-04 补:调用方多半是**拿不到**目标会话 id,而不是
|
||||
// 「知道却不传」。原来那句「就在它那条会话里读」只说了去哪读,
|
||||
// 没说 id 从哪来 —— 实测(独立 MCP 宿主 E2E)第一版客户端就卡在
|
||||
// 这里:拿到 new_mail 事件、知道该读,却不知道该传什么,于是一封
|
||||
// 信都读不到。
|
||||
//
|
||||
// id 就在**那封信的事件载荷**里:new_mail 的 payload 带 session_id
|
||||
// (实测 16 个键,含 session_id / session_alias / mail_id)。
|
||||
// 桥侧另有 GET /api/v1/agent/session/default 可查默认会话。
|
||||
" 目标会话 id 在 new_mail 事件的 payload 里(session_id 字段);"+
|
||||
"没有事件时可查 GET /api/v1/agent/session/default。")
|
||||
default:
|
||||
Error(w, http.StatusForbidden, "无权访问该会话")
|
||||
}
|
||||
|
||||
73
server/internal/handler/session_scope_hint_test.go
Normal file
73
server/internal/handler/session_scope_hint_test.go
Normal file
@ -0,0 +1,73 @@
|
||||
package handler
|
||||
|
||||
/*
|
||||
会话作用域拒绝的文案必须**指出 id 从哪来**(2026-10-04 实测驱动)。
|
||||
|
||||
★ 为什么要这一格:
|
||||
实测「独立 MCP 宿主 E2E」时,第一版客户端卡在这里 ——
|
||||
它收到了 new_mail 事件、知道该读那封信,却**不知道该传什么 session_id**,
|
||||
于是一封信都读不到,报的是:
|
||||
|
||||
这封信不在你当前所在的那条会话里:…你当前在会话 。
|
||||
|
||||
原文案说了「就在它那条会话里读」(去哪读),却没说 **id 从哪来**
|
||||
(怎么去读)。缺的那半句正是宿主唯一缺的信息。
|
||||
|
||||
★ 判据盯两件事,缺一不可:
|
||||
① 提到 session_id(名字本身)
|
||||
② 指出**来源**(new_mail 事件的 payload / session/default 端点)
|
||||
—— 只加「请传 session_id」等于把问题原样推回去,宿主仍不知道去哪拿。
|
||||
*/
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSessionScopeHintTellsWhereToGetSessionID(t *testing.T) {
|
||||
b, err := os.ReadFile("agent_discovery.go")
|
||||
if err != nil {
|
||||
t.Fatalf("读 agent_discovery.go: %v", err)
|
||||
}
|
||||
src := string(b)
|
||||
|
||||
i := strings.Index(src, "这封信不在你当前所在的那条会话里")
|
||||
if i < 0 {
|
||||
t.Fatal("找不到会话作用域的拒绝文案")
|
||||
}
|
||||
// ★ 只取这一条 Error(...) 调用,不能按固定窗口往后截。
|
||||
// 初版用「起点 + 2200 字符」,而**同一文件别处的文档注释**里也写了
|
||||
// `/agent/session/default`(那是 `agentScope` 上方的说明)⇒
|
||||
// 删掉提示后窗口里仍有它 ⇒ 判据假绿。
|
||||
// 这与「变异必须确认真的应用」同族,但更隐蔽:变异应用了、判据却因为
|
||||
// **看错了范围**而没反应。
|
||||
seg := src[i:]
|
||||
if end := strings.Index(seg, `Error(w, http.StatusForbidden, "无权访问该会话"`); end > 0 {
|
||||
seg = seg[:end]
|
||||
}
|
||||
|
||||
// ① 提到 session_id
|
||||
if !strings.Contains(seg, "session_id") {
|
||||
t.Error("★ 拒绝文案里没有 session_id —— 调用方不知道该传什么")
|
||||
}
|
||||
// ② 指出来源:事件载荷 或 默认会话语义端点
|
||||
hasEvent := strings.Contains(seg, "new_mail")
|
||||
hasEndpoint := strings.Contains(seg, "/agent/session/default")
|
||||
if !hasEvent && !hasEndpoint {
|
||||
t.Error("★ 只说了「要传 session_id」却没说**从哪拿** ——" +
|
||||
" 那等于把问题原样推回去,宿主仍然读不到信(实测卡点)")
|
||||
}
|
||||
// ③ 不得泄露目标会话 id(安全边界:那是别人的会话)
|
||||
if strings.Contains(seg, `self = (*scope).String()`) &&
|
||||
strings.Contains(seg, "self+"+"\" 目标会话") {
|
||||
t.Error("★ 不得把目标会话 id 拼进文案 —— 那是安全边界(只报自己所在的会话)")
|
||||
}
|
||||
}
|
||||
|
||||
func min2(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
Reference in New Issue
Block a user