• 把「人物」做成微信通讯录样式 + 联系人档案页(以人为中心聚合的地基)。
    
    - data:Person 加微信资料字段(nickname/remark/alias/pinyin/pyInitial/phone) + 迁移 v5→v6;
      PersonDao observe(id) / updateContactInfo;SegmentDao observeBySpeaker(声纹→录音)
    - WeChatEnrollWorker 重写两段:Pass1 给**全部真人联系人**upsert 资料(通讯录全员可见),
      Pass2 仅对有脸头像建 faceEmbedding 锚点。幂等
    - ContactList:搜索 + 拼音 A–Z 粘性分组 + 头像 + 人脸/声纹标,点进档案页
    - PersonProfileScreen:头像 + 微信名/备注/微信号 + 声纹绑定 + 关联照片列表 + 录音列表
      (录音 = segments.speakerId=person.voiceSpeakerId,绑声纹后出现)
    - 标注对话框改可滚动/搜索/固定底部按钮(联系人多时不再卡死)
    - extract 脚本补全字段(nickname/remark/alias/pinyin)
    
    实测:4357 真人联系人入通讯录(1339 人脸锚点),A–Z 分组 + 头像 + 档案页正常。
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • 把微信联系人头像建成人脸锚点,照片处理时自动认出"这是谁",标注时直接带名字。
    
    - data:Person 加 faceEmbedding 锚点字段 + 迁移 v4→v5(ALTER 加 BLOB 列,
      DDL 对过 Room;真机迁移零数据损失);PersonDao anchors/getByContactRef
    - FaceMatcher:gallery 统一 = 人物锚点(头像) + 已标注脸样本;match() 共享余弦匹配;
      PhotoProcessor 改用统一 gallery
    - WeChatEnrollWorker:读 filesDir/wx_enroll/(enroll.json + 头像) → 对每张头像跑
      **端上 FaceEngine**(必须同引擎才能跟照片人脸比),恰 1 脸就建 Person(name,
      contactRef=wechat:wxid, avatarPath, faceEmbedding)。幂等、单轮预算 8min、retry 续
    - UI:人物页「从微信导入联系人」按钮;人物卡片标注「微信/声纹」来源
    - server/services/wechat/extract-wechat-avatars.py:Mac 侧提取脚本
      (sqlcipher 解密 EnMicroMsg.db → rcontact⨝img_flag 筛真人 → 下载头像)
    
    实测:4338 真人联系人,头像有脸覆盖率 41%(1796/4337),端上 90s 建 284 锚点(继续后台跑)。
    头像/姓名属敏感 PII,存 _wx_enroll/ 已 gitignore。
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • - HermesAccessibilityService: 折叠态封面屏(1080×2364)下 A11y 节点 bounds 全 0、
      ACTION_CLICK/SET_TEXT 失效,改用固定坐标 dispatchGesture 全流程(搜索图标 →
      设剪贴板长按粘贴 → 首条结果 → IME 剪贴板候选插入 → 绿色发送按钮),
      删 ~750 行旧反射/节点路径(-749/+74)。
    - setup_termux_boot.sh: frida-server 路径挪到 Termux 家目录(~/hermes-wechat/bin),
      弃用 /data/local/tmp(Termux 路径约定)。
    - server/services/wechat/wechat-contacts.{json,md}: 从 EnMicroMsg.db rcontact 导出
      5423 联系人(发送时确定真实 wxid 的权威来源)。
    - wechat_plain/scripts/frida-daemon/: Frida 调试脚本(已废弃发送路径,留作参考)。
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • 合并「先读哪三份 / 闭环四步 / 关键事实 / 先汇报再动手」为一条自包含 prompt。
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 两个角色各一段可复用 prompt:claude-run(改代码,靠 .restart-request flag 自助重启)+
    hermes(跑需要 root 的设备操作)。纳入 deploy-to-phone.sh DOCS 一起部署到 ~/hermes-wechat/。
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • claude-run 跑在 proot、拿不到 Magisk root,杀不了 root 的 auto-reply 进程。
    keepalive.sh 主循环新增:检测到 ~/hermes-wechat/.restart-request flag 就以 root
    pkill auto-reply 并删 flag,随后既有的 is_auto_reply_running 检测用新代码重启。
    只暴露「请求重启」一个动作,不是任意命令执行。已端到端验证:u0_a327 touch flag
    → keepalive ~15s 内重启 bot。
    
    - keepalive.sh: 纳入版本控制 + 加 flag 桥
    - deploy-to-phone.sh: 收 keepalive.sh(.sh 给 755)+ 末尾加重启 keepalive 提示
    - AGENTS.md / HANDOFF.md §零: 文档化 flag 重启途径
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • - fix(auto-reply): 群引用回复改用 rawContent 调 isQuoteMe。normalize 已剥掉
      <refermsg> XML,而 isQuoteMe 需原始 XML 判断引用的是不是机器人,否则永远
      false → 只引用不 @ 的回复被静默忽略([NORMALIZE] 之后无下文)
    - deploy-to-phone.sh: 修正推送清单(原漏了 auto-reply.js / sqlcipher tracker,
      从未真正部署过运行时代码);改走 /sdcard dd + chown u0_a327 + md5 校验
    - HANDOFF.md v4: 加 §零「claude-run 环境须知」(.claude 被 chown root 致 Bash
      全挂 / 唯一 live 日志 / bot root vs agent u0_a327 须用 su);删 lemon-acp clone
    - AGENTS.md + CLAUDE.md: agent 一次性可读的接手入口(claude-run 改代码 +
      hermes 跑 su 操作 的混合分工)
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 症状:owner 用引用回复发"...@小火子",机器人完全没反应,日志只有
    [NOTIF] 没有 [GROUP-AT]。
    
    根因:
    1. 引用回复在 message 表的 type=822083633(0x31000031 appmsg),
       tracker fetchNew 的 `WHERE type IN (1, 10002)` 把它过滤掉,根本没 fetch
    2. 即使 fetch 了,content 是 appmsg XML,"@小火子" 在 <title> 里且后面
       跟 </title>(不是空格),isAtMe 的 `@昵称(\s|\n|$)` 也匹配不上
    
    修复:
    - tracker: type IN (1, 10002, 822083633)
    - handleMessage 顶部规范化:appmsg/refermsg XML → 提取 <title> 文本
      重组成 "senderPrefix:\n<title>",下游 @ 检测/quote/hermes 都用干净文本
    - 保留 rawContent(含 refermsg)传给 parseApprovalCommand,
      这样 owner 用引用回复发"同意"时仍能从 refermsg 判断同意哪条 pending
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • #1 auto-reply.js: callHermesRunStream 的 res.on('end') 还在调旧的 flush()
       (重构成 flushTextBuf/emit 时漏改),触发 ReferenceError: flush is not defined
       → 之前直接崩 node(被 keepalive 重启打断 run)。改用 flushTextBuf/flushReasoningBuf。
       (上一个 commit 的 unhandledRejection 兜底捕获到了这个真凶)
    
    #2 HermesAccessibilityService @ 选人卡死:
       - 成员名匹配:选人面板节点文本是"昵称: ZZ"等带前缀格式,严格 equals
         匹配不上 → 新增 findMentionMember 做包含匹配 + 向上找 clickable 祖先
       - 找不到成员时 fallback:之前直接 typeFinalContent 但仍停在"选择提醒的人"
         整页/半屏页,找不到 bkk 输入框 → 卡死。改成先 GLOBAL_ACTION_BACK 退出
         选人页回到聊天页,800ms 后再 findInnerEditText + typeFinalContent
    
    实测:宜创群回复曾卡在 HalfScreenTransparentActivity(选择提醒的人),
    要 @ 的 ZZ 在面板显示为"昵称: ZZ",严格匹配失败 + 无退出逻辑 → 卡死。
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 症状:宜创群授权后的 hermes 回复 run 跑到一半,node 进程自己退出,
    keepalive 检测到重启,打断了正在进行的 run,回复没发出。
    
    根因推断:Node 26 默认遇未捕获的 Promise rejection 就退出进程。
    流式 callHermesRunStream + sendChain Promise 链路某处漏接 reject。
    
    修复:入口加 process.on('unhandledRejection') / ('uncaughtException')
    兜底,捕获后记日志([UNHANDLED REJECTION] / [UNCAUGHT EXCEPTION])
    保持进程存活,不再被 keepalive 打断。下次复现能从日志看到真正的错误栈。
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • - 角色(机器人本体 / owner)、门禁判定逻辑、授权交互(同意/拒绝)
    - .whitelist.json 格式 + 手动加白名单方法
    - 引用回复授权的坑(type 57 appmsg,title + refermsg 提取)
    - 相关方法表
    - TL;DR 加 step 0 白名单门禁;避雷清单加引用回复授权条目
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 症状:"同意 宜创AI全员群" 用引用回复发出后没被识别,pending 没清。
    
    根因:WeChat 引用回复存成 type=57 appmsg XML,真正的回复文本在
    <title> 里、被引用的原消息在 <refermsg> 里。parseApprovalCommand
    拿到整段 XML 匹配不上 ^(同意|...) 正则。
    
    修复:
    - parseApprovalCommand 检测 <appmsg>/<refermsg>,提取 <title> 作为
      回复文本(指令动词),提取 <refermsg> 作为 quotedContext
    - handleApproval 目标匹配优先级:显式 target → quotedContext 里出现的
      pending displayName(引用哪条授权请求就同意哪个)→ 最近一个 pending
    
    实测:引用回复"同意"(refermsg 含"宜创")→ [AUTH] Approved 宜创AI全员群
    → 加白名单 + 自动回复 pending 的 "@小火子 在吗"
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 需求:不再对任何人自动回复,只有白名单的群/私聊才自动回;
    非白名单的发信人,先转给主人(owner)请示是否授权。
    
    实现:
    - WhitelistStore(持久化 .whitelist.json):allowed Set + pending dict
    - CONFIG.ownerWxid / ownerDisplayName(默认 yibooo / 火星人,env 可覆盖)
    - owner 永远在白名单(constructor 自动加)
    
    handleMessage 门禁逻辑(replyType 判定后):
    - 发送者身份 senderWxid:私聊=talker,群聊=content 前缀里的 senderWxid
    - owner 自己发的(私聊/群@):先 parseApprovalCommand 看是不是授权指令,
      否则放行
    - 非 owner 且 talker 不在白名单:requestAuthorization 转给 owner,不回复
    - 白名单内:generateAndSendReply(原流程,抽成独立方法)
    
    授权流程:
    - requestAuthorization: 给 owner 发「【授权请求】X 发来:...,回复『同意』授权」,
      记入 pending(同一 talker 不重复请示)
    - parseApprovalCommand: 解析「同意/授权/批准/approve」「拒绝/忽略/deny」+ 可选目标名
    - handleApproval: 按 wxid/昵称/senderName 匹配 pending(没指定取最近一个),
      同意→加白名单+回复 TA 刚才那条 pending 消息;拒绝→移除 pending
    - sendPlainTo: 给 owner 发纯文本(不走 hermes),复用 A11y broadcast
    
    重构:把回复生成从 handleMessage 抽成 generateAndSendReply({talker,content,
    msgId,replyType,senderName,raw}),授权通过后可复用(回复 pending 消息时 msgId=null)
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 代码(server/services/wechat/auto-reply.js):
    - 切到 /v1/runs + GET /events SSE 长连接替代 /v1/chat/completions 同步阻塞
    - 监听新事件:
        message.complete  → 主路径多步消息(每 turn 末尾的可见 content)
        message.delta     → content tokens 兜底
        reasoning.delta   → 默认丢弃;STREAM_REASONING=true 时加 🧠 前缀
        reasoning.available → 同上
        tool.started      → flush 累积;STREAM_TOOL_PROGRESS=true 时发 🔧 进度
        run.completed     → 最终 output utterance + resolve
    - 串行 sendChain:第一条带 quote+@,后续仅文本;A11y 完成前不发下一条
    - 诊断日志节流(高频事件 5s 一次)
    - log() 去掉 fs.appendFileSync(sync I/O 在高频流式下会卡死 node)
    
    Hermes upstream 补丁(phone 上 ~/hermes-agent/gateway/platforms/api_server.py):
    - 加 _reasoning_cb + reasoning_callback wire-up(_create_agent 签名 / AIAgent 构造)
    - 加 _interim_cb + interim_assistant_callback wire-up(同上)
    - /v1/runs 启动 agent 时把两个新回调都传进去
    - 发现:原 hermes /v1/runs 默认只挂 stream_delta_callback + tool_progress_callback,
      漏了 gateway 平台用的另外几个关键回调,导致 /v1/runs 客户端只能收到最终一条回复
    
    实测 Kimi 限制:
    - Kimi-k2.6 在 hermes agentic mode 下 assistant_message.content 永远空
    - 所有内容在 reasoning_content → interim_assistant_callback 因 content 为空 return → 不 fire
    - 短期 workaround: STREAM_REASONING=true
    - 长期: prompt 工程 / SOUL.md 优化
    
    HANDOFF.md:
    - 新章节 §十一 A11y 显式状态机(5 状态 + classifier + dispatch)
    - 新章节 §十二 流式多步消息(SSE 事件表 + hermes 补丁说明 + Kimi 限制 + ENV flag 表)
    - 已知待办从 6 条扩到 9 条(新增 Kimi content 沉默、长消息分段、群消息合流、流式重试、多模型 fallback、SOUL.md 优化等)
    - 避雷清单加 4 条今天新踩的坑
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • 旧路径:/v1/chat/completions 同步阻塞拿完整 reply → 一条微信消息。
    长 agent loop 用户体验差("稍等" 之后再无下文)。
    
    新路径:
      1) POST /v1/runs { input, conversation_history, session_id, instructions }
         拿 run_id 立即返回
      2) GET /v1/runs/{run_id}/events SSE 长连接,消费事件:
         - message.delta { delta }  → 累积到 textBuf
         - tool.started             → flush(false): textBuf → onUtterance
         - run.completed { output } → flush(true): 最后一段
         - run.failed/cancelled     → reject
    
      3) onUtterance 在串行 Promise 队列(sendChain)里跑:
         - idx===0 的 utterance 带 quoteText + atSender(群里 @ 原消息)
         - 后续 idx>0 仅文本,不重复 quote/@
         - 发完后 await sleep(14000 或 9000) 等 A11y 跑完整一轮 chat-list-click
           → quote → @ → type → send,避免下一个 broadcast 打断当前 FSM 状态
    
    handleMessage:
      - 移除旧的 this.replyGenerator + sendTextMessageBroadcast 同步路径
      - 改成 callHermesRunStream(messages, sessionId, onUtterance)
      - state.markReplied 在 run 整体完成后才标记(避免中间步骤已发但失败时重发漏掉)
    
    诊断:增加 [STREAM EV] 日志统计事件类型 + textBuf 长度,
    方便诊断"agent loop 跑很久但没 utterance"等情况(实测发现
    深度探索时 hermes 几乎只调 tool 不发 text,多步分割效果依赖 LLM 行为)。
    
    keepalive.sh: HERMES_TIMEOUT 180s → 600s,长 agentic run 经常 3-5min。
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • - 群聊页 obn 标题带 (成员数) 后缀,equals 比对永远 false → strip 后再比
    - LauncherUI fragment 容器 + CLEAR_TASK 不重置内部 fragment → 状态机
      NO_CHAT_VIEW 优先点底部"微信"tab fallback BACK
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • - pgrep -f 在 su shell 下自匹配的 spawn 风暴 / 永远不重启二选一
    - start-ar.sh dash wrapper 触发 phone 上 hermes-snap hook 污染 *.js 末尾
    - HERMES_TIMEOUT 120s 在 turns=9 长 context 下偶发不够
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • #1 主进入路径改造(用户提议)— A11y 在不在目标聊天页时,
       优先扫主界面 chat list 直接点击进入,而不是走"搜索 → 点结果"。
       理由:搜索 "丁鹏、🍋火星人、火星人" 这种群名会被 WeChat 解读成
       "创建包含这 3 人的新群"建议,clickFirstSearchResult 误点进了
       MMCreateChatroomUI,然后 bkk 不出现,整条发送链路挂掉。
    
       新流程:
       - HermesAccessibilityService.enterChatViaListOrSearch:
         扫 root 下所有 ID_CHATLIST_NAME (kbq) 节点
         文本严格 equals pendingToUserDisplayName 时
         向上找 ID_CHATLIST_ROW (cj1) clickable 容器并 ACTION_CLICK
         等 2.2s 后检查 bkk 出现就 performTypeAndSend
       - 找不到匹配(chat 不在可见列表 / 不在 LauncherUI)回退到 performSearch
       - 实测:群在 chat list 第一/二行可定位,整段比搜索快 ~3s
    
    #2 @ 选人面板修复
       - auto-reply.js: senderName 是 wxid("yibooo"),但群 @ 面板显示
         的是昵称("火星人"),传 wxid 进 picker 永远找不到。改成
         getContactNickname(senderName) 翻译后再传给 broadcast 的 atSender。
       - HermesAccessibilityService.pickMentionMemberAndContinue:
         @ 失败 fallback 时旧 edit 节点引用已过期(IPC backed,600ms
         postDelayed 后失效),SET_TEXT 静默 false 整条发送挂掉。
         fallback 路径重新 getRootInActiveWindow + findInnerEditText
         拿 fresh edit 节点再 type 内容。
    
    实测(msgId 1556 type=57):
      ChatList scan: 10 kbq names → matched 丁鹏、小火子、火星人、延墨...
      Clicking row → Chat UI loaded
      Quote bubble @小火子 测试一下 → long-press → 引用
      @ mention typing → Clicking @ member: 火星人
      Append "延墨好!欢迎欢迎..." → Clicking send button
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 症状:群没有自定义名字时 rcontact.nickname 为空,getContactNickname
    fallback 返 wxid(如 "57100316212@chatroom"),A11y 拿这个去搜索框
    搜不到群,发送失败。
    
    修复:rcontact 没拿到 conRemark/nickname 时,对 @chatroom 后缀的
    wxid 额外查 chatroom.displayname,这是微信前端默认拼出来的群名
    (如 "丁鹏、🍋火星人、火星人"),可以被 A11y 搜索定位。
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 症状:群里 @小火子 发消息,auto-reply 完全没响应,日志里只有 [NOTIF]
    没有 [GROUP-AT],但 DB 里这条消息确实存在。
    
    根因:sqlcipher .mode line 输出多行 TEXT(如 message.content 是
    "yibooo:\n@小火子 在吗")时长这样:
    
        content = yibooo:
                  @小火子 在吗
        createTime = 1779591001000
    
    旧 _parseRows 看到第二行 "@小火子 在吗" 没有 ": " 或 " = " 分隔符,
    就直接 IGNORED,导致 content 被截断成 "yibooo:"。isAtMe("yibooo:")
    返回 false,replyType=null,handleMessage 静默 return(没 log)。
    
    修复:
    - 引入 KNOWN_COLS 白名单(msgId/type/isSend/talker/content/createTime),
      只在 key 是已知列名时才当作新字段,避免内容里碰巧带 "xxx = yyy"
      误判为新字段
    - 不匹配新字段的行作为上一字段 value 的续行追加(带 \n)
    - 添加单元测试式的语义注释解释 .mode line 输出格式
    
    unit test (synthetic input):
      input:  'content = yibooo:\n@小火子 在吗\ncreateTime = 1779...'
      before: { content: 'yibooo:', createTime: '1779...' }  ❌
      after:  { content: 'yibooo:\n@小火子 在吗', createTime: '1779...' }  ✅
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 新增功能:
    - HermesAccessibilityService 支持广播 extras quoteText / atSender
    - quoteText: 在聊天页找文本含此 substring 的 bkl 气泡 → 长按 → 点「引用」
      菜单项 → 输入回复文本 → 点发送
    - atSender: 在输入框先打「@」触发选人面板 → 找匹配成员点击 → 追加回复
    
    实测路径完整跑通(msgId 1409 验证):
    1. 广播触发 A11y service
    2. 进入聊天页("already in correct chat" 或经过搜索)
    3. findBubbleByPartialText 在 bkl 列表里倒序匹配
    4. dispatchGesture 派发 800ms 合成长按手势(ACTION_LONG_CLICK 在 WeChat
       GestureDetector 实现上不触发菜单,必须用 gesture 派发)
    5. 等 1.2s 后用 findClickableByTextAllWindows 搜「引用」 — 关键:菜单在
       popup window 不在 active window,必须遍历 getWindows() 才能找到
    6. 点击引用 → 引用 chip 注入到输入框 → 写文本 → 点发送
    7. DB 写入的 message 是 type=57 (微信内部引用回复消息类型)
    
    关键依赖(Android AccessibilityService):
    - accessibility_service_config.xml 必须设 canPerformGestures="true"
      否则 dispatchGesture 不回调 onCompleted
    
    群聊路径:
    - auto-reply.js handleMessage 在 replyType=group-at/group-quote 时自动
      从触发消息 content 解析 bubbleText 当 quoteText,senderName 当 atSender
    - android-wechat-sender.js sendTextMessageBroadcast 接受 options.quoteText
      / options.atSender,拼到 am broadcast --es 参数里
    
    测试覆盖(在 Pixel 实测):
    - ✅ 私聊回归:plain text 路径不影响(msgId 1408)
    - ✅ 引用回复:私聊场景已验证(msgId 1409 type=57)
    - ⏸ 群聊 + @ 群成员:代码已写完,需要真实群消息触发才能测
    
    APK 已重编 + 用 debug.keystore 签 + adb install -r 升级(A11y 权限保留)。
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • #17 群昵称查询:
    - android-wechat-tracker-sqlcipher.js:getContactNickname 删掉
      if (wxid.endsWith('@chatroom')) return wxid 的早退分支
    - 群(xxxx@chatroom)和私聊都走同一 rcontact 表查询,nickname 就是群名
    - A11y 现在可以用群名搜索进入群聊
    - 在 Pixel 已部署并重启 auto-reply (PID 18678)
    
    #18 wake-lock + keepalive 重写:
    - 通过 am startservice com.termux.service_wake_lock 启用 wake-lock
      (确认 dumpsys power 已持有 'termux:service-wakelock')
    - 重写 keepalive.sh 对齐当前部署:
      - 守护 4 项:wake-lock / A11y service / hermes gateway / auto-reply
      - hermes 走 termux native gateway 而不是 proot-distro Ubuntu(已淘汰)
      - 健康检查端口 8642(不再是 3010)
      - A11y 被关掉时自动 settings put secure 重新启用
    - 在 Pixel 已部署到 ~/hermes-wechat/keepalive.sh
    
    #16 残留清理(仅 phone 操作,无 git 文件):
    - pkill -9 frida-server + rm -rf ~/.frida_queue/
    - ~/.hermes/auth.json 删掉 credential_pool['kimi-coding'] 老 sk-kimi 条目
      保留 anthropic + kimi-coding-cn
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 包含:
    - 运行中进程清单(auto-reply / hermes gateway / A11y service)
    - phone 路径 ↔ Mac repo 路径对照表
    - 启停 / A11y 校验 / 日志查看 / 手动测试发送命令速查
    - Clash mixed proxy 网络拓扑陷阱(Termux UID 被 VPN 排除,必须走 7890)
    - 当前 hermes 后端配置(Moonshot CN + kimi-k2.6)
    - 两种接手开发流程(phone 上 clone repo / 手动 scp)
    - 已知待办(群昵称查询、引用@回复、wake-lock、清理)
    - 避雷清单(前人踩过的所有坑)
    
    同步到 phone: /data/data/com.termux/files/home/hermes-wechat/HANDOFF.md
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 把 6 步链路接到唯一可用的发送路径(HermesAccessibilityService):
      1. termux-notification-list 监听通知(息屏兜底)
      2. input keyevent 224 点亮屏幕 + wm dismiss-keyguard 解锁
      3. am start LauncherUI 切微信前台
      4. sqlcipher 轮询 EnMicroMsg.db 拉新消息 + 最近 N 条上下文
      5. 调 hermes-agent /v1/chat/completions 生成回复
      6. am broadcast com.hermes.wechat.SEND_MSG → A11y UI 自动化发送
    
    关键改动:
    - android-wechat-sender.js
      - sendTextMessageBroadcast 增加 options.toUserDisplayName(A11y 搜索必传)
      - 新增 checkAccessibilityServiceEnabled / enableAccessibilityService
    - android-wechat-tracker-sqlcipher.js
      - 新增 getContactNickname(wxid):查 rcontact 拿 conRemark/nickname,
        带 filehelper/weixin 等内置 helper 的硬编码 fallback
      - 新增 fetchHistory(talker, limit):拉最近 N 条文本消息正序返回
    - auto-reply.js
      - 移除已废弃的 sendTextMessageFrida
      - 启动时校验 A11y service,未启用则尝试自动启用并打印 manual fix 命令
      - handleMessage 中:先 getContactNickname 拿 displayName、再 fetchHistory
        给 hermes 做多轮上下文,最后用 broadcast + displayName 发送
      - buildHermesMessages 重写:自动把历史按 isSend 转 user/assistant 多轮
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 通过 DB 实证与 LSPosed 日志对比定位到:
    
    - LSPosed `HermesWeChatModule.sendTextMessageDirect` 在 WeChat 8.0.72 上
      返回 `Send result: true` 后,主线程异步 NetScene 提交触发 NPE 被 swallow,
      消息**不进 message 表**。这是误报。
    - Frida daemon 队列方案(`android-wechat-sender-frida.js`)在 Frida 17 +
      微信 8.0.72 上注入会触发反调试,进程崩溃,无法生产使用。
    - 实际可用路径是 `HermesAccessibilityService`(lsposed-wechat-sendmsg 模块内):
      监听同一广播 `com.hermes.wechat.SEND_MSG`,纯 UI 自动化点击微信完成发送。
    - A11y service 易被系统关闭(`enabled_accessibility_services = null`),
      这是消息发送失败的最常见根因,docs 中加上启用 / 验证命令。
    
    更新范围:
    - CLAUDE.md
    - server/services/wechat/README.md, SKILL.md
    - server/services/wechat/android-wechat-sender-frida.js(标注废弃)
    - wechat_plain/scripts/local-testkit/docs/README.md, HERMES_WECHAT_ARCHITECTURE.md
    - wechat_plain/scripts/lsposed-wechat-sendmsg/README.md
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • - HERMES_WECHAT_ARCHITECTURE.md: v2.0 → v3.0, rewrite for Node.js plugin
    - README.md: update architecture diagram, file list, API endpoint, env vars
    - server/services/wechat/README.md: add sqlcipher tracker + frida sender
    - SKILL.md: add Frida daemon and SQLCipher tracker usage
    - wechat-auto-reply-status.md: mark SQLCipher + Frida as verified, update plans
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • - auto-reply.js 接入 hermes-agent OpenAI-compatible API (:8642)
    - 新增 android-wechat-tracker-sqlcipher.js 替代 LSPosed 日志 tail
    - 新增 android-wechat-sender-frida.js 替代 am broadcast 发送
    - 更新 keepalive.sh、README、HermesWeChatModule.java
    - 添加 .gitignore SQLite 变体规则
    - 新增 docs: 息屏发送尝试记录与屏幕自动唤醒方案
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • - auto-reply.js: connect to hermes /api/master/chat for AI reply generation
      - SSE response parsing, session isolation per talker
      - notification watcher (termux-notification-list) for screen-off fallback
      - wechat foreground check + wake before send
    - hermes_wechat_daemon.py: prefer hermes API, fallback to direct LLM API
    - config.json: add hermes host/port for local API connection
    - keepalive.sh: termux-wake-lock + process guardian for hermes + auto-reply
    - Move docs into local-testkit/docs/ directory
    - .gitignore: exclude server/db/
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • Mac side (lemonPCRM):
    - Express server with /api/master/chat SSE endpoint
    - MasterSession multi-round orchestration
    - AgentManager + ToolRegistry
    - ClaudeCliProtocol ACP implementation
    
    Pixel side (wechat_plain/scripts/):
    - hermes_wechat_daemon.py: standalone daemon
      - SQLCipher DB decryption with WAL checkpoint fix
      - Direct LLM API calls (Kimi/Anthropic/OpenAI)
      - am broadcast to trigger LSPosed send
      - Auto wake WeChat to foreground before poll/send
    - lsposed-wechat-sendmsg: LSPosed module
      - sendTextMessageDirect via WeChat internal API
      - u01.r1.a -> builder -> m1.a() full chain
      - Background thread NetScene submission
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »