feat: 新增企业微信助手 Agent,并修正 wecom-cli 条目 ref 漂移 (#112)

## 概述 / Overview

两件事:新增「企业微信助手」Agent(自带 15 个技能),并修正 `wecom-cli` 条目钉在 6 月快照的 ref 漂移。

Two changes: adds the **WeCom Assistant** agent (bundling 15 skills),
and fixes the `wecom-cli` entry whose pinned ref was stuck on a June
snapshot.

## 1. 新增企业微信助手 Agent

覆盖企业微信 **14 类服务、95
个方法**:消息、群聊历史、通讯录、日程、会议、待办、邮件、在线文档、在线表格、智能表格、智能文档、文档管理、微盘、媒体文件。

**采用内联形态 + 自带私有技能**:Agent 安装对 `agents/<id>/` 整目录递归复制且 `skills/`
不在排除集合里,因此装 Agent 即带全部技能,用户无需再单独获取技能合集。

### 技能集(15 个,约 5000 行)

- 基于上游 [wecom-cli](https://github.com/WecomTeam/wecom-cli) 官方
Skill(MIT,© WecomTeam)改写,每个技能末尾保留归属声明
- **新增 `wecom-chat`**:补齐上游零覆盖的群聊历史读取
- 补齐上游未覆盖的 `message.send`、`doc.create`,方法覆盖达 **95/95**
- 修正上游三处文档漂移:邮件能力描述与实际相反、会议室参数名已过时、`title_highlight` 字段不存在

### 相对上游的核心增量:风险治理

- 26 个对外可见或不可逆的方法逐个写明**执行前确认要求**
- 4 个条件升级方法给出**参数级判据**,而非按方法名一刀切
- 文档权限扩散两项加重处理,涉及**企业外可见**时单独再确认一次
- 内部标识禁止外露,不因用户索要而放宽
- 拒绝导出可识别到具体自然人的隐私字段

### 三条真机实测得出、上游未覆盖的硬约束

1. 机器人**只能写入/修改自己创建的数据**,真人创建的只能读
2. 每次响应携带的 `extra_identity_context` **禁止透露给用户**
3. 权限错误(`850002`/`851008`/`853006`)**不得重试**,须将 `help_message`
**逐字原样**转给用户

## 2. 修正 wecom-cli 条目 ref 漂移

`source.ref` 原钉在 2026-06-28 的 `72e14f7`,该快照只有 7
个子技能且用已废弃的旧命名(`msg`/`schedule`)。上游 v1.2.0 已扩展到 **14 个**技能。按旧 ref
安装的用户拿到的是三个月前的快照。

- `source.ref` → `78c514b2afee7c0d3d7be715628478421f37ee63`
- `children` 由 `scripts/gen-collection-children.py` 重新生成,**7 → 14**
- sidecar 同步 `provenance.content.ref`、`childCount` 与 `children`

## 验证 / Verification

**静态**
- 215 条示例命令追加 `--dry-run` 实跑,**215/215 退出码 0**
- 未知方法 0、未知参数 0、`--json` 未知字段 0、枚举违规 0
- 15 个 `SKILL.md` 的 frontmatter 经客户端 `skillFrontmatterSchema` 校验全部通过
- `validate_catalog_metadata.py --require-complete` 与
`gen-collection-children.py`:**0 error**

**真机(在真实企业微信账号上端到端)**
- **待办域 6/6 方法全通**(含 2 个 write-high),`items` 必填的隐蔽坑实测证实
- **日程域 5 个方法全通**(含 3 个 write-high)
- 消息发送、通讯录解析、微盘列表、邮件搜索、文档搜索、会议列表、智能表格创建均已实测通过
- 测试数据已全部清理,未污染真实账号

**尚未实测**:群聊历史(机器人未开通该品类)。相关文档已明确标注验证状态,未实测的能力不写「实际效果」段落。
This commit is contained in:
2026-09-03 03:50:00 -04:00
committed by GitHub
parent c83f917901
commit aec2e7c28b
57 changed files with 16893 additions and 45 deletions

View File

@@ -0,0 +1,106 @@
# 消息与会话
以机器人身份往企业微信的单聊或群聊里发消息——文字、图片、文件、语音、视频都行,
也能把聊天里的图片和文件取下来。发消息是**发出去就收不回**的操作,所以助手每次都会先复述再发。
这一域的重心不在「怎么发」,而在**「怎么确保发对人」**。
## 你可以怎么说
> 「给张三发条消息:会议改到明天下午三点」
> 「在项目 A 群里通知一下,周报截止时间推迟到周五」
> 「把这个文件发到企微」
> 「我现在能给哪些人发消息?」
> 「把刚才那张图下载下来」
## 📋 验证状态
| 项 | 状态 |
|---|---|
| 查询可发送的会话列表 | ✅ **已实测**:返回 1 个会话 |
| 以机器人身份发消息 | ✅ **已实测:真实发送成功**(发给授权人本人) |
| 发图片 / 文件 / 语音 / 视频 | ⚠️ 未实测 |
| 取聊天里的媒体文件 | ⚠️ 未实测 |
| 另一条「非机器人身份」的发送路径 | ❌ **完全未验证,助手默认不用它**(见下) |
| 完整链路(你说一句话 → 助手自动发完) | ⚠️ 未实测 |
**实测记录**(命令层,人工在真实账号上执行):
```bash
wecom-cli message aibot sessions list # 返回 1 个会话
wecom-cli message aibot send ... # 返回 {"success": true},消息真实送达
```
发送对象是授权人本人,属于高风险写入,实测时是明确知情后执行的。
**实测中的一个发现**:单聊场景下,**会话的标识就是对方本人的成员标识**(两者是同一个值)。
这解释了为什么「发给你自己」不需要先查会话列表。
## 能力清单
| 能做什么 | 命令 | 风险 |
|---|---|---|
| 列出机器人最近的会话(也就是「能发给谁」) | `wecom-cli message aibot sessions list` | 读取 |
| 以**机器人身份**发 markdown / 图片 / 文件 / 语音 / 视频 | `wecom-cli message aibot send` | **高风险写入** |
| 发**纯文本**消息(非机器人身份,未经验证) | `wecom-cli message send` | **高风险写入** |
| 把聊天消息里的图片 / 文件 / 语音 / 视频取下来 | `wecom-cli message files get` | 读取 |
发送前,助手会向你复述这样一句(**措辞示意,不是实测记录**
> 即将以机器人的身份,向「项目 A 群」发送 markdown 消息:「周报截止时间推迟到周五。」——确认发送吗?
复述里一定有**发给谁(可读名称)、什么类型、正文原文或摘要**三项。回一句「嗯」「你看着办」不算同意,
助手会再确认一次。
## 注意事项
**「能发给谁」是一个很窄的集合,而且不等于「你能发给谁」。**
企业微信只允许机器人往两类对象发消息:
1. **你本人**(授权人自己)——随时可以。
2. **机器人最近有消息往来的会话**——单聊加群聊,**最多 20 个**,按最后一条消息时间从新到旧排,
不支持翻页也不支持筛选。
目标不在这 20 个里面,就是发不了。这时助手会**停下来**,告诉你「对方不在机器人最近的会话范围内,
需要对方先给机器人发一条消息」——**它不会换个更宽松的方法把消息硬发出去**。
另外,已解散、已封禁、机器人已被移出的群不会出现在这个列表里。
**发消息前它每次都会重新确认一次会话,所以偶尔多花一两秒。**
这不是卡顿,是刻意的:会话列表按最后消息时间排序,你思考选哪个群的这段时间里顺序可能已经变了。
你在多个候选里选完之后,助手还会**再查一次**,用你选定的对象重新匹配当次的结果——
宁可多查一遍,也不要发错群。
**通讯录里的人 ≠ 能发消息的对象。** 这两个集合不是一回事。同理,「能读历史的群」
(见 [04 群聊历史](04-群聊历史.md))和「能发消息的会话」也是两个不同的集合,标识不能互相搬运。
**有一条路径助手默认不用。** 除了机器人身份发送,接口层还有一条「发纯文本」的路径,
它的**实际发送身份(收件人看到是谁发的)从未验证过**,只能发纯文字、上限也更低。
助手的默认选择永远是机器人身份那条;只有你**明确要求「不要以机器人身份发」**时才会考虑另一条,
而且会先告诉你「这条路径未经验证」,再单独取得一次同意。
**「目标不在会话列表里」不是切换到这条路径的理由。**
**发图片和文件要多一步。** 本地文件得先换成企业微信内部的媒体形态才能发出去,
所以发图片、发文件比发文字多一个步骤,这一步由助手自动完成(见 [15 媒体文件](15-媒体文件.md))。
语音必须是真正的 AMR 格式,改个扩展名冒充是发不出去的。
**长度上限有两套口径。** markdown 正文按字节算20480纯文本路径按字符算2048
视频的标题和描述也按字节。超了助手不会**悄悄截断**——它会请你缩短,或者在你明确同意后拆成多条发。
**它不编造消息编号。** 接口本身也不返回消息编号,发送成功后助手只会告诉你「发给谁、发了什么类型」。
### 三条通用边界在本域怎么体现
1. **只能改它自己建的东西**——发消息是新建,不受这条限制。但**已经发出去的消息,
助手既不能撤回也不能编辑**,接口层根本没有这两个能力。
2. **能力按品类逐项开通**——消息属于基础品类。未开通时助手会把官方开通指引原样转给你然后停下,
不重试、不绕路。
3. **危险动作先问你**——两个发送方法都是高风险写入,**每一次发送前都会复述并等你点头**
没有例外。详见 [99 风险与确认](99-风险与确认.md)。
## 相关
- [02 通讯录](02-通讯录.md)——把人名解析成内部标识(但要注意:发消息的目标不从这里取)
- [04 群聊历史](04-群聊历史.md)——读群里聊了什么(与本域是两套独立的会话范围)
- [08 邮件](08-邮件.md)——发邮件是另一套能力,不走这里
- [14 微盘](14-微盘.md)——把文件放进微盘,而不是发给某人
- [15 媒体文件](15-媒体文件.md)——发图片 / 文件时中间那一步在做什么
- [99 风险与确认](99-风险与确认.md)——发送前的确认怎么算数