Skip to content

fix(chat-select): bounded non-blocking reads and shared composer finder - #170

Open
longXboy wants to merge 2 commits into
thisnick:mainfrom
longXboy:fix/chat-select-timeout-and-composer-helpers
Open

fix(chat-select): bounded non-blocking reads and shared composer finder#170
longXboy wants to merge 2 commits into
thisnick:mainfrom
longXboy:fix/chat-select-timeout-and-composer-helpers

Conversation

@longXboy

Copy link
Copy Markdown

Summary

Fixes two reliability issues in wx send and deduplicates the ghost-frame composer logic:

  1. chat-select.py could hang past its deadline. The read loops used blocking
    proc.stdout.readline(), so when the frida child stayed alive but silent (e.g. a hook
    that never fires), the time-based guard never got a chance to run and wx send timed
    out. Replaced all three loops with a select()-based read_lines_until() that reads
    the raw fd non-blocking, so the wall-clock deadline holds even for partial lines.

  2. Clicking an already-selected chat never fires the hook. Clicking the currently
    selected chat list item does not trigger selectSession(), so with force=True the
    frida hook waited out its deadline and reported a false "Hook did not fire" failure.
    The already-selected short-circuit now applies unconditionally — current_sel is
    freshly read on every invocation, so there is no stale state for force to override.

  3. Shared composer finder. Moved the ghost-frame-aware edit+send pair finder from
    send_message.rs into ia::helpers and reused it in chat_open.rs, which still had
    the naive take-first-pair logic and could pick a stale detached-chat composer. Ranking
    is now a lexicographic min_by_key (focused > send-enabled > main frame > DFS order),
    and the main-frame check accepts both "Weixin" and "WeChat" frame names.

With (2) making the skip safe, send_message now always runs chat-select even in state
"chat" — the open chat may not be the target, and chat-select short-circuits when it is.

Test plan

  • wx send to a chat that is already selected (previously: false "Hook did not fire")
  • wx send to a different chat while another chat is open
  • wx send with a leftover detached/ghost chat window in the a11y tree
  • cargo test in packages/agent-server-rust

longXboy and others added 2 commits June 7, 2026 13:58
WeChat's accessibility tree can contain multiple edit+send pairs: the
live main-window composer plus stale "ghost" frames left behind by chats
previously detached into separate windows. The old depth-first "take the
first pair" logic grabbed the wrong (ghost) composer, whose input never
received text and whose Send button stayed DISABLED forever, causing the
plan to loop and ultimately fail with "No action selected".

Collect every candidate edit+send pair and rank them so the genuinely
active composer wins:
  1. editable currently FOCUSED        (strongest signal)
  2. Send button NOT disabled          (composer already has text)
  3. pair under the main "Weixin" frame (not a ghost/detached frame)
DFS order breaks any remaining ties.

This makes 'wx messages send' reliable even when stale frames are present,
removing the need to restart the container to clear ghost windows.
- chat-select.py: replace blocking readline() loops with a select()-based
  read_lines_until() so deadlines hold even when the frida child stays
  alive but silent; short-circuit when the target chat is already
  selected regardless of force, since clicking the selected item never
  fires the selectSession hook
- move the ghost-frame-aware edit+send composer finder from
  send_message.rs into ia::helpers and reuse it in chat_open.rs, ranking
  candidates lexicographically (focused > send-enabled > main frame)
- send_message: always run chat-select even in "chat" state; the open
  chat may not be the target and chat-select skips when it already is

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant