@yanhaidao/wecom 2.3.270 → 2.4.120

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/MENU_EVENT_CONF.md +500 -0
  2. package/MENU_EVENT_PLAN.md +440 -0
  3. package/README.md +80 -3
  4. package/UPSTREAM_CONFIG.md +170 -0
  5. package/UPSTREAM_PLAN.md +175 -0
  6. package/changelog/v2.4.12.md +37 -0
  7. package/package.json +1 -1
  8. package/scripts/wecom/README.md +123 -0
  9. package/scripts/wecom/menu-click-help.js +59 -0
  10. package/scripts/wecom/menu-click-help.py +55 -0
  11. package/src/agent/event-router.test.ts +421 -0
  12. package/src/agent/event-router.ts +272 -0
  13. package/src/agent/handler.event-filter.test.ts +65 -1
  14. package/src/agent/handler.ts +375 -21
  15. package/src/agent/script-runner.ts +186 -0
  16. package/src/agent/test-fixtures/invalid-json-script.mjs +1 -0
  17. package/src/agent/test-fixtures/reply-event-script.mjs +29 -0
  18. package/src/agent/test-fixtures/reply-event-script.py +17 -0
  19. package/src/app/account-runtime.ts +1 -1
  20. package/src/capability/agent/upstream-delivery-service.ts +96 -0
  21. package/src/capability/bot/sandbox-media.test.ts +221 -0
  22. package/src/capability/bot/sandbox-media.ts +176 -0
  23. package/src/capability/bot/stream-orchestrator.ts +19 -0
  24. package/src/channel.config.test.ts +33 -0
  25. package/src/channel.meta.test.ts +10 -0
  26. package/src/channel.ts +4 -1
  27. package/src/config/accounts.ts +16 -0
  28. package/src/config/schema.ts +58 -0
  29. package/src/context-store.ts +41 -8
  30. package/src/outbound.test.ts +211 -2
  31. package/src/outbound.ts +323 -70
  32. package/src/runtime/session-manager.test.ts +39 -0
  33. package/src/runtime/session-manager.ts +17 -0
  34. package/src/runtime/source-registry.ts +5 -0
  35. package/src/shared/media-asset.ts +78 -0
  36. package/src/shared/media-service.test.ts +111 -0
  37. package/src/shared/media-service.ts +42 -14
  38. package/src/target.ts +40 -0
  39. package/src/transport/agent-api/client.ts +233 -0
  40. package/src/transport/agent-api/core.ts +101 -5
  41. package/src/transport/agent-api/upstream-delivery.ts +45 -0
  42. package/src/transport/agent-api/upstream-media-upload.ts +70 -0
  43. package/src/transport/agent-api/upstream-reply.ts +43 -0
  44. package/src/types/account.ts +2 -0
  45. package/src/types/config.ts +74 -0
  46. package/src/types/message.ts +2 -0
  47. package/src/upstream/index.ts +150 -0
  48. package/src/upstream.test.ts +84 -0
  49. package/vitest.config.ts +15 -4
@@ -0,0 +1,440 @@
1
+ # WeCom 菜单事件可配置回调功能规划(PLAN)
2
+
3
+ ## 1. 背景与目标
4
+
5
+ 当前插件对 Agent 入站 `event` 使用固定白名单,未覆盖企业微信“菜单事件”全量场景(如 `click`、`view`、`scancode_push`、`location_select` 等)。
6
+
7
+ 目标是把“是否放行 event、放行哪些 eventType、由谁处理”从代码硬编码改为可配置:
8
+
9
+ 1. 支持在 OpenClaw 配置中声明是否启用 `event` 处理。
10
+ 2. 在 `event` 内支持按 `eventType` 白名单(例如只放行 `click`、`scancode_push`)。
11
+ 3. 每个事件可绑定处理器(内置 handler / 外部 TS/JS / 外部 Python)。
12
+ 4. 默认安全:不配置即保持现有行为兼容,不自动放通新增事件。
13
+
14
+ ## 2. 需求范围
15
+
16
+ ### 2.1 本期(MVP)
17
+
18
+ 1. 菜单点击类事件可配置放通:
19
+ - `click`
20
+ - `view`
21
+ - `view_miniprogram`
22
+ - `scancode_push`
23
+ - `scancode_waitmsg`
24
+ - `pic_sysphoto`
25
+ - `pic_photo_or_album`
26
+ - `pic_weixin`
27
+ - `location_select`
28
+ 2. 配置驱动白名单:`eventEnabled` + `eventType`。
29
+ 3. 事件分发器:按匹配规则将事件交给指定处理器。
30
+ 4. 脚本处理器能力:
31
+ - Node 脚本(TS/JS,先支持 JS,TS 通过 tsx/ts-node 作为可选)
32
+ - Python 脚本
33
+ 5. 统一执行输入输出协议(JSON stdin/stdout),并把接收到的 event 参数透传给外部脚本。
34
+ 6. 最小可观测能力:审计日志、超时、退出码、错误摘要。
35
+
36
+ ### 2.2 后续增强(非 MVP)
37
+
38
+ 1. 脚本热更新和缓存。
39
+ 2. 并发/速率限制(按事件类型或账号维度)。
40
+ 3. 幂等增强(事件去重键可配置)。
41
+ 4. 回调重试策略(指数退避 + 死信)。
42
+ 5. handler 沙箱隔离(容器/受限用户执行)。
43
+
44
+ ## 3. 设计原则
45
+
46
+ 1. 兼容优先:未新增配置时,行为与当前版本一致。
47
+ 2. 显式放通:只处理配置明确允许的类型。
48
+ 3. 最小权限:外部脚本执行能力默认关闭或仅允许受信目录。
49
+ 4. 可追踪:每次分发都可在日志中定位“为什么放通/为什么拒绝/由谁处理”。
50
+ 5. 可替换:处理器接口稳定,后续可新增 webhook/queue 等执行后端。
51
+
52
+ ## 3.1 事件格式盘点(基于文档 90240)
53
+
54
+ 按文档事件目录统计,建议全部纳入“可配置支持范围”,默认采用 deny by default(不放通)。
55
+
56
+ ### A. 一级事件格式数量
57
+
58
+ 共 17 类一级事件格式:
59
+
60
+ 1. 成员关注及取消关注事件
61
+ 2. 进入应用
62
+ 3. 上报地理位置
63
+ 4. 异步任务完成事件推送
64
+ 5. 通讯录变更事件
65
+ 6. 菜单事件
66
+ 7. 审批状态通知事件
67
+ 8. 企业互联共享应用事件回调
68
+ 9. 上下游共享应用事件回调
69
+ 10. 模板卡片事件推送
70
+ 11. 通用模板卡片右上角菜单事件推送
71
+ 12. 长期未使用应用停用预警事件
72
+ 13. 长期未使用应用临时停用事件
73
+ 14. 长期未使用应用重新启用事件
74
+ 15. 应用低活跃预警事件
75
+ 16. 低活跃应用事件
76
+ 17. 低活跃应用活跃恢复事件
77
+
78
+ ### B. Event 字段可枚举值数量
79
+
80
+ 共 26 个 Event 值(建议全部支持配置):
81
+
82
+ 1. subscribe
83
+ 2. unsubscribe
84
+ 3. enter_agent
85
+ 4. LOCATION
86
+ 5. batch_job_result
87
+ 6. change_contact
88
+ 7. click
89
+ 8. view
90
+ 9. view_miniprogram
91
+ 10. scancode_push
92
+ 11. scancode_waitmsg
93
+ 12. pic_sysphoto
94
+ 13. pic_photo_or_album
95
+ 14. pic_weixin
96
+ 15. location_select
97
+ 16. open_approval_change
98
+ 17. share_agent_change
99
+ 18. share_chain_change
100
+ 19. template_card_event
101
+ 20. template_card_menu_event
102
+ 21. inactive_alert
103
+ 22. close_inactive_agent
104
+ 23. reopen_inactive_agent
105
+ 24. low_active_alert
106
+ 25. low_active
107
+ 26. active_restored
108
+
109
+ ### C. 含 ChangeType 的展开数量
110
+
111
+ 如果把 `change_contact` 按 `ChangeType` 展开,建议按“二级事件”管理,共 7 种:
112
+
113
+ 1. create_user
114
+ 2. update_user
115
+ 3. delete_user
116
+ 4. create_party
117
+ 5. update_party
118
+ 6. delete_party
119
+ 7. update_tag
120
+
121
+ 因此,配置层可支持的“可路由事件项”建议按 32 项预算:
122
+
123
+ 1. 26 个 Event 值
124
+ 2. 其中 change_contact 再细分 7 个 ChangeType(路由维度)
125
+
126
+ ### D. 配置建议(用于实现)
127
+
128
+ 1. 第一层:`eventEnabled`(开关,先解决“支持 event”)。
129
+ 2. 第二层:`eventType`(即 Event,用于主白名单和主路由)。
130
+ 3. 第三层:`changeType` 或 `eventKey`。
131
+ - `changeType`:仅当 `eventType=change_contact` 时启用。
132
+ - `eventKey`:菜单事件精细路由,支持精确值/前缀/正则。
133
+ 4. `messageType` 级别白名单作为后续扩展,不纳入本期 MVP 必选项。
134
+
135
+ ## 4. 配置模型草案
136
+
137
+ 建议在 `channels.wecom.accounts.<accountId>.agent` 下新增:
138
+
139
+ ```yaml
140
+ channels:
141
+ wecom:
142
+ accounts:
143
+ default:
144
+ agent:
145
+ # 1) event 入站白名单配置(MVP)
146
+ inboundPolicy:
147
+ eventEnabled: true
148
+ eventPolicy:
149
+ mode: allowlist
150
+ allowedEventTypes:
151
+ - subscribe
152
+ - enter_agent
153
+ - click
154
+ - view
155
+ - view_miniprogram
156
+ - scancode_push
157
+ - scancode_waitmsg
158
+ - pic_sysphoto
159
+ - pic_photo_or_album
160
+ - pic_weixin
161
+ - location_select
162
+
163
+ # 2) 事件分发配置
164
+ eventRouting:
165
+ unmatchedAction: ignore # ignore | forwardToAgent
166
+ routes:
167
+ - when:
168
+ eventType: click
169
+ eventKey: "MENU_HELP"
170
+ handler:
171
+ type: node_script
172
+ entry: "./scripts/wecom/menu-click-help.js"
173
+ timeoutMs: 5000
174
+ - when:
175
+ eventType: scancode_push
176
+ handler:
177
+ type: python_script
178
+ entry: "./scripts/wecom/scancode_handler.py"
179
+ timeoutMs: 8000
180
+
181
+ # 3) 脚本执行安全设置
182
+ scriptRuntime:
183
+ enabled: true
184
+ allowPaths:
185
+ - "./scripts/wecom"
186
+ maxStdoutBytes: 262144
187
+ maxStderrBytes: 131072
188
+ defaultTimeoutMs: 5000
189
+ pythonCommand: "python3"
190
+ nodeCommand: "node"
191
+ ```
192
+
193
+ ## 5. 分发与执行架构
194
+
195
+ ### 5.1 处理链路
196
+
197
+ 1. 接收并解析 XML(现有能力)。
198
+ 2. 生成标准入站上下文 `InboundEventContext`。
199
+ 3. 先走 `inboundPolicy` 判定:
200
+ - 非 `event` 消息保持现状(按现有逻辑处理)。
201
+ - `eventEnabled=false` => 拒绝 event。
202
+ - `eventType` 不允许 => 拒绝。
203
+ 4. 命中后进入 `eventRouting`:
204
+ - 按 routes 顺序匹配(首个命中即执行)。
205
+ - 未命中走 `unmatchedAction`。
206
+ 5. 执行 handler:
207
+ - `builtin`:调用内置函数。
208
+ - `node_script`:子进程执行 Node 脚本。
209
+ - `python_script`:子进程执行 Python 脚本。
210
+ 6. 汇总 handler 结果,决定:
211
+ - 是否回复用户。
212
+ - 是否继续默认消息流水线。
213
+ - 是否仅记录审计并结束。
214
+
215
+ ### 5.2 统一处理器返回协议(建议)
216
+
217
+ 外部脚本输入(stdin)必须包含完整事件参数,至少包括标准字段 + 原始字段:
218
+
219
+ ```json
220
+ {
221
+ "version": "1.0",
222
+ "channel": "wecom",
223
+ "accountId": "default",
224
+ "receivedAt": 1760000000000,
225
+ "message": {
226
+ "msgType": "event",
227
+ "eventType": "click",
228
+ "eventKey": "MENU_HELP",
229
+ "changeType": null,
230
+ "fromUser": "zhangsan",
231
+ "toUser": "wwxxxx",
232
+ "chatId": null,
233
+ "agentId": 1000002,
234
+ "createTime": 1760000000,
235
+ "msgId": null,
236
+ "raw": {
237
+ "ToUserName": "wwxxxx",
238
+ "FromUserName": "zhangsan",
239
+ "MsgType": "event",
240
+ "Event": "click",
241
+ "EventKey": "MENU_HELP",
242
+ "AgentID": "1000002"
243
+ }
244
+ },
245
+ "route": {
246
+ "matchedRuleId": "menu_help_click",
247
+ "handlerType": "node_script"
248
+ }
249
+ }
250
+ ```
251
+
252
+ 说明:
253
+
254
+ 1. `message.raw` 为原始 XML 解析结果(扁平对象),用于外部脚本读取任意参数。
255
+ 2. `message` 顶层为标准化字段,降低脚本解析成本。
256
+ 3. 对菜单事件扩展字段(如 `ScanCodeInfo`、`SendPicsInfo`、`SendLocationInfo`)需原样放入 `message.raw`。
257
+ 4. 后续新增事件字段无需改协议版本,直接在 `message.raw` 增量透传。
258
+
259
+ 外部脚本从 stdout 返回 JSON:
260
+
261
+ ```json
262
+ {
263
+ "ok": true,
264
+ "action": "reply_text",
265
+ "reply": {
266
+ "text": "已收到菜单点击: MENU_HELP"
267
+ },
268
+ "chainToAgent": false,
269
+ "audit": {
270
+ "tags": ["menu", "click"]
271
+ }
272
+ }
273
+ ```
274
+
275
+ 字段建议:
276
+
277
+ 1. `ok`: boolean,处理是否成功。
278
+ 2. `action`: `none | reply_text | reply_markdown | call_internal`。
279
+ 3. `reply`: 回复负载。
280
+ 4. `chainToAgent`: 是否继续走默认 AI 会话。
281
+ 5. `audit`: 附加审计标签。
282
+ 6. `error`: 失败时错误消息。
283
+
284
+ ### 5.3 参数透传要求(强约束)
285
+
286
+ 1. 所有外部 handler(Node/Python)都必须收到完整 event 参数,不允许只传 `eventType/eventKey`。
287
+ 2. 参数透传应包含:基础字段、事件字段、扩展子结构、原始解析对象。
288
+ 3. 当字段缺失时保留 `null` 或空对象,不要静默删除键,避免脚本分支判断失效。
289
+ 4. 当入站不是 `event` 时,仍保持统一 envelope 结构,便于未来复用。
290
+
291
+ ## 6. 代码改造建议
292
+
293
+ ### 6.1 配置与类型
294
+
295
+ 1. 扩展配置类型:
296
+ - `src/types/config.ts`
297
+ - `src/config/schema.ts`
298
+ 2. 增加默认值与兼容合并逻辑:
299
+ - `src/config/runtime-config.ts`
300
+
301
+ ### 6.2 入站过滤改造
302
+
303
+ 1. 将当前 `shouldProcessAgentInboundMessage` 的硬编码白名单改为:
304
+ - 先判断 `inboundPolicy.eventEnabled`
305
+ - 对 `event` 再读 `eventPolicy.allowedEventTypes`
306
+ 2. 保留现有关键保护逻辑:
307
+ - `sys` 发送者保护
308
+ - 缺失 sender 保护
309
+ - 已有去重保护
310
+
311
+ ### 6.3 新增事件分发器
312
+
313
+ 建议新增模块:
314
+
315
+ 1. `src/agent/event-router.ts`
316
+ - 路由匹配
317
+ - handler 选择
318
+ 2. `src/agent/handler-runner.ts`
319
+ - builtin / node / python 统一执行
320
+ - 超时、stdout/stderr 限流
321
+ - stdin 注入标准 envelope(含完整 event 参数)
322
+ 3. `src/agent/handler-protocol.ts`
323
+ - 输入输出协议定义
324
+
325
+ ### 6.4 在主处理流程接入
326
+
327
+ 在 `src/agent/handler.ts` 的 `event` 分支:
328
+
329
+ 1. 在放通后优先调用事件分发器。
330
+ 2. 分发器返回 `handled=true` 且 `chainToAgent=false` 时,终止默认 AI 流程。
331
+ 3. 需要默认流程时继续原有 `processAgentMessage`。
332
+
333
+ ## 7. 安全与风险控制
334
+
335
+ 1. 默认关闭脚本执行,需显式 `scriptRuntime.enabled=true`。
336
+ 2. 仅允许执行 `allowPaths` 下脚本,防止任意路径执行。
337
+ 3. 进程级超时,超时强制 kill。
338
+ 4. 限制 stdout/stderr 最大字节,防止日志/内存膨胀。
339
+ 5. 子进程不继承敏感环境变量(可配置白名单透传)。
340
+ 6. 记录审计:账号、eventType、eventKey、handler、耗时、退出码。
341
+
342
+ ## 8. 测试计划
343
+
344
+ ### 8.1 单元测试
345
+
346
+ 1. `inboundPolicy` 判定测试:
347
+ - eventEnabled/eventType allow/deny 组合。
348
+ 2. `eventRouting` 匹配测试:
349
+ - eventType + changeType/eventKey 优先级。
350
+ 3. handler runner 测试:
351
+ - 正常输出
352
+ - 非法 JSON
353
+ - 超时
354
+ - 非 0 退出码
355
+ - stdin 中包含完整 event 参数与 raw 字段
356
+
357
+ ### 8.2 集成测试
358
+
359
+ 1. 构造 `click`、`view`、`scancode_push` XML 回调,验证完整链路。
360
+ 2. 验证 `unmatchedAction` 两种模式。
361
+ 3. 验证配置缺失时与当前版本行为一致。
362
+ 4. 验证外部脚本可读取 `ScanCodeInfo/SendPicsInfo/SendLocationInfo` 等扩展参数。
363
+
364
+ ### 8.3 回归测试
365
+
366
+ 1. `subscribe` / `enter_agent` 现有行为不回归。
367
+ 2. 文档/日程等现有 `doc_*`、`wedoc_*`、`smartsheet_*` 事件不回归。
368
+
369
+ ## 9. 里程碑与交付
370
+
371
+ ### M1:配置化放通(1~2 天)
372
+
373
+ 1. 完成配置 schema/type 扩展。
374
+ 2. `shouldProcessAgentInboundMessage` 改为 `eventEnabled + eventType` 配置驱动。
375
+ 3. 增加单元测试。
376
+
377
+ ### M2:事件分发器(2~3 天)
378
+
379
+ 1. 实现 route 匹配和 builtin handler。
380
+ 2. 接入主处理流程。
381
+ 3. 增加集成测试。
382
+
383
+ ### M3:外部脚本执行(2~3 天)
384
+
385
+ 1. Node/Python runner。
386
+ 2. 超时和输出限制。
387
+ 3. 审计字段补齐。
388
+
389
+ ### M4:文档与示例(1 天)
390
+
391
+ 1. README 增加配置示例与安全建议。
392
+ 2. 增加 `scripts/wecom` 示例脚本。
393
+
394
+ #### M4 已落地内容(2026-04-10)
395
+
396
+ 1. 已在 README 增加 Agent 事件路由章节,包含:
397
+ - 三层配置示例(`eventEnabled -> eventType -> changeType/eventKey`)
398
+ - Node/Python handler 配置样例
399
+ - stdin/stdout 协议说明
400
+ - 安全建议
401
+ 2. 已新增可运行示例脚本:
402
+ - `scripts/wecom/menu-click-help.js`
403
+ - `scripts/wecom/menu-click-help.py`
404
+ - `scripts/wecom/README.md`
405
+ 3. 现状说明:
406
+ - M1 已完成:event 配置化放通
407
+ - M2 已完成:事件路由与 builtin handler
408
+ - M3 已完成:Node/Python runner + 超时/输出限制 + 审计
409
+ - M4 已完成:文档和示例交付
410
+
411
+ ## 10. 首批内置 handler 建议
412
+
413
+ 1. `menu_click_echo`:回显 `eventType` + `eventKey`,用于联调。
414
+ 2. `menu_click_route_to_prompt`:将菜单键映射成固定提示词进入默认 pipeline。
415
+ 3. `menu_click_call_script`:作为脚本执行桥接器。
416
+
417
+ ## 11. 兼容性策略
418
+
419
+ 1. 旧配置无 `inboundPolicy` 时,使用“兼容默认白名单”(与当前一致)。
420
+ 2. 新配置启用后,按配置优先生效。
421
+ 3. 对未识别配置字段仅告警不崩溃。
422
+
423
+ ## 11.1 为什么先不做 messageType 白名单
424
+
425
+ 1. 当前痛点集中在 `event` 被整体过滤,最小改动是先引入 `eventEnabled`。
426
+ 2. 现有 `text/image/file/...` 已在当前链路可处理,本期不需要额外开关干预。
427
+ 3. 先做 event 维度可显著降低配置复杂度与回归风险。
428
+ 4. 后续若出现“非 event 类型也需要细粒度开关”的需求,再增补 messageType 白名单。
429
+
430
+ ## 12. 开放问题(需确认)
431
+
432
+ 1. 菜单事件 `view/view_miniprogram` 是否默认只审计不触发回复?
433
+ 2. 脚本 handler 是否允许访问网络,是否需要开关控制?
434
+ 3. 是否需要在每个 route 上支持 `retryPolicy`?
435
+ 4. 是否需要支持 webhook handler(HTTP 回调)作为第三类外部处理器?
436
+
437
+ ---
438
+
439
+ 该规划优先保证“可配置放通 + 可扩展处理 + 安全默认值”。
440
+ 如果确认此方案,可按 M1 开始先落地配置化白名单,再逐步引入路由与脚本执行能力。
package/README.md CHANGED
@@ -163,6 +163,12 @@
163
163
  > 项目保持高频迭代,全面对齐甚至超越企业真实业务诉求。
164
164
  > **为保持精简,以下仅展示近期 5 次重要更新,完整历史版本(含全部 `v2.2.x`)请前往 [changelog/ 目录](./changelog/) 查阅。**
165
165
 
166
+ #### 📌 v2.4.12(2026-04-12)
167
+ - **[新增优化] 自建应用菜单事件可路由到本地脚本** ⚙️ 企业微信自建应用收到 `click` 等 `event` 事件后,现在可以按 `eventType`、`eventKey`、`changeType` 等规则命中本地 `Node.js` / `Python` 脚本,不再只能一律进入默认 Agent 流程。
168
+ - **[新增优化] 脚本既能直接回复,也能继续链到 Agent** 🔀 脚本返回结果里可以控制是否继续进入 AI 流程,适合把“菜单先走固定逻辑,复杂问题再交给 Agent”的模式真正落地。
169
+ - **[新增能力] 上下游企业现在可通过 Agent 渠道互通** 🏢 如果你的自建应用已经共享给上下游企业,现在插件可以根据下游 `CorpID` 和 `agent.upstreamCorps` 把回复准确发回对应企业,图片和语音等常见媒体链路也一起补稳了。
170
+ - **[重要修复] Webhook 入站文件不再被固定 5MB 限制误拦** 📎 之前有些企业微信附件虽然在实际配置允许范围内,但进入会话工作区前仍会被旧的固定阈值挡住。这一版已经改成按当前 WeCom 配置解析后的大小限制执行,入站文件接入更稳。
171
+
166
172
  #### 📌 v2.3.27(2026-03-27)
167
173
  - **[重要修复] `channel add` 重新支持 WeCom guided setup** 🧭 之前有些环境下,`wecom` 虽然已经安装,却仍会在 OpenClaw 里显示成 “does not support guided setup yet”,导致无法直接通过交互式向导添加。现在插件已经对齐 OpenClaw 当前的 `setupWizard` 接口,`openclaw channels add` 会重新正常识别和进入配置流程。
168
174
  - **[重要修复] 修复 `installedCatalogById is not defined`** 🔧 部分用户在渠道添加或选择阶段会直接遇到 `ReferenceError: installedCatalogById is not defined`,表现上像是“选了渠道就报错”或“添加流程突然失效”。这一版已经修复对应的目录访问逻辑,添加流程恢复稳定。
@@ -184,9 +190,6 @@
184
190
  - **[多账号硬隔离]** 彻底重构 MCP 缓存池实现 `accountId + category` 的二次硬维隔离,无论您的矩阵挂载了多少家企业的助手,上下文及鉴权缓存绝不会交叉重叠。
185
191
  - **[媒体通道重构]** 补齐 Bot WS 本地的媒体上传链,同时设立了严格的 `5秒熔断机制`,若 WebSocket 长通道大文件卡死将无感静默降级到 Agent 私信发送。
186
192
 
187
- #### 📌 v2.3.16(2026-03-16)
188
- - **[解析增强] 混合消息媒体正确接管** 🛠 重点修复在 `Bot WS` 通道下,用户如果发了“一张截图 + 一段文字指示”,以前容易丢掉截图或者 AI 只能看到无法查看的腾讯云临时链接。新版底层引擎将自动扫过所有的媒体节点摘取 URL 与解密 AES Key,还大模型一双慧眼。
189
-
190
193
  *(查看更早期关于“超时熔断代投、动态扩容矩阵”等功能的更新日志,请移步 [changelog/ 目录](./changelog/))*
191
194
 
192
195
  ---
@@ -262,6 +265,12 @@ openclaw plugins enable wecom
262
265
  "dm": {
263
266
  "policy": "open",
264
267
  "allowFrom": []
268
+ },
269
+ "upstreamCorps": { // 可选:给上下游企业用户发消息时使用
270
+ "ww_partner_corp": {
271
+ "corpId": "ww_partner_corp",
272
+ "agentId": 1000002
273
+ }
265
274
  }
266
275
  }
267
276
  }
@@ -296,6 +305,7 @@ openclaw plugins enable wecom
296
305
  - 旧的 `channels.wecom.media.maxBytes` 仍然兼容,但仅作为向后兼容兜底;新配置建议统一改成 `mediaMaxMb`。
297
306
  - 这些目录会和 OpenClaw 默认允许的媒体目录一起生效,不会覆盖默认白名单。
298
307
  - 也就是说,像 `~/Downloads/01.png` 这类本机文件现在默认就可以直接发到企微,不需要再单独配置。
308
+ - 如果你需要给上下游企业用户回消息,可以在 `agent` 下追加 `upstreamCorps`;下面的 `1.6` 会单独展开说明。
299
309
 
300
310
  > **注意:** 历史配置里的 `agent.corpSecret` 引擎依然能够向后兼容拾起,但后续的新项目推荐采用标准的 `agentSecret` 作为对齐键。
301
311
 
@@ -385,6 +395,73 @@ openclaw plugins enable wecom
385
395
 
386
396
  一句话:`localRoots` 管“能不能读这个本地路径”,`mediaMaxMb` 管“最多读多大”。
387
397
 
398
+ ### 1.6 上下游企业配置:如何给上下游企业用户发消息
399
+
400
+ 如果你的企业微信应用已经共享给上下游企业,插件现在可以根据下游企业的 `CorpID` 和 `AgentID`,把回复准确发回对应的上下游用户。
401
+
402
+ 这件事适合的场景很明确:
403
+
404
+ - 你的主企业已经把自建应用共享给经销商、供应商或合作方
405
+ - 这些上下游企业用户会从不同 `CorpID` 进入同一个 Agent 通道
406
+ - 你希望插件能自动识别“这是下游企业用户”,并走对应企业的应用身份发消息
407
+
408
+ 最小配置示例如下:
409
+
410
+ ```jsonc
411
+ {
412
+ "channels": {
413
+ "wecom": {
414
+ "accounts": {
415
+ "default": {
416
+ "agent": {
417
+ "corpId": "ww_primary_corp",
418
+ "agentId": 1000001,
419
+ "agentSecret": "PRIMARY_AGENT_SECRET",
420
+ "token": "PRIMARY_CALLBACK_TOKEN",
421
+ "encodingAESKey": "PRIMARY_ENCODING_AES_KEY",
422
+ "upstreamCorps": {
423
+ "ww_partner_corp": {
424
+ "corpId": "ww_partner_corp",
425
+ "agentId": 1000002
426
+ }
427
+ }
428
+ }
429
+ }
430
+ }
431
+ }
432
+ }
433
+ }
434
+ ```
435
+
436
+ 可以这样理解这组配置:
437
+
438
+ - `agent.corpId` / `agent.agentId` 是上游主企业自己的应用配置
439
+ - `upstreamCorps.<key>.corpId` 是某个下游企业的 `CorpID`
440
+ - `upstreamCorps.<key>.agentId` 是这个下游企业里共享应用对应的 `AgentID`
441
+ - 下游企业不需要单独配置 `agentSecret`;仍然使用主企业应用的鉴权链路
442
+
443
+ 这些参数通常可以从两条路拿到:
444
+
445
+ - 直接从企业微信管理后台查看下游企业的 `CorpID` 和共享应用的 `AgentID`
446
+ - 通过企业微信“获取应用共享信息”接口批量拉取
447
+
448
+ 如果你打算走自动拉取,最关键的信息只有两个:
449
+
450
+ - 官方文档:`https://developer.work.weixin.qq.com/document/path/95813`
451
+ - 你需要把返回里的 `corp_list[].corpid` 映射到 `upstreamCorps.<key>.corpId`,把 `corp_list[].agentid` 映射到 `upstreamCorps.<key>.agentId`
452
+
453
+ 插件内部的工作逻辑是:
454
+
455
+ - 收到消息时,会先看回调里的 `ToUserName`
456
+ - 如果这个 `CorpID` 和主企业 `corpId` 不一致,就把它识别成上下游企业用户
457
+ - 回复时会自动走对应的上下游 target 和下游企业配置,而不是误发回主企业通道
458
+
459
+ 需要特别注意三点:
460
+
461
+ - `upstreamCorps` 只解决“发给哪个下游企业”的问题,不替代主企业应用本身的授权配置
462
+ - 上下游企业需要先在企业微信后台完成应用共享,并确保应用已加入“可调用接口的应用”
463
+ - 如果你只是想快速看完整字段说明、接口映射和日志样例,可以直接看 [UPSTREAM_CONFIG.md](./UPSTREAM_CONFIG.md)
464
+
388
465
  ---
389
466
 
390
467
  ## 二、🏢 企业微信后台回调挂载指南 (针对使用了 Webhook 或 Agent Callback 的重度用户)