用户:「任意 agent 的寻址是任意的,而不是按工作区区分,去落实吧」。 ① 拆掉两处"按工作区收窄"(那是我把**寻址**当成了**权限**): · AgentListContacts 不再走 ListContactsInWorkspace ⇒ 列表 = 我参与过的会话(事实,不是授权); · AgentSuggestAddress 的会话候选不再走 SuggestSessionCandidatesInWorkspace。 两个 InWorkspace 变体(连同钉旧口径的用例)一并删除,避免死代码。 生产实测:pi 的联系人从"只剩同工作区"变成 5 条,横跨 TrueAgent/agentmail/其它工作区。 agentScope 仍调用(校验 session_id 格式 + 未声明时告警),只是它的工作区不再当过滤器。 ② 联系人地址的 path 取自**会话**,不再取第一封邮件的 from_workspace: `COALESCE(NULLIF(s.workspace,''), NULLIF(m.to_workspace,''), '')`。 那条老路把地址拼成 `zcode@zcode.<别名>` —— 正是用户预言的"感染":一个可被复制出去的 错误地址。from_workspace 与"对方在哪"无关,只用 to_*。 ③ **清除感染源(数据)**:658 行 from_workspace = from_name(pi 406 / dsh 137 / zcode 89 / homeagent 17 / opencode 9)已清空。带去重前备份(/root/gotmp/agentmail-pre-fromws-purge-*.db) 与回滚脚本,回滚**在副本库上真跑过**(恢复 658 行)才敢落地。
64 lines
2.3 KiB
Go
64 lines
2.3 KiB
Go
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))
|
||
}
|
||
}
|
||
|