mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 04:07:44 +02:00
[eric] browser: restore the Send-button handoff (points the model at the Send index after a fill); deleting it brought the Send-hunt back, it was NOT redundant with the wait
This commit is contained in:
@@ -196,6 +196,22 @@ def _delta_state(text: str, seen_lines: set[str]) -> str:
|
||||
)
|
||||
|
||||
|
||||
# A button row whose name is exactly a Send control (not "Send InMail credit" or
|
||||
# "Send a message to X"); used to hand the model the Send button after it types,
|
||||
# so it never burns turns hunting a button that's right there.
|
||||
_SEND_ROW_RE = re.compile(r'\[(\d+)\]\*?<\s*button\s+"([^"]*)"', re.I)
|
||||
|
||||
|
||||
def _send_index_in_state(state_text: str):
|
||||
"""(index, name) of a real Send button in an interactives list, or None.
|
||||
Strict exact match so it never grabs an upsell or a profile 'Send a message' link."""
|
||||
for line in (state_text or "").splitlines():
|
||||
m = _SEND_ROW_RE.search(line)
|
||||
if m and m.group(2).strip().lower() in ("send", "send now", "send message"):
|
||||
return int(m.group(1)), m.group(2)
|
||||
return None
|
||||
|
||||
|
||||
def _is_composer_fill(tool_name: str, tool_input: dict) -> bool:
|
||||
"""True if this action typed a message into a composer (the moment the Send
|
||||
button is about to matter). Covers the solo fill, BrowserType, and a batched
|
||||
@@ -251,7 +267,19 @@ async def _post_action_state(
|
||||
if not isinstance(lst, dict) or "error" in lst or not lst.get("text"):
|
||||
return ""
|
||||
state = lst["text"] if seen_lines is None else _delta_state(lst["text"], seen_lines)
|
||||
return f"\n\n{PAGE_STATE_MARKER}\n{_truncate_state(state)}"
|
||||
out = f"\n\n{PAGE_STATE_MARKER}\n{_truncate_state(state)}"
|
||||
# Hand the Send button's index over after a composer fill so the model clicks it
|
||||
# directly instead of scanning the list or hunting via CSS/JS/screenshots. This is
|
||||
# the POINTER; the wait above is what makes Send actually present to point at, the
|
||||
# two work together (removing this brought the Send-hunt back, observed live).
|
||||
if _composer_fill:
|
||||
_si = _send_index_in_state(lst["text"])
|
||||
if _si:
|
||||
out = (f"\n\n[send-ready] Your message is typed and the Send button is index "
|
||||
f"{_si[0]} below. To deliver, click it SOLO with BrowserClickIndex + an "
|
||||
f"`expect` proof. Do NOT hunt for it with CSS/JS/screenshots, it is right here."
|
||||
) + out
|
||||
return out
|
||||
|
||||
|
||||
async def _request_browser_approval(
|
||||
|
||||
@@ -1440,6 +1440,17 @@ def test_composer_fill_detection():
|
||||
assert not _is_composer_fill("BrowserScroll", {})
|
||||
|
||||
|
||||
def test_send_index_handoff_points_only_at_a_real_send_button():
|
||||
# after a composer fill we hand the model the Send button's index so it clicks
|
||||
# it directly instead of hunting; must never mistake an upsell/profile link for it
|
||||
from backend.apps.agents.browser.browser_agent import _send_index_in_state
|
||||
page = '[1]<link "Tyler Chen">\n[33]<textbox "Write a message">\n[44]<button "Send">'
|
||||
assert _send_index_in_state(page) == (44, "Send")
|
||||
assert _send_index_in_state('[12]<button "Send InMail credit">') is None
|
||||
assert _send_index_in_state('[5]<button "Send a message to Maya">') is None
|
||||
assert _send_index_in_state("") is None
|
||||
|
||||
|
||||
def test_strip_lone_surrogates():
|
||||
from backend.apps.agents.browser.browser_agent import _strip_lone_surrogates, _format_tool_result
|
||||
# an orphan UTF-16 surrogate (half an emoji from the webview) is what crashes
|
||||
|
||||
Reference in New Issue
Block a user