From 9ef5c1692841c1afcd2d7e7a08a168364044098b Mon Sep 17 00:00:00 2001 From: Gyanu Mayank Date: Fri, 11 Sep 2026 18:46:30 +0530 Subject: [PATCH] Follow the browser's real active tab in web_scan. The bridge now reports tab activation, and web_scan uses that instead of the last tab the agent operated on. --- TMWebDriver.py | 11 ++++++++- assets/tmwd_cdp_bridge/background.js | 8 +++++-- frontends/tests/test_web_scan_active_tab.py | 25 +++++++++++++++++++++ ga.py | 2 ++ 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 frontends/tests/test_web_scan_active_tab.py diff --git a/TMWebDriver.py b/TMWebDriver.py index 8683e5019..d30d9a8fc 100644 --- a/TMWebDriver.py +++ b/TMWebDriver.py @@ -143,10 +143,19 @@ def handle(self) -> None: sess.mark_disconnected() for tab in tabs: session_id = str(tab['id']) - session_info = {'url': tab.get('url'), 'title': tab.get('title', ''), 'connected_at': time.time(), 'type': 'ext_ws'} + session_info = { + 'url': tab.get('url'), + 'title': tab.get('title', ''), + 'connected_at': time.time(), + 'type': 'ext_ws', + 'active': bool(tab.get('active')), + 'windowId': tab.get('windowId'), + } sess = driver.sessions.get(session_id) if sess and sess.is_active(): sess.info = session_info else: driver._register_client(session_id, self, session_info) + if tab.get('active'): + driver.default_session_id = session_id elif data.get('type') == 'ack': driver.acks[data.get('id','')] = True elif data.get('type') == 'result': driver.results[data.get('id')] = {'success': True, 'data': data.get('result'), 'newTabs': data.get('newTabs', [])} diff --git a/assets/tmwd_cdp_bridge/background.js b/assets/tmwd_cdp_bridge/background.js index 1ef32695b..b3528a6bd 100644 --- a/assets/tmwd_cdp_bridge/background.js +++ b/assets/tmwd_cdp_bridge/background.js @@ -357,7 +357,7 @@ function connectWS() { const tabs = (await chrome.tabs.query({})).filter(t => isScriptable(t.url)); ws.send(JSON.stringify({ type: 'ext_ready', - tabs: tabs.map(t => ({ id: t.id, url: t.url, title: t.title })) + tabs: tabs.map(t => ({ id: t.id, url: t.url, title: t.title, active: !!t.active, windowId: t.windowId })) })); console.log('[TMWD-WS] Sent ext_ready with', tabs.length, 'tabs'); }; @@ -412,7 +412,7 @@ async function sendTabsUpdate() { const tabs = (await chrome.tabs.query({})).filter(t => isScriptable(t.url) && !/streamlit/i.test(t.title)); ws.send(JSON.stringify({ type: 'tabs_update', - tabs: tabs.map(t => ({ id: t.id, url: t.url, title: t.title })) + tabs: tabs.map(t => ({ id: t.id, url: t.url, title: t.title, active: !!t.active, windowId: t.windowId })) })); } chrome.tabs.onUpdated.addListener((_, changeInfo) => { @@ -420,3 +420,7 @@ chrome.tabs.onUpdated.addListener((_, changeInfo) => { }); chrome.tabs.onRemoved.addListener(() => sendTabsUpdate()); chrome.tabs.onCreated.addListener(() => sendTabsUpdate()); +chrome.tabs.onActivated.addListener(() => sendTabsUpdate()); +if (chrome.windows && chrome.windows.onFocusChanged) { + chrome.windows.onFocusChanged.addListener(() => sendTabsUpdate()); +} diff --git a/frontends/tests/test_web_scan_active_tab.py b/frontends/tests/test_web_scan_active_tab.py new file mode 100644 index 000000000..da3320791 --- /dev/null +++ b/frontends/tests/test_web_scan_active_tab.py @@ -0,0 +1,25 @@ +"""web_scan should follow the browser's real active tab, not the last operated one.""" +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent.parent +BRIDGE = (ROOT / "assets" / "tmwd_cdp_bridge" / "background.js").read_text(encoding="utf-8") +DRIVER = (ROOT / "TMWebDriver.py").read_text(encoding="utf-8") +GA = (ROOT / "ga.py").read_text(encoding="utf-8") + + +def test_bridge_pushes_active_and_listens_for_tab_activation(): + assert "active: !!t.active" in BRIDGE + assert "chrome.tabs.onActivated.addListener" in BRIDGE + assert "chrome.windows.onFocusChanged.addListener" in BRIDGE + + +def test_driver_promotes_active_tab_to_default_session(): + assert "if tab.get('active'):" in DRIVER + assert "driver.default_session_id = session_id" in DRIVER + assert "'active': bool(tab.get('active'))" in DRIVER + + +def test_web_scan_marks_the_active_tab(): + assert "sess['active'] = str(sess.get('id')) == str(driver.default_session_id)" in GA diff --git a/ga.py b/ga.py index e1ee909f2..f7907560a 100644 --- a/ga.py +++ b/ga.py @@ -132,6 +132,8 @@ def web_scan(tabs_only=False, switch_tab_id=None, text_only=False, maxlen=35000) sess['url'] = sess.get('url', '')[:50] + ("..." if len(sess.get('url', '')) > 50 else "") tabs.append(sess) if switch_tab_id: driver.default_session_id = switch_tab_id + for sess in tabs: + sess['active'] = str(sess.get('id')) == str(driver.default_session_id) result = { "status": "success", "metadata": {