package repo import ( "context" "testing" "github.com/google/uuid" ) /* 工作区维度:一个 Agent 同时服务**所有**工作区,所以"参与过"不等于"该看"。 # 用户报的缺陷 「agentmail 工作区的邮件会话被 trueagent 工作区的 agent 看到了,还需要我亲自去解释。」 根因不是某处漏了一个 WHERE,而是**隔离单位选的是 Agent**: - `AgentCanAccessSession(agentName, sid)` 判的是「这个 Agent 名出现在这条会话的 from/to/cc 里」; - 而 Agent 注册时 `workspaces` 是空的(B-1.2:cwd 由每封邮件的 `to_workspace` 决定),于是同一个 agent `pi` 既"参与过" agentmail 的会话、也"参与过" TrueAgent 的会话 —— 两个工作区之间没有任何边界。 现场证据:`mail_reads` 里 2026-09-14 08:11–09:19 有 8 次"同一瞬间读了多个不同工作区 的会话"(最典型 08:23:59 一次跨 agentmail / TrueAgent / webui4frpc 三条会话), 而那正是按 Agent 整表读的特征。更要紧的是 `mail_reads` 只记 `reader_name`, **没有"读的人当时在哪个工作区"这一列** —— 这类越界读在数据上与正常读无法区分。 # 判据两侧都验 只验"跨工作区被拒"是不够的:把函数写成永远拒绝也能过。所以同时验 - 同工作区必须放行(否则等于把所有 Agent 都锁死); - 未声明 scope 时的旧语义(迁移期妥协,必须明确写下来,不能靠"没人测"存在); - 拒绝的**原因**要分得清(没参与过 vs 跨工作区)—— 否则调用方无法自查; - 列表类接口的反向对照:不带收窄时两条会话都在(证明收窄真的在起作用)。 */ func sessionIn(t *testing.T, agent, ws, title string) uuid.UUID { t.Helper() id, err := CreateSession(context.Background(), nil, "human", title, ws) if err != nil { t.Fatalf("create session(%s): %v", title, err) } seedMailInSession(t, id, agent) return id } func TestListContactsWithEmptyScopeStillWorks(t *testing.T) { setupTestDB(t) ctx := context.Background() sessionIn(t, "pi", "/home/program/agentmail", "随便一条线索") all, err := ListContactsFor(ctx, "", false) if err != nil { t.Fatalf("scope 为空(管理员看全部)不该报错:%v", err) } if len(all) != 1 { t.Fatalf("应当列出 1 条(实际 %d 条)", len(all)) } }