From d1c200a24384abc615dfac92cecb3f8cdede3b6b Mon Sep 17 00:00:00 2001 From: iven Date: Sat, 21 Mar 2026 16:23:57 +0800 Subject: [PATCH] docs(spec): revise architecture optimization spec based on review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes from code review: - Fix terminology: VZustand → Valtio - Add terminology table for clarity - Add existing code analysis section - Add migration mapping table - Remove incorrect Web Worker claims - Update Hands section to focus on XState - Update Intelligence cache to acknowledge existing impl - Add detailed task breakdown with estimates - Add performance measurement methods - Update dependency versions to specific versions - Add 2-week buffer to timeline Co-Authored-By: Claude Opus 4.6 --- ...-03-21-architecture-optimization-design.md | 408 +++++++++--------- 1 file changed, 203 insertions(+), 205 deletions(-) diff --git a/docs/superpowers/specs/2026-03-21-architecture-optimization-design.md b/docs/superpowers/specs/2026-03-21-architecture-optimization-design.md index d528b13..120f5bb 100644 --- a/docs/superpowers/specs/2026-03-21-architecture-optimization-design.md +++ b/docs/superpowers/specs/2026-03-21-architecture-optimization-design.md @@ -1,6 +1,6 @@ # ZCLAW 架构优化设计规格 -> **版本**: 1.0 +> **版本**: 1.1 > **日期**: 2026-03-21 > **状态**: 待审核 > **作者**: Claude + 用户协作 @@ -13,22 +13,30 @@ ZCLAW 是面向中文用户的 AI Agent 桌面客户端,经过分析发现以下需要改进的领域: -- **安全风险**: 浏览器 eval() XSS 风险、localStorage 凭据回退 +- **安全风险**: localStorage 凭据回退明文存储、WebSocket 非 localhost 允许 ws:// - **性能瓶颈**: 流式更新时重建整个消息数组、无界消息数组 - **架构问题**: 50+ lib 模块缺乏统一抽象、测试覆盖不足 ### 1.2 目标 -- 在 14 周内完成全面架构优化 +- 在 14 周内完成全面架构优化 (含 2 周缓冲) - 采用激进架构优先策略 - 重点优化四个核心系统:对话、Hands、Intelligence、技能 -### 1.3 关键决策 +### 1.3 术语表 + +| 术语 | 含义 | 备注 | +|------|------|------| +| **Valtio** | Proxy-based 状态管理库 | pmnd.rs/valtio,将替代 Zustand | +| Zustand | 当前使用的状态管理库 | 将被 Valtio 替代 | +| XState | 状态机库 | 用于 Hands 状态管理 | + +### 1.4 关键决策 | 决策点 | 选择 | 理由 | |--------|------|------| -| 状态管理 | VZustand | Proxy 细粒度响应,性能更好 | -| 安全策略 | Web Worker 隔离 | 最安全的执行隔离方案 | +| 状态管理 | **Valtio** (替代 Zustand) | Proxy 细粒度响应,性能更好 | +| 安全策略 | 凭据加密存储 + WSS 强制 | 解决实际存在的安全风险 | | 整体方案 | A+C 混合 | 渐进式 + 领域驱动结合 | --- @@ -51,7 +59,7 @@ ZCLAW 是面向中文用户的 AI Agent 桌面客户端,经过分析发现以 ┌─────────────────────────────────────────────────────────────────┐ │ State Layer │ │ ┌──────────────────────────────────────────────────────────┐ │ -│ │ VZustand Stores (基于 Proxy) │ │ +│ │ Valtio Stores (基于 Proxy) │ │ │ │ - 细粒度响应 │ │ │ │ - 领域划分: Chat, Hands, Intelligence, Skills │ │ │ └──────────────────────────────────────────────────────────┘ │ @@ -84,7 +92,7 @@ ZCLAW 是面向中文用户的 AI Agent 桌面客户端,经过分析发现以 desktop/src/ ├── domains/ # 领域模块 (新建) │ ├── chat/ # 对话系统 -│ │ ├── store.ts # VZustand store +│ │ ├── store.ts # Valtio store │ │ ├── types.ts # 类型定义 │ │ ├── api.ts # API 调用 │ │ └── hooks.ts # React hooks @@ -111,15 +119,40 @@ desktop/src/ └── components/ # UI 组件 (保持) ``` +### 2.3 现有代码分析 + +| 模块 | 现有文件 | 行数 | 迁移策略 | +|------|----------|------|----------| +| Chat | `store/chatStore.ts` | 689 | 迁移到 Valtio,保留 API | +| Hands | `store/handStore.ts` | 535 | 迁移到 XState 状态机 | +| Intelligence | `lib/intelligence-client.ts` | 956 | 保留,增强缓存层 | +| Browser | `lib/browser-client.ts` | 461 | 保留,无需 Worker | +| Secure Storage | `lib/secure-storage.ts` | 350 | 增强,添加加密回退 | +| Gateway | `lib/gateway-client.ts` | 1100 | 保留,强制 WSS | + +### 2.4 迁移映射表 + +| 现有文件 | 新位置 | 操作 | +|----------|--------|------| +| `store/chatStore.ts` | `domains/chat/store.ts` | 迁移 + 重写 | +| `store/handStore.ts` | `domains/hands/store.ts` | 迁移 + 状态机 | +| `store/skillStore.ts` | `domains/skills/store.ts` | 迁移 | +| `lib/intelligence-client.ts` | `domains/intelligence/client.ts` | 迁移 + 增强 | +| `lib/error-handling.ts` | `shared/error-handling.ts` | 迁移 | +| `lib/secure-storage.ts` | `shared/secure-storage.ts` | 增强 | +| `store/agentStore.ts` | 保留 | 不变 | +| `store/connectionStore.ts` | 保留 | 不变 | +| `lib/gateway-client.ts` | 保留 | 不变 | + --- ## 3. 核心组件设计 -### 3.1 VZustand 状态管理 +### 3.1 Valtio 状态管理 (替代 Zustand) **问题**: 当前 Zustand 每次更新都会触发整个订阅组件重渲染 -**解决方案**: 使用 Proxy 实现细粒度响应 +**解决方案**: 使用 Valtio 的 Proxy 实现细粒度响应 ```typescript // domains/chat/store.ts @@ -174,106 +207,21 @@ function StreamingIndicator() { } ``` +**迁移策略**: +- 现有 `chatStore.ts` (689 行) 将被重写 +- 保持相同的 API 接口,确保组件无需修改 +- 逐步迁移,先迁移 Chat,再迁移其他 Store + **预期收益**: - 流式更新时性能提升 70% - 代码更简洁 (直接 mutate) - 选择性渲染减少不必要的重渲染 -### 3.2 Web Worker 隔离执行 +### 3.2 Hands 状态机 (XState) -**问题**: browser.eval() 在主线程执行用户输入,存在 XSS 风险 +**问题**: 当前 `handStore.ts` (535 行) 状态转换逻辑分散,难以追踪 -**解决方案**: Web Worker 完全隔离 - -```typescript -// workers/pool.ts -export class BrowserWorkerPool { - private workers: Worker[] = []; - private maxWorkers = 4; - - async execute(script: string, args: unknown[]): Promise { - const worker = this.getAvailableWorker(); - - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - worker.terminate(); - this.recycleWorker(worker); - reject(new Error('Execution timeout (30s)')); - }, 30000); - - worker.onmessage = (e) => { - clearTimeout(timeout); - if (e.data.error) { - reject(new ExecutionError(e.data.error)); - } else { - resolve(e.data.result); - } - }; - - worker.onerror = (e) => { - clearTimeout(timeout); - reject(new ExecutionError(e.message)); - }; - - worker.postMessage({ type: 'eval', script, args }); - }); - } - - private getAvailableWorker(): Worker { - // 复用或创建新 Worker - if (this.workers.length < this.maxWorkers) { - const worker = new Worker(new URL('./browser-worker.ts', import.meta.url)); - this.workers.push(worker); - return worker; - } - // 等待可用 Worker... - } -} - -// workers/browser-worker.ts -const ALLOWED_SCRIPTS = new Set([ - 'navigate', 'click', 'type', 'screenshot', 'extract', - 'scroll', 'wait', 'select', 'hover' -]); - -self.onmessage = async (e) => { - const { type, script, args } = e.data; - - if (type === 'eval') { - try { - if (!ALLOWED_SCRIPTS.has(script)) { - throw new Error(`Script not allowed: ${script}`); - } - - // 在受限环境中执行 - const result = await executeInSandbox(script, args); - self.postMessage({ result }); - } catch (error) { - self.postMessage({ error: error.message }); - } - } -}; - -async function executeInSandbox(script: string, args: unknown[]): Promise { - // 无 DOM 访问 - // 无 localStorage 访问 - // 只有受限的 API - // ... -} -``` - -**安全保证**: -- Worker 无法访问 DOM -- Worker 无法访问 localStorage/cookie -- 脚本白名单限制 -- 执行超时保护 -- 错误隔离 - -### 3.3 Hands 状态机 - -**问题**: 当前 HandStore 状态转换不清晰,难以追踪 - -**解决方案**: 使用 XState 实现状态机 +**解决方案**: 使用 XState 实现状态机,与现有 Store 集成 ```typescript // domains/hands/machine.ts @@ -284,9 +232,7 @@ export const handMachine = createMachine({ initial: 'idle', states: { idle: { - on: { - TRIGGER: 'validating', - }, + on: { TRIGGER: 'validating' }, }, validating: { on: { @@ -314,31 +260,44 @@ export const handMachine = createMachine({ TIMEOUT: 'error', }, }, - completed: { - on: { - RESET: 'idle', - }, - }, - error: { - on: { - RETRY: 'validating', - RESET: 'idle', - }, - }, - cancelled: { - on: { - RESET: 'idle', - }, - }, + completed: { on: { RESET: 'idle' } }, + error: { on: { RETRY: 'validating', RESET: 'idle' } }, + cancelled: { on: { RESET: 'idle' } }, }, }); + +// domains/hands/store.ts - 与现有 API 集成 +import { createActorContext } from '@xstate/react'; + +export const HandContext = createActorContext(handMachine); + +// 使用方式 +function HandPanel() { + const state = HandContext.useSelector(s => s); + const send = HandContext.useActorRef(); + + return ( +
+ {state.matches('pending_approval') && } + {state.matches('executing') && } +
+ ); +} ``` -### 3.4 Intelligence 缓存策略 +**迁移策略**: +- 现有 `handStore.ts` 保持向后兼容 +- 新建 `domains/hands/` 模块 +- 逐步将状态逻辑迁移到 XState +- 保持现有组件 API 不变 -**问题**: 每次请求都需要调用 Rust 后端 +### 3.3 Intelligence 缓存增强 -**解决方案**: LRU 缓存 + TTL +**问题**: 现有 `intelligence-client.ts` (956 行) 已有 localStorage 回退缓存,但缺少 LRU 淘汰和 TTL + +**解决方案**: 增强现有缓存层,添加 LRU + TTL + +> **注意**: 这是增强现有实现,而非替换 ```typescript // domains/intelligence/cache.ts @@ -391,7 +350,11 @@ export class IntelligenceCache { ## 4. 安全设计 -### 4.1 凭据存储加密 +### 4.1 凭据存储加密增强 + +> **现有实现**: `lib/secure-storage.ts` (350 行) 已使用 OS keyring + localStorage fallback +> **问题**: localStorage fallback 存储明文密钥,存在安全风险 +> **策略**: 增强现有实现,为 localStorage fallback 添加加密 ```typescript // shared/secure-storage.ts @@ -524,32 +487,28 @@ describe('ChatState', () => { ### 5.2 集成测试 ```typescript -// domains/hands/executor.test.ts -describe('HandExecutor', () => { - let pool: BrowserWorkerPool; - - beforeEach(() => { - pool = new BrowserWorkerPool(); +// domains/hands/machine.test.ts +describe('HandMachine', () => { + it('should transition from idle to validating on TRIGGER', () => { + const service = interpret(handMachine).start(); + service.send('TRIGGER'); + expect(service.state.value).toBe('validating'); }); - afterEach(async () => { - await pool.terminateAll(); + it('should reach pending_approval for hands requiring approval', () => { + const service = interpret(handMachine).start(); + service.send('TRIGGER'); + service.send('VALID'); + service.send('NEEDS_APPROVAL'); + expect(service.state.value).toBe('pending_approval'); }); - it('should execute allowed script', async () => { - const result = await pool.execute('navigate', ['https://example.com']); - expect(result).toBeDefined(); + it('should cancel on REJECT', () => { + const service = interpret(handMachine).start(); + // ... navigate to pending_approval + service.send('REJECT'); + expect(service.state.value).toBe('cancelled'); }); - - it('should reject disallowed script', async () => { - await expect(pool.execute('eval', ['alert(1)'])) - .rejects.toThrow('Script not allowed'); - }); - - it('should timeout long execution', async () => { - await expect(pool.execute('infiniteLoop', [])) - .rejects.toThrow('Execution timeout'); - }, 35000); }); ``` @@ -564,65 +523,101 @@ describe('HandExecutor', () => { ## 6. 实施计划 +> **总周期**: 14 周 (含 2 周缓冲) + ### 6.1 Phase 1: 安全 + 测试 (2周) **目标**: 建立安全基础和测试框架 -**任务**: -- [ ] 实现 Web Worker 隔离执行引擎 -- [ ] 实现凭据加密存储 -- [ ] 强制 WSS 连接 -- [ ] 建立测试框架 (Vitest + Playwright) -- [ ] 设置覆盖率门禁 (60%) -- [ ] 添加 chatStore 基础测试 +#### TASK-001: 凭据加密存储增强 +- **输入条件**: 现有 `secure-storage.ts` 可用 +- **输出产物**: 增强的 `shared/secure-storage.ts` +- **验收标准**: + - localStorage fallback 使用 AES-GCM 加密 + - 所有单元测试通过 + - 无回归问题 +- **预估工时**: 3 人日 +- **依赖**: 无 -**交付物**: -- `workers/browser-worker.ts` -- `workers/pool.ts` -- `shared/secure-storage.ts` -- 测试配置文件 +#### TASK-002: 强制 WSS 连接 +- **输入条件**: 现有 `gateway-client.ts` 可用 +- **输出产物**: 更新的 `gateway-client.ts` +- **验收标准**: + - 非 localhost 连接必须使用 wss:// + - 测试覆盖边界情况 +- **预估工时**: 1 人日 +- **依赖**: 无 + +#### TASK-003: 测试框架建立 +- **输入条件**: 无 +- **输出产物**: Vitest + Playwright 配置 +- **验收标准**: + - 覆盖率门禁 60% 生效 + - CI 集成完成 +- **预估工时**: 2 人日 +- **依赖**: 无 + +#### TASK-004: chatStore 基础测试 +- **输入条件**: 测试框架就绪 +- **输出产物**: `store/chatStore.test.ts` +- **验收标准**: + - 核心功能测试覆盖 + - 覆盖率 > 80% +- **预估工时**: 2 人日 +- **依赖**: TASK-003 ### 6.2 Phase 2: 领域重组 (4周) -**目标**: 按领域重组代码,迁移到 VZustand +**目标**: 按领域重组代码,迁移到 Valtio -**任务**: -- [ ] 创建 domains/ 目录结构 -- [ ] 迁移 Chat Store 到 VZustand -- [ ] 迁移 Hands Store + 状态机 -- [ ] 迁移 Intelligence Client -- [ ] 迁移 Skills Store -- [ ] 提取共享模块 -- [ ] 更新导入路径 +#### TASK-101: 创建 domains/ 目录结构 +- **预估工时**: 1 人日 -**交付物**: -- `domains/chat/` -- `domains/hands/` -- `domains/intelligence/` -- `domains/skills/` -- `shared/` +#### TASK-102: 迁移 Chat Store 到 Valtio +- **输入条件**: 现有 `chatStore.ts` (689 行) +- **输出产物**: `domains/chat/store.ts` +- **验收标准**: + - API 向后兼容 + - 性能提升 > 50% + - 测试覆盖率 > 90% +- **预估工时**: 5 人日 + +#### TASK-103: 迁移 Hands Store + XState +- **输入条件**: 现有 `handStore.ts` (535 行) +- **输出产物**: `domains/hands/store.ts` + `machine.ts` +- **验收标准**: + - 状态机完整实现 + - 审批流程正常 + - 测试覆盖率 > 85% +- **预估工时**: 5 人日 + +#### TASK-104: 增强 Intelligence 缓存 +- **输入条件**: 现有 `intelligence-client.ts` (956 行) +- **输出产物**: 增强的缓存层 +- **预估工时**: 3 人日 + +#### TASK-105: 提取共享模块 +- **预估工时**: 2 人日 ### 6.3 Phase 3: 核心优化 (6周,并行) -**Track A: Chat 优化** -- [ ] VZustand 性能优化 -- [ ] 虚拟滚动实现 -- [ ] 消息分页 +**Track A: Chat 优化 (2人)** +- [ ] Valtio 性能优化 +- [ ] 虚拟滚动实现 (react-window) +- [ ] 消息分页 + 惰性加载 - [ ] 流式响应 AsyncGenerator -**Track B: Hands 优化** +**Track B: Hands 优化 (1人)** - [ ] XState 状态机完善 - [ ] 审批流程配置化 -- [ ] Worker 隔离集成 - [ ] 错误恢复机制 -**Track C: Intelligence 优化** +**Track C: Intelligence 优化 (1人)** - [ ] Rust 后端功能完善 -- [ ] LRU 缓存实现 -- [ ] 向量检索准备 +- [ ] LRU 缓存集成 - [ ] 性能调优 -### 6.4 Phase 4: 集成 + 清理 (2周) +### 6.4 Phase 4: 集成 + 清理 (2周 = 1周实施 + 1周缓冲) **目标**: 完成集成,清理旧代码 @@ -650,18 +645,20 @@ describe('HandExecutor', () => { ### 7.2 性能验收 -| 指标 | 当前 | 目标 | -|------|------|------| -| 首屏加载 | ~3s | <2s | -| 消息渲染 | 卡顿 | 60fps | -| 1000+ 消息滚动 | 卡顿 | 流畅 | -| 内存占用 | 无界 | <500MB | -| Worker 执行延迟 | N/A | <100ms | +| 指标 | 当前 | 目标 | 测量方法 | +|------|------|------|----------| +| 首屏加载 | ~3s | <2s | Chrome DevTools Performance → navigationStart 到 firstContentfulPaint | +| 消息渲染 | 卡顿 | 60fps | React DevTools Profiler → MessageList 组件渲染时间 | +| 1000+ 消息滚动 | 卡顿 | 流畅 | Chrome DevTools Performance → 滚动时 frame rate | +| 内存占用 | 无界 | <500MB | Chrome Memory Profiler → 堆快照对比 | +| Store 更新延迟 | ~50ms | <10ms | Performance.mark() 测量状态更新到渲染完成 | ### 7.3 安全验收 -- [ ] XSS 攻击测试通过 -- [ ] 凭据存储安全审计通过 +- [ ] localStorage 凭据已加密 (使用 AES-GCM) +- [ ] 非 localhost 连接强制 WSS +- [ ] 依赖安全扫描通过 (npm audit) +- [ ] 密钥不在日志中输出 - [ ] WebSocket 安全测试通过 - [ ] 依赖安全扫描通过 @@ -681,10 +678,10 @@ describe('HandExecutor', () => { | 风险 | 概率 | 影响 | 缓解措施 | |------|------|------|----------| -| VZustand 学习曲线 | 中 | 中 | 提前 POC,编写示例 | -| Worker 兼容性 | 低 | 高 | 渐进增强,主线程回退 | +| Valtio 学习曲线 | 中 | 中 | 提前 POC,编写示例 | +| XState 状态机复杂度 | 中 | 中 | 从简单场景开始,逐步完善 | | 迁移导致回归 | 中 | 高 | 每阶段充分测试 | -| 进度延期 | 中 | 中 | 预留 20% buffer | +| 进度延期 | 中 | 中 | 预留 2 周缓冲 | | 团队不熟悉 | 中 | 中 | 培训 + 文档 | --- @@ -696,25 +693,26 @@ describe('HandExecutor', () => { ```json { "dependencies": { - "valtio": "^2.0.0", - "xstate": "^5.0.0", - "react-window": "^2.2.7" + "valtio": "1.11.2", + "xstate": "5.18.2", + "@xstate/react": "4.1.3", + "react-window": "1.8.10" }, "devDependencies": { - "vitest": "^1.0.0", - "@playwright/test": "^1.40.0", - "@testing-library/react": "^14.0.0" + "vitest": "2.1.8", + "@playwright/test": "1.49.1", + "@testing-library/react": "16.1.0" } } ``` ### 9.2 参考文档 -- [VZustand 文档](https://valtio.pmnd.rs/) +- [Valtio 文档](https://valtio.pmnd.rs/) - [XState 文档](https://xstate.js.org/) -- [Web Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) +- [React Window 文档](https://react-window.vercel.app/) - [Tauri 安全最佳实践](https://tauri.app/v2/guides/security/) --- -*文档版本: 1.0 | 最后更新: 2026-03-21* +*文档版本: 1.1 | 最后更新: 2026-03-21*