diff --git a/server/internal/handler/agent_discovery.go b/server/internal/handler/agent_discovery.go index 9b4045a..45b64e8 100644 --- a/server/internal/handler/agent_discovery.go +++ b/server/internal/handler/agent_discovery.go @@ -98,13 +98,21 @@ func canReadSession(w http.ResponseWriter, r *http.Request, agentName string, sc // 记忆是隔离的」。所以**不再拿工作区当理由**(旧文案就是 cross-workspace, // 它让 zcode 推断出"那五位 agent 不存在",把错误结论写进了给人看的信里)。 // 报出"我当前在哪条会话"是安全的(那是调用方自己的);**不报**目标会话在哪。 - self := "" + // + // ★ 2026-10-05:`reason` 是**一个值管三种拒绝**(没声明 / 声明了别人的 / + // 不是参与方),而「你当前在会话 」在 self 为空时会拼出 + // 「你当前在会话 。」—— 实测(独立 MCP 宿主读自己那封信)确实读到这句, + // 残缺的话会让模型以为自己在一个无名会话里,转而去猜。 + // 所以先按 self 有无分句,**不给空值留位置**。 + var where string if scope != nil { - self = (*scope).String() + where = "你当前在会话 " + (*scope).String() + "。" + } else { + where = "你当前**没有**声明自己所在的那条会话,所以无从判断这封信是不是你的。" } Error(w, http.StatusForbidden, "这封信不在你当前所在的那条会话里:每个会话各有各的收件箱(会话之间的记忆是隔离的)。"+ - "你当前在会话 "+self+"。要读它,就在它那条会话里读 —— 每封来信都会把 worker 唤醒到"+ + where+"要读它,就在它那条会话里读 —— 每封来信都会把 worker 唤醒到"+ "它自己那条会话上,那时你看到的就是那条会话的收件箱。"+ // ★ 2026-10-04 补:调用方多半是**拿不到**目标会话 id,而不是 // 「知道却不传」。原来那句「就在它那条会话里读」只说了去哪读, diff --git a/server/internal/handler/session_scope_hint_test.go b/server/internal/handler/session_scope_hint_test.go index 9974631..bf8dacc 100644 --- a/server/internal/handler/session_scope_hint_test.go +++ b/server/internal/handler/session_scope_hint_test.go @@ -58,13 +58,56 @@ func TestSessionScopeHintTellsWhereToGetSessionID(t *testing.T) { t.Error("★ 只说了「要传 session_id」却没说**从哪拿** ——" + " 那等于把问题原样推回去,宿主仍然读不到信(实测卡点)") } - // ③ 不得泄露目标会话 id(安全边界:那是别人的会话) +// ③ 不得泄露目标会话 id(安全边界:那是别人的会话) if strings.Contains(seg, `self = (*scope).String()`) && strings.Contains(seg, "self+"+"\" 目标会话") { t.Error("★ 不得把目标会话 id 拼进文案 —— 那是安全边界(只报自己所在的会话)") } } +/* +★ 「你当前在会话 。」—— 一个值管三种拒绝,文案却只有一句(2026-10-05)。 + + `AgentMayReadSession` 用**同一个 reason** 覆盖三种拒绝: + ① 没声明 session_id(scope == nil) + ② 声明了别人的会话 + ③ 声明了但**不是参与方**(session_id 由请求方提供,自述不是事实) + + 原文案无条件拼 `"你当前在会话 " + self + "。"`,于是 ① 拼出: + +这封信不在你当前所在的那条会话里:…你当前在会话 。要读它,… + + 实测(独立 MCP 宿主读自己刚收到的那封信)确实读到这句。 + 残缺的话会让模型以为自己在一个**无名会话**里,转而去猜/去搜 —— + 比不说更糟。 + +★ 这条判据盯的是**结构**:文案必须按 scope 有无分句, + 而不是靠手测一次。 +*/ +func TestSessionScopeHintNeverLeavesEmptySessionSlot(t *testing.T) { + b, err := os.ReadFile("agent_discovery.go") + if err != nil { + t.Fatalf("读 agent_discovery.go: %v", err) + } + src := string(b) + + // ★ 回归:不得再出现「无条件拼接 self」的写法 —— + // 它就是那句残缺文案的唯一来源。 + if strings.Contains(src, `"你当前在会话 "+self+"。`) { + t.Error("★ 又用回「你当前在会话 \" + self」的无条件拼接 —— " + +"scope 为 nil 时会拼出「你当前在会话 。」,模型会以为自己在一个无名会话里") + } + + // 替代写法必须按 scope 有无分句 + if !strings.Contains(src, "if scope != nil {") { + t.Error("文案没有按 scope 有无分句") + } + if !strings.Contains(src, "你当前**没有**声明") { + t.Error("★ 没有「未声明会话」这一句的文案 —— " + +"调用方不声明 session_id 时读不到任何提示该传什么") + } +} + func min2(a, b int) int { if a < b { return a