Skip to content

feat(wecom): 新增企业微信 IM 与群通知 - #1107

Merged
DavidShenXD merged 27 commits into
mainfrom
xdt/wecom-im-notify
Aug 1, 2026
Merged

feat(wecom): 新增企业微信 IM 与群通知#1107
DavidShenXD merged 27 commits into
mainfrom
xdt/wecom-im-notify

Conversation

@DavidShenXD

@DavidShenXD DavidShenXD commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

这次改了什么

摘要

为 Desktop 新增两条独立的企业微信能力:

  1. 企业微信智能机器人双向 IM:通过企业微信官方 SDK 建立 WebSocket 长连接,让用户可在企业微信单聊或群聊中与 Cindy Agent 交互。
  2. 企业微信群机器人通知:通过群机器人 Webhook 将显式启用的定时任务结果推送到指定企微群。

智能机器人接入复用 Cindy 现有共享 IM 编排,支持文本交互、图片/文件/视频/语音入站、主动与被动回复、会话路由、权限交互和附件发送。首位完成单聊的企业微信用户会被绑定为本机 owner,后续仅接受该 owner 的单聊和群聊触发,避免企业内其他成员直接操作本机 Agent。

群通知配置使用本地安全存储,Renderer 只读取掩码状态;Webhook URL 限定为企业微信官方 HTTPS 地址。发送失败不会改变 Agent 回合或定时任务的终态。

变更类型

  • feat 新功能
  • fix 缺陷修复
  • refactor / perf 重构或性能优化
  • docs / test / chore 文档、测试或工程维护
  • 其他:

范围

  • 关联 Issue / 需求:企业微信智能机器人与企微群通知接入
  • 本 PR 包含:
    • @cindy/im 企业微信 transport、消息编解码、媒体下载与有界去重
    • Desktop 企业微信 adapter、owner TOFU 绑定、单聊/群聊 lane 路由、主动/被动回复降级
    • 企业微信 Bot ID / Secret 与群 Webhook 的安全存储和 IPC 边界
    • 企业微信群通知的保存、测试、清除、分块发送与严格 URL 校验
    • 设置页企业微信群通知总开关和 scheduler 的 notify.wecomGroup 单任务开关
    • SQLite migration 0084:为 schedule 增加默认关闭的 notify_wecom_group 字段
    • Settings、定时任务表单、四语 i18n、术语表、第三方声明与 SBOM
    • transport、通知、默认设置、scheduler、数据库映射和 UI 契约测试
  • 明确不包含:
    • 服务端改动
    • Mobile 原生配置、原生依赖或 runtime fingerprint 变更
    • 模板卡片等后续富交互形态
    • 本地技术设计草案文档
  • 用户可见变化:
    • 设置页可配置并连接企业微信智能机器人
    • 通知设置可配置、测试和清除企微群机器人 Webhook
    • 定时任务可单独选择是否推送到企微群
  • 是否存在 breaking change:无

UI 变化

  • 设置页新增企业微信智能机器人配置卡片和连接状态。
  • 通知设置新增企微群机器人配置、测试与清除操作。
  • 定时任务表单新增企微群通知开关。
  • 引用的设计规范:docs/design-rules/DESIGN.md §2 Color Palette & Roles、§4 Component Stylings、§5 Layout Principles、§10 Theme System & Token Reference(含 Light / Dark Dual-Mode Delivery Gate)、§11 Voice & Content、§14 Interaction Conventions。新增 UI 复用现有 Settings/Form 组件与语义 token,同时提供 zh-CN / en / ja / ko 文案。

怎么验证的

自动验证

pnpm test:unit
结果:通过;Desktop、Mobile、共享 packages 与协议 packages 的 unit tier 全部 PASS。

pnpm --filter desktop run --if-present typecheck
结果:通过。

pnpm --filter @cindy/im run --if-present typecheck
pnpm --filter @cindy/maker-core run --if-present typecheck
pnpm --filter @cindy/maker-scheduler run --if-present typecheck
pnpm --filter @cindy/maker-shared run --if-present typecheck
结果:通过或包内无对应脚本时按 --if-present 跳过。

pnpm --filter @cindy/im build
结果:通过。

pnpm check:dco
结果:通过;1 个提交的 author 与 Signed-off-by 一致。

pnpm --filter @cindy/im exec vitest run src/wecom/__tests__
结果:通过;3 个测试文件、13 项测试。

pnpm --filter desktop exec vitest run src/main/__tests__/wecomGroupNotification.test.ts src/main/__tests__/notificationService.test.ts src/main/im/__tests__/defaultSettingsStore.test.ts src/main/scheduler-host/__tests__/notifier.test.ts
结果:通过;4 个测试文件、47 项测试。

git diff --check origin/main...HEAD
结果:通过。

手工验证

已使用真实企业微信 Bot ID / Secret 完成智能机器人连接与消息黑盒验收,并完成企业微信群机器人通知测试。

未执行的验证

  • 未执行 Light / Dark 两种模式实机目检:已使用现有 themed 组件和语义 token,但仍需在联调阶段目检。
  • 未执行 Mobile 本地运行验证:本 PR 不修改 Mobile 代码、原生配置或 runtime fingerprint;Mobile unit tier 已通过。

风险

风险分类

  • 无已知风险
  • SQLite / migration
  • system prompt
  • 协议兼容
  • 权限 / 安全 / 用户数据
  • 原生层 / fingerprint / OTA
  • 跨平台差异
  • 其他:

影响与回滚

  • 影响范围:
    • Desktop 新增企业微信长连接和 Webhook 网络访问。
    • Bot Secret、owner 标识与 Webhook URL 进入 owner-scoped 安全存储,不写入普通配置、数据库或日志。
    • migration 0084 仅新增默认关闭的 nullable/default-false 调度通知字段,不改变既有任务行为。
    • 媒体按现有 Cindy 媒体账本或渠道托管目录处理。
    • Desktop 在 Windows/macOS/Linux 上共用实现;真实企业微信媒体和长连接行为仍需跨平台联调。
    • Mobile runtime fingerprint 不变,没有修改 apps/mobile 原生配置、原生依赖或 config plugin。
  • 回滚 / 降级方式:
    • 可关闭/清除企业微信智能机器人和群 Webhook 配置,既有 IM 与通知渠道不受影响。
    • 代码回滚时移除 wecom channel 与通知 wiring;历史 migration 保留,新增布尔字段可继续保持默认关闭,不需要破坏性回滚数据库。
    • 企业微信服务异常时,连接状态会进入断开/重连流程;群通知失败不会影响 Agent 或 scheduler 的最终状态。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s,见 DCO
  • UI 改动已在「UI 变化」注明引用的设计规范章节(不涉及 UI 则跳过)
  • 未提交凭证、令牌或授权文件
  • 已补充必要文档
  • 已确认测试结果或说明未执行原因

Copilot AI review requested due to automatic review settings July 30, 2026 17:08
Comment thread packages/lizi-im/src/wecom/media.ts Fixed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

schedulePatchToRow 当前会在旧调用方漏传 wecomGroup 时误清空该列,属于会导致用户配置被意外覆盖的逻辑问题。

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

本 PR 为 Desktop 增加企业微信(WeCom)两条能力:智能机器人双向 IM(WebSocket 长连接)以及企业微信群机器人 Webhook 通知,并把该通知能力接入普通会话通知与 scheduler 的 per-schedule 通知开关,同时补齐本地安全存储、IPC 边界、DB migration、i18n/术语与 SBOM 记录。

Changes:

  • @cindy/im 新增 WeCom transport:消息编解码、媒体落盘/缓存、owner TOFU 绑定、lane 路由与去重,并导出公共 codec。
  • 在 Desktop main/renderer 接入 WeCom:设置页配置与状态同步、群 Webhook 的安全存储与严格 URL 校验、通知服务与 scheduler notifier 的通道分发。
  • 为 scheduler 增加 notify.wecomGroup 开关,并通过 SQLite migration 0084 落库(默认关闭)。
File summaries
File Description
pnpm-lock.yaml 锁定新增 @wecom/aibot-node-sdk@1.0.7 依赖版本。
packages/maker-shared/src/scheduleTypes.ts 扩展远端 schedule notify config:新增 wecomGroup 字段。
packages/maker-shared/src/scheduleForm.ts 移动端草稿/模板/输入构建支持 wecomGroup
packages/maker-scheduler/src/types.ts scheduler 类型新增 wecomGroup 通知字段。
packages/maker-core/src/agents/base-agent.ts permission origin 的 IM channel 值域加入 wecom
packages/lizi-im/src/wecom/media.ts WeCom 媒体文件名净化、落盘与出站文件读取/类型推断。
packages/lizi-im/src/wecom/index.ts WeCom IM 主实现:连接管理、收发、owner 绑定、lane 队列与去重。
packages/lizi-im/src/wecom/codec.ts group lane 编解码、markdown 分块与转义。
packages/lizi-im/src/wecom/tests/media.test.ts media helpers 的单测覆盖。
packages/lizi-im/src/wecom/tests/index.test.ts owner TOFU、lane 路由、去重、同步启动失败与消息串行的单测。
packages/lizi-im/src/wecom/tests/codec.test.ts lane codec 与 markdown chunking/escape 的单测。
packages/lizi-im/src/types.ts IMHost 增加 wecomMediaDir,host media cache integration 增加 wecom
packages/lizi-im/src/index.ts 导出 WeCom IM 与 codec API。
packages/lizi-im/package.json 增加 @wecom/aibot-node-sdk 运行依赖。
i18n/GLOSSARY.md 增加 WeCom 相关术语条目(proposed)。
i18n/glossary.json 增加 WeCom/WeCom bot 相关术语(proposed,多语)。
docs/legal/notices/sbom/desktop-win.spdx.json Windows SBOM 记录新增依赖与生成时间/namespace 更新。
docs/legal/notices/sbom/desktop-macos.spdx.json macOS SBOM 记录新增依赖与生成时间/namespace 更新。
docs/legal/notices/sbom/desktop-linux.spdx.json Linux SBOM 记录新增依赖与生成时间/namespace 更新。
apps/desktop/src/shared/sessionSource.ts session source 值域加入 wecom
apps/desktop/src/shared/imDefaultSettings.ts IM 默认设置 channel 加入 wecom,并引入“remote IM 权限模式限制”判定。
apps/desktop/src/renderer/vite-env.d.ts renderer 侧 ElectronAPI 类型新增 wecomBot 与 wecomGroupNotification。
apps/desktop/src/renderer/i18n/locales/zh-CN/common.json 增加 WeCom bot 与企微群通知相关文案(中文)。
apps/desktop/src/renderer/i18n/locales/ko/common.json 增加 WeCom bot 与企微群通知相关文案(韩文)。
apps/desktop/src/renderer/i18n/locales/ja/common.json 增加 WeCom bot 与企微群通知相关文案(日文)。
apps/desktop/src/renderer/i18n/locales/en/common.json 增加 WeCom bot 与企微群通知相关文案(英文)。
apps/desktop/src/renderer/hooks/useWecomGroupNotificationSettings.ts renderer 侧企微群通知开关(localStorage)+ main 状态读取/操作封装。
apps/desktop/src/renderer/hooks/useWecomBot.ts renderer 侧 WeCom bot 状态同步、配置/重连/解绑操作封装。
apps/desktop/src/renderer/features/scheduler/lib/scheduleFormLogic.ts schedule 表单输入构建加入 notify.wecomGroup
apps/desktop/src/renderer/features/scheduler/lib/projectAutomationConfig.ts project automation config 的 notify shape 加入 wecomGroup
apps/desktop/src/renderer/features/scheduler/hooks/useScheduleForm.ts schedule 表单默认值与回填支持 notifyWecomGroup
apps/desktop/src/renderer/features/scheduler/components/ScheduleFormDialog.tsx scheduler 表单 UI 增加企微群通知开关与跳转配置入口。
apps/desktop/src/renderer/features/cc-agent/CCAgentSidebarUpper.tsx 会话完成通知 payload 增加 wecomGroup channel。
apps/desktop/src/renderer/components/settings/WecomBotSection.tsx Settings 增加 WeCom Intelligent Bot 配置卡片与交互。
apps/desktop/src/renderer/components/settings/NotificationSection.tsx Settings 通知页增加企微群 Webhook 保存/测试/清除与开关。
apps/desktop/src/renderer/components/settings/ImDefaultSettingsSection.tsx WeChat/WeCom 统一走 remote IM 权限模式限制提示与禁用逻辑。
apps/desktop/src/renderer/components/settings/ImBotSection.tsx Personal IM 列表加入 WeCom bot 配置入口。
apps/desktop/src/renderer/tests/schedulerTemplateEntry.test.ts scheduler template 入口测试适配 wecomGroup 通知与 API mock。
apps/desktop/src/renderer/tests/makerSharedFixtureParity.test.ts shared fixture parity 测试覆盖 wecomGroup 草稿映射。
apps/desktop/src/renderer/tests/automationGeneratedSessions.test.ts session source 测试加入 wecom
apps/desktop/src/preload/preload.ts preload 暴露 wecomBot 与 wecomGroupNotification IPC API。
apps/desktop/src/main/wecomGroupNotification.ts main:企微群 Webhook 服务(严格 URL 校验、序列化发送、owner-scope secrets、IPC)。
apps/desktop/src/main/scheduler-host/project-automation-loader.ts project automation reconcile 支持 notify.wecomGroup
apps/desktop/src/main/scheduler-host/notifier.ts scheduler notifier 新增企微群发布通道,并把文本渲染函数抽象为 external。
apps/desktop/src/main/scheduler-host/index.ts scheduler wiring 注入 wecomGroupNotification publisher。
apps/desktop/src/main/scheduler-host/tests/storage.db.test.ts scheduler storage DDL 测试加入 notify_wecom_group 列。
apps/desktop/src/main/scheduler-host/tests/notifier.test.ts notifier 单测覆盖企微群通知发送与失败非致命。
apps/desktop/src/main/notificationService.ts 普通会话通知分发加入 wecomGroup channel,并复用 external 文案构建。
apps/desktop/src/main/localDb/schema.ts schedules 表新增 notify_wecom_group 列映射。
apps/desktop/src/main/localDb/mapper.ts schedule row 映射/patch 映射支持 notifyWecomGroup
apps/desktop/src/main/im/wecom/uiText.ts WeCom 渠道 UI 文案包(slash 命令、交互卡文本等)。
apps/desktop/src/main/im/wecom/textInteractions.ts WeCom 文本交互:权限/计划/提问的 pending 管理与超时。
apps/desktop/src/main/im/wecom/index.ts WeCom orchestrator wiring(adapter + interactions)。
apps/desktop/src/main/im/wecom/adapter.ts WeCom adapter:sessionId/title/workdir/vendorOptions 等落地。
apps/desktop/src/main/im/shared/types.ts IM channel 值域加入 wecom
apps/desktop/src/main/im/index.ts 启动 orchestrators 时接入 wecomIm 并导出。
apps/desktop/src/main/im/host.ts IMHost wiring:创建 wecomIm,配置 wecomMediaDir,并加入 IM 聚合。
apps/desktop/src/main/im/defaultSettingsStore.ts 默认设置写入时对 remote IM(含 wecom)限制部分 permissionMode。
apps/desktop/src/main/im/tests/defaultSettingsStore.test.ts 单测覆盖 wechat/wecom 的 permissionMode 限制。
apps/desktop/src/main/bootstrap-electron.ts bootstrap:注册企微群通知 IPC、notificationService 注入 publisher、remote IM 校验更新。
apps/desktop/src/main/tests/wecomGroupNotification.test.ts WeCom 群通知安全边界与串行发送单测。
apps/desktop/src/main/tests/notificationService.test.ts notificationService 覆盖企微群通道失败不影响其它通道。
apps/desktop/drizzle/meta/_journal.json drizzle journal 新增 migration 0084 记录。
apps/desktop/drizzle/0084_small_gwen_stacy.sql migration:为 schedules 增加 notify_wecom_group(默认 false)。
Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file
  • Files reviewed: 66/70 changed files
  • Comments generated: 3
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread apps/desktop/src/main/scheduler-host/notifier.ts Outdated
Comment thread apps/desktop/src/main/notificationService.ts
Comment thread apps/desktop/src/main/localDb/mapper.ts Outdated
Copilot AI review requested due to automatic review settings July 30, 2026 17:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

WeCom 媒体文件名消毒未规避 Windows 保留设备名(如 CON/PRN/NUL 等),会导致在 Windows 上特定附件名必现写入失败。

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file
  • Files reviewed: 67/71 changed files
  • Comments generated: 1
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread packages/lizi-im/src/wecom/media.ts Outdated
Copilot AI review requested due to automatic review settings July 30, 2026 17:35
@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 修复 scheduler 部分通知更新会误清空企微群通知配置的问题。
  • 更新普通会话与 scheduler 外部通知渲染注释,明确飞书和企微群共用范围及 Markdown 子集。
  • 确认并回复 CodeQL 文件名正则告警,相关修复已在前一提交完成。
  • 以上 4 条 review conversation 均已逐条回复并标记 resolved。

变更:

  • schedulePatchToRow 对非空 notify 对象仅更新实际出现的渠道 key;显式清空时才关闭全部渠道。
  • 增加旧调用方不触碰 notifyWecomGroup 以及显式清空三列的回归测试。
  • 将飞书专属的过时注释调整为外部通知通用约束。
  • CodeQL 修复将尾部正则替换为线性倒序扫描,并覆盖长连续空格输入。

验证:

  • pnpm test:unit:通过。
  • pnpm --filter desktop run --if-present typecheck:通过。
  • Desktop 目标测试:3 个测试文件、49 项测试通过。
  • pnpm --filter @cindy/im build:通过。
  • WeCom media 目标测试:3 项通过。
  • pnpm check:dco:通过。

备注:

  • 修复已推送至 xdt/wecom-im-notify,最新提交为 6f33920e
  • GitHub Actions / CodeQL 由新提交重新触发;本轮未等待远端检查完成。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

wecomGroupNotification 的 save-and-test 存在“发送测试成功到落盘写入之间”的队列竞态窗口,可能导致并发通知读取到未配置并失败。

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Comments suppressed due to low confidence (1)

apps/desktop/src/main/wecomGroupNotification.ts:148

  • 这里 saveAndTest 先通过 enqueue() 发送测试消息,但把 secrets.write() 放在队列外执行;当测试发送完成到写入之间有并发 publishMarkdown() / test() 入队时,会在 URL 尚未落盘的窗口期读取到“未配置”并失败(随后又立刻配置成功),属于可复现的竞态。建议把“测试成功后的落盘写入”也放进同一个 enqueue 操作里,使队列在写入完成前不释放,从而消除竞态,并保持“测试失败不落盘”的安全属性。
    const url = parseWebhookUrl(rawUrl);
    await this.enqueue(() => {
      if (!isAccountCurrent()) throw new ImAccountScopeClosedError();
      return this.send(url, 'Cindy 企业微信群通知测试成功');
    });
  • Files reviewed: 67/71 changed files
  • Comments generated: 0 new
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI review requested due to automatic review settings July 30, 2026 18:04
@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 修复企业微信媒体文件名未规避 Windows 保留设备名的问题。
  • 已逐条回复并 resolve 对应 review conversation。

变更:

  • safeWecomFilename 按 stem 检测 CON、PRN、AUX、NUL、COM1–COM9、LPT1–LPT9,命中时前缀下划线;带扩展名同样生效。
  • 使用固定集合实现,避免引入新的正则回溯风险。
  • 增加保留设备名及相似普通文件名的回归测试。

验证:

  • pnpm test:unit:通过。
  • pnpm --filter @cindy/im exec vitest run src/wecom/__tests__/media.test.ts:4 项测试通过。
  • pnpm --filter @cindy/im build:通过。
  • pnpm check:dco:通过。

备注:

  • 修复已推送至 xdt/wecom-im-notify,最新提交为 e7db2854
  • GitHub Actions / CodeQL 已由新提交重新触发;本轮未等待远端检查完成。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

发现确定性错误码匹配缺陷会导致 WeCom 出站附件失败原因被错误归类,且有一处已更新逻辑对应注释未同步。

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Comments suppressed due to low confidence (2)

packages/lizi-im/src/wecom/index.ts:271

  • 这里用 errorCode(error)(只读 error.code)来判断 WECOM_FILE_EMPTY / WECOM_FILE_TOO_LARGE / WECOM_FILE_NOT_FOUND,但 readWecomOutboundFile 抛的是 new Error("WECOM_FILE_…")(错误码在 message 里而不是 code)。结果这些分支永远匹配不到,会把“空文件/超大”等确定性错误误报为 UPLOAD_FAIL/SEND_FAIL。建议把判断逻辑同时覆盖 Error.message(或在抛错时设置 err.code)。
    apps/desktop/src/main/scheduler-host/notifier.ts:64
  • 这里的注释仍写“desktop / feishu 全关”,但实际判定条件已经包含 wecomGroup(下面 if 条件也已更新)。建议同步更新注释,避免后续维护误判 mobile 推送门槛。
    // 手机推送随 schedule 的通知意愿走:desktop / feishu 全关表示用户不想被这个
    // 调度打扰,mobile 不得绕过(schedule 级暂无独立 mobile 开关,任一通道开启即
    // 视为允许提醒);是否真的收到仍由手机端注册 token 决定,发送侧防打扰在
    // device-link 模块收口,失败静默。
  • Files reviewed: 67/71 changed files
  • Comments generated: 0 new
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 幂等确认 Windows 保留设备名反馈已在 e7db2854 处理,并再次回复、resolve 对应 review thread。

变更:

  • 本轮无新增代码。既有修复已让 safeWecomFilename 按 stem 识别 CON、PRN、AUX、NUL、COM1–COM9、LPT1–LPT9,并以前缀下划线生成可安全落盘的文件名。

验证:

  • 本轮未重复执行:这是对已完成 thread 的重复派发;既有验证包括 pnpm test:unit、企微媒体定向测试(4 项通过)、@cindy/im 构建及 pnpm check:dco

备注:

  • PR head 仍为 e7db2854;未执行 merge。

@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 再次确认 Windows 保留设备名反馈已由提交 e7db2854 修复,并已回复、resolve 对应 review thread。

变更:

  • 本轮无新增代码;既有实现会按文件名 stem 识别 Windows 保留设备名,并以前缀下划线生成可安全落盘的名称。

验证:

  • 本轮未重复执行:这是同一已完成 thread 的重复派发;既有验证包含全量单元测试、企微媒体定向测试、@cindy/im 构建和 DCO 检查,均已通过。

备注:

  • 本地与远端 PR head 均为 e7db2854;未执行 merge。

@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 幂等确认 Windows 保留设备名问题已由 e7db2854 修复,并再次回复、resolve 对应 review thread。

变更:

  • 本轮无新增代码;既有实现已覆盖 CON、PRN、AUX、NUL、COM1–COM9、LPT1–LPT9 等保留 stem,例如 CON.txt 会安全转换为 _CON.txt

验证:

  • 本轮未重复执行:该 discussion 已完成修复与验证;此前全量单元测试、企微媒体定向测试、@cindy/im 构建和 DCO 检查均已通过。

备注:

  • 本地与远端 PR head 均为 e7db2854;未执行 push 或 merge。

@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 再次幂等确认 Windows 保留设备名问题已由 e7db2854 修复,并已回复、resolve 对应 review thread。

变更:

  • 本轮无新增代码;现有修复按文件名 stem 识别保留设备名并添加下划线前缀,避免 Windows 文件写入失败。

验证:

  • 本轮未重复执行:该 discussion 的既有全量单测、定向测试、@cindy/im 构建和 DCO 检查均已通过。

备注:

  • 本地与远端 PR head 均为 e7db2854;未执行 push 或 merge。

@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 幂等确认 Windows 保留设备名 discussion 已由 e7db2854 修复,并已回复、resolve 对应 thread。

变更:

  • 本轮无新增代码;既有实现按文件名 stem 识别 Windows 保留设备名并添加下划线前缀。

验证:

  • 本轮未重复执行:既有回归测试、全量单元测试、@cindy/im 构建及 DCO 检查均已通过。

备注:

  • 本地与远端 PR head 均为 e7db2854;未执行 push 或 merge。

@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 再次幂等确认 Windows 保留设备名 discussion 已由 e7db2854 修复,并已回复、resolve 对应 thread。

变更:

  • 本轮无新增代码;既有实现按文件名 stem 识别 Windows 保留设备名并添加下划线前缀。

验证:

  • 本轮未重复执行:既有回归测试、全量单元测试、@cindy/im 构建及 DCO 检查均已通过;GitHub 本轮返回 isResolved: true

备注:

  • 本地与远端 PR head 均为 e7db2854;重复派发可能源于监控状态未及时收敛;未执行 push 或 merge。

@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 第七次幂等确认 Windows 保留设备名 discussion 已由 e7db2854 修复,并再次回复、resolve 对应 thread。

变更:

  • 本轮无新增代码;既有实现按文件名 stem 识别 Windows 保留设备名并添加下划线前缀。

验证:

  • 本轮未重复执行:既有回归测试、全量单元测试、@cindy/im 构建及 DCO 检查均已通过;GitHub 本轮仍返回 isResolved: true

备注:

  • 本地与远端 PR head 均为 e7db2854;持续重复派发应由监控状态未收敛导致;未执行 push 或 merge。

@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 第八次幂等确认 Windows 保留设备名反馈已由 e7db2854 修复,并已回复、resolve 对应 review thread。

变更:

  • 本轮无新增代码;既有实现会按文件名 stem 识别 CON、PRN、AUX、NUL、COM1–COM9、LPT1–LPT9,并添加下划线前缀。

验证:

  • 本轮未重复执行:既有回归测试、全量单元测试、@cindy/im 构建及 DCO 检查均已通过;GitHub 本轮再次返回 isResolved: true

备注:

  • 本地与远端 PR head 均为 e7db2854;持续重复派发应由监控状态未收敛导致;未执行 push 或 merge。

Copilot AI review requested due to automatic review settings July 31, 2026 05:24
@DavidShenXD
DavidShenXD marked this pull request as ready for review July 31, 2026 05:26
@DavidShenXD
DavidShenXD requested a review from a team as a code owner July 31, 2026 05:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

wecomGroupNotification 的 publishMarkdown() 在未配置 webhook 时会抛出“disabled”错误,错误语义不一致且可能误导上层处理。

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Suppressed comments (1)

apps/desktop/src/main/wecomGroupNotification.ts:202

  • publishMarkdown() 目前只检查 getState().enabled,在「未配置 webhook」或「存量 webhook 无效」时也会抛 WECOM_GROUP_NOTIFICATIONS_DISABLED,与 requireStoredUrl()WECOM_GROUP_WEBHOOK_NOT_CONFIGURED 语义不一致,容易让上层误判配置状态。建议先区分 configured/enabled:未配置时抛 NOT_CONFIGURED,已配置但总开关关闭时再抛 DISABLED。
  • Files reviewed: 75/79 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown

Greptile Summary

本 PR 为 Desktop 新增企业微信智能机器人与群通知能力,并完成相关调度、存储和界面接入。

  • @cindy/im 与 Desktop 主进程中新增企业微信长连接、消息编解码、媒体处理、owner TOFU 绑定及会话路由。
  • 新增安全存储支持的企业微信群 Webhook 配置,以及由总开关和单任务开关共同控制的调度通知。
  • 通过 SQLite migration 0084、共享调度类型和表单映射持久化 notify.wecomGroup
  • 新增设置页、定时任务表单、四语文案、测试及第三方依赖声明。

Confidence Score: 5/5

当前变更看起来可以安全合并。

先前的 owner 首绑竞争报告已由同步、无让出点的临界区证伪,普通会话不发送共享群通知属于明确产品边界,企微群开关误触发移动推送的问题也已在当前代码和回归测试中修复,因此没有仍需阻止合并的故障。

Important Files Changed

Filename Overview
packages/lizi-im/src/wecom/index.ts 新增企业微信 WebSocket transport、owner TOFU、入站队列、去重、媒体及回复处理;先前报告的 owner 首绑竞争不成立。
apps/desktop/src/main/im/wecom/adapter.ts 将企业微信 transport 接入 Desktop 共享 IM 编排和安全存储边界。
apps/desktop/src/main/wecomGroupNotification.ts 新增企业微信群 Webhook 配置校验、安全存储、消息分块与发送服务。
apps/desktop/src/main/scheduler-host/notifier.ts 接入独立的企微群通知发布路径,并已避免企微单独启用时误触发移动推送。
apps/desktop/drizzle/0084_small_gwen_stacy.sql 为 schedules 表新增默认关闭且非空的 notify_wecom_group 字段。
apps/desktop/src/renderer/components/settings/WecomBotSection.tsx 新增企业微信智能机器人凭据配置与连接状态界面。
apps/desktop/src/renderer/components/settings/NotificationSection.tsx 新增企微群通知的启用、保存、测试和清除界面。
apps/desktop/src/renderer/features/scheduler/components/ScheduleFormDialog.tsx 在定时任务表单中新增按任务控制的企微群通知开关。

Sequence Diagram

sequenceDiagram
  participant User as 企业微信用户
  participant Bot as 企业微信智能机器人
  participant IM as Cindy IM 编排
  participant Agent as Cindy Agent
  participant Scheduler as Scheduler
  participant Group as 企业微信群机器人
  User->>Bot: 文本或媒体消息
  Bot->>IM: WebSocket 入站事件
  IM->>IM: owner 校验与会话路由
  IM->>Agent: 启动或继续 Agent 回合
  Agent-->>IM: 回复与附件
  IM-->>Bot: 主动或被动回复
  Scheduler->>Scheduler: 检查总开关与 notify.wecomGroup
  Scheduler->>Group: Webhook 推送任务结果
Loading

Reviews (30): Last reviewed commit: "fix(wecom): close queued notification ra..." | Re-trigger Greptile

Comment thread packages/lizi-im/src/wecom/index.ts
Comment thread apps/desktop/src/main/bootstrap-electron.ts
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
Signed-off-by: david <david@xd.com>
@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 已回应两条重复的主分支冲突提醒(#issuecomment-5144421776#issuecomment-5144943072):分支已 rebase 到最新 origin/main@5dbaab80 并推送 eddbe88f,当前不再落后于该基线。
  • 企业微信 session 级文案统一使用“任务”,聊天通道使用“对话”。
  • 企业微信群通知发送前清除 xdt-file:// / cindy-media:// 本地附件引用。
  • Webhook 配置增加代次校验,清除或切换开关后,排队中的旧保存不能复活配置。
  • 已保存 Bot ID 时,错误态/冲突态仍可执行“断开并清除”。
  • group lane 增加严格 base64url 字符集与规范化解码校验。
  • 附件失败提示改为主动消息,不再占用后续用户消息的回调帧。
  • 被动流式回复安全窗口降为 2 分 45 秒,超过窗口后改走主动消息。
  • 文件写入前后校验账户归属,失效时删除暂存文件;图片以可回收 staging 写入并在失效时回滚。
  • 上述 9 个 review conversation 均已逐条回复并标记 resolved。

变更:

  • 修改 Desktop 企业微信设置、群通知与调度通知逻辑,以及 @cindy/im 的企微 lane、终态回复和媒体暂存逻辑。
  • 新增/扩展术语、附件引用清洗、Webhook 并发、lane 非法输入、回调帧隔离、3 分钟窗口、文件与图片竞态回滚测试。
  • 未修改 Mobile;未合并 PR。

验证:

  • pnpm test:unit:通过(最新 main 基线上全仓 unit tier 全部通过)。
  • pnpm --filter desktop run --if-present typecheck:通过;@cindy/im 无 typecheck script,按仓库门禁自动跳过。
  • pnpm check:i18npnpm check:i18n-glossary:通过。
  • Desktop 本轮变更文件 ESLint:通过;@cindy/im 本轮变更文件除既存 no-useless-escape 规则外通过。
  • pnpm check:dco:通过(25 个 PR commits);git diff --check origin/main...HEAD:通过。

备注:

  • pnpm --filter @cindy/im lint 仍报告 3 个既存错误:2 个 Telegram 未使用符号、1 个本 PR 之前已存在的企微正则转义;本轮未扩大范围修复。
  • GitHub Actions / CodeQL 等远端检查可能仍在运行,本轮未等待。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Human review recommended

引入新的远程 IM 通道与 Webhook 出站通知、涉及安全存储/IPC 边界与数据库迁移,风险面较大需要人工最终把关。

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Suppressed comments (1)

apps/desktop/src/main/im/wecom/uiText.ts:17

  • WeCom 渠道已在 slashCommands 中为文本通道放行了 /permission(用于切换权限模式),但这里的 /help 文案与 unknownCommand 提示未包含该命令,容易导致用户不知道该入口或被误导为不支持。建议把 /permission 加入命令列表与提示中。
  • Files reviewed: 94/103 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eddbe88f9c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/lizi-im/src/wecom/index.ts
Signed-off-by: david <david@xd.com>
@MagicLizi

Copy link
Copy Markdown
Contributor

@DavidShenXD 👋 这个 PR 还有 1 条 review conversation 没 resolve(packages/lizi-im/src/wecom/index.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Human review recommended

变更跨越 IM 通道/IPC/安全存储/DB migration/通知链路等多个高风险边界,需人工重点复核安全与兼容性(并处理已指出的细节问题)后再合入。

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Suppressed comments (2)

apps/desktop/src/renderer/tests/schedulerTemplateEntry.test.ts:204

  • 这里对 window.electronAPI.wecomGroupNotification.getState 的 mock 只返回了 { configured: false },缺少 enabled 字段(以及 setEnabled/clear 的返回形状也与 ElectronAPI 契约不一致)。这会让 useWecomGroupNotificationSettingsenabled 设成 undefined,从而掩盖契约回归。建议 mock 返回完整字段并补齐必要方法。
    apps/desktop/src/main/wecomGroupNotification.ts:306
  • wecomGroupNotification:save-and-test 的 IPC handler 里先 parseTestMessage(testMessage),但 WecomGroupNotificationService.saveAndTest() 内部又会再次 parseTestMessage(),导致重复 trim/字节上限校验并增加维护成本。建议 IPC 层只做类型校验,然后把原始字符串交给 service 统一校验。
  • Files reviewed: 96/105 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6618d745d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/desktop/src/main/wecomGroupNotification.ts
@MagicLizi

Copy link
Copy Markdown
Contributor

@DavidShenXD 👋 这个 PR 还有 2 条 review conversation 没 resolve(packages/lizi-im/src/wecom/index.ts / apps/desktop/src/main/wecomGroupNotification.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

Signed-off-by: david <david@xd.com>
@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 修复排队中的企业微信群 webhook 测试在配置清除、切换或替换后仍向旧地址发送的问题。
  • 修复企微入站图片在缓存写入完成后、最终账户归属检查前失效时未回滚暂存引用的问题。

变更:

  • 测试发送入队时记录配置代次,执行前再次校验;配置变化时返回 WECOM_GROUP_CONFIG_CHANGED
  • 图片下载结果携带暂存回滚句柄穿过归一化流程,混合消息合并全部回滚句柄,并补充两项竞态回归测试。

验证:

  • pnpm --filter desktop exec vitest run src/main/__tests__/wecomGroupNotification.test.ts:19 个测试通过。
  • pnpm --filter @cindy/im exec vitest run src/wecom/__tests__/index.test.ts:28 个测试通过。
  • pnpm --filter @cindy/im lint:通过。
  • pnpm --filter @cindy/im run --if-present typecheck:通过。
  • pnpm --filter desktop run --if-present typecheck:通过。
  • pnpm test:unit:全量通过。
  • git diff --checkpnpm check:dco:通过。

备注:

  • 已推送提交 e3d2b3db;两条 conversation 均已回复并 resolved。GitHub 自动检查可能仍在运行。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Human review recommended

It introduces new WeCom IM + webhook networking, secure storage/IPC boundaries, scheduler wiring, and a DB migration, which warrants careful human security and cross-platform review.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Suppressed comments (2)

packages/lizi-im/src/wecom/codec.ts:54

  • chunkWecomMarkdown 在循环中每次用 Buffer.byteLength(current + point) 重新计算整段字符串字节数,长文本会退化为 O(n²)(每次都扫描更长的 candidate),在 IM 输出较长时可能造成明显 CPU 开销。建议像本文件其它分块逻辑一样维护累计字节数,按字符增量计算并在超限时切块。
    apps/desktop/src/renderer/tests/schedulerTemplateEntry.test.ts:204
  • 这里的 wecomGroupNotification.getState() mock 返回值缺少 enabled 字段,但 hook 会读取 state.enabled 并写入 React state;这会把 enabled 置为 undefined(与运行时契约不一致),也容易在后续改动中导致测试出现隐性失败。建议 mock 返回完整的 state 形状。
  • Files reviewed: 96/105 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3d2b3db4c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/desktop/src/renderer/i18n/locales/zh-CN/common.json
@MagicLizi

Copy link
Copy Markdown
Contributor

@DavidShenXD 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/desktop/src/renderer/i18n/locales/zh-CN/common.json),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 评估“企业微信默认设置改为任务”的建议;确认该建议与 /new 的实际数据行为及仓库权威命名规则冲突,因此保留“新对话 / 已有对话”。

变更:

  • 无代码变更;企业微信 /new 仍在同一任务条目内重置上下文和渠道默认配置,不会创建新任务。

验证:

  • 静态核对 resetSessionToDefaults:对原有 sessions 行执行 update,未新增任务。
  • 核对 docs/product-rules/task-and-conversation-naming.md §6.0.3:明确要求 settings.imBot.defaults.* 使用“新对话”。
  • git status --short --branch:工作区干净;未执行测试,因为没有代码或文案改动。

备注:

  • 指定 conversation 已回复并 resolved;本轮无需提交或 push。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants