Commit 1da7d8ae01f31979df4ed89d1cd8537911ec5e19
1 parent
678fd73a
docs(CLAUDE.md): 反映 device/ 与 dsh 取代 Hermes 的新布局
新增 §device/(手机路径表、cron、引擎、避坑);phoneuse/wechat_plain 段落指向 device/; Hermes 运行时现状标注为历史。含此前未提交的机型/发送链路修订。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Showing
1 changed file
with
40 additions
and
13 deletions
CLAUDE.md
| @@ -4,13 +4,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co | @@ -4,13 +4,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co | ||
| 4 | 4 | ||
| 5 | ## 项目概述 | 5 | ## 项目概述 |
| 6 | 6 | ||
| 7 | -本仓库(`aipcrm/`)的**主线 = `lifelog-recorder/`** —— Pixel 8 Pro 上一个**完全端上、零云端**的 Local-First 个人记忆 + 人脉管理(Personal CRM)原生应用:24/7 录音 + 本地中文 ASR + 声纹/人脸识别 + 端上 LLM 纪要/对话 + 以「人」为中心的联系人档案 + 全相册语义索引。所有能力跑在设备上,**不依赖任何 Mac/PC 服务**。 | 7 | +本仓库(`aipcrm/`)的**主线 = `lifelog-recorder/`** —— Pixel 10 Pro Fold 上一个**完全端上、零云端**的 Local-First 个人记忆 + 人脉管理(Personal CRM)原生应用:24/7 录音 + 本地中文 ASR + 声纹/人脸识别 + 端上 LLM 纪要/对话 + 以「人」为中心的联系人档案 + 全相册语义索引。所有能力跑在设备上,**不依赖任何 Mac/PC 服务**。 |
| 8 | 8 | ||
| 9 | | 子项目 | 路径 | 形态 | 状态 | | 9 | | 子项目 | 路径 | 形态 | 状态 | |
| 10 | |---|---|---|---| | 10 | |---|---|---|---| |
| 11 | | **lifelog-recorder** | `lifelog-recorder/` | Pixel 原生 Android 应用(Kotlin + Compose) | **主线 v0.8** | | 11 | | **lifelog-recorder** | `lifelog-recorder/` | Pixel 原生 Android 应用(Kotlin + Compose) | **主线 v0.8** | |
| 12 | -| **phoneuse** | `phoneuse/` | Hermes Agent 的 Android MCP 工具层(Python/Termux + Kotlin APK 双实现) | 并行实验,多数工具未验证 | | ||
| 13 | -| **wechat_plain** | `wechat_plain/` | Pixel 上微信 bot 的 Xposed/A11y 模块(端上) | 辅助 | | 12 | +| **phoneuse** | `phoneuse/` | Android MCP 工具层的设计文档 + Mac 侧 frida_bridge(实现代码已迁到 `device/`);现由 **dsh** 消费 | 并行实验,多数工具未验证 | |
| 13 | +| **device** | `device/` | **Pixel 上运行时代码的快照**(微信自动回复、手机 MCP、cron、日报链、wechat-viewer、dsh 配置) | 手机是主版本,见 §device/ | | ||
| 14 | +| **wechat_plain** | `wechat_plain/` + `xhs_lsposed_a16_fix/` | Pixel 上微信 bot:编排/审批走 `wechat_plain/`,实际发送 hook 源码在 `xhs_lsposed_a16_fix/wechathook_src/`(LSPosed 内部 API hook,非 A11y) | 辅助 | | ||
| 14 | | **lemonPCRM** | `server/` + `frontend/` + `packages/` | Node.js + Express 后端 + 静态前端 | **已废弃** | | 15 | | **lemonPCRM** | `server/` + `frontend/` + `packages/` | Node.js + Express 后端 + 静态前端 | **已废弃** | |
| 15 | 16 | ||
| 16 | > [!IMPORTANT] 方向已转向纯端上 App | 17 | > [!IMPORTANT] 方向已转向纯端上 App |
| @@ -22,26 +23,52 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co | @@ -22,26 +23,52 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co | ||
| 22 | 23 | ||
| 23 | ### wechat_plain(端上微信 bot,辅助) | 24 | ### wechat_plain(端上微信 bot,辅助) |
| 24 | 25 | ||
| 26 | +> [!IMPORTANT] 发送链路已迁移,本节的 A11y 方案是历史记录 | ||
| 27 | +> **当前实际生效的发送链路已不是 A11y**,而是 `xhs_lsposed_a16_fix/`(源码 `wechathook_src/WeChatHook.java`,安装包名 `com.example.wechathook`)里的 LSPosed 内部 API hook:注入 `com.tencent.mm` 进程 → 广播 `com.hermes.wechat.SEND_MSG_INTERNAL` → hook 内部发送 API 入队 → 一次 HOME→前台切换(onResume)flush 送达确认。`device/pixelphone/wechat_reply/wechat_reply_orchestrator.py` / `device/pixelphone/event_bus/wechat_hook_send.py` 都走这条路,细节见 memory `微信 API 发送 onResume 派发`。下面的 `HermesAccessibilityService`(`lsposed-wechat-sendmsg` 模块,包名 `com.hermes.wechat.xposed`)**现在手机上已不装**,仅留作当年绕开反射/hook 失败时的方案记录,别照着这段去启用它。 | ||
| 28 | + | ||
| 25 | Pixel 上微信发送/跟踪的落地,纯设备内运行(不依赖 Mac): | 29 | Pixel 上微信发送/跟踪的落地,纯设备内运行(不依赖 Mac): |
| 26 | 30 | ||
| 27 | -- **发送消息(唯一可用链路)**:`am broadcast com.hermes.wechat.SEND_MSG` → **`HermesAccessibilityService`**(lsposed-wechat-sendmsg 模块内)→ 启动微信 LauncherUI → 点搜索按钮 → 搜索 nickname(如"火星人")→ 点结果 → `bkk` 容器内 EditText 输入文本 → 点 `bql` 发送按钮。纯 A11y UI 自动化,绕开所有反射 / hook 失败。 | ||
| 28 | -- **已废弃的两条发送路径(命名误导,勿用)**:LSPosed 反射 API(`HermesWeChatModule.sendTextMessageDirect`,`Send result: true` 是误报,消息不进 `message` 表);Frida daemon(Frida 17 + 微信 8.0.72 注入大 bundle 触发反调试崩溃)。真正能推进 message 表的只有 A11y service。 | 31 | +- **~~发送消息(唯一可用链路)~~(历史,已被上面的内部 API hook 取代)**:`am broadcast com.hermes.wechat.SEND_MSG` → **`HermesAccessibilityService`**(lsposed-wechat-sendmsg 模块内)→ 启动微信 LauncherUI → 点搜索按钮 → 搜索 nickname(如"火星人")→ 点结果 → `bkk` 容器内 EditText 输入文本 → 点 `bql` 发送按钮。纯 A11y UI 自动化,绕开所有反射 / hook 失败。 |
| 32 | +- **已废弃的两条发送路径(命名误导,勿用)**:LSPosed 反射 API(`HermesWeChatModule.sendTextMessageDirect`,`Send result: true` 是误报,消息不进 `message` 表);Frida daemon(Frida 17 + 微信 8.0.72 注入大 bundle 触发反调试崩溃)。 | ||
| 29 | 33 | ||
| 30 | -> [!IMPORTANT] A11y service 启用方式 | 34 | +> [!NOTE] A11y service 启用方式(历史记录,当前链路不需要) |
| 31 | > `enabled_accessibility_services` 系统设置易被关掉,重启: | 35 | > `enabled_accessibility_services` 系统设置易被关掉,重启: |
| 32 | > ```bash | 36 | > ```bash |
| 33 | > adb shell "su -c 'settings put secure enabled_accessibility_services com.hermes.wechat.xposed/com.hermes.wechat.xposed.accessibility.HermesAccessibilityService; settings put secure accessibility_enabled 1'" | 37 | > adb shell "su -c 'settings put secure enabled_accessibility_services com.hermes.wechat.xposed/com.hermes.wechat.xposed.accessibility.HermesAccessibilityService; settings put secure accessibility_enabled 1'" |
| 34 | > ``` | 38 | > ``` |
| 35 | 39 | ||
| 36 | -详见 `wechat_plain/` 内文档。 | 40 | +详见 `wechat_plain/` 内文档。编排脚本(orchestrator / watcher / policy)的当前版本在 `device/pixelphone/wechat_reply/`,`wechat_plain/scripts/auto-reply/` 只留文档。 |
| 41 | + | ||
| 42 | +## device/(手机运行时快照;dsh 取代 Hermes,2026-10-17) | ||
| 43 | + | ||
| 44 | +> 手机是**主版本**,`device/` 只是快照:先在手机上改,再 `device/sync-device.sh` 拉回来 `git diff` / commit。不拉 `.db`/日志/`.env`/`.hermes_env.sh`,脚本末尾扫密钥,命中即中止。 | ||
| 45 | + | ||
| 46 | +**Hermes 已被 dsh(DeepSeek Harness,`@deepseek-ai/dsh`)取代,gateway 已下线**(`hermes` CLI 不可用,别再写调 `hermes chat/-z` 的新代码)。手机上的新布局: | ||
| 47 | + | ||
| 48 | +| 手机路径 | 内容 | | ||
| 49 | +|---|---| | ||
| 50 | +| `~/pixelphone/{event_bus,wechat_reply,scripts,digest,cron-logs}` | 手机 MCP(`android_mcp.py`)、微信自动回复(watcher→orchestrator→reply-sender)、cron 脚本、日报链(`digest/wechat-group-digest`、`digest/gpt-image-gen`) | | ||
| 51 | +| `~/dsh/` | dsh 安装(5 个 bionic 补丁 `patches/apply.cjs`)、`run-headless.sh`、`run-web.sh`(带 `no-phone.yml` 补丁)、`phone_lazy_mcp.py`(把 ~65 个手机 MCP 工具收成 `phone_list/describe/call` 三个元工具) | | ||
| 52 | +| `~/.dsh/skills/` | 保留的 ~47 个 skill(**必须平铺**,dsh 不递归嵌套目录);原 597 个 Hermes skill 仍在 `~/.hermes/skills/`,只待归档 | | ||
| 53 | +| `~/.dsh/profiles/{headless,web}/cordis.patch.yml` | 默认模型 `zai-coding/glm-5.3-flash`、persona(原 SOUL.md)、手机 MCP 接入 | | ||
| 54 | +| `/data/adb/service.d/{crond,dsh-web,wechat-reply-watcher}.sh` | Magisk 开机自启(Hermes gateway 相关脚本已挪到 `/data/adb/retired/`) | | ||
| 55 | +| Termux `crond`(`crontab -l`,见 `device/crontab.txt`) | 取代 Hermes cron:reply-sender 5min、viewer 看门狗 10min、学习管线、BOSS 简历 6h、gate-audit、**TGO 日报 01/02/03 点链**(`daily-digest.sh gen|img|send`)+ 每小时 TGO 预摘要 | | ||
| 56 | + | ||
| 57 | +**微信自动回复引擎**:member(闲聊)与 admin(任务)两个分支都走 `dsh headless`(`WECHAT_REPLY_ENGINE`,默认 `dsh`)。admin 读 `~/.dsh/skills/reply-task-executor`,手机 MCP 已放开;但 `WECHAT_REPLY_NO_DIRECT_SEND=1` 闸门仍会让 `wechat_hook_send.send_*` 直发被拒,**发送只能走 orchestrator**。不可信内容的任务(viewer 文章摘要)用 `--patch ~/dsh/patches/no-phone.yml` 关掉手机 MCP。 | ||
| 58 | + | ||
| 59 | +**别踩**: | ||
| 60 | +- 不要启动 runit 的 `crond` 服务(已 `down`),否则与 service.d 拉起的 crond 双跑,`reply-sender` 会重复发送。 | ||
| 61 | +- 看门狗清 `orch.lock` 的阈值必须 > `GEN_TIMEOUT`(600s),否则 admin 长任务会被第二个 drain 重复处理(已改为 900s)。 | ||
| 62 | +- 手机 Termux 对 `/sdcard` 无权限,涉及 `/sdcard` 的脚本要 `su -c`(BOSS 管线即如此)。 | ||
| 63 | +- 回滚点:手机 `~/premigrate-backup.tgz`(迁移前的 event_bus/wechat_reply/scripts 代码)。 | ||
| 37 | 64 | ||
| 38 | -## phoneuse(Hermes Agent 的 Android MCP 工具层,并行实验) | 65 | +## phoneuse(Android MCP 工具层,并行实验;消费方已从 Hermes 换成 dsh) |
| 39 | 66 | ||
| 40 | > 完整架构见 `phoneuse/ARCHITECTURE.md`,Sprint 任务 + 真机审计记录见 `phoneuse/TASKS.md`,产品 vision 原始稿见 `phoneuse/phoneuse.md`。本节只给定位 + 别踩的坑,细节进文件看。 | 67 | > 完整架构见 `phoneuse/ARCHITECTURE.md`,Sprint 任务 + 真机审计记录见 `phoneuse/TASKS.md`,产品 vision 原始稿见 `phoneuse/phoneuse.md`。本节只给定位 + 别踩的坑,细节进文件看。 |
| 41 | 68 | ||
| 42 | ### 定位 | 69 | ### 定位 |
| 43 | 70 | ||
| 44 | -把 Pixel 上**所有 App** 虚拟化成 **Hermes Agent**(独立 LLM agent runtime,跑在设备 Termux 里)可调用的数据源/能力源:App Runtime Access → Semantic Extraction → Memory Graph → Agent Planning,目标是替代"截图 + OCR + 点击"那种第一代 phone use。跟 lifelog-recorder 是**不同的项目脉络**,2026-06-23 才拍平合并进本仓库,目前两边没有打通。 | 71 | +把 Pixel 上**所有 App** 虚拟化成 **LLM agent runtime(原 Hermes Agent,现为 dsh,跑在设备 Termux 里)**可调用的数据源/能力源:App Runtime Access → Semantic Extraction → Memory Graph → Agent Planning,目标是替代"截图 + OCR + 点击"那种第一代 phone use。跟 lifelog-recorder 是**不同的项目脉络**,2026-06-23 才拍平合并进本仓库,目前两边没有打通。 |
| 45 | 72 | ||
| 46 | > [!IMPORTANT] 不是零 Mac | 73 | > [!IMPORTANT] 不是零 Mac |
| 47 | > lifelog-recorder 的"完全本地零云端"约束**不适用于 phoneuse 的 Frida 层**:`frida_bridge.py` 跑在 Mac 上,设备端 MCP 工具通过 `adb reverse tcp:8767 tcp:8767` 调用 Mac 上的 frida Python 库去 attach 设备进程。判断这部分能力时不要套用 lifelog-recorder 那套"零网络"假设。 | 74 | > lifelog-recorder 的"完全本地零云端"约束**不适用于 phoneuse 的 Frida 层**:`frida_bridge.py` 跑在 Mac 上,设备端 MCP 工具通过 `adb reverse tcp:8767 tcp:8767` 调用 Mac 上的 frida Python 库去 attach 设备进程。判断这部分能力时不要套用 lifelog-recorder 那套"零网络"假设。 |
| @@ -73,11 +100,11 @@ Pixel 上微信发送/跟踪的落地,纯设备内运行(不依赖 Mac): | @@ -73,11 +100,11 @@ Pixel 上微信发送/跟踪的落地,纯设备内运行(不依赖 Mac): | ||
| 73 | - 真机连续性差:开发时常掉 USB,涉及真机的验证要先确认 `adb devices` 能看到设备。 | 100 | - 真机连续性差:开发时常掉 USB,涉及真机的验证要先确认 `adb devices` 能看到设备。 |
| 74 | - 微信相关的 Frida/A11y 经验跟 `wechat_plain` 是分开积累的——`wechat_plain` 已验证过 Frida daemon 注入微信大 bundle 会触发反调试崩溃(见上文 §wechat_plain);`phoneuse` 走的是 USB attach 而非常驻 daemon,崩溃模式不一定一样,但动手前先看那段记录,别重新踩同一个坑。 | 101 | - 微信相关的 Frida/A11y 经验跟 `wechat_plain` 是分开积累的——`wechat_plain` 已验证过 Frida daemon 注入微信大 bundle 会触发反调试崩溃(见上文 §wechat_plain);`phoneuse` 走的是 USB attach 而非常驻 daemon,崩溃模式不一定一样,但动手前先看那段记录,别重新踩同一个坑。 |
| 75 | 102 | ||
| 76 | -### Hermes Agent 运行时现状(手机 `~/.hermes/`,据 2026-06-24 一次性设备备份) | 103 | +### Hermes Agent 运行时现状(**历史记录**,据 2026-06-24 一次性设备备份;Hermes 已于 2026-10-17 被 dsh 取代,gateway 下线) |
| 77 | 104 | ||
| 78 | > 备份本身**不进 git**(含真实 API key + 真实微信消息原文,已加进根 `.gitignore` 的 `/hermes_backup_*/`),看完建议删掉或挪出仓库。这里只摘录"对以后开发有用"的事实,原始内容不再保留引用路径。 | 105 | > 备份本身**不进 git**(含真实 API key + 真实微信消息原文,已加进根 `.gitignore` 的 `/hermes_backup_*/`),看完建议删掉或挪出仓库。这里只摘录"对以后开发有用"的事实,原始内容不再保留引用路径。 |
| 79 | 106 | ||
| 80 | -`phoneuse/android_mcp.py` 的真实消费方是**第三方框架 Hermes Agent**(Nous Research 出品,非本项目自研),跑在手机 Termux 里,靠 `~/.hermes/config.yaml` 的 `mcp_servers.android-phone` 一条配置连过来;驱动模型是 Kimi(`model.default: kimi-for-coding`,`base_url: api.kimi.com/coding`)。Hermes 自己又在 phoneuse 之外长出了一套 skills/plugins/cron,**会直接调用 phoneuse 的 MCP 工具**,值得记下来: | 107 | +`android_mcp.py`(现位于 `device/pixelphone/event_bus/`)当时的真实消费方是**第三方框架 Hermes Agent**(Nous Research 出品,非本项目自研),跑在手机 Termux 里,靠 `~/.hermes/config.yaml` 的 `mcp_servers.android-phone` 一条配置连过来;驱动模型是 Kimi(`model.default: kimi-for-coding`,`base_url: api.kimi.com/coding`)。Hermes 自己又在 phoneuse 之外长出了一套 skills/plugins/cron,**会直接调用 phoneuse 的 MCP 工具**,值得记下来: |
| 81 | 108 | ||
| 82 | - **`memory_graph.db` 确认在线增长**:备份快照里 1020 person / 1255 message / 518 vector,对比 `TASKS.md` 2026-06-16 审计时的 522/1205/510+——证明 `ingest_wechat_to_memory`/`embed_memory_messages` 这条链路在真实持续跑,不是一次性验证。 | 109 | - **`memory_graph.db` 确认在线增长**:备份快照里 1020 person / 1255 message / 518 vector,对比 `TASKS.md` 2026-06-16 审计时的 522/1205/510+——证明 `ingest_wechat_to_memory`/`embed_memory_messages` 这条链路在真实持续跑,不是一次性验证。 |
| 83 | - **`wechat-image-sender` skill(2026-06-23 验证通过)补上了 phoneuse 自己没做的能力**:`send_wechat_message`/`mcp_android_phone_send_wechat_message` 只能发文字,发图片靠这个 skill 的 `am start -a android.intent.action.SEND -t image/png` 唤出微信"选择聊天"→OCR 找联系人(拼音兜底)→固定坐标 `(899, 2627)` 点发送,针对 1344×2992 屏幕全程验证可用。**以后要给 `wechat_send.py` 加发图能力,这条路子是现成验证过的,不用重新摸索**。该 skill 更早的笔记(`image_sending_limitations.md`)里提过想用 Frida bridge 做但因为"不在 Termux 里跑"放弃——这正是这次新增的 `frida_inspect_tools.py`/`start_frida_bridge.sh` 解决的那条链路,以后两边可以接上看看。 | 110 | - **`wechat-image-sender` skill(2026-06-23 验证通过)补上了 phoneuse 自己没做的能力**:`send_wechat_message`/`mcp_android_phone_send_wechat_message` 只能发文字,发图片靠这个 skill 的 `am start -a android.intent.action.SEND -t image/png` 唤出微信"选择聊天"→OCR 找联系人(拼音兜底)→固定坐标 `(899, 2627)` 点发送,针对 1344×2992 屏幕全程验证可用。**以后要给 `wechat_send.py` 加发图能力,这条路子是现成验证过的,不用重新摸索**。该 skill 更早的笔记(`image_sending_limitations.md`)里提过想用 Frida bridge 做但因为"不在 Termux 里跑"放弃——这正是这次新增的 `frida_inspect_tools.py`/`start_frida_bridge.sh` 解决的那条链路,以后两边可以接上看看。 |
| @@ -88,13 +115,13 @@ Pixel 上微信发送/跟踪的落地,纯设备内运行(不依赖 Mac): | @@ -88,13 +115,13 @@ Pixel 上微信发送/跟踪的落地,纯设备内运行(不依赖 Mac): | ||
| 88 | > [!NOTE] 备份不完整 | 115 | > [!NOTE] 备份不完整 |
| 89 | > 备份自带的 `manifest.json` 列了 5 个 skill(含 `screen-wake`、`android-visual-automation`),但备份目录里实际只有 3 个(`wechat-group-digest`/`wechat-image-sender`/`feishu-lark`)——别假设这份备份是手机上 `~/.hermes/` 的完整镜像。 | 116 | > 备份自带的 `manifest.json` 列了 5 个 skill(含 `screen-wake`、`android-visual-automation`),但备份目录里实际只有 3 个(`wechat-group-digest`/`wechat-image-sender`/`feishu-lark`)——别假设这份备份是手机上 `~/.hermes/` 的完整镜像。 |
| 90 | 117 | ||
| 91 | -## lifelog-recorder(Pixel 8 Pro 24/7 录音 + 本地中文 ASR + 本地 LLM,v0.8) | 118 | +## lifelog-recorder(Pixel 10 Pro Fold 24/7 录音 + 本地中文 ASR + 本地 LLM,v0.8) |
| 92 | 119 | ||
| 93 | > 完整接手地图、踩坑清单、命令速查见 `lifelog-recorder/HANDOFF.md`。本节只勒住整个项目的**架构骨架 + 关键决策来源**,不重复细节。 | 120 | > 完整接手地图、踩坑清单、命令速查见 `lifelog-recorder/HANDOFF.md`。本节只勒住整个项目的**架构骨架 + 关键决策来源**,不重复细节。 |
| 94 | 121 | ||
| 95 | ### 项目目标 | 122 | ### 项目目标 |
| 96 | 123 | ||
| 97 | -把 Pixel 8 Pro 当成**永远开机的麦克风**:7×24 后台录音 → VAD 切段 → 中文 ASR 转文字 → 入库 → 按时间轴显示和搜索 → LLM 总结成纪要。最终目的是把所有「我说过 / 别人对我说过」的内容沉淀成可搜索、可回放的个人记忆库,并在 **app 内自成端上 Personal CRM**(photolog):某段语音 → 声纹 → 联系人档案,照片 → 人脸 → 同一联系人。**整套 CRM 跑在设备上,已取代旧的 Mac 端 lemonPCRM**。 | 124 | +把 Pixel 10 Pro Fold 当成**永远开机的麦克风**:7×24 后台录音 → VAD 切段 → 中文 ASR 转文字 → 入库 → 按时间轴显示和搜索 → LLM 总结成纪要。最终目的是把所有「我说过 / 别人对我说过」的内容沉淀成可搜索、可回放的个人记忆库,并在 **app 内自成端上 Personal CRM**(photolog):某段语音 → 声纹 → 联系人档案,照片 → 人脸 → 同一联系人。**整套 CRM 跑在设备上,已取代旧的 Mac 端 lemonPCRM**。 |
| 98 | 125 | ||
| 99 | 约束:**完全本地 / 零云端依赖 / 月成本 0 元 / 隐私走 0 网络**。 | 126 | 约束:**完全本地 / 零云端依赖 / 月成本 0 元 / 隐私走 0 网络**。 |
| 100 | 127 |