diff --git a/backend/apps/agents/browser/browser_agent.py b/backend/apps/agents/browser/browser_agent.py index f46dac43..957e4831 100644 --- a/backend/apps/agents/browser/browser_agent.py +++ b/backend/apps/agents/browser/browser_agent.py @@ -2015,7 +2015,7 @@ async def run_browser_agent( DEAD_CARDS.add(browser_id) logger.info(f"[browser-agent] {browser_id} marked dead; same-host reuse will skip it") # Tear the wedged webview DOWN now, before recovery spawns a fresh card. Two heavy pages (the dead one + the recovery one) starve the renderer's event loop = the recovery-card wedge; unmounting the dead one frees its renderer so the recovery card is the only heavy neighbor. - await p_evict_dead_card(dashboard_id, browser_id) + await evict_dead_card(dashboard_id, browser_id) break if cancel_event.is_set(): @@ -2263,7 +2263,7 @@ def find_reusable_card(dashboard_id: str, url: str, parent_session_id: str | Non P_EVICT_SETTLE_S = 1.5 -async def p_evict_dead_card(dashboard_id: str | None, browser_id: str) -> None: +async def evict_dead_card(dashboard_id: str | None, browser_id: str) -> None: """Free a wedged card's webview so the recovery card isn't its heavy neighbor: tell the renderer to unmount it (frees the renderer process), drop it from the persisted layout, and WAIT for teardown before the caller spawns the recovery card. Fail-open, never raises into the diff --git a/backend/tests/test_deadcard_evict.py b/backend/tests/test_deadcard_evict.py index c48af909..898d11e7 100644 --- a/backend/tests/test_deadcard_evict.py +++ b/backend/tests/test_deadcard_evict.py @@ -1,6 +1,6 @@ """The recovery-card wedge fix: when a card is declared dead, its webview must be torn down (renderer unmount + layout removal) BEFORE recovery spawns a fresh card, -so two heavy pages never co-exist and starve the renderer. Pins p_evict_dead_card.""" +so two heavy pages never co-exist and starve the renderer. Pins evict_dead_card.""" import asyncio import backend.apps.agents.browser.browser_agent as ba @@ -36,7 +36,7 @@ def p_patch(monkeypatch, cards): def test_evict_broadcasts_unmount_and_removes_from_layout(monkeypatch): broadcasts, saved, dash = p_patch(monkeypatch, {"browser-dead": FakeCard("sess-1"), "browser-keep": FakeCard("sess-1")}) ba.ACTIVE_AGENT_CARDS.add("browser-dead") - asyncio.run(ba.p_evict_dead_card("dash-1", "browser-dead")) + asyncio.run(ba.evict_dead_card("dash-1", "browser-dead")) # the renderer is told to unmount exactly the dead card assert ("dashboard:browser_card_evict", {"dashboard_id": "dash-1", "browser_id": "browser-dead"}) in broadcasts # it's gone from the persisted layout, its neighbor is untouched @@ -50,7 +50,7 @@ def test_evict_without_a_dashboard_deletes_nothing(monkeypatch): # No dashboard = ownership unverifiable = fail SAFE: never unmount or delete # what might be the user's card; the reuse-skip alone handles it. broadcasts, saved, _ = p_patch(monkeypatch, {}) - asyncio.run(ba.p_evict_dead_card("", "browser-x")) + asyncio.run(ba.evict_dead_card("", "browser-x")) assert not broadcasts and not saved @@ -63,6 +63,6 @@ def test_user_card_is_never_evicted(monkeypatch): """A wedged USER card (no spawned_by) must never be deleted out from under the user; reuse-skip is the whole remedy. Only agent-spawned cards evict.""" broadcasts, saved, dash = p_patch(monkeypatch, {"browser-user": FakeCard(None)}) - asyncio.run(ba.p_evict_dead_card("dash-1", "browser-user")) + asyncio.run(ba.evict_dead_card("dash-1", "browser-user")) assert not broadcasts and not saved assert "browser-user" in dash.layout.browser_cards diff --git a/backend/tests/test_spawn_agent.py b/backend/tests/test_spawn_agent.py index 9ab47e35..54550eed 100644 --- a/backend/tests/test_spawn_agent.py +++ b/backend/tests/test_spawn_agent.py @@ -101,8 +101,9 @@ def test_spawn_server_schema_is_prompt_plus_background_only() -> None: def test_spawn_server_speaks_mcp_stdio() -> None: + from backend.apps.agents import spawn_agent_mcp_server as srv proc = subprocess.Popen( - [sys.executable, "backend/apps/agents/spawn_agent_mcp_server.py"], + [sys.executable, srv.__file__], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True, ) try: diff --git a/frontend/src/app/pages/Tools/Tools.tsx b/frontend/src/app/pages/Tools/Tools.tsx index 89499e43..ef2518b1 100644 --- a/frontend/src/app/pages/Tools/Tools.tsx +++ b/frontend/src/app/pages/Tools/Tools.tsx @@ -179,7 +179,6 @@ const Tools: React.FC = () => { -