diff --git a/docs/RPA-Review-Full.md b/docs/RPA-Review-Full.md new file mode 100644 index 0000000..0637f8f --- /dev/null +++ b/docs/RPA-Review-Full.md @@ -0,0 +1,788 @@ +# 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. 当前架构画像 + +```text +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 集成目标架构 + +```text +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. 推荐目标架构与接入边界 + +```text +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 实机测试。上述是源码级缺陷/风险定位,不等于已经在用户机器上复现。