feat: agent 邮件寻址能力全面补齐 + .new 别名替换
## 别名替换(让 .new 邮件可寻址)
repo/autoalias.go: AutoAliasFor + EnsureSessionAlias
- .new 建完会话立刻给别名(形如 dsh-重构导入路径)
- 名字与主题都要:只用主题跨 Agent 撞名,只用名字看不出聊什么
- sanitizeAliasPart 只留 unicode.IsLetter/IsDigit,其余折 -
- 撞名追加 -2/-3,全占用退 session-<uuid前8位>
- 不复用 SyncSessionAlias:那个假定已存在且跳过 manual
- 条件写入 WHERE alias IS NULL OR '',并发安全
- resolveTarget 的 .new 与默认会话两条路径都调
notifyRecipients 加三个字段(每个收件方拿到自己那个地址的版本):
- session_alias / reply_address / self_address
- 别名为空时退回省略 session 位,绝不写 new
FormatAddress(name,path,session) 空 path 也必须留 @ 与 .
## Agent 侧寻址发现(五个只读端点)
handler/agent_discovery.go:
- /agent/contacts + /agent/contacts/suggest(三段式补全)
- /agent/mail/{id} + /agent/mail/{id}/thread
- /agent/sessions/{id}/participants
- 不复用人类路由:scope 不同、审计需求不同
- 一律只读:归档/改名/权限决策仍只有人能做
repo/participants.go: SessionParticipants 逐封扫 from/to/cc
- Roles 用集合、MailCount 只数发信(0=还没开口的人)
- 发件人 path 不取 from_workspace(那列存的是 Agent 名)
repo.SuggestPaths 重写:mails.to_workspace(按 MAX(created_at) 倒序)
+ agents.workspaces 并集。原只读 workspaces,官方插件传 [] 永远空
## 共用模块(三插件逐字节相同)
lib/addressing.js: formatAddress/roleOf/replyAddressFor/selfAddressFor/participantsOfMail
lib/discovery.js: renderNameSuggestions/renderPathSuggestions/renderSessionSuggestions/
renderParticipants/renderContacts/renderThread
lib/inbox-format.js: renderMail 新增收件人/身份/可投递地址三段
- selfName 参数(兼容旧调用不传的情况)
check-shared-libs.sh 纳入 addressing + discovery
## 插件侧
opencode: suggest_address + list_contacts + session_participants + read_thread + read_mail
dsh: 同上 + forward_mail(此前只有 opencode 有)+ upload_attachment 改真 multipart
pi: 同上(createMailTools 加 agentName 参数)
dsh: ctx.agents.create id collision 改为 readSession 探测后 resume
dsh: 关键路径日志改 console.error(ctx.logger 不进 journalctl)
## 测试
repo: autoalias_test.go 11 + participants_test.go 7 = 18 例
plugins: addressing.test 17 + discovery.test 23 + inbox-format.test 31 = 71 例
go test ./... + npm test(opencode 155 + dsh 173 + pi 199)全绿
端到端验证:admin 发 dsh@....new 抄送 opencode@....new
→ dsh 用 session_participants 取到地址 → send_mail 给 opencode
→ 地址取自工具返回值(.crisp-planet),未手工拼写
This commit is contained in:
37
README.md
37
README.md
@ -21,6 +21,11 @@
|
||||
会话别名负责寻址,因此全局唯一。默认由 Agent 平台自己的命名机制提供 —— opencode 等平台
|
||||
本就会由模型为会话生成摘要标题和 slug,AgentMail 直接复用,不另造一套。
|
||||
|
||||
平台没有可用名字时(例如 pi 用 SDK 起的会话),桥退一级用邮件主题派生别名,
|
||||
再把服务端**定稿**的那个值写回平台。定稿而非各自命名,是因为别名要保证唯一:
|
||||
撞名时服务端会追 `-2`,而人在界面上手工改过的别名永远优先 —— 两侧各自命名的话,
|
||||
邮箱里显示 `fix-leak-2`、平台里显示 `fix-leak`,按界面上看到的名字发信会「无法送达」。
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 开发
|
||||
@ -83,6 +88,14 @@ DATABASE_URL= # 留空 = 内置 SQLit
|
||||
|
||||
### 接入 Agent
|
||||
|
||||
已有三个平台的桥接实现:
|
||||
|
||||
| 平台 | 形态 | 接入方式 |
|
||||
|---|---|---|
|
||||
| opencode | 插件 | 配置的 `plugin` 列表里加本地路径 |
|
||||
| DeepSeek Harness | Cordis 插件 | profile 的 `cordis.patch.yml` |
|
||||
| pi | **常驻守护进程** | `systemctl enable --now pi-mail-bridge` |
|
||||
|
||||
以 opencode 为例:
|
||||
|
||||
```bash
|
||||
@ -90,17 +103,23 @@ DATABASE_URL= # 留空 = 内置 SQLit
|
||||
"plugin": ["file:///path/to/agentmail/plugins/opencode-mail-bridge"]
|
||||
```
|
||||
|
||||
pi 不是插件而是独立服务,因为 pi 扩展被加载进**一条已存在的**会话,
|
||||
而三维地址要求每封邮件的 `path` 位成为会话工作目录 —— 扩展改不了这一点。
|
||||
桥用 pi 的 SDK(`createAgentSession`)按邮件起会话,一个进程里并存多条
|
||||
不同工作目录的会话。`deploy/install.sh` 会装好它的 systemd 单元。
|
||||
|
||||
插件提供六个工具(`send_mail` / `read_inbox` / `forward_mail` / `upload_attachment` /
|
||||
`download_attachment` / `connect_to_server`),并通过 SSE 监听新邮件:
|
||||
收到邮件时自动在 opencode 侧开会话处理,回信落回同一邮件会话。
|
||||
收到邮件时自动在平台侧开会话处理,回信落回同一邮件会话。
|
||||
|
||||
两类消息由插件**自动**转发,不需要模型自己调工具,也不消耗发信配额:
|
||||
|
||||
- **平台原生的权限询问**:opencode 拦下一个危险操作时(`permission.ask`),
|
||||
插件把它转成邮件问人,人在网页上点「同意/一直同意/拒绝」,插件再回复 opencode 让它继续。
|
||||
- **平台原生的权限询问**:平台拦下一个危险操作时(opencode 的 `permission.ask`、
|
||||
DSH 的 `approval/request`、pi 的 `tool_call` 钩子),插件把它转成邮件问人,
|
||||
人在网页上点「同意/一直同意/拒绝」,插件再回复平台让它继续。
|
||||
这是 harness 的职责 —— 让模型自己调一个 `request_permission` 工具的话,
|
||||
它可能忘了调,而真正被拦下的那次询问反而没人看见。
|
||||
- **本轮的最终总结**:一轮跑完(`session.idle`)时把最后那段话作为回信发回去。
|
||||
- **本轮的最终总结**:一轮跑完时把最后那段话作为回信发回去。
|
||||
模型已经把话说完了,插件只是搬运。
|
||||
|
||||
配额约束的是**模型的自主发信**,不是 harness 的转发 —— 否则配额用尽时 Agent 连交代都做不了。
|
||||
@ -120,7 +139,8 @@ DATABASE_URL= # 留空 = 内置 SQLit
|
||||
|
||||
三种生命周期:`permanent`(长期)/ `one_time`(首次使用后失效)/ `timed`(限时)。
|
||||
|
||||
环境变量见 `deploy/install.sh` 生成的 `/etc/agentmail/opencode.env`。
|
||||
环境变量见 `deploy/install.sh` 生成的 `/etc/agentmail/opencode.env`
|
||||
与 `/etc/agentmail/pi.env`。
|
||||
|
||||
## 项目结构
|
||||
|
||||
@ -140,9 +160,10 @@ agentmail/
|
||||
│ ├── middleware/ # Agent / 用户双认证
|
||||
│ ├── sse/ # 事件推送(按收件人分流)
|
||||
│ └── static/ # go:embed 的前端产物
|
||||
├── plugins/ # 各平台桥接插件(lib/ 下的纯函数模块逐字节共用)
|
||||
│ ├── opencode-mail-bridge/ # opencode
|
||||
│ └── dsh-mail-bridge/ # DeepSeek Harness(Cordis)
|
||||
├── plugins/ # 各平台桥接(lib/ 下的纯函数模块逐字节共用)
|
||||
│ ├── opencode-mail-bridge/ # opencode(插件)
|
||||
│ ├── dsh-mail-bridge/ # DeepSeek Harness(Cordis 插件)
|
||||
│ └── pi-mail-bridge/ # pi(常驻守护进程,用 SDK 起会话)
|
||||
├── web/ # 前端(React + Vite + Tailwind)
|
||||
│ └── test/manual/ # 浏览器实测脚本(量真实盒子与命中区,不进 npm test)
|
||||
└── deploy/ # systemd 单元 + 安装脚本
|
||||
|
||||
Reference in New Issue
Block a user