diff --git a/CLAUDE.md b/CLAUDE.md index 6bfb140..0b5c39d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,13 +4,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## 项目概述 -本仓库(`aipcrm/`)的**主线 = `lifelog-recorder/`** —— Pixel 8 Pro 上一个**完全端上、零云端**的 Local-First 个人记忆 + 人脉管理(Personal CRM)原生应用:24/7 录音 + 本地中文 ASR + 声纹/人脸识别 + 端上 LLM 纪要/对话 + 以「人」为中心的联系人档案 + 全相册语义索引。所有能力跑在设备上,**不依赖任何 Mac/PC 服务**。 +本仓库(`aipcrm/`)的**主线 = `lifelog-recorder/`** —— Pixel 10 Pro Fold 上一个**完全端上、零云端**的 Local-First 个人记忆 + 人脉管理(Personal CRM)原生应用:24/7 录音 + 本地中文 ASR + 声纹/人脸识别 + 端上 LLM 纪要/对话 + 以「人」为中心的联系人档案 + 全相册语义索引。所有能力跑在设备上,**不依赖任何 Mac/PC 服务**。 | 子项目 | 路径 | 形态 | 状态 | |---|---|---|---| | **lifelog-recorder** | `lifelog-recorder/` | Pixel 原生 Android 应用(Kotlin + Compose) | **主线 v0.8** | -| **phoneuse** | `phoneuse/` | Hermes Agent 的 Android MCP 工具层(Python/Termux + Kotlin APK 双实现) | 并行实验,多数工具未验证 | -| **wechat_plain** | `wechat_plain/` | Pixel 上微信 bot 的 Xposed/A11y 模块(端上) | 辅助 | +| **phoneuse** | `phoneuse/` | Android MCP 工具层的设计文档 + Mac 侧 frida_bridge(实现代码已迁到 `device/`);现由 **dsh** 消费 | 并行实验,多数工具未验证 | +| **device** | `device/` | **Pixel 上运行时代码的快照**(微信自动回复、手机 MCP、cron、日报链、wechat-viewer、dsh 配置) | 手机是主版本,见 §device/ | +| **wechat_plain** | `wechat_plain/` + `xhs_lsposed_a16_fix/` | Pixel 上微信 bot:编排/审批走 `wechat_plain/`,实际发送 hook 源码在 `xhs_lsposed_a16_fix/wechathook_src/`(LSPosed 内部 API hook,非 A11y) | 辅助 | | **lemonPCRM** | `server/` + `frontend/` + `packages/` | Node.js + Express 后端 + 静态前端 | **已废弃** | > [!IMPORTANT] 方向已转向纯端上 App @@ -22,26 +23,52 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ### wechat_plain(端上微信 bot,辅助) +> [!IMPORTANT] 发送链路已迁移,本节的 A11y 方案是历史记录 +> **当前实际生效的发送链路已不是 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 失败时的方案记录,别照着这段去启用它。 + Pixel 上微信发送/跟踪的落地,纯设备内运行(不依赖 Mac): -- **发送消息(唯一可用链路)**:`am broadcast com.hermes.wechat.SEND_MSG` → **`HermesAccessibilityService`**(lsposed-wechat-sendmsg 模块内)→ 启动微信 LauncherUI → 点搜索按钮 → 搜索 nickname(如"火星人")→ 点结果 → `bkk` 容器内 EditText 输入文本 → 点 `bql` 发送按钮。纯 A11y UI 自动化,绕开所有反射 / hook 失败。 -- **已废弃的两条发送路径(命名误导,勿用)**:LSPosed 反射 API(`HermesWeChatModule.sendTextMessageDirect`,`Send result: true` 是误报,消息不进 `message` 表);Frida daemon(Frida 17 + 微信 8.0.72 注入大 bundle 触发反调试崩溃)。真正能推进 message 表的只有 A11y service。 +- **~~发送消息(唯一可用链路)~~(历史,已被上面的内部 API hook 取代)**:`am broadcast com.hermes.wechat.SEND_MSG` → **`HermesAccessibilityService`**(lsposed-wechat-sendmsg 模块内)→ 启动微信 LauncherUI → 点搜索按钮 → 搜索 nickname(如"火星人")→ 点结果 → `bkk` 容器内 EditText 输入文本 → 点 `bql` 发送按钮。纯 A11y UI 自动化,绕开所有反射 / hook 失败。 +- **已废弃的两条发送路径(命名误导,勿用)**:LSPosed 反射 API(`HermesWeChatModule.sendTextMessageDirect`,`Send result: true` 是误报,消息不进 `message` 表);Frida daemon(Frida 17 + 微信 8.0.72 注入大 bundle 触发反调试崩溃)。 -> [!IMPORTANT] A11y service 启用方式 +> [!NOTE] A11y service 启用方式(历史记录,当前链路不需要) > `enabled_accessibility_services` 系统设置易被关掉,重启: > ```bash > 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'" > ``` -详见 `wechat_plain/` 内文档。 +详见 `wechat_plain/` 内文档。编排脚本(orchestrator / watcher / policy)的当前版本在 `device/pixelphone/wechat_reply/`,`wechat_plain/scripts/auto-reply/` 只留文档。 + +## device/(手机运行时快照;dsh 取代 Hermes,2026-10-17) + +> 手机是**主版本**,`device/` 只是快照:先在手机上改,再 `device/sync-device.sh` 拉回来 `git diff` / commit。不拉 `.db`/日志/`.env`/`.hermes_env.sh`,脚本末尾扫密钥,命中即中止。 + +**Hermes 已被 dsh(DeepSeek Harness,`@deepseek-ai/dsh`)取代,gateway 已下线**(`hermes` CLI 不可用,别再写调 `hermes chat/-z` 的新代码)。手机上的新布局: + +| 手机路径 | 内容 | +|---|---| +| `~/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`) | +| `~/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` 三个元工具) | +| `~/.dsh/skills/` | 保留的 ~47 个 skill(**必须平铺**,dsh 不递归嵌套目录);原 597 个 Hermes skill 仍在 `~/.hermes/skills/`,只待归档 | +| `~/.dsh/profiles/{headless,web}/cordis.patch.yml` | 默认模型 `zai-coding/glm-5.3-flash`、persona(原 SOUL.md)、手机 MCP 接入 | +| `/data/adb/service.d/{crond,dsh-web,wechat-reply-watcher}.sh` | Magisk 开机自启(Hermes gateway 相关脚本已挪到 `/data/adb/retired/`) | +| 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 预摘要 | + +**微信自动回复引擎**: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。 + +**别踩**: +- 不要启动 runit 的 `crond` 服务(已 `down`),否则与 service.d 拉起的 crond 双跑,`reply-sender` 会重复发送。 +- 看门狗清 `orch.lock` 的阈值必须 > `GEN_TIMEOUT`(600s),否则 admin 长任务会被第二个 drain 重复处理(已改为 900s)。 +- 手机 Termux 对 `/sdcard` 无权限,涉及 `/sdcard` 的脚本要 `su -c`(BOSS 管线即如此)。 +- 回滚点:手机 `~/premigrate-backup.tgz`(迁移前的 event_bus/wechat_reply/scripts 代码)。 -## phoneuse(Hermes Agent 的 Android MCP 工具层,并行实验) +## phoneuse(Android MCP 工具层,并行实验;消费方已从 Hermes 换成 dsh) > 完整架构见 `phoneuse/ARCHITECTURE.md`,Sprint 任务 + 真机审计记录见 `phoneuse/TASKS.md`,产品 vision 原始稿见 `phoneuse/phoneuse.md`。本节只给定位 + 别踩的坑,细节进文件看。 ### 定位 -把 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 才拍平合并进本仓库,目前两边没有打通。 +把 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 才拍平合并进本仓库,目前两边没有打通。 > [!IMPORTANT] 不是零 Mac > 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): - 真机连续性差:开发时常掉 USB,涉及真机的验证要先确认 `adb devices` 能看到设备。 - 微信相关的 Frida/A11y 经验跟 `wechat_plain` 是分开积累的——`wechat_plain` 已验证过 Frida daemon 注入微信大 bundle 会触发反调试崩溃(见上文 §wechat_plain);`phoneuse` 走的是 USB attach 而非常驻 daemon,崩溃模式不一定一样,但动手前先看那段记录,别重新踩同一个坑。 -### Hermes Agent 运行时现状(手机 `~/.hermes/`,据 2026-06-24 一次性设备备份) +### Hermes Agent 运行时现状(**历史记录**,据 2026-06-24 一次性设备备份;Hermes 已于 2026-10-17 被 dsh 取代,gateway 下线) > 备份本身**不进 git**(含真实 API key + 真实微信消息原文,已加进根 `.gitignore` 的 `/hermes_backup_*/`),看完建议删掉或挪出仓库。这里只摘录"对以后开发有用"的事实,原始内容不再保留引用路径。 -`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 工具**,值得记下来: +`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 工具**,值得记下来: - **`memory_graph.db` 确认在线增长**:备份快照里 1020 person / 1255 message / 518 vector,对比 `TASKS.md` 2026-06-16 审计时的 522/1205/510+——证明 `ingest_wechat_to_memory`/`embed_memory_messages` 这条链路在真实持续跑,不是一次性验证。 - **`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): > [!NOTE] 备份不完整 > 备份自带的 `manifest.json` 列了 5 个 skill(含 `screen-wake`、`android-visual-automation`),但备份目录里实际只有 3 个(`wechat-group-digest`/`wechat-image-sender`/`feishu-lark`)——别假设这份备份是手机上 `~/.hermes/` 的完整镜像。 -## lifelog-recorder(Pixel 8 Pro 24/7 录音 + 本地中文 ASR + 本地 LLM,v0.8) +## lifelog-recorder(Pixel 10 Pro Fold 24/7 录音 + 本地中文 ASR + 本地 LLM,v0.8) > 完整接手地图、踩坑清单、命令速查见 `lifelog-recorder/HANDOFF.md`。本节只勒住整个项目的**架构骨架 + 关键决策来源**,不重复细节。 ### 项目目标 -把 Pixel 8 Pro 当成**永远开机的麦克风**:7×24 后台录音 → VAD 切段 → 中文 ASR 转文字 → 入库 → 按时间轴显示和搜索 → LLM 总结成纪要。最终目的是把所有「我说过 / 别人对我说过」的内容沉淀成可搜索、可回放的个人记忆库,并在 **app 内自成端上 Personal CRM**(photolog):某段语音 → 声纹 → 联系人档案,照片 → 人脸 → 同一联系人。**整套 CRM 跑在设备上,已取代旧的 Mac 端 lemonPCRM**。 +把 Pixel 10 Pro Fold 当成**永远开机的麦克风**:7×24 后台录音 → VAD 切段 → 中文 ASR 转文字 → 入库 → 按时间轴显示和搜索 → LLM 总结成纪要。最终目的是把所有「我说过 / 别人对我说过」的内容沉淀成可搜索、可回放的个人记忆库,并在 **app 内自成端上 Personal CRM**(photolog):某段语音 → 声纹 → 联系人档案,照片 → 人脸 → 同一联系人。**整套 CRM 跑在设备上,已取代旧的 Mac 端 lemonPCRM**。 约束:**完全本地 / 零云端依赖 / 月成本 0 元 / 隐私走 0 网络**。