| New file |
| | |
| | | # ID-01-AUTO-INSERT 架构设计 |
| | | |
| | | 任务:`ID-01-AUTO-INSERT-ARCH` |
| | | 角色:架构师 |
| | | 日期:2026-07-13 |
| | | 结论:`ARCH_RESULT ID-01 PASS` |
| | | |
| | | ## 1. 目标与边界 |
| | | |
| | | Owner 新硬约束已经明确:本路线不再追求 Mac App Store 审核兼容,目标改为 Developer ID / 非 MAS 分发下的真正跨 App 自动上屏。手动 `Cmd+V` 不算完成,正常主路径必须是:用户触发录音,ASR 识别完成后,文字自动进入当前目标 App 输入框。 |
| | | |
| | | 本轮只做架构设计,不写生产源码、不构建、不发布、不上传、不修改 MAS 分支。架构证据文档只写入: |
| | | |
| | | `/Users/ar/Projects/PrivateVoice-independent-developer-auto-insert/03-O/K2.项目管理/ID-01-AUTO-INSERT-架构设计.md` |
| | | |
| | | 不写入旧项目 `/Users/ar/Projects/PrivateVoice/`、MAS 项目或旧 independent-developer 项目。 |
| | | |
| | | ## 2. 当前起点核验 |
| | | |
| | | 本轮只读核验到的新工作树状态: |
| | | |
| | | - Git 工作树:`/Users/ar/Projects/PrivateVoice-independent-developer-auto-insert/03-O` |
| | | - 当前分支:`independent-developer-auto-insert` |
| | | - 远端跟踪:`boborobots/independent-developer-auto-insert` |
| | | - HEAD:`a075aec123e1f7118f138738f196c2995aa96f98` |
| | | - HEAD tag:`v2.2.2-build20260703.0335-real-user-test` |
| | | - 源码根:`/Users/ar/Projects/PrivateVoice-independent-developer-auto-insert/03-O/C1.source/privatevoice.src` |
| | | |
| | | 关键源码入口核验: |
| | | |
| | | - `internal/hotkey/hotkey_darwin.go`:已存在 `CGEventTapCreate(kCGHIDEventTap, ..., kCGEventTapOptionListenOnly, ...)`、`CGEventSourceKeyState`、`CGEventSourceFlagsState`、Accessibility prompt、后台 event tap runloop。 |
| | | - `internal/input/paste_darwin.go`:已存在 `NSPasteboard` 写入 / 恢复、`CGEventPost(kCGHIDEventTap, Cmd+V)` 自动粘贴、`CGEventKeyboardSetUnicodeString` 打字 fallback、Accessibility 权限检查。 |
| | | - `internal/permissions/permissions_darwin.go`:已存在 Microphone、Accessibility、Input Monitoring 三类权限状态与请求路径,其中 Accessibility / Input Monitoring / Microphone 均为 required。 |
| | | - `CODEGRAPH.md`:当前基线声明 macOS Developer ID / notarization 脚本为 `C1.source/privatevoice.src/scripts/build-release-macos.sh`。 |
| | | |
| | | 外部官方事实参考: |
| | | |
| | | - Apple `CGEventTapCreate` / Quartz Event Services:事件 tap 用于观察或影响低层输入事件流,相关路径受隐私权限约束。 |
| | | - Apple `AXIsProcessTrustedWithOptions`:用于判断当前进程是否是受信任 Accessibility client。 |
| | | - Apple `CGEventPost`:可向指定 event tap location post 事件。 |
| | | - Apple notarization / Hardened Runtime 文档:Developer ID 分发需要签名、公证与 hardened runtime 约束;notarization 不是 App Review,也不等于 MAS 审核通过。 |
| | | |
| | | ## 3. 架构总决策 |
| | | |
| | | 独立开发版应恢复并产品化“完整自动输入能力”,采用最小必要组合: |
| | | |
| | | 1. 全局触发:`CGEventTap` + Input Monitoring,用于后台监听用户配置的全局热键 / 长按触发。 |
| | | 2. 自动上屏权限:Accessibility,用于读取前台 App / focused element、判断可写性、在必要时 post 键盘事件。 |
| | | 3. 首选输出:AX focused element direct insert。对可写的标准文本控件,直接通过 Accessibility 写入 focused text element,并用 AX readback 验证。 |
| | | 4. 自动 fallback:受保护剪贴板 + `CGEventPost` 自动 `Cmd+V`。这不是手动 `Cmd+V`,而是 App 在安全 guard 通过后自动完成输入动作。 |
| | | 5. 失败处理:失败时必须返回明确错误、可重试状态和原因;不得把“请用户手动 Cmd+V”作为正常主路径或验收成功。 |
| | | |
| | | 第一阶段不建议把 `CGEventKeyboardSetUnicodeString` 逐字输入作为主路径。它对中文、组合字符、输入法状态、快捷键冲突和高延迟目标风险较高,可保留为后续实验项,默认不进入 ID-01 最小可验收切片。 |
| | | |
| | | ## 4. 输出路由设计 |
| | | |
| | | 建议新增或产品化一个 `AutoInsertRouter`,不要让 ASR 流程直接调用旧 `Paster.Paste`。 |
| | | |
| | | 最小路由顺序: |
| | | |
| | | 1. `PreflightTarget` |
| | | - 获取 frontmost app bundle id / pid / localized name。 |
| | | - 获取 focused element role、subrole、是否 secure、是否 Terminal / shell / console 类负向目标。 |
| | | - 记录目标指纹,不记录 transcript、窗口标题、文档标题或用户输入内容。 |
| | | |
| | | 2. `AXDirectInsert` |
| | | - 只在 focused element 可写且不是 secure field 时启用。 |
| | | - 写入前读取 value / selected text range / insertion point 能力。 |
| | | - 写入后短轮询验证 value 变化、selected range 变化或目标可观测文本包含插入内容。 |
| | | - 成功时返回 `route=ax_direct verified=true`。 |
| | | |
| | | 3. `GuardedAutoPaste` |
| | | - 再次确认 frontmost app / focused element 与 preflight 一致,或符合显式允许的 focus-switch 策略。 |
| | | - Terminal / shell / console、secure field、系统授权窗口、锁屏、未知目标一律禁止自动 paste。 |
| | | - 写入剪贴板前保存 snapshot;写入后验证剪贴板 token / text 已准备。 |
| | | - 调用 `CGEventPost` 自动发送 `Cmd+V`。 |
| | | - 如 AX 可读则验证目标文本变化;如目标不可读但目标在 QA 白名单中,可记录 `posted_unverified`,但正式验收仍要由 QA 通过屏幕/日志证据证明文字实际上屏。 |
| | | - 用户中途改剪贴板时不得覆盖用户新剪贴板;只在剪贴板仍是本轮 pending text 时恢复。 |
| | | |
| | | 4. `Failure` |
| | | - 不静默降级。 |
| | | - 返回明确错误码,例如 `permission_missing`、`target_not_editable`、`focus_changed`、`terminal_blocked`、`paste_rejected`、`verification_failed`。 |
| | | - UI 可提供“重试”“复制到剪贴板”作为显式辅助动作,但这不是自动输入验收路径。 |
| | | |
| | | ## 5. 支持边界与 NO-GO |
| | | |
| | | 可支持目标: |
| | | |
| | | - TextEdit / Notes / Mail compose 等标准 AppKit 文本控件:优先 AX direct insert,可读回验证。 |
| | | - Safari / Arc / Codex / WebView / Electron 类 textarea 或 contenteditable:AX direct insert 可能不稳定,允许走 guarded auto paste;验收必须证明文字自动进入目标输入框。 |
| | | - WeChat 文本输入框:允许 guarded auto paste;如 AX 可读则做 readback,否则走 QA 可视证据。 |
| | | |
| | | 条件支持目标: |
| | | |
| | | - 自绘编辑器、Electron、Monaco、复杂 Web editor:不承诺 AX 可写,必须通过 guarded auto paste 证明自动上屏。 |
| | | - 多窗口 / 多 Space / 焦点切换:默认要求 ASR 完成时 frontmost target 与录音开始时一致;若产品要允许中途切换,必须作为单独产品规则和 QA 项。 |
| | | |
| | | 明确 NO-GO / 负向目标: |
| | | |
| | | - Terminal、shell、console、SSH session、REPL、数据库控制台等命令执行环境:默认不得自动 paste / type,避免把听写文本当命令执行。 |
| | | - Secure text field、密码框、系统授权弹窗、锁屏、登录窗口、远程桌面 / VM 控制窗口。 |
| | | - 无 frontmost editable target、焦点丢失、目标不可识别、目标禁止 paste 或目标切换无法确认。 |
| | | - 任何要求 AppleEvents、osascript、私有 API、IMK/TIS 或 App 专属注入的绕过方案。 |
| | | |
| | | ## 6. 成功判定 |
| | | |
| | | ID-01 的“成功”不是剪贴板准备成功,也不是事件已 post,而是目标 App 中出现识别文本。 |
| | | |
| | | 最小成功判定分级: |
| | | |
| | | - `verified_ax`:AX 写入后 readback 证明目标文本变化。 |
| | | - `verified_paste_readback`:自动 paste 后 AX readback 证明目标文本变化。 |
| | | - `verified_visual_qa`:目标无法 AX readback,但 QA 通过录屏 / 截图 / 目标可见文本证明自动上屏。 |
| | | - `posted_unverified`:只能作为开发诊断,不得作为最终 PASS。 |
| | | - `clipboard_only`:只表示 fallback 文本已准备,不算自动上屏成功。 |
| | | |
| | | 日志要求: |
| | | |
| | | - 可记录 route、目标 bundle id / app name、role、错误码、耗时、权限状态。 |
| | | - 不得记录 transcript、用户输入内容、目标消息内容、窗口标题、文档标题、网页标题。 |
| | | |
| | | ## 7. 测试矩阵 |
| | | |
| | | 第一轮 Coder/QA 验收矩阵: |
| | | |
| | | | 场景 | 目标 | 验收信号 | |
| | | | --- | --- | --- | |
| | | | 标准可写控件 | TextEdit | 识别后自动上屏;优先 `verified_ax` 或 `verified_paste_readback` | |
| | | | Web 输入 | Arc textarea / contenteditable | 识别后自动上屏;允许 guarded auto paste;QA 可视证据必需 | |
| | | | Codex 输入框 | Codex 桌面 / Web 输入框 | 连续 5 轮自动上屏;中英文各覆盖 | |
| | | | WeChat | 微信消息输入框 | 中英文各 3 轮自动上屏;不得只停留在剪贴板 | |
| | | | 连续多轮 | TextEdit + Codex | 5 轮连续听写,文本顺序正确,无剪贴板污染 | |
| | | | 焦点切换 | 录音开始后切到另一个编辑目标 | 按产品策略:默认应报 `focus_changed` 并不自动输入;若允许切换则必须明确写入当前前台目标 | |
| | | | 无焦点 | Desktop / Finder / 无可写目标 | 明确失败,不写剪贴板、不自动 paste | |
| | | | Terminal 负向 | Terminal / shell | 明确 `terminal_blocked`,不得自动 paste / type | |
| | | | 权限缺失 | Accessibility / Input Monitoring / Microphone 任一缺失 | 明确提示缺失权限,不启动伪成功路径 | |
| | | | 剪贴板保护 | 用户中途改剪贴板 | 不覆盖用户新剪贴板;日志证明 skip restore | |
| | | |
| | | 第二轮再扩展到 Slack/Discord/Notion/Google Docs/Microsoft Word 等复杂目标;不得在第一轮承诺所有 App 100% 成功。 |
| | | |
| | | ## 8. 权限与用户引导 |
| | | |
| | | 独立开发版的权限口径必须直接承认完整输入体验需要系统级能力: |
| | | |
| | | - Microphone:录音与本地 ASR。 |
| | | - Accessibility:读取当前 focused element、判断可写目标、必要时向系统发送自动输入事件。 |
| | | - Input Monitoring:后台全局热键 / 长按触发。 |
| | | - Background / Login Item:可选,仅用于后台常驻体验。 |
| | | |
| | | 权限 onboarding 必须是分步 gate: |
| | | |
| | | 1. 未授权时不启动录音伪流程。 |
| | | 2. 每项权限显示用途和当前状态。 |
| | | 3. 授权后必须有重新检测 / 重启热键监听能力。 |
| | | 4. 权限撤销后必须降级为明确错误,而不是静默失效或只写剪贴板。 |
| | | |
| | | ## 9. 签名、公证与分发门禁 |
| | | |
| | | 独立开发版应走 Developer ID + notarized DMG,不走 MAS。 |
| | | |
| | | 最小门禁: |
| | | |
| | | - 使用 Developer ID Application 证书签名 `.app` 与所有嵌入 dylib / helper / framework。 |
| | | - 开启 Hardened Runtime;只添加确实需要的 runtime exception。 |
| | | - 所有原生依赖、ASR dylib、Wails 相关二进制 Team ID / 签名链一致或满足 notarization 要求。 |
| | | - `notarytool` 公证通过,DMG stapled。 |
| | | - `spctl --assess --type execute` 对 `.app` 通过。 |
| | | - `codesign --verify --deep --strict --verbose=2` 对 `.app` 通过。 |
| | | - DMG / zip 有 SHA256 manifest、版本号、build 号、commit、分支、notarization 记录。 |
| | | - 首次发布前刷新 codebase-memory,并把 indexed commit / freeze commit 写入发布 manifest。 |
| | | |
| | | 当前 `CODEGRAPH.md` 已声明 Developer ID / notarization 脚本为 `scripts/build-release-macos.sh`,但 ID-01 本轮不构建,后续 Coder 只能在实现切片后按发布门禁验证。 |
| | | |
| | | ## 10. MAS 与 independent-developer 长期边界 |
| | | |
| | | MAS 路线和 independent-developer 路线必须长期分叉: |
| | | |
| | | - MAS 分支不得引入 Accessibility / Input Monitoring / CGEventTap / CGEventPost 自动输入作为核心路径。 |
| | | - independent-developer 分支可以保留完整全局热键与自动上屏能力,但不得反向合入 MAS。 |
| | | - 若需要复用代码,只允许复用无权限、无平台注入、无发布策略耦合的纯逻辑模块,例如 ASR、模型注册、设置 schema、通用 UI 组件。 |
| | | - 自动输入相关代码建议受 build tag 或目录边界保护,例如 `darwin_independent` / `!appstore`,并配静态扫描防止 MAS 构建误包含。 |
| | | - Review Notes、MAS 权限文案、MAS build scripts 不得从 independent-developer 路线拷贝。 |
| | | |
| | | ## 11. 文件级实现计划 |
| | | |
| | | 建议 Coder 下一棒只做“独立开发版自动上屏最小闭环”,不要顺手改模型、UI 大改、发布脚本或 MAS。 |
| | | |
| | | 第一切片: |
| | | |
| | | - `internal/permissions/permissions_darwin.go` |
| | | - 保留 Microphone / Accessibility / Input Monitoring required。 |
| | | - 增加权限撤销后的明确错误码和重检入口。 |
| | | |
| | | - `internal/hotkey/hotkey_darwin.go` |
| | | - 保留 `CGEventTap` 全局监听。 |
| | | - 不恢复已证伪的“默认右 Command 单侧承诺”。 |
| | | - 热键以用户配置为准;默认值如需调整必须走产品口径。 |
| | | |
| | | - `internal/input/paste_darwin.go` |
| | | - 拆分当前 `Paste` 为可组合能力:clipboard snapshot/write/verify/restore、auto Cmd+V、可选 Unicode typing。 |
| | | - 在写剪贴板前加入 target guard;Terminal / secure field / no focus 一律不写剪贴板、不 post。 |
| | | - 保留用户中途改剪贴板不覆盖的保护。 |
| | | |
| | | - `internal/axinput/` 或 `internal/textoutput/` |
| | | - 新增 AX focused element 查询、可写性判断、direct insert、readback verification。 |
| | | - 新增 `AutoInsertRouter`,统一 AX direct insert 与 guarded auto paste。 |
| | | - 日志脱敏,不记录用户文本和目标标题。 |
| | | |
| | | - `app.go` / ASR 完成回调所在服务 |
| | | - 将识别结果送入 `AutoInsertRouter`。 |
| | | - 输出结果要能返回 route、verified 状态、错误码给前端。 |
| | | |
| | | - 前端权限 / 状态页 |
| | | - 显示权限缺失、目标不可写、Terminal blocked、focus changed、verification failed。 |
| | | - 可提供显式“复制到剪贴板”按钮,但不得替代自动上屏验收。 |
| | | |
| | | - `CODEGRAPH.md` |
| | | - 实现完成后由 Coder 更新新增模块、边界和验证命令。 |
| | | |
| | | ## 12. 禁止事项 |
| | | |
| | | ID-01 及后续实现禁止: |
| | | |
| | | - 修改 MAS 分支或 MAS 项目。 |
| | | - 把手动 `Cmd+V` 定义为正常成功路径。 |
| | | - 对 Terminal / shell / console 自动 paste 或 type。 |
| | | - 在日志记录 transcript、目标消息、窗口标题、文档标题、网页标题。 |
| | | - 使用 AppleEvents / osascript / 私有 API / App 专属自动化绕过。 |
| | | - 重新押注 IMK/TIS。 |
| | | - 在未完成自动上屏 QA 前发布、打 tag、写 release manifest 或对外宣称稳定。 |
| | | |
| | | ## 13. 风险 |
| | | |
| | | 1. Accessibility / Input Monitoring 权限摩擦高,但这是非 MAS 自动上屏的必要代价。 |
| | | 2. Electron / WebView / 自绘编辑器的 AX readback 不稳定,部分目标只能依赖 guarded auto paste + QA 可视证据。 |
| | | 3. Terminal 自动输入风险高,必须默认阻断。 |
| | | 4. 剪贴板 fallback 是自动路径的一部分,但仍有副作用风险,必须严控 restore 与用户中途修改。 |
| | | 5. Developer ID notarization 可能被 dylib、hardened runtime、签名链、rpath 影响,需单独发布门禁验证。 |
| | | 6. 不承诺所有 App 100% 成功;产品口径应承诺“主流可编辑输入框优先自动上屏,失败给明确错误与重试”,而不是静默降级。 |
| | | |
| | | ## 14. 下一步归属 |
| | | |
| | | PMO 可派 Coder 进入 `independent-developer-auto-insert` 分支做第一切片,前提是继续遵守本文件边界。 |
| | | |
| | | Coder 最小验收: |
| | | |
| | | 1. 权限缺失时明确失败。 |
| | | 2. TextEdit 自动上屏 verified。 |
| | | 3. Codex / WeChat / Arc 至少通过 guarded auto paste 自动上屏,不要求 AX direct insert 一定成功。 |
| | | 4. Terminal 负向不自动 paste / type。 |
| | | 5. 连续多轮和用户中途改剪贴板保护通过。 |
| | | 6. 不改 MAS、不发布、不打 tag。 |
| | | |
| | | QA 最小验收: |
| | | |
| | | 1. Codex、WeChat、TextEdit、Arc 四类目标真实自动上屏。 |
| | | 2. 中文 / 英文、连续多轮、焦点切换、无焦点、Terminal 负向覆盖。 |
| | | 3. 证据必须证明文字进入目标输入框,而不只是剪贴板存在内容。 |
| | | |
| | | 最终架构结论: |
| | | |
| | | `ARCH_RESULT ID-01 PASS` |
| | | |