Replace the 3-separate-_cdp_call() approach (one WS connection per
message) with a single _cdp_coordinate_click_async() coroutine that
opens the WebSocket once and sequences all CDP messages on it:
1. Target.getTargets
2. Target.attachToTarget (if page target found)
3. Input.dispatchMouseEvent (mousePressed) } pipelined — both sent
4. Input.dispatchMouseEvent (mouseReleased) } before awaiting either
Benchmark vs real Lightpanda WS at ws://127.0.0.1:63372/ (300 iters):
Baseline (current main, 3 connections): 3.14ms mean, 2.97ms median
Optimized (this commit, 1 connection): 1.30ms mean, 1.11ms median
Speedup: 2.42x mean, 2.68x median, 1.62x p95
The savings come entirely from eliminating 2 TCP+WS handshakes.
mousePressed + mouseReleased are pipelined on the same connection,
so they travel in the same network burst.
21/21 tests pass.
Add optional x/y parameters to browser_click for viewport-coordinate
clicking via CDP Input.dispatchMouseEvent. When coordinates are provided,
clicks are dispatched at the browser compositor level — Chrome does its own
hit-testing, bypassing DOM selectors entirely.
Use cases where ref-based click fails but coordinate click works:
- Cross-origin iframes (OOPIFs)
- Closed shadow DOM
- Canvas/WebGL elements
- Dynamic overlays where the snapshot may be stale
Implementation:
- CDP path (preferred): Input.dispatchMouseEvent via WebSocket
(Target.getTargets + mousePressed + mouseReleased)
- agent-browser fallback: mouse move/down/up when no CDP endpoint available
- ref is no longer required — either ref OR x+y must be provided
Benchmark (real Lightpanda WS at ws://127.0.0.1:63372, 200 iterations):
CDP coord click: 3.71ms mean (2.97ms median, 2.61ms min, 7.01ms p95)
Single WS conn baseline: 1.57ms mean (cost per connection open+call)
agent-browser IPC: 0.20ms mean per HTTP call
The 3.71ms per CDP click comes from 3 sequential fresh WS connections
(pre-existing architecture in browser_cdp_tool.py). A persistent WS
connection pool would bring this to ~3.1ms (just the 2 mouse events).
Both paths are well under the 100ms human perception threshold.
Files:
- tools/browser_tool.py: schema update (x/y, ref no longer required),
_cdp_coordinate_click(), _coordinate_click_via_agent_browser(),
updated browser_click() with validation and dispatch
- tests/tools/test_browser_coordinate_click.py: 21 tests covering
validation, CDP path, fallback path, ref preservation, schema, registry
- scripts/benchmark_click_paths.py: real-browser latency benchmark
- Remove dead metadata.get('reply_to') fallback in _send_raw_message;
nothing in the codebase ever sets 'reply_to' inside a metadata dict —
the key only appears as a top-level send_voice() keyword argument
- Simplify _status_thread_metadata construction in run.py to use a
single dict literal instead of create-then-mutate pattern; the
or-{} guard was dead since source.thread_id implies _progress_thread_id
is also set for Feishu
- Add yuqian@zmetasoft.com to AUTHOR_MAP for contributor attribution
Follow-up to the salvaged fix for /goal ENAMETOOLONG drop — adds
AUTHOR_MAP entry so the release script resolves the commit author to
the correct GitHub user.
- Fix /compact → /compress in context-overflow tips (closes#20020)
- Evict cached agent after session hygiene and /compress so system
prompt refreshes with current SOUL.md, memory, and skills
- Restore memory authority across compaction: change 'informational
background data' to 'authoritative reference data' in memory block
and SUMMARY_PREFIX, with backward-compatible regex
Based on:
- PR #20027 by @LeonSGP43
- PR #18767 by @MacroAnarchy
- PR #17380 by @vominh1919
PR #17121 boundary marker fix already merged to main (2eef395e1).
PR #9262 user-message anchoring already on main via _ensure_last_user_message_in_tail().
Salvage follow-up for PR #20344:
- AUTHOR_MAP entry for rob-maron (required by CI)
- 17 parametrized tests covering _is_arcee_trinity_thinking,
_fixed_temperature_for_model Trinity override, and
_compression_threshold_for_model, including sibling-model negatives
(trinity-large-preview, trinity-mini) and the OpenRouter slug form.
WhatsApp bridge (bridge.js) only sets ptt:true when file extension is .ogg
or .opus, causing mp3/wav files (from Edge TTS, NeuTTS, etc.) to arrive
as file attachments instead of voice bubbles — silently, with no error.
Fix: when audio type is sent with a non-ogg/opus format, run ffmpeg
conversion to ogg/opus in a temp file before sending. This makes
send_voice() self-sufficient regardless of what format the caller provides.
Fallback: if ffmpeg is unavailable, original buffer is sent (previous
behaviour) with a console.warn — no crash.
Addresses veloguardian's review comment on PR #4992.
After PR #13725 replaced the module-level _LOCK_DIR/_LOCK_FILE constants
with a dynamic _get_lock_paths() helper, the xdist-isolation fixture
needs to patch the function instead of the removed constants.