mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 20:27:44 +02:00
[eric] browser: send-script surface-gate = fire only on full-page /messaging/ composers, decline the profile overlay (live A/B r246-r251: firing on the overlay ran 4x slower + 1/3 vs 3/3 delivery, net-negative; only /messaging/thread/ has a listable Send)
This commit is contained in:
@@ -1222,7 +1222,7 @@ async def run_browser_agent(
|
||||
p_script = await asyncio.wait_for(browser_send_script.run_send_script(
|
||||
task, browser_id, tab_id, preloaded_perception,
|
||||
execute_browser_tool, send_index_in_state, payload_in_textbox,
|
||||
payload_source=user_prompt,
|
||||
payload_source=user_prompt, current_url=current_url,
|
||||
), timeout=30.0)
|
||||
except Exception as p_se:
|
||||
logger.info(f"[browser-sendscript] outer skip ({p_se})")
|
||||
|
||||
@@ -71,6 +71,22 @@ def composer_index_in_state(state_text: str):
|
||||
return hits[0] if len(hits) == 1 else None
|
||||
|
||||
|
||||
# Surfaces where a filled composer reliably exposes a detectable Send button. The
|
||||
# live A/B (r246-r251) proved the profile docked-overlay composer is the opposite:
|
||||
# the script fills it, LinkedIn's overlay Send never lands in the interactives list,
|
||||
# the script aborts, and the half-staged handoff + recovery path ran 4x SLOWER and
|
||||
# less reliable than a clean model run. So the script fires ONLY on a full-page
|
||||
# composer surface; anywhere else it declines UNTOUCHED (no fill) and the model owns it.
|
||||
P_COMPOSER_PAGE_RE = re.compile(r"/messaging/(thread|compose)/", re.I)
|
||||
|
||||
|
||||
def surface_supports_script(current_url: str) -> bool:
|
||||
"""A full-page messaging composer (Send renders as a real listed button), not
|
||||
the profile /in/ docked overlay (Send is a lazy, unlisted control). Empty URL
|
||||
is unknown -> decline, since firing on the wrong surface is net-negative."""
|
||||
return bool(current_url and P_COMPOSER_PAGE_RE.search(current_url))
|
||||
|
||||
|
||||
async def run_send_script(
|
||||
task: str,
|
||||
browser_id: str,
|
||||
@@ -80,6 +96,7 @@ async def run_send_script(
|
||||
send_index_in_state,
|
||||
payload_in_textbox,
|
||||
payload_source: str = "",
|
||||
current_url: str = "",
|
||||
) -> dict | None:
|
||||
"""None = stage not script-ready or aborted pre-click (model path, stage
|
||||
untouched except a possibly committed fill, which the model sees). A dict
|
||||
@@ -88,6 +105,9 @@ async def run_send_script(
|
||||
prompt; the composed task carries the routing brief whose own quoted strings
|
||||
made every real payload look ambiguous (r242/r243)."""
|
||||
t0 = time.monotonic()
|
||||
if not surface_supports_script(current_url):
|
||||
logger.info(f"[browser-sendscript] decline: surface {current_url[:60]!r} not a full-page composer (overlay firing is net-negative)")
|
||||
return None
|
||||
if P_READONLY_RE.search(task) or P_READONLY_RE.search(payload_source or ""):
|
||||
logger.info("[browser-sendscript] decline: read-only directive in task")
|
||||
return None
|
||||
|
||||
@@ -32,16 +32,37 @@ def make_exec(list_script):
|
||||
return execute, calls
|
||||
|
||||
|
||||
async def run(task, state0, list_script):
|
||||
# A full-page messaging composer: the only surface the script fires on (the live
|
||||
# A/B proved firing on the profile /in/ overlay is net-negative). Tests that exercise
|
||||
# the fill/send mechanism pass this; the surface-gate test passes the overlay URL.
|
||||
THREAD_URL = "https://www.linkedin.com/messaging/thread/2-abc/"
|
||||
PROFILE_URL = "https://www.linkedin.com/in/tylerchen1200/"
|
||||
|
||||
|
||||
async def run(task, state0, list_script, url=THREAD_URL):
|
||||
ex, calls = make_exec(list_script)
|
||||
r = await ss.run_send_script(task, "b1", "", state0, ex, send_index_in_state, payload_in_textbox)
|
||||
r = await ss.run_send_script(task, "b1", "", state0, ex, send_index_in_state,
|
||||
payload_in_textbox, current_url=url)
|
||||
return r, calls
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_surface_gate_declines_profile_overlay():
|
||||
"""The live A/B (r246-r251) proved firing on the profile /in/ docked-overlay is
|
||||
net-negative: fill commits, overlay Send never lists, abort, and the half-staged
|
||||
handoff ran 4x slower than a clean model run. So a profile URL declines UNTOUCHED."""
|
||||
ex, calls = make_exec([COMPOSER_FILLED, COMPOSER_FILLED, COMPOSER_SENT])
|
||||
r = await ss.run_send_script(TASK, "b1", "", COMPOSER_EMPTY, ex, send_index_in_state,
|
||||
payload_in_textbox, payload_source=TASK, current_url=PROFILE_URL)
|
||||
assert r is None
|
||||
assert not calls["clicks"] # nothing touched, model gets a clean composer
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_opener_hop_full_success():
|
||||
"""Prestage stopped on the profile: script opens the composer, fills,
|
||||
sees it commit, finds the late Send, clicks, sees it clear -> receipt passes."""
|
||||
"""On a messaging THREAD-LIST page (full-page surface): script opens the
|
||||
conversation composer, fills, sees it commit, finds the late Send, clicks,
|
||||
sees it clear -> receipt passes."""
|
||||
r, calls = await run(TASK, PROFILE, [COMPOSER_EMPTY, COMPOSER_FILLED, COMPOSER_FILLED, COMPOSER_SENT])
|
||||
assert r is not None and r["sent"] is True
|
||||
assert r["payload"] == "[test] hello world r9-os"
|
||||
@@ -113,7 +134,7 @@ async def test_composed_task_brief_quotes_fire_via_payload_source():
|
||||
ex, calls = make_exec([COMPOSER_FILLED, COMPOSER_FILLED, COMPOSER_SENT])
|
||||
r = await ss.run_send_script(COMPOSED, "b1", "", COMPOSER_EMPTY, ex,
|
||||
send_index_in_state, payload_in_textbox,
|
||||
payload_source=TASK)
|
||||
payload_source=TASK, current_url=THREAD_URL)
|
||||
assert r is not None and r["sent"] is True
|
||||
assert r["payload"] == "[test] hello world r9-os"
|
||||
|
||||
@@ -130,6 +151,6 @@ async def test_readonly_probe_never_fires():
|
||||
ex, calls = make_exec([COMPOSER_FILLED, COMPOSER_FILLED, COMPOSER_SENT])
|
||||
r = await ss.run_send_script(probe, "b1", "", COMPOSER_EMPTY, ex,
|
||||
send_index_in_state, payload_in_textbox,
|
||||
payload_source=TASK)
|
||||
payload_source=TASK, current_url=THREAD_URL)
|
||||
assert r is None
|
||||
assert not calls["clicks"]
|
||||
|
||||
Reference in New Issue
Block a user