Skip to content

🏗️ arch(browser): 四端移除旧浏览器实现,按 ZCode 蓝图重写(阶段:清理+删除已落地) - #860

Open
CavinHuang wants to merge 43 commits into
mainfrom
feat/browser-zcode-alignment
Open

🏗️ arch(browser): 四端移除旧浏览器实现,按 ZCode 蓝图重写(阶段:清理+删除已落地)#860
CavinHuang wants to merge 43 commits into
mainfrom
feat/browser-zcode-alignment

Conversation

@CavinHuang

Copy link
Copy Markdown
Owner

目标

以 ZCode 桌面端内嵌浏览器(IAB)的逆向还原为蓝图,重建 Lume 右侧面板浏览器。本 PR 承载两个阶段:

  1. 对齐尝试期(保留于历史):清理死面/测试白名单归位/桥版本闸/卸载式驻留状态机;
  2. 推倒重写期(用户决策):四端全删现有实现,按 ZCode 蓝图 1:1 重写(进行中)。

⚠️ 分支内浏览器面板暂时不可用(删除已落地、重写未合入),PR 在重写完成(R2–R6)前不合入 main。

已完成

阶段一:对齐尝试(后随重写决策部分被删除,历史保留)

  • 测试白名单改目录级发现(+43 个此前不受 CI 保护的测试);修复失效 electronMockStub
  • 死面清理:5 个零消费方事件 + webmcp 推送链;保护谓词收敛
  • IAB 桥协议版本闸(incompatible_protocol fail closed)
  • 卸载式驻留状态机(suspend-pending → 卸载 → suspended;唤醒 restoring)

阶段二:推倒重写(当前)

  • 删除清单(docs/plans/2026-08-30-browser-removal-manifest.md):四端 ~90 整文件 + ~30 共享文件剥离点逐一执行,总计 −33,256 行 / 247 文件
  • 重写设计(docs/plans/2026-08-30-browser-rewrite-design.md):目标模块映射表、46 命令 zod 协议、事件面、安全模型(保留 mountToken 增强)、六阶段实施
  • 施工蓝图:ZCode 逆向还原源码 15.5k 行(结构化提取)+ 字节精确注入脚本

验证

typecheck 测试
desktop 0 错误 262 pass / 0 fail
web 0 错误 287 tests(1 失败为 HEAD 预存在,已 git show 验证)
sidecar 仅剩预存在 obsidian 错误 rpc 204 pass / 0 fail
shared 0 错误

预存在问题(与本 PR 无关,HEAD 即有):sidecar obsidian vault-facade.test.ts 类型错误、web SettingsView 副本断言、sidecar 全量 test:unit 挂起、layering-boundary 两条例外边。

后续(同一 PR 内继续提交)

  • R2 shared 协议包(ZCode 46 命令 zod/常量/错误码/capability 矩阵)
  • R3 desktop 核心(BrowserGuestManager/驻留协调器/截图表面协议/playwright-over-CDP 执行引擎/CUA 输入/字节精确注入脚本/录像/对话框控制器/IPC/guest preload)
  • R4 renderer SidePane(tab 模型/池/工具栏/摆位)
  • R5 sidecar IAB 后端 + control-browser skill
  • R6 端到端冒烟

Constraint: 重写完成前不合入 main;浏览器面板在本分支中间态不可用
Rejected: 增量对齐旧实现 | 旧核心与 ZCode 语义差异缠绕,蓝图与还原源码齐备后重写净收益更高

盘点确认 Lume 已是 ZCode 同型架构(webview + 主进程权威 + CDP + 挂载授权),
对齐是补差而非重写。计划列出 9 项前置清理(测试白名单/死代码/光标路径/
截图 fallback/挂起收拢/桥版本握手/webmcp 隔离/一次性 webview/方法面盘点)
与对齐主线的依赖顺序,并冻结两条桥的契约规则。

Constraint: 对齐期间不改 browser:request 帧、browser:event 事件名、挂载协议
Constraint: 清理不改行为;行为变更一律进对齐主线 PR
Rejected: 直接重写 BrowserRuntime | 现有实现与目标同型,重写无收益
Not-tested: 纯文档提交
原 test 脚本为 30+ 文件显式白名单,漂移导致 12 个浏览器测试文件
(action-effect/action-queue/agent-script/download-sweep/input-ledger/
locator/semantic-snapshot/guest-preload×2/webmcp-consumer/annotation-security)
与 scripts 侧 5 个测试从未在 CI 运行——重构防护形同虚设。

- test 脚本改为 bun test ./src ./scripts 目录级发现
- node-repl-worker.test.mjs 更名 .suite.mjs(bun 发现会误跑 node:test 套件,
  它必须用 node --experimental-vm-modules 运行)
- electronMockStub 补 nativeTheme(browser-runtime 依赖,缺失导致
  browser-webmcp-consumer.test.ts 单独运行即挂,系预存在损坏)

结果:633 pass/0 fail(原白名单仅 590),typecheck 通过,ci-workflow 契约 6/6。

Constraint: test:smoke 白名单保持原样(CI 只跑 smoke,最小改动)
Tested: bun test ./src ./scripts 全绿;node 套件独立验证;根 ci-workflow 契约测试
- enforceBackgroundLimit 的内联保护过滤提取为 isSuspendProtected 谓词,
  并补齐 agentDispatching/dialogOpen 两个缺口(对齐 ZCode
  isBrowserTabResidencyProtected 的 operationActive 语义):
  此前用户在 agent 动作在途/对话框待处理时切换 tab 可触发冻结
- screenshot() 标注为截图唯一入口并写明表面协议替换边界:
  视口三层策略链(含父窗口裁剪分支)是对齐 ZCode
  prepare/ready/release 协议时的整体替换对象

Constraint: 行为零变更(agentDispatching/dialogOpen 保护是行为收紧,见下)
Tested: typecheck 通过;bun test 633 pass/0 fail
消费方驱动的死面审计(45 方法 × 19 事件 × sidecar/web/main 全语料):
- 方法面无死项(45/45 均有消费方,含仅单引号调用的 print/devtools/emulate/
  share/unshare/bounds/focus)
- 5 个自始零消费方的事件删除 emit:agent-cursor(光标每次移动的热路径死发)、
  cursor-error、debugger-detached、lease-revoked、tab-share-changed
- webmcp 推送通知链整体移除:lume:browser-page-event 通道 + handlePageEvent/
  handleBrowserPageEvent + guest-preload onToolsChanged 转发 + 死测试断言;
  拉取链(webmcp:list/invoke + shim + enable 门)完整保留——推送事件自始
  无消费方,消费侧本就按需 list
- main-webmcp-ipc.wiring 守卫改为反向断言防回潮;weread webview(159 处
  web 引用)确认是活功能,不在清理范围

Constraint: 只删零引用面;不改任何有消费方的命令/事件/payload
Tested: typecheck 通过;bun test 628 pass/0 fail
desktop/sidecar 独立发版后,握手描述符的协议范围此前仅在 extension 后端
被校验(sanitizeExtensionRuntimeDescriptor),IAB 后端从未验证——版本错配时
命令会静默命中未知方法/载荷,表现为难以诊断的桥接错误。

- dispatch 队列内新增 assertIabProtocolCompatible:握手描述符声明范围与
  sidecar 构建期常量(BROWSER_PROTOCOL_MIN/MAX_SUPPORTED)不相容时
  fail closed 抛 incompatible_protocol
- 握手不可达时放行:真实命令以真实传输错误失败,闸只对正向不相容证据行动,
  不吞更有诊断价值的原始错误;未声明范围的端点交由 MAC+sequence 桥兜底
- incompatible_protocol 纳入 sidecar 稳定错误码集(原排除理由'仅 legacy
  插件客户端抛出'随本闸引入失效)
- broker 测试:main fake 补握手协议字段、调用序断言过滤 handshake、
  新增不相容/未声明两分支专项用例

Constraint: 闸置于队列内部,不改变 per-tab 串行语义与队列槽位回收(#615)
Tested: broker 26 pass/0 fail;services/browser 40 pass;tools/browser 31 pass;
typecheck 除预存在的 obsidian vault-facade 错误外通过(HEAD 上即存在)
记录四个提交的落地情况、死面审计结论(方法面零死项/5 死事件/webmcp
推送链)、与原计划的六处偏差(C2 上游已做/C4 缩水/C7 C8 结论反转/
C3 C9 缓行)及对齐主线就绪状态。
原挂起仅 CDP 冻结(guest 常驻占内存),唤醒为 fire-and-forget 解冻,
命令与解冻存在竞态。对齐 ZCode BrowserTabResidencyCoordinator 语义,
按 Lume 骨架化简(主进程驱动,无需 renderer ack——挂载机制本就
代际守卫,池经 guestState:unmounted 自动丢弃、guest-mount-required
自动重建):

- 挂起:suspend-pending(CDP 冻结)→ 卸载 guest(复用 release 关闭
  路径,释放渲染进程内存)→ suspended;冻结 await 期间被唤醒则取消
- 唤醒:wakeTab——卸载态经 recoverGuest 触发重挂(guest-mount-required
  → 池 recover → attachGuest 落 live),仍挂载的旧冻结解冻;dispatch
  的 navigate 分支先等待重挂完成再导航,消除空 guest 竞态
- 保护谓词迁至 browser-residency-policy 纯模块并补 isLoading
  (加载中卸载会打断导航),五处唤醒点统一走 wakeTab
- lifecycle 枚举扩展 suspend-pending/restoring(过渡态不透出模型上下文,
  context-assembler 白名单天然过滤);协议版本 8→9

Constraint: renderer 零改动(池已有 discard/recover 语义)
Constraint: 卸载式挂起丢失页内未持久状态(表单/SPA 状态),与 ZCode 同语义取舍
Tested: desktop typecheck + 634 pass;sidecar browser 面 71 pass;
ci-workflow 契约 6/6
决策:四端全删现有实现,以 ZCode IAB 还原源码为施工蓝图重建;
批注子系统随核心删除;新核心采用 ZCode 命令协议(46 命令/domSnapshot 优先/
cua/recording),sidecar 工具层同步重写,传输层沿用 MAC 桥。
文档定义目标模块映射表、协议设计(命令/事件/安全)、六阶段实施与回退策略。

Constraint: 首版不含批注/WebMCP/extension/Chrome导入/advancedCdp/凭据填充
Rejected: 增量对齐旧实现 | 用户决策改为全删重写,蓝图与还原源码齐备
Not-tested: 纯文档
删除 types/browser-runtime、browser-api-registry 及 agent.ts 的浏览器
附件/认证类型群、runtime-event 的 browserAttachments 字段、rpc-error 的
BROWSER_UNAVAILABLE、tool-names 的 mcp__browser__ 前缀;allowlist 及其
契约测试同步剥离。plugin-market 的 browser-auth/chrome-extension 枚举
属插件市场通用元数据,保留。
按删除清单(manifest 提交)全量移除:
- desktop:runtime/执行引擎/批注+overlay/凭据/下载/审计/网络守卫/导入/
  workspace 存储/guest+auth preload/webmcp/main.ts 全部浏览器接线
  (3870→3445 行,零 browser 引用);weread 窗口、office/file-protocol、
  connection-vault 等邻近功能验证无浏览器依赖,保留
- web:BrowserShell/池/右面板浏览器 tab/设置页/agent 浏览器提及与附件/
  工具结果渲染器(约 6.5k 行);settings 缓存更名 clearRendererCaches
- sidecar:broker/工具层/external-chrome 传输/RPC 序列/bundled 插件/
  in-app-browser skill/agent 链路浏览器附件与 auth 管道/node-repl
  browserRequest 桥/kernel.rs 浏览器哈希管道(-4.1k 行)
- 传输断点两侧同步:desktop 不再发 browser:* 通知,sidecar 不再受理

保留:文件浏览器(file-browser)、插件市场 browser-auth 枚举(通用元数据)、
命名巧合(McpSettings 图标启发式/browser_download_url)。

Constraint: 原子移除,重写前 browser 面板在本分支不可用(不合入 main)
Tested: desktop typecheck 0 错+262 pass;web typecheck 0 错+287 pass
(1 个失败为 HEAD 预存在);sidecar typecheck 仅剩预存在 obsidian 错误,
rpc 204 pass/0 fail
装配与接线(按 docs/plans/2026-08-30-browser-rewrite-design.md):
- desktop assemble.ts:createLumeBrowserRuntime 全链工厂——A2 驻留协调器
  (onEvict→closeTabForLimit)、A8 截图表面(nativeImage CSS 重采样)、
  A4+A5+A6 执行链(dispatcher+playwright-bridge→locator/dom-snapshot 会话)、
  A7 输入原语+对话框控制器、A9 录像器,全部注入 BrowserGuestManager(A3)
- main.ts:runtime/ipc whenReady 创建(加固先于任何 webview)、恢复协议注册、
  事件转发、dispatchCommand 路由 lume:browser-view-*、will-quit 销毁
- sidecar:IAB 后端(ZCode 形状描述符+30s 超时先 cancel)+协议版本闸+
  19 工具族+control-browser SKILL;lume:browser-execute 桥(HMAC+sequence,
  响应携带 protocolVersion 完成握手)
- 修复:dispatcher clipboardItems 缺 {entries} 包装(运行时 type 必抛)、
  publicResult 吞 error 载荷(模型看不到稳定错误码协议)、fill→type 路由
- renderer 白名单 9 invoke + 10 event;vite 插件发出 guest-preload CJS

已声明缝(后续 PR):suspend 发起方(当前仅淘汰路径)、recoveryStore、
playwright-core 打包、设置页门控字段

Tested: desktop typecheck 0错+339 pass;web typecheck 0错+287 pass(1 预存在);
sidecar typecheck 仅预存在 obsidian;shared 0错+28 pass;
注入脚本与 ZCode 运行时产物字节相等(9 项断言)
…交互面)

四路并行逆向汇总:15 种 tab 类型全表、三层壳架构(reducer/Hook/壳组件)、
布局性能三闸、终端真 PTY+ChannelServer 协议栈、Git 只读四作用域与
diff 降级管线、Shiki+diffs-worker 渲染、WikiReference 引用装配器、
交互面(快捷键/会话联动/多窗口/per-window 状态结论)。
五份分报告与 40+ 字节偏移切片同步归档。
Q1 subagent 面板族(三入口共用 L$,readOnly+rewind/可写双形态,V4 订阅,
剪枝闭环);Q2 白板(内存canvas)/轨迹(请求级timeline)/开发者工具(半成品);
Q3 RepoWiki 生成面板(host agentLane 两段式 tool-use,增量智能);
Q4 计划详情(ExitPlanMode 双通道,流式容错解析);Q5 git 执行层闭环
(host 裸 spawn CLI,环境加固,三级杀灭,partial commit 临时索引);
Q6 代码视图 Xt(catalogTree chunk,diffs-container Shadow DOM,Worker 池)
Q7 V4会话数据层(帧归约/CAS/恢复状态机/配额表);Q8 MessagePort协议栈
+fileWatcher(fs.watch+150ms防抖);Q9 轨迹JSONL数据源+三层快照缓存
(ETag/LRU);Q10 远程工作区语义(三态门/降级矩阵/移动overlay);
Q11 App壳状态机盲区补漏(33KB重切片+修正);Q12 composer事件管道
(10种CustomEvent/三档契约/三条插入路径)。逆向覆盖度终版宣告:
右侧面板无已知盲区。
@CavinHuang

Copy link
Copy Markdown
Owner Author

浏览器重写代码审查报告(逆向比对结论)

本 PR 的浏览器重写以 ZCode 桌面端 3.10.1 的逆向还原为蓝图。以下是对已落地代码的审查结论,按模块列出验证结果、发现的问题及修复状态。


一、总体结论

重写的 10 个模块全部与 ZCode 逆向蓝图语义等价,注入脚本实现字节级精确还原,全仓 typecheck/test 绿。发现并已修复 3 个运行时 bug;遗留 6 条已声明缝(均为功能增量而非正确性问题)。

二、逐模块审查

1. shared 协议包(packages/shared/src/browser/)

  • ✅ 49 命令 zod 判别联合与 ZCode 权威枚举冻结比对通过(28 项协议测试)
  • ✅ viewport 校验 320×320~3840×2160 与 ZCode fr 常量一致
  • ✅ capability 矩阵:claimTab/finalize/markDeliverable/markHandoff/recording.* 强制开(与 ZCode apiSupportOverrides 对齐);fileChooser 上传类默认禁用并产出稳定错误 "File uploads are not supported by iab"(逐字一致)
  • ⚠️ 偏差:ZCode 文档口径 "46 命令",权威枚举实为 49(多出的 fill/waitFor/capabilities 在执行器无路由,返回 capability_unsupported);waitForEvent(filechooser)/fileChooserSetFiles 保留在联合内使管理器产出稳定错误而非解析失败
  • ⚠️ 协议版本:本 PR 定为 v1(新协议新起点);ZCode 内部为 v3(wireVersion)/v8(命令面)——与 ZCode 不互通是设计意图

2. BrowserGuestManager(core/guest-manager.ts,4034 行)

  • ✅ 97 个方法全部移植(grep 校验无 MISSING),45 条 ZCode 原名对照
  • ✅ attachGuest 校验链完整:fromId 存在→isWebviewType→closed→五元组 scope→residency 代际→CDP debugger.attach("1.3")→四跟踪器→viewport override→resolveWaiters
  • ✅ 46 命令 execute/executeInScope 含 _M isSideEffecting sideEffect:"uncertain" 语义
  • ✅ ensureGuest 2 轮 attach flight / restoreSuspendedGuest(navigationHistory.restore + loadURL 兜底 + ERR_ABORTED 容差)
  • ✅ beginGuestTeardown(waitForGuestCdpIdle + debugger.detach 确认)
  • ⚠️ close-tab 事件载荷为五元组近似(ZCode Ij 原实现不可见,已标注)
  • ⚠️ suspendFlights/suspendAckWaiters 写入端(挂起发起路径)由装配层填充——当前仅淘汰路径可达挂起,无独立的"冻结后卸载"挂起发起方(见遗留缝)

3. 驻留协调器(core/residency.ts)

  • ✅ 与 ZCode BrowserTabResidencyCoordinator 逐行等价(23 项测试):upsert generation 归 0、suspend-pending 救活代数+1、commitCancelledSuspendgeneration <= 拒绝、markAttached 代数校验、LRU 三级 + tabId 决胜、onEvict false 即停
  • ✅ 状态集 live-visible|live-background|suspend-pending|restoring|suspended 完整

4. 执行引擎(core/executor/)

  • executeBrowserCommandOnView(ZCode jg)22 命令分发 + 错误归一化(cancelled/timeout/execution_error)
  • 注入脚本字节相等验证通过(9 项断言):SNAPSHOT(200,false)=5837B、RESOLVE("e5")=240B、ELEMENT_AT_POINT(640,360)=2831B、CHECK=390B、SELECT=822B、EVALUATE=320B、Fj=4402B(token __zcodeIabInputTargetToken 内联)、elementInfoRuntime=1137B、overlayRuntime=734B
  • ✅ playwright-over-CDP:隔离世界(zcode-playwright-locator)、帧链(describeNode→getContentQuads→attachToTarget{flatten} for OOPIF)、actionProbe(scrollIntoView 对齐轮换 + rAF 稳定性 10 帧/2 稳 + el-table 换 node 重解析 + expectHitTarget)、50ms 重试 + isRecoverableLocatorRace + Runtime.terminateExecution
  • ✅ DOM 快照:PlaywrightDomSnapshotSession(预算 3s/1s/500ms)、aria 快照 iframe 合并、四步归一化(剥 ref/删 img/压平 generic/iframe 内联)
  • 🔴 已修复的运行时 bug:dispatcher 内联 clipboardItems{ entries, presentation_style } 包装,Fj 消费 item.entries 会在每次 type 命令时抛 TypeError——集成阶段已用规范版(core/injected/text-input.ts)替换
  • 🔴 已修复的协议 bug:sidecar publicResult 吞掉 error 载荷导致模型只看到 {"ok":false},稳定错误码/sideEffect 反馈协议完全不可见——已改为只剥 meta/elapsedMs

5. 截图表面协议(core/screenshot-surface.ts,1123 行)

  • DesktopBrowserScreenshotSurfaceCoordinator(Vg)+ ActivityController(qg)+ 透明窗口引导(KH)完整移植
  • ✅ 校验阈值逐项对齐:视口 ±1px、surfaceScale 差 ≤0.001(unscaled)、timeoutMs 3000+1000、活动看门狗 35s
  • ✅ 侧面板屏外定影实现(position:fixed + opacity:.001 + rAF 双采样)已由 A10 落地
  • ⚠️ 透明窗口引导仅 darwin/win32 启用(与 ZCode 一致);Linux 依赖 capture pump

6. 录像子系统(core/recording/,1065 行)

  • ✅ 录像器 HTML 字节校验通过(6929→6928B,唯一差异 zcode:lume: 前缀 5→4 字符;修复过两处全角标点才达成)
  • ✅ setDisplayMediaRequestHandler 仅应答录制页 frame 的 video 请求;canvas 定频重绘 → captureStream → MediaRecorder VP8;分片不 transfer(structured clone)
  • ✅ 光标 overlay(18px #ff4d4f 圆点)、10 类动作 DSL、每 tab 单录制、90s 上限

7. 传输与 V4 会话数据层

  • ✅ MessagePort 每条 postMessage = 一条完整 RPC 帧(VSBuffer 序列化,VQL 变长整数 + 标签字节);握手 server 构造即发 200 Initialize
  • ⚠️ ZCode 的 V4 会话订阅(subscribeSessionsIndexV4,wireVersion 3,分片 crc32/1MB/16MB 组装,CAS baseRevision/baseLogEpoch,恢复状态机 + 30s deadline 升级 forceSnapshot)是通用会话基础设施——Lume 重写的会话面板走自有 sidecar RPC,未移植此层。若后续 SidePane 会话面板(非浏览器)需要对齐,此层是最大的独立工程

8. 语义对齐确认(与 ZCode 逐项比对)

语义项 ZCode 本 PR 一致
挂起 = 冻结 + 卸载 guest ✅(suspend-pending → close → suspended)
恢复 = recoverGuest → 池重建 → attach 落 live
激活 suspended 先 ensureResident
挂起期间唤醒 → 取消(代数++) ✅(lifecycle 离开 suspend-pending 即取消)
弹窗自动化代答 ✅ beginAutomation ✅ 同款
下载跟踪(will-download + 等待者)
弹窗 setWindowOpenHandler deny→外开/新tab
URL 白名单(仅 about:blank + http/https)
文件上传不支持(稳定错误)
32KB 输出头尾截断(CDP) ✅(toolOutput 截断引用)
光标 overlay(录像) ✅ 18px #ff4d4f ✅ 同款
录像 HTML CSP + 定频 canvas + VP8 ✅(字节校验)

三、发现并已修复的 Bug(3)

# 严重性 描述 修复
1 🔴 高 dispatcher clipboardItems{entries} 包装 → 每次 type 命令运行时 TypeError 集成时替换为 core/injected/text-input.ts 规范版
2 🔴 高 sidecar publicResult 吞掉 error 载荷 → 模型只看到 {"ok":false},错误码/sideEffect 协议不可见 改为只剥 meta/elapsedMs
3 🟡 中 sidecar fill 命令映射到无路由的 fill(必返 capability_unsupported) 改映射到可达的同形 type 路由(click-ref + replace-paste)

四、遗留缝(后续 PR)

# 描述 影响
1 挂起发起方:当前仅 enforceBackgroundLimit 淘汰路径触发挂起,无独立的"冻结后卸载"挂起 后台 tab 不会自动释放内存(对齐目标行为)
2 recoveryStore 未实现:restoreTabs 返回 [],跨重启 tab 恢复不可用 可选增强
3 playwright-core 需打包进 app(node_modules 打包或 snapshot) 缺失时快照/定位降级 capability_unsupported
4 设置页浏览器门控字段(generalSettings.browser.agentToolsEnabled)未类型化 功能可用但设置项未暴露
5 打包期 bundled-plugins 复制到 resources dev 模式已正常
6 端到端(live Electron)未验证——单侧 typecheck/test 全绿但需真机冒烟 合入前建议跑 test:browser-e2e 重写版

五、逆向证据链

全部结论可复核:

  • 逆向报告 16 份:docs/analysis/(SidePane 壳层/Git/Wiki/终端/交互面/subagent/白板/RepoWiki/计划/远程/composer/V4/git闭环/录像/传输/Xt)
  • ZCode 还原源码:.zcode/analysis/extracted/(15.5k 行 + 字节精确注入脚本)
  • 架构汇总:.zcode/analysis/sidepane/README-consolidated.md

Review by: 逆向分析(16 报告 + 字节精确注入脚本 14 件)→ 10 模块并行移植 → 2 轮集成 → 全仓验证

…起发起/recoveryStore/打包

差距审计驱动的收尾批次:
- 🔴 BrowserSidePane 挂载进右面板(browser 功能 tab,workspaceKey 与
  sidecar 同源派生);open-browser-url 队列+揭示流;后退/前进按钮态
- 标签栏完整功能:dnd-kit 拖拽重排/溢出渐隐/最近关闭重开栈(8)/
  总览弹层/操作横幅/resize 警告(aEt 移植)/缩放补偿/三类错误卡
- 工作区分桶持久化(Map≤50 LRU,browserUrls 映射,suspended 壳保活,
  后台 tab 事件路由)
- 挂起发起方:suspendTabForIdle 完整飞行(beginSuspend→快照先行→
  通知→5s ack→代际复检→卸载→commitSuspend)+ 空闲调度器(5min/60s)
- recoveryStore:JSON 原子写/7天修剪/50壳上限/损坏兜底
- fill 命令三层打通;IAB 描述符收敛 shared 单源;打包守卫
  (playwright-core 钉 1.59.1);设置门控类型化;E2E 冒烟(真机两遍)

Tested: desktop typecheck 0错+370 pass;web typecheck 0错+146 browser/
right-panel pass;sidecar typecheck 仅预存在;shared 28 pass
@CavinHuang

Copy link
Copy Markdown
Owner Author

遗留缝修复状态更新(全面对齐批次已推送)

审查报告中列出的 6 条遗留缝全部已修复,随 d9b9b5131 推送:

# 原遗留缝 状态 修复方式
1 挂起发起方缺失(后台 tab 不自动释放内存) ✅ 已修复 suspendTabForIdle 完整飞行(beginSuspend 代际+1 → 恢复快照先行 → 通知 renderer → 5s ack 等待 → 代际复检救援守卫 → 卸载 → commitSuspend)+ 空闲调度器(默认 5min 空闲/60s 轮询,保护位过滤);ack 超时回滚 live-background 下轮重试
2 recoveryStore 未实现(restoreTabs 返回 []) ✅ 已修复 createRecoveryStore(configDir):JSON 原子写(临时文件+改名+串行化)、7 天修剪、50 壳上限、损坏兜底;已接入 assemble 的 manager options
3 playwright-core 未打包(快照/定位降级) ✅ 已修复 依赖精确钉 1.59.1(上游 1.60+ 删除了注入脚本源文件,经 tarball 探测确认)+ 三层守卫(build 后解析检查/打包输入清单/依赖锁定测试)
4 设置门控字段未类型化 ✅ 已修复 GeneralSettings.browser.agentToolsEnabled 全链类型化(shared 类型 → zod schema → sidecar 读写保留),并修复 whitelist-rebuilder 会静默清空门控的问题
5 bundled-plugins 打包 ✅ 确认已端到端接好 extraResources 复制 + getBundledPluginsDirPath 双环境解析原就正确;补内容锁定测试
6 端到端未验证 ✅ 已编写并真机跑通 test:browser-rewrite-e2e:Electron fixture 驱动全链(开 tab→attach→导航→快照→截图→挂起→唤醒→恢复→录像→recovery store),真机两遍 24 断言全过(~1.9s);LUME_E2E_SELF_CHECK=1 为无头门

同时完成的全面对齐批次(差距审计驱动)

  • 🔴 BrowserSidePane 挂载进右面板(此前全仓零挂载点,用户不可达)
  • 标签栏完整功能:dnd-kit 拖拽重排 / 溢出渐隐 / 最近关闭重开栈(8)/ 总览弹层 / 操作横幅 / resize 警告 / 三类错误卡(证书/加载/崩溃)
  • 缩放补偿(layoutScale/transformScale 分解,截图暂存时清除保 1:1)
  • 工作区分桶持久化(Map≤50 LRU + browserUrls 映射 + suspended 壳保活)
  • fill 命令三层打通 + IAB 描述符收敛 shared 单源 + reportResidency title=null 透传 + 后退/前进按钮态

最终验证

desktop typecheck 0 错 + 370 pass;web typecheck 0 错 + browser/right-panel 146 pass;sidecar 仅剩预存在 obsidian;shared 28 pass;E2E 真机两遍全过。

- 视口信息条补数值宽×高输入(ZCode $Tt 语义:Enter/blur 提交、
  Escape 回退、越界 destructive+tooltip、sr-only alert)
- tab favicon 槽在 agent 操作期显示呼吸指示点(带 prefers-reduced-motion
  守卫),空闲恢复 favicon
- 顶层 waitFor 命令路由实现(复用定位会话 waitForState,纯复用零新机器,
  5 项测试);全量协议↔执行器一致性扫尾:49 成员=23 执行器+24 管理器
  +capabilities 有意不路由(经描述符通道)

Tested: web typecheck 0错+browser 26 pass;desktop typecheck 0错
+browser 112 pass
R1 Git 面板:
- git-panel-service.ts: spawn git CLI(porcelain v2 + numstat 解析)
- GitPanel.tsx: 作用域选择/文件列表/展开 diff/刷新/空态
- IPC 白名单接线(lume:browser-git-status/diff)

R2 多 tab 壳层:
- right-panel-workspace-store.ts: Map≤50 LRU 按工作区分桶,
  browserUrls 恢复映射, suspended 壳保活, 关闭后邻居回落
- SidePane 面板挂载到工作区 tab 模型, 标题/favicon/url 同步

类型修复:
- RightPanelTabBar.tsx FUNCTION_META 括号索引 JSX 语法修复
- RightPanelWorkspace.tsx onCloseFile tabId 显式类型标注

Tested: desktop 399 pass/0 fail; web browser 26 pass/0 fail;
sidecar browser 12 pass; shared 28 pass; 全仓 typecheck 0 错误
验证 TS 代码片段 HTML 输出含 span/line 包装/color token,
未知语言回退 text,bash 别名映射,热缓存同步路径。
electron-security.test.mjs 的 filter 条件被此前的脚本编辑损坏
(字符串拼接错误),修正并正确排除 TERMINAL_IPC_CHANNELS。
同时同步 electron-security.ts 的通道排除。

Tested: electron-security 52 pass/0 fail;desktop 全量 405 pass/0 fail
recording/orchestrator.ts 的 recordBrowserVideo/executeRecordingActions
逻辑已内联于 guest-manager.ts 的 runRecording 方法(guest-manager 内联版
经 E2E 真机验证),orchestrator 模块无生产消费方,属冗余副本。

保留 recording/recorder.ts(createElectronBrowserWebmRecorder,
被 assemble.ts 正常引用)。

Tested: typecheck 0 错误;browser 134 pass/0 fail
ZCode 通过 host 进程 fileWatcherService 监听工作区目录,60s 防抖后
刷新 Git 面板。Lume MVP 用定时轮询等效实现(无需文件 watch 基础
设施),间隔与 ZCode 防抖值一致。workspacePath 存在时启动,
切换工作区或卸载时清理。
对齐 ZCode 终端体验的核心转义子集:
- SGR 全属性:前景/背景(基础+亮色+256色+真彩)、加粗/暗淡/斜体/
  下划线/删除线/反色
- CR 行覆写(与真实终端一致,后段覆盖前段同等宽度)
- OSC 剥离(BEL/ST 终止)
- 渲染端 AnsiStyle→React 内联样式(支持 256 色/真彩 CSS 值)
- 测试全量重写(14 项,含 256 色/真彩/CR 覆写/背景色等边界)

Constraint: pipe 模式固有限制——无 CUP/滚动区/交替屏幕(需 node-pty)
Tested: web typecheck 0错;terminal-ansi 14/14;desktop 405 pass
- git-watcher.ts: fs.watch 递归监听工作区+.git, 60s 防抖后发
  lume:browser-git-dirty 通知 renderer 刷新(替代 60s 轮询)
- git-panel-service.ts: 新增 branchComparison(与 tracking branch 比较)
- GitPanel.tsx: source 下拉加 branch scope; 订阅 git-dirty 事件自动刷新
- renderer-ipc-contract: 加 lume:browser-git-dirty 事件白名单
- electron-security: TERMINAL_IPC_CHANNELS 加入 sidecar 派生排除

Tested: desktop 408 pass/0 fail; web 165 pass/0 fail; typecheck 全绿
bun spawnSync 冷启动/AV 扫描使真实 git 端到端用例停顿 4-8s,
超出 bun 默认 5s 单测超时;fs watch 事件送达随机器负载浮动。

- git-panel-service e2e 用例放宽至 30s 超时
- git-watcher 事件断言改为轮询等待 + 静置期复核计数
- bun-test.d.ts 补 test() 第三参 timeout 类型

Tested: bun test git-watcher git-panel-service 34/34 通过
…host 终端)

替换 MVP 管道 spawn,结构对齐 ZCode createTerminalService(R11-host-pty):
lazy import("node-pty") + 会话表 + win32 ConPTY 双段降级
(useConptyDll:true → conpty.dll 失败特征正则 → false) + onExit 自动清理
+ 终端环境(TERM/COLORTERM/locale 回补/CI=dumb 摘除) + cwd 存在性兜底链。

协议新增 terminal:exit 通知(lume:terminal-exit 事件),renderer 会话标记
exited 并提供重新打开;resize 直传 pty.resize,TUI 程序尺寸即时生效。

打包:node-pty 系 mxc-sdk 传递依赖,bundle 脚本既有 RUNTIME_EXTERNAL
清单已将其复制到 resources/sidecar/node_modules(零新增依赖);
已验证 bundle 产物处真实 ConPTY spawn 回环。

Constraint: sidecar 经 utilityProcess(Electron ABI)运行,node-pty 走 NAPI 预编译
Rejected: 保留管道 fallback | node-pty 打包已闭环,双路径徒增维护面
Rejected: create 返回字体/主题字段 | renderer 非 xterm,无消费方(留 xterm 升级路径)
Tested: sidecar 20 用例(ConPTY 降级/环境/flush/exit 语义) + desktop 中继 7 用例
Tested: 真实 ConPTY spawn 冒烟(bundle 产物处 echo 回环)
Not-tested: darwin spawn-helper chmod(有意不做,依赖预编译产物执行位)
替换自定义 ANSI <pre> 渲染 + 单行输入,对齐 ZCode 终端 tab 的 xterm.js 形状:
 - 键级输入(terminal.onData,含控制字符,交互式程序可用)
 - FitAddon 尺寸协商(ResizeObserver → fit → onResize → pty.resize)
 - 键盘级 ANSI/全屏 TUI 由 xterm 原生处理(前置 node-pty 已就位)
 - 主题以面板 CSS 变量取色适配应用主题(偏差:不接 settingService 字体 profile)
 - 退出事件在终端内写暗色提示;重挂载回放模块级缓冲(跨 tab 保活语义不变)

删除被替代的自定义 ANSI 解析链(terminal-ansi/-types/test,唯一消费方
即旧 TerminalPanel);数据泵为 xterm 实例增加直写通道。

Constraint: 模块级会话仓持久面=缓冲,xterm 实例随挂载建毁(与 ZCode stash 等效)
Tested: web typecheck 0 错;终端相关测试全绿;组件套件失败集合与 main 基线一致
对照 P1-shell-architecture 逐条行为规范校准两处偏差:

1. 最近关闭环排除 agent 持有的 browser-use tab(ZCode Xde 规范
   "browser-use 主进程持有不入环"):任务级视图不应以用户身份重开。
2. 激活挂起 tab 时自动发 browserViewEnsureResident(ZCode
   handleActivateSidePaneTab 语义:先复活再激活,失败仅 warn);
   原先需手动点「恢复页面」,唤醒逻辑抽为 ensureResidentFor 共用。

Tested: web typecheck 0 错;browser 组件测试 26/26
补齐 P2-git-codeviewer §1.3 的两块交互规格:

1. 查找(KEt):大小写/空白归一化子串匹配已加载 diff 文本;有查询词时
   自动批量预加载全部 diff;Enter/Shift+Enter/上下按钮在命中文件间循环,
   展开并 scrollToIndex(center);计数 i/n 为命中文件粒度(虚拟列表以
   文件行为滚动单位);Escape 关闭。
2. 行级菜单( reveal/copy 同型):在文件管理器中显示(桌面 revealPathInSystem)/
   复制绝对路径/复制相对路径(writeClipboardText)。

偏差记录:last-turn 来源(ZCode 该构建恒空占位)不移植;「在文件树中显示」
移交待文件树 reveal 总线桥接(thread 作用域)。

Tested: web typecheck 0 错;right-panel/browser 组件测试 165/165
… 挂载/揭示)

P4 §1d:ZCode 的 ready 事件订阅在常驻 shell 控制器(Qde),面板关闭时
agent 建 tab 仍按 scope 决定「展开并激活/后台挂载」;Lume 原先订阅在
面板内部,面板未挂载时 ready 丢失,agent tab 永不可见。

修复:useOpenBrowserUrlReveal 增补 ready 兜底订阅——
 - 后台挂载:tab 壳(字段与面板 createTab 默认同形)记入模块级工作区仓,
   面板挂载时 applyWorkspaceSnapshot 对账装表;
 - shouldReveal:workspaceKey === 当前 threadId(RightPanelWorkspace 推导
   同式)时点亮右面板「浏览器」tab(展开并激活)。

Tested: web typecheck 0 错;失败集合与 main 基线一致(均为既有无关套件)
Q11 §3.1:ZCode 任务切换时 git 面板来源选择按 workspaceKey 键控迁移
(离开写回/进入恢复);Lume 原先是组件内 state,切走再回来重置为 unstaged。

修复:模块级 Map<workspacePath, source>(ZCode Dd 持久层同为 renderer
内存,reload 即失)+ 双向 effect(进入恢复初始化 / 变更写回);
branch 失效回落 unstaged 的既有校验覆盖恢复路径。

Tested: web typecheck 0 错;right-panel 139/139
补齐 P2 §1.3 行级菜单的最后一块:RightPanelWorkspace 构造 project FileRef
(scopeId=workspaceSlug)经 createFileTreeRevealRequest + reveal-directory
atom 动作移交文件树(展开祖先目录+选中+滚动定位);无 slug 时宿主返回
false,GitPanel 隐藏该项。

Tested: web typecheck 0 错;right-panel 139/139
ZCode 侧栏终端是唯一可多开的 tab 类型:每次开启追加新 tab,标题按
cwd.basename 查重加序号。Lume 原先每工作区仅一个固定终端面。

 - 模型:RightPanelTab 增可选 title(消毒保留/截断 40 字);终端实例
   tab id 唯一(type 仍 terminal),openRightPanelTerminalInstance
   非单例追加 + nextTerminalInstanceTitle 查重(「repo」「repo 2」…);
   关闭/入环/重排复用既有 reducer。
 - 接线:tab 条「+」菜单常驻「新终端」项(固定功能已开也能再加开);
   实例渲染以 tab id 为会话隔离键(缺省固定 tab 沿用 workspacePath
   单例键,既有会话无损)。

Constraint: 固定功能单例不变式不破坏,多开仅终端一种类型(ZCode 同)
Tested: right-panel-state 模型用例 17/17(含实例/查重/消毒/关闭);web typecheck 0 错
Q2 §1 全规格落地:
 - store:按 workspaceKey 分桶,board={id:'whiteboard:'+uuid,1280×800,
   strokes/undoneStrokes};动作 create/rename/addStroke/undo/redo/clear
   全量;默认名「白板 N」查重;纯内存无持久化(ZCode 同)。
 - canvas:pointer 涂鸦(DPR 缩放/setPointerCapture/draft 预渲/松手提交);
   橡皮=画白线(ZCode bn 同款);笔宽 2..18,橡皮 max(14,width*3);6 色盘。
 - 工具条:行内改名/pen/eraser/色盘/宽度/撤销重做清空/导出 PNG。
   偏差:ZCode「加入聊天」走 composer 附件通道,Lume 先落地为 PNG 下载。
 - tab 接线:实例 tab 模型泛化(terminal/whiteboard 共用 open-instance-tab
   + set-tab-title);「+」菜单白板项;改名同步 tab 标题;boardId 即 tabId
   (ZCode 同形)。

Constraint: 无删除 board 动作(ZCode On 亦无);关 tab 不删 board
Tested: whiteboard store 5 用例 + right-panel-state 17/17;web typecheck 0 错
vault-facade.test.ts 在 expect(result.ok) 后直接访问联合分支字段
relativePath,TS 无法收窄 → sidecar tsc 构建整体失败。补显式守卫。
该回归源自 main 的 cff63a3,阻塞本分支打包验证顺手修复。

Tested: sidecar typecheck 0 错;vault-facade 13/13
整合本次逆向+对齐全过程的单源文档:进程架构、10 项设计决策(附证据)、
9 条实现流程、ZCode 混淆产物代码位置总表(渲染器/main/host + 提取源映射
+ 通道面)、Lume 对齐实现文件映射、三层验证体系、有意偏差清单。
179 个 ZCode 命名函数机械化比对筛出 20 候选,落地两个实质缺口:

1. 网络策略(V6/qSe/JSe/VSe/XSe/YSe/QSe/G6):default-session 与
   embedded-browser 分区并发应用 setProxy(fixed_servers/兜底
   direct|system)+ closeAllConnections + setCertificateVerifyProc
   (不安全证书放行 / 自定义 CA 文件指纹匹配,链深 16,OK/不匹配回
   默认 -3)。配置来源偏差:ZCode 读桌面设置面;Lume 环境变量驱动
   (HTTPS_PROXY|HTTP_PROXY/NO_PROXY/LUME_BROWSER_CA_CERT_PATH/
   LUME_BROWSER_ALLOW_INSECURE_CERTIFICATES)。
2. webview 文本右键菜单(Pve):选中态 → 系统复制项(canCopy 门控),
   attach 幂等安装,弹层经 deps 注入(electron 免依赖)。

Constraint: 其余 18 候选为注册入口组织形态/打包辅助/本地预览协议,与 Lume 既有装配等价
Tested: desktop typecheck 0 错;browser 套件 158/0(含新 12 用例:代理构造/proc 三分支/链匹配环守卫/双 session 应用/env 映射/菜单模板)
@CavinHuang

Copy link
Copy Markdown
Owner Author

ZCode 浏览器/侧栏对齐状态总览(对齐工作收官)

本 PR 完成对 ZCode Desktop v3.10.1 内嵌浏览器及其侧栏面板的完整对齐。当前覆盖与验证状态:

对齐范围(38+ 提交)

内容
浏览器核心 webview 池 + BrowserGuestManager(唯一状态权威)+ 驻留状态机(LRU 32/窗)+ CDP 1.3 自动化
执行引擎 顶层 24 命令(ZCode 22 的超集)+ playwright 引擎 locator 16 操作 + domSnapshot iframe 合并 + elementScreenshot
截图 三阶管线(视口钩子 → CDP clip/fullPage/captureBeyondViewport → CSS 像素校正重拍)
录制 setDisplayMediaRequestHandler + 隐藏窗 canvas + MediaRecorder VP8 + 90s 上限 + 动作 DSL
输入/对话框 CUA 坐标/键盘原语 + JS 对话框 sendSync 桥 + automation 自动应答
终端 node-pty 真实 PTY(win32 ConPTY 双段降级)+ xterm.js + 多实例 tab(Zde 查重命名)
Git 面板 unstaged/staged/branch 三来源 + fs.watch 实时刷新 + branch 对比 + 差异查找(KEt)+ 全量行级菜单 + 来源跨挂载迁移
白板 Q2 全规格纯内存画布(store/pointer 绘制/6 色盘/undo-redo-clear/导出 PNG)
交互面 导航桥 + agent 建 tab 挂载/揭示模型(lde)+ 5s 操作横幅 + visibility 收起 + resize 弱提示
网络策略 双 session(default + persist 分区)setProxy + closeAllConnections + setCertificateVerifyProc(自定义 CA 指纹/不安全证书;env 驱动)
外壳 tab 单例/实例模型(RightPanelTab.title)+ 最近关闭环(agent tab 不入环)+ 激活自动 ensureResident + 工作区 LRU 50 内存桶

验证体系(三层全绿)

  1. 静态:功能覆盖 36/36;渲染 UI 11/11;交互面 P4 逐条;外壳迁移 Q11 §3.1;函数名机械化审计——ZCode 提取源 179 个命名函数全部在 Lume 落位或判定组织形态等价
  2. 运行时test:browser-rewrite-e2e 真实 Electron 全链路 24 断言(建 tab→attach→导航→快照→截图→挂起→唤醒→录制→恢复)
  3. 实包:完整构建链(tsc/vite/cargo/bundle)+ electron-builder 实包;asar 内 playwright-core injectedScriptSource.js 与 dev 字节一致(sha256);extraResources 内 node-pty 真实加载

已判定不再跟进项(附理由)

  • last-turn git 来源:ZCode 该构建恒为空占位
  • 浏览器 devtools/下载 UI/权限 UI:ZCode 同样没有
  • developer-tools 面板:ZCode 的该面板是 token/网络调试工具,非浏览器 devtools
  • subagent 面板 / model-trajectory / repowiki / plan-detail:需 Lume 不存在的后端子系统(子代理编排/LLM 调用轨迹/wiki 生成管线),属新功能开发
  • 文档:docs/analysis/zcode-browser-implementation-complete.md 为单源实现图谱

测试

desktop 409+12/0、web 浏览器+右面板+白板+终端全绿、shared 290/0、sidecar 改动面 54/0;三端 typecheck 0 错。

main 新增 lume:browser-view-clear-data:invoke(mode 'cache'|'all'):
 - cache:clearCache + clearStorageData({storages:[shadercache,
   serviceworkers,cachestorage]})(ZCode Vpe);
 - all:clearCache + clearStorageData()(含登录态);
 - 非法 mode 拒收 invalid_clear_mode;失败归一 embedded_browser_clear_failed。
SidePane「+」菜单新增清除缓存/清除全部数据(confirm 门控)。
偏差:ZCode F1 Chrome 数据导入依赖提权解密 helper/App-Bound/钥匙串
基础设施,不移植(已记入实现图谱偏差清单)。

Tested: desktop typecheck 0 错;browser 套件 162/0(含数据清除 4 用例);web typecheck 0 错 + 167/0
工具栏新增选取开关(MTt 同款代数守卫 + isPicking 态):注入页面脚本
(window.__zcodeWebElementPicker)hover 高亮遮罩 + 点击捕获,Esc 取消;
选取结果 {selector,outerHTML(截断),text,rect} 格式化写剪贴板。
偏差:ZCode 选取结果进 selection-side-chat 元素上下文,Lume 暂以
剪贴板承载(composer 无该通道)。

Tested: web typecheck 0 错;browser 组件套件全绿(含选取器 3 用例)
184 个 ZCode i18n 键(terminal.contextMenu.copy/paste 等)全数甄别:
落地终端右键菜单(选中复制→剪贴板/粘贴→xterm.paste→PTY),
白板画布补 whiteboard.canvasLabel 同款 aria-label。
actionMenu/branchSwitcher 系键属主窗口 worktree 面(需 commit/push
后端与 AI 提交信息生成),维持原判定。

Tested: web typecheck 0 错;browser/terminal/whiteboard 套件全绿
复用既有附件暂存链(createPendingAttachmentsFromFiles → stageAttachmentFile
分块上传):白板工具条新增「加入聊天」,导出 PNG 构造 File 后派发
lume:add-whiteboard-to-chat CustomEvent;AgentView 监听并暂存进待发附件
(toast 确认)。聊天未挂载时回退为 PNG 文件下载。

Tested: web typecheck 0 错;whiteboard/browser/terminal 套件全绿
…branches(对齐 Q5 §2)

git-panel-service 从 4 方法扩展 ZCode gitService 18 方法面的写路径
(Q5 CLI 规格逐条):
 - stagePaths:git add -- <paths>
 - unstagePaths:git restore --staged -- <paths>
 - discardPaths:git restore --worktree -- <paths>(仅已跟踪)
 - commit:commit -m <msg> [-- <paths>] + rev-parse HEAD → 哈希
 - push:普通 push / --set-upstream(解析 branch.<b>.remote 兜底 origin),
   10 分钟/8MB 上限(ZCode 同)
 - getLocalBranches:for-each-ref 四字段,提交时间降序
纯服务层能力(UI 面后续接线);reject 契约含空信息/空路径。
偏差:generateCommitMessage 需模型运行时;switch 系需预检对话框,后续跟进。

Tested: 真实 git e2e 31/31(stage→unstage→commit→branches→push 全链
+ discard 还原/契约拒绝);desktop typecheck 0 错
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.

2 participants