fix(auth): 四家桥的读端点也带上会话收窄 + 转发同一条命(工作区隔离第 2 步)

第 1 步(1b8cd43)把工作区判据放在服务端、pi 桥接上了线。这一步补齐另外四家,
并把**转发**纳入:转发是"把原文引出去",能转发就等于能读到那条线索的全部内容,
与 read_mail 同一条命(服务端 ForwardMail 也加了同一道校验)。

四家各自的会话来源,与各自的 read_inbox 同一处(不引入第二个来源):
- dsh:`mailSessionOf(exec)`(工具第二个参数)—— 五个读工具原本没接 exec,这次补上
- opencode:`reverseMap.get(context.sessionID)`
- zcode:`process.env.AGENTMAIL_SESSION_ID`(一轮一个进程)
- homeagent:`p.currentSessionID`(新增 `scopeQuery(sep)`,与 inboxURL 同构)

判据(每条两侧都钉:包住了 / 没包住的不存在):
- dsh:静态对照,且额外钉 **dist** —— 那是真被 dsh 加载的那份(main: dist/index.js),
  src 改了忘了 build 就是"源码对、线上旧代码"
- opencode / zcode:同上(opencode 还钉"会话来自 context 而不是模块级变量")
- homeagent:起 httptest 当网关,**五个读工具 + 转发真调一遍**,断言请求 URL 带
  session_id;对照侧:不在回合里(currentSessionID 为空)时不许带
- pi:把 post 的 URL 也纳入记录,forward 进用例表

★ zcode 那条判据我第一版**对照组写错**了:对照组只写裸 URL,而它本来就是
`withScope(\`裸URL\`)` 的子串 ⇒ `!includes(bare)` 恒假。夹具形状不对时判据会以
"恒红/恒绿"的方式骗人(这次是恒红,一眼可见;恒绿就麻烦了)。

变异:homeagent 去掉 read_mail 的收窄 ⇒ 恰好那条断言红。

(工作区共享,只 add 了上面这 12 个文件;dsh 的 dist 是 gitignore 的,由
redeploy-plugin.sh 在 staging 里构建。)
This commit is contained in:
2026-09-14 23:18:12 +08:00
parent 1b8cd43935
commit 2a5e3d7d15
12 changed files with 435 additions and 36 deletions

View File

@ -212,7 +212,9 @@ export function createMailTools({ client, log, agentName = '', onReconnect, getM
},
async execute(_id, params, _signal, _onUpdate, ctx) {
// 路径带 mail_id(POST /mail/{id}/forward),不是请求体里的字段
const result = await client.post(`/mail/${params.mail_id}/forward`, {
// 转发要读原文 ⇒ 与 read_mail 同一条命:带上自己那条会话,
// 服务端据此要求原文与调用方同工作区。
const result = await client.post(withScope(`/mail/${params.mail_id}/forward`), {
to: params.to,
comment: params.comment || '',
cc: params.cc || '',

View File

@ -48,7 +48,8 @@ function fakeResponse(url) {
function toolsWith(urls, sessionId) {
const client = {
get: async (u) => { urls.push(u); return fakeResponse(u); },
post: async () => ({}),
// post 也要记:转发是"把原文引出去",读的同样是邮件原文。
post: async (u) => { urls.push(u); return { mail_id: 'new-1', session_id: 's-1' }; },
downloadFile: async () => Buffer.from(''),
};
const list = createMailTools({
@ -70,6 +71,8 @@ const READ_CALLS = [
{ tool: 'read_thread', args: { mail_id: 'm-1' } },
{ tool: 'session_participants', args: { session_id: 's-1' } },
{ tool: 'suggest_address', args: { name: 'dsh', path: '/home/program/x' } },
// 转发也读原文(把原文引出到别的会话),所以同样要带收窄。
{ tool: 'forward_mail', args: { mail_id: 'm-1', to: 'x@/home/program/y' } },
];
for (const c of READ_CALLS) {