From c57a05963a1fe2e1b7b7263dc1875bcc00cb0b41 Mon Sep 17 00:00:00 2001 From: operator-main Date: Sun, 4 Oct 2026 03:19:48 +0000 Subject: [PATCH] Fix sidechat CDP and button selectors\n\n- sidechat_manager._ev: use id=1 and returnByValue to match ev()\n- cmd_sidechat_create: find + button via Side chats header proximity\n- cmd_sidechat_create: use cmd_send for initial message (not DOM hack)\n- Integrate ensure_sidebar with retry --- bin/muse-chat-api.py | 54 ++++++++++++++++++++++++++--------------- bin/sidechat_manager.py | 6 ++--- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/bin/muse-chat-api.py b/bin/muse-chat-api.py index 78ce144..36dffc8 100755 --- a/bin/muse-chat-api.py +++ b/bin/muse-chat-api.py @@ -279,16 +279,40 @@ def cmd_sidechat_create(ws): if (btn) btn.click(); })()""") _time.sleep(2) + # The + button sits next to the "Side chats" header (see screenshot 2026-10-04). + # It's not labeled "New side chat" - find it by proximity to the header text. result = ev(ws, """(() => { - const btn = [...document.querySelectorAll('button')].find(b => + // Strategy 1: Find button with "+" text near "Side chats" + const header = [...document.querySelectorAll('*')].find(el => + el.textContent.trim() === 'Side chats' && el.children.length === 0); + if (header) { + // Look for + button in the header's parent or siblings + const parent = header.parentElement; + if (parent) { + const plus = [...parent.querySelectorAll('button')].find(b => + (b.textContent||'').includes('+') || (b.getAttribute('aria-label')||'').toLowerCase().includes('add')); + if (plus) { plus.click(); return 'CLICKED_VIA_HEADER'; } + } + } + // Strategy 2: Find any button with "+" that's near the top + const plusBtn = [...document.querySelectorAll('button')].find(b => { + const t = (b.textContent||'').trim(); + const al = (b.getAttribute('aria-label')||'').toLowerCase(); + return t === '+' || al.includes('new side') || al.includes('add chat'); + }); + if (plusBtn) { plusBtn.click(); return 'CLICKED_VIA_PLUS'; } + // Strategy 3 (fallback): old aria-label exact match + const oldBtn = [...document.querySelectorAll('button')].find(b => (b.getAttribute('aria-label')||'') === 'New side chat'); - if (!btn) return 'NOT_FOUND'; - btn.click(); - return 'CLICKED'; + if (oldBtn) { oldBtn.click(); return 'CLICKED_VIA_ARIA'; } + return 'NOT_FOUND'; })()""") if result == 'NOT_FOUND': - print("ERROR: 'New side chat' button not found (sidebar may be closed)", file=sys.stderr) + print("ERROR: New side chat + button not found (sidebar may be closed)", file=sys.stderr) sys.exit(1) + # Log which strategy worked for debugging + import sys as _sys + print(f"Sidechat create: {result}", file=_sys.stderr) import time as _time # Poll for URL to change to a thread URL (up to 15s) # Fixed 2026-10-04: was sleeping 5s and reading once, often captured @@ -306,21 +330,11 @@ def cmd_sidechat_create(ws): # the first message. Send a system message to trigger ID assignment, # then re-read the URL. if url.endswith("/thread/new"): - ev(ws, """(() => { - const input = document.querySelector('[contenteditable="true"]'); - if (!input) return 'NO_INPUT'; - input.focus(); - document.execCommand('insertText', false, '[System: Side chat created]'); - return 'READY'; - })()""") - _time.sleep(1) - # Click send - ev(ws, """(() => { - const btn = [...document.querySelectorAll('button')].find(b => - (b.getAttribute('aria-label')||'').toLowerCase().includes('send')); - if (btn) { btn.click(); return 'SENT'; } - return 'NO_BTN'; - })()""") + # Use proven cmd_send to trigger real thread ID + try: + cmd_send(ws, "[System: Side chat created]") + except Exception as e: + print(f"Warning: initial send failed: {e}", file=sys.stderr) # Poll for URL to change from /thread/new to /thread/ for i in range(15): _time.sleep(1) diff --git a/bin/sidechat_manager.py b/bin/sidechat_manager.py index fb6b548..fae6ab8 100644 --- a/bin/sidechat_manager.py +++ b/bin/sidechat_manager.py @@ -15,13 +15,13 @@ import json def _ev(ws, js, await_result=True): """Evaluate JS via CDP, return result. - Uses id=1 to match muse-chat-api.py's ev() on shared websocket. - Fixed 2026-10-04: was using id=100, causing response mismatch. + Matches muse-chat-api.py's ev() exactly (id=1, returnByValue). + Fixed 2026-10-04: was using id=100, missing returnByValue. """ import json as _json ws.send(_json.dumps({ "id": 1, "method": "Runtime.evaluate", - "params": {"expression": js, "awaitPromise": True} + "params": {"expression": js, "returnByValue": True, "awaitPromise": await_result} })) resp = _json.loads(ws.recv()) result = resp.get("result", {}).get("result", {})