Files
MailUI4Agents/plugins/pi-mail-bridge/test
JianFeeeee e3c10cbd65 fix(pi-bridge)★★: 身份绑定冲突时中止,不再以错误身份继续运行
2026-10-05。起因是一份 bug 报告里的三条,我只认了其中一条真的。

## 先说报告里那两条**不成立**的(我实测了)

① 「服务端会用 200 + {"error":…} 返回业务失败,所以回执是假成功」

   实测穷举所有失败路径,**没有一条 200 带 error**:
       跨会话读 403   缺 workspace 400   坏凭证 401   邮件不存在 404
       send 缺参数 400   收件人不存在 404
   根因:`Error(w, status, msg)` 的 status 是必填参数,
   全仓不存在 `Error(w, 200, …)`。所以 `post()` 只在 `!res.ok` 时抛错是对的。

② 「sent_total=0 说明没发出去」

   **Agent 身份没有「已发送」端点**:
       GET /api/v1/me/mail/sent → 401(那是人类端点)
   Agent 能用的邮件端点只有 mail/{id}、mail/{id}/thread、contacts、calendar。
   `sent_total=0` 不是「计数为 0」,是**根本查不到** ——
   把「查不到」读成「没发出去」,这是整条误判链的起点。

   而实际证据是**信送到了**:
       mail_id 317a0b4b…  from laptoppi-win → jianf
       mail_reads: reader_name=jianf, read_at=2026-10-05 02:55:49
   (发信后 13 分钟被人亲自读了)

## 报告里那条**真的**:换 key 后不自知

它的根因描述准确:身份由密钥决定,而配置在模块加载期读入,改 env 不热生效。
本仓库的桥同构(`index.mjs` 顶部 `const AGENT_SECRET = process.env…`),
且启动日志只印**本地配置里**的名字 —— 于是漂移在日志里看不出来。

## 修法:服务端早就内置了这道闸,桥没接

实测 `POST /api/v1/agent/register`:

    {"name":"pi"} + dsh 的 key
    → {"error":"该密钥已绑定到 Agent \"dsh\",不能用于注册 \"pi\""}

「这把 key 到底是谁的」服务端能权威回答。
旧代码把注册失败**当普通错误打印一行就继续跑** —— 于是继续以错误身份
发信/收信/上报心跳,而日志里那句「身份 ${AGENT_NAME}」读起来一切正常。

两处改动:

1. **冲突时中止**:识别 `/已绑定到 Agent/` 后 `throw`,
   并打印**怎么查**(sqlite 查 agent_keys)、**怎么改**(改 env + 必须重启)。
   ★ 只对绑定冲突停 —— 密钥无效 / 网络不通属可恢复的,
     停掉会连带 SSE 与心跳,而那两样本就该重试后自愈。

2. **启动日志印 key 前 8 位**:身份实际由密钥决定,只印配置里的名字
   不足以代表真实身份。现在一行能对出来:

       已接入 http://127.0.0.1:8180,身份 pi(密钥认证 c31044d6…)

   ★ 印 8 位不发全串 —— 避开日志泄密。

## 判据(新建,5 格,3 变异全红)

变异① 只记日志不中止 → 转红
变异② 认不出冲突     → 转红
变异③ 日志不印 key 片段 → 转红

pi 桥全量:533 → **538 格**(+5),0 失败。已部署并重启线上服务。

## 没能修的:Windows 上那个扩展

`C:\Users\21989\.pi\agent\extensions\agentmail.ts` 本机不可达。
它需要做的两件事,本仓库这个提交就是参照:

1. 注册/首次调用时拿服务端权威答复核对身份,不一致就**停下并说清怎么查**
2. 启动日志同时印出 `agent_name` + `key 前 8 位`,让漂移可见
2026-10-05 11:04:55 +08:00
..