用户:「你还是没修好不同 session agent 收件箱隔离的问题」。上一轮我只修了 **pi**, 另外四家还漏着 —— 它们是**每一家各自实现** read_inbox,不修就还是漏。 ## 缺陷 列表按 Agent 列(整个收件箱),而 read_inbox 按契约把**列出来的都标成已读** ⇒ A 会话的回合会把 B 会话的未读标掉 ⇒ B 之后按 `?status=unread` 补投时 再也看不到那封信(静默丢信,不是"少看一封")。用户是在别的 Agent 上看到它的。 ## 四家的修法(各自平台能力不同,但都要"并发安全") | 桥 | 会话来源 | 为什么这样做 | |---|---|---| | dsh | 工具第二参数 `exec.agent.id` → `reverseMap` | 平台就在上下文里给了会话;**不能用模块级"当前会话"变量**(同进程可能同时跑多条会话的回合,会互相覆盖) | | opencode | 工具第二参数 `context.sessionID` → `reverseMap` | 同上 | | zcode | `AGENTMAIL_SESSION_ID`(在**调用时**读) | 一轮一个进程,驱动本来就注入它给授权钩子用;调用时读,避免将来复用进程拿到旧值 | | homeagent | `p.currentSessionID`(回合开始设、结束清) | Go 插件,本来就有这个状态 | 取不到会话一律**退回整体收件箱**(历史行为),不猜 —— 猜错就是静默丢信。 ## 判据 - 服务端语义:`server/internal/repo/session_scope_test.go`(读 A 不动 B、列表收窄、 计数与列表口径一致)。 - 桥侧接线:dsh 4 条、opencode 3 条、zcode 3 条、homeagent Go 1 条 (`TestInboxURLScopedBySession`,直接断言拼出来的 URL)。 每家都带**判据自检**:拿旧写法喂进来必须判红;dsh/opencode 还专门断言 "不得用模块级当前会话变量"。 - **部署件**(不是仓库):四家的部署快照里都能 grep 到 `session_id=`。 - **线上实测**:用 opencode 自己的 Agent 身份请求收窄列表 —— 会话 A 3 封、 会话 B 0 封、两者无交集、且都是全量的子集。 套件:opencode **331**、dsh **381**、zcode **385**、homeagent ok,全绿。 四家桥已重新部署(dsh/opencode/zcode 快照切换 + homeagent 新 plugin.bin 并重启), 四个服务均 active。
33 lines
1.5 KiB
JavaScript
33 lines
1.5 KiB
JavaScript
/**
|
||
* read_inbox 必须收窄到**自己那条会话**(用户:「你还是没修好不同 session agent
|
||
* 收件箱隔离的问题」)。
|
||
*
|
||
* zcode 的特殊之处:一轮一个进程,驱动已经把本轮邮件会话注入 AGENTMAIL_SESSION_ID
|
||
* (授权钩子本来就用它)⇒ 直接读 env 即可,天然并发安全。
|
||
*/
|
||
import { test } from 'node:test';
|
||
import assert from 'node:assert/strict';
|
||
import { readFileSync } from 'node:fs';
|
||
import { dirname, join } from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
|
||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||
const tools = readFileSync(join(HERE, '..', 'lib', 'tools.mjs'), 'utf8');
|
||
const index = readFileSync(join(HERE, '..', 'src', 'index.mjs'), 'utf8');
|
||
|
||
test('驱动确实注入了本轮邮件会话', () => {
|
||
assert.match(index, /AGENTMAIL_SESSION_ID: sessionId/, '驱动要把邮件会话传下去');
|
||
});
|
||
|
||
test('read_inbox 会拼上会话收窄,且在调用时读 env', () => {
|
||
assert.match(tools, /const mailSessionID = process\.env\.AGENTMAIL_SESSION_ID \|\| ''/, '调用时读(不是 import 时读死)');
|
||
assert.match(tools, /mail\/inbox\?status=\$\{encodeURIComponent\(status\)\}&limit=\$\{limit\}\$\{scope\}/);
|
||
assert.match(tools, /session_id=\$\{encodeURIComponent\(mailSessionID\)\}/);
|
||
});
|
||
|
||
test('★ 判据自检:旧写法(不带 env、不带 scope)必须判红', () => {
|
||
const old = '`/mail/inbox?status=${encodeURIComponent(status)}&limit=${limit}`';
|
||
assert.ok(!/\$\{scope\}/.test(old));
|
||
assert.ok(!/AGENTMAIL_SESSION_ID/.test(old));
|
||
});
|