JianFeeeee
7f03ee7ca2
fix(pi-bridge)!: 沙箱 rw 漏了 agentDir 本身 —— pi 侧的 Agent 整个不工作(凭据存储的锁文件写在它直下)
**症状(实测,2026-09-15)**:pi 处理不了任何一条消息 —— 回给 dsh 的是一封
「处理失败」通知:
auth: Credential store read failed for llmsproxy:
EACCES: permission denied, mkdir '/root/.pi/agent/auth.json.lock'
已尝试 1 个:llmsproxy/AUTO
即**不是某次工具调用失败,而是这个 agent 完全不工作** —— 而它正是这条线上唯一的对端。
**根因**:`sandboxWritePaths` 把 `<agentDir>/sessions` 放进了 `--rw`,
**却没放 `<agentDir>` 本身**:
pushDir(join(agentDir, 'sessions')); // 少了 pushDir(agentDir)
而 pi 的凭据存储在 **`<agentDir>` 直下**建锁文件 `auth.json.lock`
⇒ Landlock 拒绝在 `agentDir` 里新建条目 ⇒ 读凭据这条路直接失败。
**因果链已在真二进制上闭合**(`/opt/agentmail/bin/am-sandbox`,非推理):
# 只给 sub、不给父(= 修复前的形状)
--rw /tmp/ll2/allowed/sub → echo > /tmp/ll2/allowed/newfile
/bin/sh: cannot create …: Permission denied ← 就是 pi 的那个 EACCES
# 给父目录(= 修复后的形状)
--rw /tmp/ll2/allowed → 退出码 0,文件建出来
也确认了 `am-sandbox` 的语义确实是「**及其子树**」(`--rw` 给出的目录连同子树可写),
所以补上父目录这一条就够,不需要为锁文件单独加 `--rw-file`。
**为什么以前的判据护不住这一处 —— 夹具形状把出问题的那一角简化掉了**:
`sandbox-launch.test.mjs` 每个用例手写
`fsWith([..., `${HOME}/.pi/agent/sessions`, ...])`,**只列 sessions、不列 agentDir**,
于是"agentDir 在不在 rw 里"在这套测试里**永远测不出来**。
已加一个贴着生产形状的夹具 `fsRealShape()`(agentDir 与 sessions **都在**),
并把两个 workerLaunch 用例换成它。
**判据(两条,含反面对照)**:
· `agentDir` 本身必须在 `--rw` 里,且 `sessions` 也仍在(两个写点,不是替代关系);
· 反面对照:`agentDir` **不存在**时不得硬塞进 rw ——
`am-sandbox` 对不存在的 `--rw` 路径 fail closed(退出码 126),
所以"加 agentDir"不能变成"无条件加"。
★ 已按既定纪律先验区分力:临时移除 `pushDir(agentDir)` ⇒ 新用例**变红**(11/1),
`cp` 恢复后 `cmp` 校验一致。
**旁注(写点清单的教训)**:原来的注释只按"我们已知的写点"列(会话工作区、临时目录、
/dev/null、sessions、配置目录),而**凭据存储在它自己的目录里加锁**是另一个写点,
且它在**读凭据**这条路上 —— 所以漏了它的症状不是"某个工具不能用",而是"agent 不工作"。
写点清单要按**真实进程的行为**列,不能只按已知的那几处列。
验证:pi 桥 491/491(新增 2 条);sandbox-launch 12/12;`go test ./cmd/am-sandbox/` ok。
2026-09-15 00:13:13 +08:00
..
2026-09-11 11:32:47 +08:00
2026-09-15 00:13:13 +08:00
2026-09-14 23:50:35 +08:00
2026-09-15 00:13:13 +08:00
2026-09-14 20:00:16 +08:00