PRA-only-demo/docs/RPA-Review-Full.md

60 KiB
Raw Permalink Blame History

RPA 项目技术审查与 Agent-Employee 集成实施总方案

文档性质:统一总方案 + 最新源码复审记录。
审查对象:资料库中的 PRA-only-demo-main(2).zip(本轮解压源码快照)。
审查重点:RPA Core 边界、运行时安全、能力注册、驱动注入、平台适配、元素拾取、Agent-Employee 接入。
结论状态:静态源码审查;未将未执行的测试写成通过。

0. 执行摘要

这份文档统一此前几轮讨论、项目原有技术文档和两轮源码审查结论。它不是“项目已经成熟”的背书,而是当前代码能否安全演示、能否抽成可复用 RPA Core、能否接入 Agent-Employee 的事实清单与实施路线。

**总体判断:**项目已经从单纯 Electron Demo 走到有流程引擎、能力注册、驱动接口、元素拾取和能力目录的阶段;但当前 @rpa/core 还不能被认定为真正独立、可注入、可跨平台的执行内核。最主要的阻塞不在于“再加多少能力”,而在于执行取消/重试安全、Engine/Registry/Driver 生命周期、Electron 依赖残留、包依赖闭包和宿主治理契约。

当前建议:

  1. Windows 10/11 演示优先,先收敛一条稳定端到端流程,不要为了演示临时扩展 Linux/macOS。
  2. 接入 Agent-Employee 前先修 P0/P1,尤其是取消与重试、Driver 注入、动态 Electron 依赖和包依赖闭包。
  3. 先做垂直切片:Gateway 授权 → RPA Runtime → 一个文件能力 → 一个浏览器能力 → 一个桌面能力 → 事件/证据回传,再逐批搬能力。
  4. 跨平台按能力域分批实现。当前代码是 Windows-first,不是 Windows/Linux/macOS 三端可用;类型里出现平台占位不代表已有实现。

1. 项目目标与已确认约束

以下是此前项目讨论中已经明确的目标,不是本轮从代码推断出来的事实:

  • 项目定位:面向办公自动化的 RPA 平台,后续接入已有 Agent-Employee / opencode 底座。
  • 用户负责 RPA 部分;商业项目已有独立的服务端 AI 组件,RPA Core 不应重复承担服务端模型部署职责。
  • 总体方向:AI 模式 hybrid;部署方式 local;跨平台目标 all。
  • 元素拾取策略:本地优先、云端兜底;hover 实时识别高亮,click 确认。
  • 目标软件范围:Office、浏览器、Electron、Qt 以及自绘界面。
  • 识别策略:UIA/Accessibility 等结构化接口优先,OCR/视觉模型补足;不把 YOLO 训练作为前置条件,也不把 SAM 作为当前主链路。
  • 当前实际交付重点:Windows 10/11;Linux/macOS 是后续架构目标,不应在未完成驱动与平台能力矩阵前宣传已支持。
  • 技术现状:Electron + Node.js + React + TypeScript;RPA Core 位于 packages/rpa-core,宿主 UI/拾取窗口仍在 src/main / renderer。

2. 当前架构画像

Electron Desktop Host
  ├─ Renderer / Flow Canvas / Properties UI
  ├─ Main Process
  │   ├─ flow persistence / settings / logging
  │   ├─ desktop picker / overlay / vision lab
  │   └─ host bridge initialization
  └─ @rpa/core (workspace package)
      ├─ Flow Runtime / expression / error model
      ├─ module-global Capability Registry
      ├─ capability handlers (files, HTTP, Office, desktop, OCR, vision...)
      ├─ WindowsDriver / PowerShell helpers
      ├─ HostBridge / SubflowSource module singletons
      └─ Browser CDP transport / external browser launcher

2.1 已经具备的基础

  • 有独立的 packages/rpa-core 目录和包名 @rpa/core。
  • 有 Flow Runtime、RunContext、NodeState、RuntimeError、能力注册表与能力元数据。
  • 有 Windows PowerShell driver 封装及鼠标、键盘、窗口、屏幕、UIA 五域接口定义。
  • 有 registerAll() 能力分组加载入口。
  • 有 HostBridge / SubflowSource 抽象,减少部分宿主 API 的直接依赖。
  • 有元素拾取相关代码、OCR/视觉能力、能力目录和较多单元/冒烟测试。

2.2 不能据此宣称已经完成的事项

  • Core 仍有动态 import("electron"),不是 Electron-free。
  • createEngine() 当前没有真正接收 Driver;registerAll(engine) 也没有把注册表绑定到该 Engine。
  • 多个能力仍直接依赖 PowerShell、Win32、COM、Electron 屏幕截图 API。
  • 当前没有 MacDriver / LinuxDriver。
  • @rpa/core 的 package manifest 没有声明源码实际使用的全部外部依赖。
  • 尚未完成 Agent-Employee 真实契约的编译级对接。
  • 本轮未运行 Vitest、typecheck、Electron build、Bun test、Windows 实机 E2E。

3. 本轮继续审查新增发现

P1-NEW-01:RPA Core 内仍有动态 Electron 依赖,依赖护栏漏检

证据:

  • packages/rpa-core/src/capabilities/vision.ts:18,66,432:动态 import("electron"),读取 desktopCapturer / screen。
  • packages/rpa-core/src/capabilities/cv-vision.ts:51,213:动态 import("electron"),获取截图与 DIP 坐标转换。
  • packages/rpa-core/scripts/check-deps.mjs:20-20,38-43:当前扫描表达式只匹配静态 import/export ... "specifier",不匹配 import("electron")。
  • packages/rpa-core/src/engine/host-bridge.ts:6-8 与 docs/rpa-core-migration-guide.md:28 却声称 Core 禁止 Electron / 完全不依赖 Electron。

影响:

  • Node/Bun 独立运行时,调用视觉能力时会尝试加载 Electron;CLI 只跑未触发这些分支的能力,不能证明整个 Core 脱离 Electron。
  • Agent-Employee backend 若不是 Electron 主进程,视觉能力会在运行时失败或无法复用。
  • 依赖护栏“通过”目前只能说明它没有识别到静态违规,不能作为边界验收证据。

整改:

  1. 将屏幕截图、显示器信息、DPI 换算抽成 ScreenCaptureHost / DisplayCoordinateService 接口,由 Electron Host 实现注入。
  2. vision.ts、cv-vision.ts 只依赖接口,不再动态加载 Electron。
  3. 依赖护栏补动态 import、require()、createRequire() 与 import() 字符串扫描;最好使用 TypeScript AST,而不是继续堆正则。
  4. CI 增加一个纯 Node/Bun 进程加载 Core 并实际调用视觉 Host mock 的测试。

**验收:**在没有 Electron 包可解析的 Node/Bun 环境下,Core 能加载;视觉能力在 Host 未注入时返回明确的 host_service_unavailable,注入 Fake Host 后可完成截图/坐标测试。

P1-NEW-02:@rpa/core 包依赖声明不完整

证据:

  • packages/rpa-core/src/capabilities/vision.ts 使用 @napi-rs/canvas。
  • packages/rpa-core/src/capabilities/pdf.ts 使用 pdfjs-dist、@napi-rs/canvas、@cantoo/pdf-lib。
  • packages/rpa-core/package.json 未声明上述三个依赖;它们目前只出现在根项目依赖(其中 @cantoo/pdf-lib、@napi-rs/canvas、pdfjs-dist)。
  • package-lock.json 的 packages/rpa-core workspace 条目同样未列出这些依赖。

**影响:**当前 monorepo 根目录安装时可能通过依赖提升“碰巧可用”,但把 packages/rpa-core 单独迁入 Agent-Employee 或单独安装时,相关能力可能出现 Cannot find package。这属于包边界和发布闭包问题。

**整改:**逐个核对 Core 的运行时外部依赖;凡 Core 源码运行时 import 的依赖,要么声明在 Core dependencies/optionalDependencies,要么从 Core 移除并由 Host 注入。原生模块需额外验证目标 OS 与 Bun/Node 兼容性。

**验收:**在隔离目录只安装 @rpa/core 声明的依赖,运行包加载、PDF/OCR/图像相关代表能力;不得依赖根项目的幽灵依赖。

P1-NEW-03:registerAll(engine) 参数和 Engine 实例没有实际隔离效果

证据:

  • packages/rpa-core/src/capabilities/register-all.ts:49-56 接收 engine,但只执行模块动态 import,最终仅 return engine。
  • packages/rpa-core/src/engine/registry.ts:33-34 的 handlers/metas 是模块级 Map。
  • packages/rpa-core/src/engine/runtime.ts:260 从全局 getHandler() 取 handler。
  • packages/rpa-core/src/engine/engine.ts:45-49 的 driver 固定为 new WindowsDriver(),createEngine() 无注入参数。
  • packages/rpa-core/src/capabilities/desktop.ts:9、common-final.ts:11、system-extended.ts:36、window-extended.ts:7 读取 defaultEngine.driver。

**影响:**调用者可能以为每个 Engine 有独立的 Driver、能力注册表和权限域,实际执行仍共享进程级注册表和默认 WindowsDriver。多 Agent、多桌面会话、测试并行、不同租户策略会互相污染。createEngine() 目前更像状态容器,不是完整隔离的执行实例。

整改:

  1. 先确定目标架构:建议 RpaEngine 持有 Registry、Driver、HostServices、SessionContext。
  2. createEngine({ driver, host, registry, ... }) 显式注入。
  3. handler 注册写入 engine.registry;Runtime 从 ctx.engine 或构造参数拿 handler,不读全局 Map。
  4. 能力 handler 不捕获 defaultEngine.driver;改为从 RunContext 注入的 execution services 读取。
  5. 过渡期如保留全局 API,明确标记 deprecated,不能让 registerAll(engine) 假装实例化注册。

**验收:**创建两个 Engine,分别注入 FakeDriverA/B 并注册不同能力集;并行运行时互不串用 Driver、handler、HostBridge、子流程源。

P1-NEW-04:CLI 冒烟测试不能证明 Engine 注入和 Core 独立性

证据:

  • scripts/rpa-cli.ts:24-25 同时 import { ... } from "@rpa/core" 和 import "@rpa/core/capabilities"(副作用全量注册)。
  • scripts/rpa-cli.ts:47-49 随后调用 createEngine() / registerAll(engine),但该参数没有绑定 Registry。
  • scripts/rpa-cli.ts:53-58 通过全局 getHandler() 执行 handler,而不是通过 Engine 获取。
  • scripts/rpa-cli.ts:5-10 声称 5 项代表能力,实际执行了 Window_GetActiveWindow、Window_GetScreenSize、Excel_Cs_NewV5、File_Write、File_Read、Http_Get 共 6 次 handler 调用。

**影响:**CLI 可以验证某些能力在当前 Node + 根目录依赖 + Windows 环境下能运行,但不能验证 Driver 注入、Engine 隔离、显式注册、无 Electron 依赖或独立包依赖闭包。文档将它当成“零宿主独立运行”的证明,结论过度。

**整改:**移除副作用 import;测试只使用显式注册;通过 Engine API 获取 handler 并运行;测试 FakeDriver 注入;将 CLI 验收拆成纯 Node、Windows Host、Electron Host 三种,不把联网 HTTP 作为唯一固定验收依赖。

P1-NEW-05:迁移文档与任务清单互相矛盾,完成度表述不可信

证据:

  • docs/rpa-core-migration-guide.md:22 写“改造已全部完成”;第 28 行写 Core 完全不依赖 Electron。
  • docs/rpa-core-refactor-tasks.md:17-91 阶段 0 至阶段 6 的任务仍全部保留 [ ] 未勾选。
  • 源码又存在动态 Electron import、Driver 注入缺失、包依赖缺项。

**影响:**接手者会按“已完成”理解架构状态,低估接入工作量;也无法判断哪些是设计目标、哪些是已验收能力。

**整改:**统一文档状态标记:已实现(代码定位)、已测试(测试证据)、已在目标宿主验证、计划中。每项完成必须附 commit / 测试命令 / 测试结果,不以文档描述代替代码事实。

4. 元素拾取与视觉定位:统一技术路线

元素拾取不应被等同为“找一个 AI 模型识别按钮”。办公 RPA 需要的是可执行、可复定位的 Target,而不仅是截图框。

4.1 建议识别优先级

  1. 结构化可访问性接口:Windows UIA;浏览器 DOM/CDP;macOS AX;Linux AT-SPI。
  2. OCR + 几何关系:用于文字、标签、按钮文本与相邻控件关系,输出文字和 bounding box。
  3. 免训练视觉候选检测:当前模型实验可作为候选框生成器,但不要将模型类别直接当成业务语义。
  4. 模板/局部视觉匹配:对图标、无文本控件和自绘 UI 作兜底。
  5. 人工确认:候选冲突、低置信度或高风险动作时要求用户确认。

4.2 Selector 应保存“语义 + 定位证据”,而不是只存坐标

建议 Target 结构至少包含:

  • platform / app / process / window:目标环境身份;
  • accessibility:AutomationId、Name、ControlType、ClassName、祖先路径;
  • browser:URL/域名、frame、DOM role/aria-label/test id 等;
  • ocr:识别文本、语言、置信度、相对区域;
  • visual:bbox、截图裁剪 hash、模型标识、相似度;
  • fallback:最后一次屏幕坐标、DPI/缩放、窗口矩形、采集时间。

运行时定位顺序:结构化 selector → 同窗口/同 frame 限定查找 → OCR/视觉重定位 → 低置信度时停止并要求确认。禁止将拾取时 screen=x,y,w,h 当作长期稳定定位。

4.3 当前代码复核原则

  • 之前报告指出的 candidateId 回填错位问题,在本次 ZIP 中已看到映射修复,不重复当作当前缺陷。
  • desktop-pick.ts 中候选排序、筛选与执行 selector 的一致性仍需专项回归,尤其 Edge debug/CDP、DPI、多屏和 hover 高频刷新。
  • vision.ts / cv-vision.ts 的 Electron 直接调用应迁到 Host 服务,不能放在可复用 Core 中。
  • OCR 是语义补充,不应单独负责确定控件类型;模型候选类别也不能直接映射成“登录按钮”等业务含义。

5. Agent-Employee 集成目标架构

Agent-Employee Workflow / Agent Runtime
        │
        ▼
RPA Adapter(唯一入口)
  ├─ Workflow/Step 状态映射
  ├─ Capability Gateway:授权、租约、审批、审计
  ├─ 参数/返回值 Schema 转换
  ├─ Trace / Event / Evidence 关联
  └─ 取消、超时、幂等与重试策略
        │
        ▼
RPA Core(每次执行/会话实例)
  ├─ Flow Validator
  ├─ Runtime / Expression / Error Contract
  ├─ Engine-scoped Registry
  ├─ Capability Handlers
  └─ Execution Context(AbortSignal / Trace / Permission / Evidence)
        │
        ├──────── Host Services(宿主注入)
        │          ├─ Browser Adapter
        │          ├─ Screenshot / Display / DPI
        │          ├─ Dialog / AI Config / Subflow Store
        │          └─ Event / Evidence Sink
        │
        └──────── Computer Control Driver
                   ├─ WindowsDriver(当前优先)
                   ├─ MacDriver(后续)
                   └─ LinuxDriver(后续)

权责原则:

  • Agent-Employee Workflow/Runtime 是宿主最终 Run/Step 状态的唯一权威来源;RPA Runtime 作为执行器或执行子流程,不另建第二套冲突状态系统。
  • Capability Gateway 是所有带副作用能力的唯一授权入口。RPA Core 不再另建一套与其冲突的 allowlist。
  • computer-control 是桌面观察/定位/动作契约的优先对齐对象;不要让 RPA Driver 与宿主契约长期并行。
  • HostServices 与 Driver 按 Engine/Session 注入,不使用模块级全局 setter 承载多租户/多桌面会话状态。
  • 高风险能力(命令执行、键鼠注入、文件删除/覆盖、发送邮件/消息、数据库写操作)必须有权限、审批/租约、审计和资源范围。

6. 当前问题优先级总表

优先级 问题 接入影响 建议门槛
P0 超时只让外层 Promise reject,底层操作不一定停止 可能重叠执行、重复副作用 接入前修复
P0 取消状态与 NodeState 不一致;取消可能被当失败/跳过 宿主状态与执行状态冲突 接入前修复
P0 retry 默认策略过宽,非幂等动作可能重试 重复点击/输入/发信/覆盖 接入前修复
P0 Engine 无 Driver 注入;能力使用 defaultEngine 无法按会话隔离/测试替身 接入前修复
P1 Registry 模块级全局 Map,registerAll(engine) 不实例化注册 多 Agent/多权限域污染 接入前明确并改造
P1 HostBridge / SubflowSource 模块级单例 多宿主/多会话覆盖 接入前改造
P1 vision/cv-vision 动态依赖 Electron Core 不能独立运行 迁移前修复
P1 Core manifest 缺失 canvas/pdf 依赖 单包安装不完整 迁移前修复
P1 CLI 依赖全局注册,不能证明注入/独立性 现有验收证据不足 重写验收
P1 Flow Graph 无完整 Validator 多出口/非法边可能静默部分执行 接入前补校验
P1 CapabilityMeta 缺权限/副作用/幂等字段 Gateway 映射不完整 Gateway 对接前补齐
P1 Windows 专属能力散落在能力层 平台能力边界不清 平台适配前整理
P1 catalog platformTag 类型异常 平台过滤/界面标记不可信 修复并加 schema test
P1 Agent-Employee 契约尚未逐接口编译验证 估算与实现存在偏差 联调前必须做
P1 BrowserSession 的 locate 与执行查找器语义不一致 拾取后 XPath/Shadow DOM 选择器可能执行失败 浏览器能力验收前修复
P1 iframe 未纳入拾取/定位/执行上下文 iframe 内元素无法保证端到端可用,跨域 iframe 尤其明显 浏览器能力验收前明确支持边界
P1 CDP 单条命令无响应超时 浏览器命令可能永久 pending,流程卡死 接入前补统一超时与清理
P1 外部浏览器“前台标签跟随”取 pages[0] 多标签时可能切错目标页 多标签回归前修复
P1 AI 拾取取消未贯穿检测/OCR任务 取消后后台推理仍可能继续,OCR 等待无硬超时 拾取稳定性验收前修复
P2 依赖护栏只扫静态 import,且只覆盖 TS/TSX 边界回归容易漏检 尽快增强
P2 AI 请求没有调用方 AbortSignal 拾取取消不能立即中止请求 交互体验整改
P2 包没有独立 build/pack 验收 迁移消费方式不稳定 发布前补齐

7. 执行安全整改方案

7.1 取消必须贯穿整条调用链

RunContext.signal → Runtime → Handler → Host/Driver → PowerShell 子进程 / HTTP / Browser CDP。每层都必须接收 signal,并在取消时清理资源。

PowerShell 子进程需要持有 ChildProcess 引用,收到 abort 后执行 terminate/kill;仅设置 execFile timeout 不等于响应上游取消。浏览器动作、等待、网络请求、OCR/AI 请求也应尽量可取消。

7.2 超时不等于操作已停止

当前 withTimeout() 只让外层 Promise reject,不保证底层 handler 结束。超时后必须先 abort 底层任务并等待其停止/清理,再决定是否重试;不能直接开始第二次点击或输入。

7.3 重试必须基于能力语义

新增能力元数据:sideEffect、idempotent、retryPolicy、permission、approvalRequired、resourceScope。非幂等能力默认不重试;只有明确幂等、可安全重复的操作才能自动重试。对“发送消息/邮件、提交表单、点击付款、删除/覆盖文件”等默认禁止盲目重试。

7.4 状态机

至少统一 pending → running → succeeded | failed | cancelled | skipped。取消不能被转成普通失败,也不能误走错误分支。RunResult、NodeState、Agent-Employee Step 状态需要一份映射表和契约测试。

8. 平台适配路线

8.1 当前定位

当前实际是 Windows-first:PowerShell、Win32、UIA、Office COM、Windows 剪贴板和屏幕 API 占据大量路径。platforms: ["win32"] 的类型声明只是当前标记,不是跨平台实现。

8.2 建议分阶段

  • 阶段 A:Windows 可靠性:Windows 10/11、UIA、Edge/Chrome CDP、Office COM、DPI、多显示器、PowerShell 取消/超时、安装包资源。
  • 阶段 B:Linux 桌面基础:先选 X11 或 Wayland 的支持边界;AT-SPI、输入注入、截图、窗口管理分别实现;Wayland 必须明确权限与限制。
  • 阶段 C:macOS 桌面基础:Accessibility API、CGEvent/AppleScript、TCC 辅助功能和屏幕录制授权流程。
  • 阶段 D:Office/文件能力矩阵:Windows COM;macOS AppleScript/Office;Linux LibreOffice 或标记不支持。每项能力逐个标平台,不能按“同名软件存在”推断等价。

8.3 平台标记单一来源

register-all.ts 的能力分组、CapabilityMeta.platforms、catalog.platformTag 必须由一份平台能力表派生;当前发现 catalog.json 中 1016 项里 1013 项 platformTag=null,另 3 项是数字 1,与 src/types.ts 的 string | null 不符。build-catalog.mjs 的 component.platformTag || ... 会保留 truthy 数字,必须增加 schema 校验并修复源数据。

9. 推荐实施顺序与阶段门禁

阶段 0:先固化演示基线(Windows)

  • 冻结一个端到端演示流程,准备稳定的测试软件、数据和账号。
  • 把必需节点与不稳定节点分开;演示脚本不得依赖外网随机响应。
  • 验收:连续完整执行多轮;每轮有运行日志、节点状态、失败截图/证据;取消后无残留子进程/后台动作。

阶段 1:Core 接入前 P0

  • 贯通 AbortSignal 和底层取消。
  • 取消状态正确映射。
  • 重试策略按幂等性约束。
  • Engine 真正支持 Driver/Host/Registry 注入。
  • 验收:FakeDriver、超时、取消、重试、双 Engine 隔离测试全绿。

阶段 2:包边界闭环

  • 移除 Core 动态 Electron import。
  • 补齐/收敛 package dependencies。
  • 依赖扫描升级为 AST 或覆盖动态 import/require。
  • 重写 CLI,去掉副作用全量 import,验证 Engine-scoped registry。
  • 验收:隔离安装、Node/Bun 纯 Core 加载、Host Mock 视觉路径。

阶段 3:Agent-Employee 契约对齐

  • 获取目标仓库真实源码与当前 commit。
  • 对 workflow-engine、capability-gateway、computer-control、windows-plugin、browser-plugin 的类型、生命周期、事件和错误模型逐接口对照。
  • 输出 Adapter 映射表与 ADR,不根据迁移指南中的摘要直接开始实现。
  • 验收:编译通过、Gateway 拒绝时 handler 零调用、事件/证据可追踪。

阶段 4:小范围垂直切片

  • 先接一个纯文件能力、一个浏览器能力、一个桌面能力。
  • 跑通授权、租约、审批、执行、取消、事件、证据回传。
  • 验收后才开始分批迁移能力组。

阶段 5:能力规模化与跨平台

  • 能力分组迁移、平台标记、风险元数据补齐。
  • Linux/macOS 按驱动和能力矩阵逐步开放,不做虚假全平台开关。

10. 交付给接入团队的材料清单

  1. 本总方案(本文件)。
  2. docs/RPACore二次源码审查与接入整改报告.md:此前两轮源码审查细项与原始定位,作为附录参考。
  3. docs/rpa-core-migration-guide.md:现有迁移指南,但其中“完全不依赖 Electron / 已全部完成”等表述应按本总方案纠正。
  4. docs/rpa-core-refactor-tasks.md:原改造任务清单,当前勾选状态与迁移指南冲突,应重新按证据更新。
  5. docs/06-AI拾取模型选型与RPA执行方案.md、docs/图像与OCR问题检测报告.md:拾取与视觉路线参考,需结合当前实现再次做 Windows 实机回归。

11. 本轮审查边界

  • 已读取资料库中的最新 PRA-only-demo-main(2).zip,并检查 packages/rpa-core、核心运行时、注册入口、Driver、依赖护栏、CLI、catalog 和项目文档。
  • 已核验 check-deps.mjs 的实现逻辑;该脚本的扫描方式确实漏掉动态 import("electron")。
  • 未安装项目依赖;未执行 Vitest、TypeScript typecheck、Electron build、Bun test、打包和 Windows 实机 E2E。不得把这些项目标成“通过”。
  • 当前没有 Agent-Employee 目标仓库真实源码作为本轮输入,因此本文件给出的是接入架构和待核验契约清单,不代表已完成目标仓库编译对接。
  • ZIP 中不包含 node_modules;本轮静态结论以该 ZIP 源码快照为准。

附录 A:此前二次源码审查细项

以下保留此前审查报告的完整细项,包括已修复问题回归、P0/P1/P2 代码定位、Graph Validator、HostBridge、Registry、平台标记、能力元数据和验收标准,避免统一文档时丢失前几轮审查证据。

RPACore 二次源码审查与 Agent-Employee 接入整改报告

审查对象:资料库中的 PRA-only-demo-main(2).zip(约 154.8 MB)
审查日期:2026-11-06
审查范围:packages/rpa-core/、流程运行时、能力注册、Driver/HostBridge、能力目录、桌面拾取与打包配置、原有迁移文档
审查方式:源码静态审查 + 项目结构/JSON/脚本检查;未在 Windows 真实桌面环境执行端到端测试。
重要说明:本报告区分“源码已确认”“设计风险/需产品语义确认”“尚未运行验证”,不把静态推断写成运行结果。


0. 总体结论

当前项目已经不是简单的 Electron 自动化 Demo:它有独立的 packages/rpa-core workspace、流程运行时、能力注册表、五域 Driver 类型、HostBridge、外部浏览器 CDP、桌面 UIA/视觉拾取以及较多能力测试。现有技术栈不需要推翻,真正需要解决的是运行时语义、依赖注入闭环、能力治理和平台边界。

但当前状态还不适合直接宣称“可无修改迁入 Agent-Employee”或“已具备跨平台能力”。建议定位为:Windows-first 的 RPA 能力包,已建立初步包边界,仍需完成执行语义与宿主契约收口。

本轮核验到的项目事实

  • ZIP 内约 272 个条目、约 230 个文件;packages/rpa-core/src 包含 35 个 *.test.ts 文件。
  • packages/rpa-core 的包名仍是 @rpa/core,private: true,入口直接指向 src/index.ts,尚未形成面向普通 Node/Bun 部署的独立构建产物契约。
  • node packages/rpa-core/scripts/check-deps.mjs 本次执行通过;这只证明该脚本当前扫描规则未发现违规 import,不等于包完全没有宿主/平台耦合。
  • resources/catalog.json 声明 1016 个组件:1013 个 platformTag 为 null,3 个 platformTag 是数字 1,与 CatalogComponent.platformTag: string | null 类型不一致。
  • electron-builder.config.mjs 当前已经把 resources/desktop-picker.ps1 放入 extraResources;AI 候选覆盖层当前用 candidateId 回传并按 ID 查找原候选。这两项是旧版元素拾取报告中的 P0,本次最新快照中已看到对应修正,不应继续作为“当前未修复问题”重复上报。

建议优先级

优先级 主题 结论
P0 超时、取消与重试的副作用安全 先处理;否则自动化可能出现重复点击/重复写入
P0 Engine/Driver/Registry/HostBridge 依赖闭环 接入前必须明确,否则宿主无法可靠隔离会话与权限域
P1 流程图结构校验和执行语义 防止无效图被静默部分执行
P1 能力元数据与 Capability Gateway Agent 不应绕过授权直接调用高影响能力
P1 平台目录、Driver 边界与 Windows 专属能力 不应把“抽象接口存在”描述成 Linux/macOS 已支持
P1 包构建、Bun 兼容和集成验收 迁入前必须有目标运行时证据
P2 依赖扫描增强、注册冲突治理、可观测性 提高长期维护质量

1. P0:执行安全与依赖闭环

P0-01 超时只终止 Runtime 等待,不保证底层能力停止

源码位置:packages/rpa-core/src/engine/runtime.ts:33-55, 279-307;packages/rpa-core/src/engine/types.ts:118-137;packages/rpa-core/src/driver/windows/powershell.ts:45-69。

确认事实:withTimeout() 通过计时器拒绝外层 Promise;它没有创建并传递专属 AbortController,也没有在超时后通知 handler 停止。RunContext 虽然有 signal,但 AutomationDriver 的五域方法没有 signal 参数,PowerShell helper 也没有接受调用方 AbortSignal 的接口。PowerShell helper 自己有 30 秒等进程超时,但这与 Runtime 节点超时不是同一个取消链路。

影响:节点超时后 Runtime 可能已经开始重试,前一次 PowerShell/桌面操作却仍在执行。对点击、输入、删除、覆盖文件、发邮件、提交表单等非幂等动作,可能产生重复副作用。用户按取消后,运行状态结束也不代表底层操作已经停止。

整改:

  1. 为每次节点尝试创建独立 AbortController,把 Run 级 signal 与 Node 级 timeout 合并。
  2. Handler/Driver/PowerShell/HTTP/浏览器操作逐层接收 signal;可取消的操作必须真实中止。
  3. 对不能中止的原生/外部动作,标记为 nonCancellable,超时后不得自动重试,必须等待进程退出或进入人工恢复状态。
  4. 将重试策略从“按错误码猜测是否可重试”改为能力元数据声明:idempotent、retryPolicy、sideEffectLevel。
  5. 默认重试次数设置保守上限;当前 retryCount 被允许到 100,且 isRetryable() 将大多数未知错误视为可重试,不适合作为有副作用能力的默认策略。

验收:构造一个执行 5 秒的 Fake Handler,节点 timeout 设 100ms;验证 100ms 后 handler 收到 abort 并停止;再构造“第一次执行延迟完成、第二次执行”的测试,证明不会发生并发重入。对文件写入/点击类能力增加“超时不自动重试”测试。

P0-02 取消时节点状态可能记录为 failed,而非 cancelled

源码位置:runtime.ts:326-351;types.ts:139-160。

确认事实:executeNode() 捕获异常后,普通错误分支会将状态设为 failed 或 skipped;RuntimeError("cancelled") 没有在节点状态层单独映射为 cancelled。Run 层随后可能返回 status: cancelled,但对应 NodeState 仍显示 failed/skipped。

影响:Agent-Employee 若要做步骤级恢复、审批、审计和用户界面状态展示,会遇到 Run 已取消但 Step 显示失败的语义冲突。

整改:在 executeNode catch 中优先判断 cancelled,写入 state.status = "cancelled",不执行 skipOnError 和普通重试;统一发出 cancelled 事件,并记录取消原因/发起方。

验收:在节点执行中取消;断言 Run 与对应 NodeState 都是 cancelled,后续节点不执行,且不进入 error route。

P0-03 Engine 创建接口没有真正的 Driver 注入能力

源码位置:engine.ts:24-53;driver/types.ts:1-10, 158-165;packages/rpa-core/README.md:50-57。

确认事实:RpaEngine.driver 类字段固定 new WindowsDriver();createEngine() 无参数。Driver 类型注释和 README 却声称 createEngine() 可注入自定义 Driver。

影响:不能注入 Agent-Employee computer-control 适配器,也不能在单测中使用 Fake Driver;非 Windows 平台创建 Engine 时仍会实例化 Windows 实现。

整改:提供 createEngine({ driver, registry, hostBridge, subflowSource, ... }),将默认 Driver 的选择放在平台工厂或宿主装配层;Engine 不应在跨平台包内部无条件创建 WindowsDriver。

验收:Fake Driver 注入测试覆盖 mouse/keyboard/window/screen/element;Linux/macOS 上若无 Driver,明确返回 platform_unsupported,不得误报初始化成功。

P0-04 Registry 与 Engine 生命周期不一致,registerAll(engine) 参数没有生效

源码位置:registry.ts:33-62;register-all.ts:49-57;runtime.ts:3, 260。

确认事实:handlers/metas 是模块级 Map;registerAll(engine) 只动态 import 模块,能力模块通过全局 register() 写入同一个 Registry;runFlow() 用全局 getHandler() 查找 handler。传入的 Engine 不决定注册目标。

决策背景:原迁移指南明确选择过“保留全局注册表”。因此问题不是简单的代码疏漏,而是既有决策带来的隔离限制。

整改建议:

  • 短期单后端、单能力集合:可保留进程级 Registry,但把 API 改成语义真实的 loadCapabilities(groups),文档明确不支持 Engine 级能力隔离。
  • 长期多 Agent/多租户/不同权限域:改成 CapabilityRegistry 实例,由 Engine 持有;显式注册函数接收 Registry,运行时从 Engine Registry 取 handler。

验收:至少有两个 Engine A/B 的注册隔离测试(若采用实例级方案);若暂时采用全局方案,测试需明确证明共享行为是预期设计,并验证重复注册、重复加载和冲突覆盖规则。

P0-05 HostBridge/SubflowSource 是模块级单例

源码位置:engine/host-bridge.ts(HostBridge 与 SubflowSource 的模块级变量/set/get);ai-service.ts:20-24;human-dialog.ts;window-extended.ts。

确认事实:宿主桥通过模块级变量保存,setHostBridge() / getHostBridge() 读取同一进程级对象;子流程源也采用相同模式。

影响:多桌面会话、多 Engine、测试并行或多宿主容器时,后一次注入会覆盖前一次配置;AI 配置、DPI 换算、弹窗窗口工厂可能串到错误会话。

整改:把 HostServices 放入 Engine 构造依赖;若宿主明确只有一个全局桌面会话,可保留进程级服务,但应在 ADR 中限定部署模型,并提供 reset/restore 测试机制。不要让业务 handler 在模块导入时捕获全局桥对象。

验收:两个 Engine 注入不同 HostBridge,分别触发 AI 配置读取/DPI 转换/弹窗,结果不能交叉。


2. P1:流程图语义与运行时完整性

P1-01 执行器缺少运行前 Graph Validator

源码位置:runtime.ts:178-219;types.ts:115-145;runtime.error-route.test.ts:51-75。

确认事实:有边时,运行时选择一个起点(优先无入边 Trigger,其次任意无入边节点,再退回 top[0]),然后沿边递归执行;普通边通过 edges.find(...) 只取第一条非 error 边。访问过的节点由 visited 集合直接跳过。当前测试明确验证断开的 orphan 节点不执行,因此“孤立节点不执行”是现有行为,不应未经产品确认就当作 Bug。

风险:如果画布允许多个入口、一个节点多个普通出口、重复边、目标不存在、普通边形成环,运行时可能只执行其中一条路径或静默停止,且没有在执行前给出结构错误。流程编辑器允许的图结构与 Runtime 支持的图结构需要有统一契约。

整改:新增 validateFlowGraph(flow),运行前检查:节点 ID 唯一;边 source/target 必须存在;入口数量与 Trigger 规则一致;普通出口基数符合节点类型;error handle 唯一;不允许意外环;容器子节点 slot 合法;孤立节点明确警告或拒绝。若业务确实支持多出口/并行,必须实现明确的调度语义,不能用 find() 随机取第一条。

验收:非法多出口、孤立节点、环、重复 ID、悬空边均在执行前得到稳定错误码;合法 IF/循环/异常分支测试保持通过。

P1-02 skipOnError、错误分支与 Stop/Break/Continue 的状态语义需统一

源码位置:runtime.ts:141-154, 203-211, 326-351。

风险:目前错误路由、skipOnError、Break/Continue/Stop 都在不同层捕获;需要明确一个节点失败后何时走 error edge、何时 skip 并走 normal edge、何时终止整个 Run。特别要保证 cancelled 不会被当作普通可跳过错误。

整改:定义一张状态转移表并由测试固定:成功→normal;可跳过失败→skipped+normal;不可跳过失败且有 error edge→error;无 error edge→Run failed;cancelled→Run cancelled 且不走 error edge;Stop→按产品定义终止状态;Break/Continue 只能在循环上下文捕获。


3. P1:能力元数据、安全治理与平台支持

P1-01 能力目录平台标记数据异常

源码位置:resources/catalog.json;src/types.ts:49-70;scripts/build-catalog.mjs:234-249。

确认事实:当前 catalog 有 1016 个组件,其中 1013 个 platformTag=null,3 个值为数字 1(Basic_BusinessOrderCount、Dialog_GetConfigV7、Dialog_GetConfigV5),但类型要求 string | null。构建脚本的 component.platformTag || ... 会把 truthy 的数字 1 原样保留,无法靠类型声明自动纠正 JSON 数据。

影响:前端/Agent 不能可靠判断某个能力在哪个平台可执行;类型层与运行时 JSON 事实不一致。

整改:平台支持使用统一枚举 win32 | darwin | linux 或 null;生成 catalog 时做 schema 校验并拒绝非字符串值;平台能力映射只保留一个权威来源,运行时元数据与构建期目录从同一份数据派生。当前只声明 Windows 已实现,其他平台标记为未支持,不要为了“跨平台目标”提前填支持标记。

验收:catalog 构建后 1016 条均通过 schema 校验;每个能力都有明确平台状态;Windows 专属能力不会被误标为跨平台。

P1-02 CapabilityMeta 不足以承载 Agent-Employee 权限治理

源码位置:registry.ts:7-31;能力目录 CatalogComponent.risk 在 types.ts:65。

确认事实:Registry 的元数据目前包含 summary/params/returns/timeout/errorCodes/needsScreenshot/platforms;目录只有 risk: low | high。未看到统一的权限范围、审批要求、幂等性、副作用类型、资源访问范围等完整治理元数据。

影响:Agent-Employee 的 capability-gateway 无法仅凭当前元数据可靠地决定授权、审批、租约范围和审计字段;若直接暴露全部 handler,会绕过宿主治理边界。

整改:在宿主适配层建立唯一 CapabilityRunner:授权 → 租约/策略校验 → 输入校验 → 执行 → 证据/审计。为能力补充结构化治理元数据,建议包含 riskLevel、permissions、approvalRequired、sideEffect、idempotent、platforms、timeoutBudget;字段最终以 Agent-Employee 契约为准。高影响能力不得通过全局 getHandler() 直接暴露给 Agent。

验收:命令执行、键鼠输入、文件删除/覆盖、邮件发送、网络写操作等均经过 Gateway;审批拒绝/租约过期时 handler 不会执行;审计可关联 Agent、Run、Capability、审批结果和执行结果。

P1-03 Driver 抽象尚未形成平台隔离边界

源码位置:driver/types.ts:133-165;driver/windows/windows-driver.ts;capabilities/desktop-uia.ts;capabilities/vision.ts、cv-vision.ts、remaining-core.ts、system-extended.ts、word.ts 等。

确认事实:五域接口存在,但 ScreenDriver 目前只有 size();注释明确截图等 System.Drawing 操作仍可留在能力层。ElementDriver 通过 setElementDriverImplementation() 模块级 setter 接到 WindowsDriver;多个能力仍直接调用 PowerShell、Win32、COM 或 Windows 剪贴板。

影响:接口“看起来五域齐全”不等于能力层已平台无关。未来增加 Mac/Linux Driver 时,若不先盘点调用点,平台差异会继续散落在能力 handler 中。

整改:按能力建立 Windows-only 白名单;把截图、剪贴板、窗口/元素观察、键鼠动作、Office COM 等原语逐项归入 Driver/Host adapter;对不适合抽象的能力显式标记平台限制。不要为了抽象而一次性重写全部能力,按高频基础原语先收口。

验收:Core 通用层静态扫描不允许出现未列入白名单的 Windows API/PowerShell/COM 调用;Fake Driver 可覆盖核心自动化能力。

P1-04 跨平台目标与当前实现状态需要分开表达

确认事实:当前 WindowsDriver 使用 powershell.exe、user32.dll、Windows UIA、Office COM 等;外部浏览器启动和系统能力也存在 Windows 专属实现。没有看到可用的 MacDriver / LinuxDriver。

建议表述:项目目标是 Windows/Linux/macOS;当前可交付实现以 Windows 为主。Linux 需先明确 X11/Wayland 边界,macOS 需处理 Accessibility、Screen Recording 等系统授权;Office 自动化三端也不是同一套实现。不要将“接口预留”写成“已支持跨平台”。


4. P1/P2:包工程化与质量护栏

P1-05 @rpa/core 还不是完整的可发布包

源码位置:packages/rpa-core/package.json。

确认事实:包名为 @rpa/core,private: true,main / types / exports 直接指向 TypeScript 源文件;包脚本没有 build、pack 或 exports 产物检查。

影响:在当前 workspace/Bun 环境中可能可以直接消费 TS 源码,但不能据此推断它可被普通 Node.js、生产打包器或独立服务稳定消费。Agent-Employee backend 使用 Bun 的事实仍需在目标仓库实测。

整改:确定宿主采用源码 workspace 依赖还是构建产物依赖。若源码消费,锁定 Bun 版本并建立 Bun test/typecheck/运行测试;若产物消费,补 tsc/tsup 构建、exports 的 types/import/default 条目、files 白名单和 npm pack --dry-run 检查。

P2-01 依赖护栏是正则扫描,不是完整依赖边界证明

源码位置:packages/rpa-core/scripts/check-deps.mjs:20-55。

确认事实:脚本扫描 .ts/.tsx,用正则匹配静态 import/export ... from;没有覆盖 import("electron")、CommonJS require()、createRequire()、运行时拼接加载、.mjs/.js 文件,也没有校验包外部依赖 allowlist。当前脚本执行通过。

整改:保留快速扫描,同时增加 TypeScript AST 扫描;对 import()、require、createRequire 和禁用模块路径进行检查;另加 package.json 依赖审计和构建后 bundle 检查。脚本通过只能写成“当前规则扫描通过”,不要写成“彻底无 Electron 依赖”。

P2-02 Registry 重复注册与元数据覆盖规则不明确

源码位置:registry.ts:36-41。

确认事实:同 ID 再次注册会直接覆盖 handler;如果第二次注册没有 meta,旧的 meta 不会删除,可能出现 handler 已更新但 meta 仍是旧值。

整改:定义重复注册策略(拒绝/显式覆盖/版本化);覆盖 handler 时同步处理 meta;注册冲突输出可审计信息。

P2-03 registerAll() 可能发生部分注册

源码位置:register-all.ts:49-56。

确认事实:按传入顺序逐组 import;如果前面几个组已加载、后续遇到未知组才抛错,会留下部分全局注册状态。

整改:先校验全部 group 名称,再执行加载;长期方案采用显式 Registry,支持事务式注册或失败回滚。

P2-04 AI 请求缺少调用方取消信号

源码位置:capabilities/ai-service.ts:39-76。

确认事实:AI 请求使用 AbortSignal.timeout(timeoutMs),但 ChatCompletionOptions 没有调用方 signal 字段。交互式请求默认超时 45 秒;UI 拾取取消时不能从该 API 直接中止 HTTP 请求。

影响:界面退出后请求仍可能继续占用网络/服务端资源;连续识别可能造成无用请求并发。

整改:增加 signal?: AbortSignal,把用户取消与内部 timeout 合并;新请求开始时按业务策略取消旧请求;对 408/429/5xx 以外的错误明确重试策略。


5. 上一版元素拾取报告的回归核验

以下项目在 2026-11-01 的旧报告中列为高优先级,本次针对最新 ZIP 复核后状态如下:

旧问题 最新源码核验 当前结论
desktop-picker.ps1 未列入 electron-builder extraResources electron-builder.config.mjs:26-32 已包含 resources/desktop-picker.ps1 已修复,仍需安装包实测资源路径
候选框排序后用数组下标回填错位 desktop-pick.ts:238-253, 596-616 使用 candidateId 识别和回查候选 已看到修复,不应再列为当前 Bug
视觉坐标执行路径与 UIA 执行路径不一致 旧报告指出过此风险;本次未完成所有 selector source 到执行器分发的逐函数回归 待专项复核,不在本报告宣称已修复
AI 请求超时/取消 Core AI API 目前有 timeout,但没有外部 AbortSignal 仍有取消链路缺口,见 P2-04

元素拾取总体仍建议维持“UIA 快路径 → 局部 OCR/CV → 低频 AI 兜底”的方向。普通 hover 不应触发大模型;AI 候选应绑定稳定 candidateId,并最终形成可重新定位的 ElementDescriptor,而不是只保存一次性屏幕坐标。


6. 推荐目标架构与接入边界

Agent-Employee workflow-engine
        |
        v
RPA Adapter / CapabilityRunner
  - 权限授权、租约、审批、审计
  - Run/Step 状态映射
  - 输入输出 schema 适配
        |
        v
RPA Core Engine (per execution/session)
  - Flow Validator + Runtime
  - CapabilityRegistry(建议长期实例级)
  - RunContext (signal / trace / evidence / variables)
        |
        +---- Capability Handlers
        |       - pure Node capabilities
        |       - Windows-only capability allowlist
        |
        +---- AutomationDriver (injected)
        |       - WindowsDriver
        |       - LinuxDriver (future)
        |       - MacDriver (future)
        |
        +---- HostServices (injected)
                - Browser adapter
                - Dialog / DPI / config
                - Subflow repository
                - Event / evidence sink

职责原则:

  • workflow-engine 是宿主权威 Run/Step 生命周期的候选所有者;RPA Core 提供流程执行算法或节点执行器。最终只能有一个权威状态源。
  • Capability Gateway 是所有高影响能力的唯一授权入口;Core 不另建一套冲突的权限系统。
  • computer-control 是桌面观察/定位/动作契约的优先对齐对象;RPA Core 的 Driver 不应与宿主契约长期并行演化。
  • HostServices/Browser/子流程存储由宿主注入,Core 不直接依赖 Electron BrowserWindow 或应用全局状态。

7. 整改顺序与验收门槛

第一阶段:执行安全(接入前)

  • P0-01 节点级 AbortSignal 贯通 Runtime → Handler → Driver/PowerShell/HTTP/Browser。
  • P0-02 取消状态映射一致,取消不触发 skip/error route/retry。
  • P0-03 非幂等能力默认不重试;能力元数据声明幂等性与重试策略。
  • P1-01 Graph Validator 覆盖入口、出口、环、悬空边、孤立节点和容器 slot。

第二阶段:依赖闭环(接入前)

  • P0-04 明确 Registry 是进程级还是 Engine 级,并更新 API/ADR。
  • P0-05 Engine 构造时注入 Driver 与 HostServices。
  • P1-02 registerAll() 与实际注册作用域一致,重复注册规则明确。
  • P1-03 建立 Capability Gateway 适配层和权限元数据映射。

第三阶段:平台与包工程化(联调阶段)

  • P1-04 catalog 平台标记修复并增加 schema 校验。
  • P1-05 完成 Driver/Windows-only 能力白名单。
  • P1-06 选定 Bun 源码消费或构建产物消费方案,执行 Bun typecheck/test/运行验证。
  • P2-01 AST 依赖检查覆盖动态 import/require。
  • P2-02 AI 取消信号与拾取流程取消联动。

接入验收最低标准

  1. Core 在目标 Bun 版本下 typecheck/test 通过;不能以 Node 测试代替 Bun 验证。
  2. Fake Driver 注入成功,核心能力不启动真实 PowerShell。
  3. 两个 Engine 的依赖/状态隔离行为符合 ADR。
  4. 所有有副作用能力经过 Capability Gateway,权限拒绝时 handler 零调用。
  5. 取消/超时/重试测试证明不会重复执行非幂等动作。
  6. Graph Validator 能阻止非法流程在执行前运行。
  7. catalog 平台字段通过 schema 校验,平台支持范围与真实实现一致。
  8. Windows 10/11 实机完成 UIA、浏览器、Office、DPI、多屏幕、安装包资源和取消场景回归。

8. 需要与 Agent-Employee 团队共同确认的 ADR

ADR 必须确认的问题 建议
Registry 生命周期 一个 backend 是否存在多个 Agent/租户/权限域? 长期采用 Engine 级 Registry;短期单进程固定能力集可临时全局共享
Runtime 所有权 workflow-engine 还是 RPA Core 持有 Run/Step 最终状态? 宿主持有权威状态,Core 负责执行并回传事件
Driver 契约 RPA Driver 是否直接实现 computer-control? 对齐宿主契约,避免双份抽象
HostServices 作用域 一个进程是否会管理多个桌面会话? 按 Engine/Session 注入,不使用全局 setter
能力权限 哪些能力需要审批/租约/资源范围? Capability Gateway 唯一入口
包消费方式 Bun 直接消费 TS 源码还是 Core 构建后消费? 以 backend 生产部署方式决定,并建立 CI 证明
平台范围 本轮交付是否只要求 Windows? 当前明确 Windows-first,Linux/macOS 分阶段验收

9. 审查限制与下一步

  • 本次成功读取了资料库中的最新 ZIP,并对关键文件做静态核验。
  • 本次执行了 check-deps.mjs,结果通过。
  • ZIP 未包含 node_modules,当前审查环境没有安装项目依赖,因此没有执行 Vitest、TypeScript typecheck、Electron build 或 E2E;这些结果均标记为“未验证”,不能写成通过。
  • 没有 Agent-Employee 目标仓库源码可供本轮逐接口编译对接;其契约映射仍以原迁移指南中记录的接口信息为基础,正式集成前必须在目标仓库核对真实类型定义。
  • 没有在真实 Windows 10/11 桌面执行 UIA、Office COM、Edge/CDP、多屏/DPI、安装包和 AI 取消链路。

最终建议:先修 P0 的取消/超时/重试和 Engine 依赖注入,再决定 Registry 作用域;不要先把 1016 个能力整体搬入宿主。先用一条小而完整的垂直切片(一个流程、一个浏览器能力、一个桌面能力、一个文件能力)跑通 Gateway → Runtime → Driver → Event/Evidence,再逐批迁移能力。

12. 第三轮深挖:Runtime、浏览器执行链与元素拾取

本节是对同一源码快照的进一步静态追踪。这里区分“代码路径已确认”和“运行时表现仍需实机复现”;没有把未执行的 Edge/Office/UIA 测试写成已复现故障。

12.1 P1:BrowserSession 的定位与执行查找器不一致,拾取结果不保证可执行

源码证据:

  • src/main/browser/session.ts:61-85:selectorSpec() 接受 css、xpath、text、aria 等类型,并可接收拾取器产生的 css selector。
  • src/main/browser/session.ts:243-291:withElementOnce() 对 XPath 使用 document.evaluate();对 CSS >>> 分段逐层进入 shadowRoot。
  • src/main/browser/session.ts:463-503:locateOnce() 对 text、aria 有分支,但其余类型统一执行 document.querySelectorAll(spec.value)。因此 xpath 会被当作 CSS selector;css 中的 >>> 也会被当作普通 CSS 字符串。
  • src/main/browser/session.ts:562-565:click() 首先调用 locate(),之后才调用 withElement()。
  • src/main/browser/session.ts:623-627:type() 同样先调用 locate()。
  • src/main/browser/session.ts:672-681:getText(selector, attribute) 的属性分支再次直接调用 document.querySelector(spec.value)。

结论:

  1. xpath://... 即使能通过 withElement() 找到,也会在 click() / type() 前置的 locate() 阶段走 CSS 查询路径。
  2. 拾取器会生成包含 Shadow DOM 路径分隔符 >>> 的 selector;withElement() 支持该语法,但 locate() 与 getText(attribute) 不支持。
  3. 因此“拾取器生成了 selector”不等于“所有浏览器操作都能消费这个 selector”。这属于明确的实现不一致,不是单纯的测试缺失。

**整改:**抽出唯一的 resolveElement(selector, context),统一支持 CSS/XPath/Text/ARIA/Shadow DOM;locate/click/type/getText/getRegion/getElementInfo 全部复用同一个解析器。不要让每个方法各自拼 DOM 查询表达式。

**验收:**同一批 fixture 分别覆盖 CSS、XPath、text、ARIA、open Shadow DOM、多层 Shadow DOM;每个 selector 至少通过 locate、click、type、getText、getRegion。测试断言拾取产出的 selector 能被执行器直接消费。

12.2 P1:iframe 未进入浏览器拾取与执行上下文模型

源码证据:

  • PICKER_BOOTSTRAP_SOURCE(src/main/browser/session.ts:1390 起)将拾取脚本注入当前页面 document,使用 document.elementFromPoint、event.composedPath() 和 document.querySelectorAll()。
  • selectorSpec() / withElementOnce() / locateOnce() 均以当前 target 的主 document 为根,没有 frameId、frame URL、frame path 或 frame-specific execution context。
  • getElement() 生成的 CSS 路径只描述当前 document / ShadowRoot 树,不包含 iframe 边界。

**结论:**当前代码有 Shadow DOM 的部分穿透设计,但没有 iframe frame tree 的处理。跨域 iframe 内元素不能由顶层页面脚本直接访问;同源 iframe 也没有被当前 picker 主动递归遍历。不能把“支持 Shadow DOM”宣传成“支持 iframe”。

**整改:**基于 CDP Page.getFrameTree 建立 frame registry;为每个可访问 frame 建立 execution context,selector reference 携带 frameId 与 frame 内 selector;跨域 iframe 通过 CDP frame execution context 定位,而不是依赖顶层 JS 的同源访问。对于无法接管的特殊 frame,明确返回 unsupported / inaccessible。

**验收:**同源 iframe、跨域 iframe、嵌套 iframe、iframe 内 Shadow DOM 分别测试拾取、定位、点击、输入;页面导航和 frame 重建后 frameId 必须刷新。

12.3 P1:外部 CDP 单条命令没有超时和 pending 清理

源码证据:

  • packages/rpa-core/src/browser/transport.ts:96-117:send() 将请求写入 pending,等待对应 response;没有单次请求 timer / AbortSignal。
  • 只有 WebSocket close/error 时 failAllPending() 才会清理 pending(transport.ts:76-78, 160-165)。
  • transport.ts:83-90:connect() 用 Promise.race() 做连接超时,但超时分支没有清理 timer,也没有关闭已经创建的 WebSocket transport。

**影响:**CDP 某条命令若既不返回响应、WebSocket 又没有断开,调用会一直 pending;上层 Runtime 的 withTimeout() 只能让节点 Promise 超时,不能清掉 transport 内部 pending 项。连接超时后底层 socket 也可能继续存活。

整改:send(method, params, { timeoutMs, signal }) 为每个 request 建 timer;超时后删除 pending 并 reject;连接超时必须清 timer、关闭 socket;所有 timer 在成功、失败、close 三条路径都清理。超时错误要保留 method、targetId、requestId。

**验收:**模拟不返回 response、WS 断线、连接握手超时、主动 close;断言 Promise 在期限内结束、pending 数回到 0、socket 不泄漏。

12.4 P1:所谓“跟随前台标签页”实际上取 /json/list 的第一项

源码证据:

  • src/main/browser/session.ts:205-213 的 followForegroundTab() 调用 listPageTargets() 后直接取 pages[0]。
  • src/main/browser/session.ts:1293-1305 的 picker followForegroundTab() 同样取 pages[0]。
  • packages/rpa-core/src/browser/external.ts:242-250 的 listPageTargets() 只是按 /json/list 返回顺序过滤并映射,没有查询实际窗口焦点或活动 target。
  • listTabs() 的 active 字段只是比较 page.id === this.activeTargetId(session.ts:1186-1193),代表当前 CDP session 绑定页,不代表用户实际看到的前台页。

**结论:**注释中的“前台标签页”与实现语义不一致。多标签场景下 /json/list 第一项不保证是当前活动标签页,可能切错页面,造成操作落在用户看不到的页面。

**整改:**明确产品语义到底是“用户前台页”还是“流程当前绑定页”。若要求真实前台页,使用可验证的活动 target 机制(并处理 Chromium 版本差异);若只是流程绑定页,删除“前台跟随”逻辑和误导性注释,要求通过 targetId 明确切页。

**验收:**至少 3 个标签页,交替切换前台页,比较用户可见页、CDP 当前页、RPA active 标记;点击新标签、页面跳转、关闭标签时检查绑定状态。

12.5 P1:AI 桌面拾取取消未贯穿检测和 OCR,存在后台任务继续运行的路径

源码证据:

  • src/main/desktop-pick.ts:480-490:detection 先启动截图与 detectUiElements(),OCR recognizePane() 并行启动;这两个任务都没有接收 AbortSignal。
  • desktop-pick.ts:506-528:取消 watcher 只与 detection 做 Promise.race();取消先发生时只结束外层等待,不会停止检测/截图/OCR底层工作。
  • desktop-pick.ts:557-589:OCR 后台任务独立运行并更新 overlay。
  • desktop-pick.ts:613-614:用户确认候选后直接 await detected.ocrTask,没有额外硬超时。
  • src/main/local-vision/pane-ocr-service.ts:88-112:recognizePane() 等待模型初始化和 ocr.detect(),接口没有 signal / timeout。

**影响:**用户取消时,CPU/GPU 推理或 OCR 仍可能继续;overlay 已销毁时后台检测后续仍可能访问窗口对象;用户确认后 OCR 若卡住,拾取流程可能超过 UI 的 180 秒候选等待期限继续挂起。

**整改:**为拾取建立统一 AbortController,贯通截图、检测、OCR、overlay 更新和 UIA enrichment;第三方模型不能中止时,至少在取消后丢弃结果、禁止更新 UI,并限制并发任务数。OCR 结果不应成为确认回填的无限等待条件:设定独立预算,超时允许以 AI 框 + UIA 结果回填,并将 OCR 标记为未完成。

**验收:**分别在截图、模型加载、检测、OCR、UIA enrichment 阶段取消;断言取消后不再更新 UI、不再启动后续步骤、overlay/hotkey/临时图片均清理;OCR 超时仍能按明确降级策略完成或失败。

12.6 P2:浏览器选择器属性读取路径存在二次解析偏差

getText(selector, attribute) 先经 locate() 成功,再在属性分支中直接 document.querySelector(spec.value)。即使 locate 未来补齐 XPath / Shadow DOM,属性分支仍会绕过统一解析器。应与 12.1 一并修复,不要单独打补丁。

12.7 当前测试证据不足:浏览器底层实现没有被集成测试覆盖

**源码核验:**当前 ZIP 中存在 src/main/capabilities/browser*.test.ts,但相关测试主要验证能力 handler 向 BrowserLike 转发参数及 mock 行为;未发现针对 src/main/browser/session.ts / packages/rpa-core/src/browser/transport.ts 的真实 DOM / CDP transport 单元测试。browser-common.test.ts 对 XPath 的测试验证的是把 xpath://button 传给 mock 的 getElement,并不验证 BrowserSession 能否解析 XPath。

建议新增测试层:

  1. selector-resolver.test.ts:纯 DOM fixture,统一覆盖 CSS/XPath/Text/ARIA/Shadow DOM/iframe。
  2. cdp-transport.test.ts:mock WebSocket,覆盖乱序响应、超时、close、断线和 pending 清理。
  3. browser-session.integration.test.ts:使用真实 Chromium/Electron fixture 验证拾取 selector 到 click/type 的端到端一致性。
  4. browser-multitab.test.ts:多 target 切换、前台页识别、页面导航与 target 生命周期。
  5. desktop-picker-cancel.test.ts:各阶段取消、OCR 超时、overlay/hotkey/temp file 清理。

12.8 本轮深挖结论与修复顺序

顺序 事项 等级 建议处理
1 统一 selector resolver,修复 XPath / Shadow DOM 的 locate 执行不一致 P1 先修,直接影响浏览器自动化成功率
2 CDP request timeout、AbortSignal、pending 清理 P1 先修,避免浏览器节点永久挂起
3 iframe/frameId 支持边界与实现 P1 先明确 MVP 支持范围,再实现
4 修正前台标签语义,不能把 pages[0] 当 active P1 多标签演示前修复
5 AI 拾取取消贯穿检测/OCR/资源清理 P1 桌面拾取稳定性验收前修复
6 增加 BrowserSession/CDP/Picker 真实集成测试 P1 修复后必须补证据

**本轮仍未执行:**Vitest、TypeScript typecheck、Electron build、真实 Edge/Chrome CDP 测试、Windows UIA/Office/DPI 实机测试。上述是源码级缺陷/风险定位,不等于已经在用户机器上复现。