Skip to content

FoxIR v1.0.15

Choose a tag to compare

@wolf0x wolf0x released this 25 Sep 08:13
· 6 commits to main since this release

FoxIR v1.0.15 — The Built-in Browser Gets Reliable, and Elements Get Addressed by Number

Heads-up on scope: this is the first published release since v1.0.8. Tags v1.0.9 … v1.0.14
were cut but never packaged, so this zip carries everything up to 3fb33cc — 51 commits of
history since the download you may have now. The short version of that gap, by release commit:
P0 security work + multi-session (v1.0.10), per-session mode/STOP (v1.0.11), main-chat
protection and language rules (v1.0.12–13), the Tools-page capability center and the first
browser launch fix (v1.0.14).

This round: browser_cdp reliability

Every item below was reproduced on the real machine (Edge 154) before it was changed, and each
has a test that fails on the old behaviour.

  • Browser state is sharded per agent — one page per invocation inside one shared browser, so
    a finished run can no longer close the browser another session is using. In visible-window
    mode it is one page per chat session, so a login page stops multiplying into tabs.
  • A browser that outlived its own launcher is now adopted, not mourned. Edge's launcher
    process hands the work to the real browser process and exits 0; the pipe transport then reports
    "browser exited before the websocket URL could be resolved", while a live browser keeps owning
    the profile — every later launch failed the same way. The port is recovered from
    <profile>/DevToolsActivePort, verified against /json/version (same browser family, headless
    UA) and connected.
  • fetch_targets() is gone. In chromiumoxide 0.9.1 it replays on_target_created, which
    replaces every registered target and silently kills all held page handles — the source of the
    channel disconnected errors. Listing goes through a plain CDP command now.
  • Shutdown waits. Every path that drops a browser now waits for the process to exit before
    re-launching, because Chromium's single-instance mutex is per user-data-dir.
  • Memory can no longer declare a tool dead. The fact curator refuses "the built-in browser is
    broken, use Edge headless instead", and the prompt states that memory is authoritative about the
    past, not about whether a tool works right now. (A stale fact had been making the model skip the
    tool entirely, then improvise with shell_exec and write evidence outside the case directory.)
  • Launch failures name the actual cause, and the profile stays inside the case directory.

Three action-level bugs, with their measurements

  • Default max_chars collapsed to 1 character. unwrap_or(0).min(budget).max(1) turned "not
    provided" into "give me one character", so get_text / get_html / execute_js returned ".
    A field test: 30 characters came back as 1; now the context budget is used and
    max_chars: 1 still truncates with next_offset.
  • A page that never finishes loading was reported as a failed navigation. On a local server
    that stalls mid-body, Page.navigate answers after 30.0s with Request timed out while the
    page had already arrived — the caller retried four times and burned two minutes. A late reply
    now returns loaded: false plus requested_url, and url only ever says what the browser
    itself will admit to.
  • A rejected selector was reported as a missing element after an 8s poll (real text:
    Error -32000: DOM Error while querying at 4.7ms). Bad syntax now fails fast and says so;
    genuinely absent elements keep the old message. navigate also stopped claiming the browser's
    stale about:blank as the destination.

New: snapshot + index addressing

CSS cannot match visible text, and that is the thing the agent keeps needing: on four real pages,
0 of 4 text targets could be written as a legal CSS selector, and only 15–16% had an id to
hang one on — so the model wrote a:contains('设置'), failed, and re-scanned the DOM by hand.

  • snapshot numbers the visible interactive elements and returns [N]<button 第一个按钮/>
    lines (measured: 25–27 elements ≈ 1.0–1.7 KB; 300 elements ≈ 10 KB; the pass itself is
    7–48 ms).
  • click / type_text accept index, resolved through the existing element path.
  • Stale indices say "re-snapshot, don't guess another number" and return in ~2.2s instead of
    polling for 8s.
  • Known cost: numbering writes a data-fx attribute into the page (attributes only), which can
    trip a site's own MutationObserver; iframe and shadow-DOM contents are not numbered.

Verification

Real machine (Edge 154): 15 #[ignore] browser tests pass, including the handoff-adoption,
visible-window switch and bounded-click cases. cargo test --lib 376 passed; the 2 failures are
pre-existing environment tests that read live Prefetch/Recent folders.

Not covered / known gaps

  • The Tools and Settings pages have not been re-clicked by a human after this version bump; the
    visible-window login path is verified by tests and by confirming a real window appears, not
    end-to-end by a person.
  • Element::click() on a link that navigates still never gets a reply from Chromium — actions are
    bounded around it and report "delivered, result unknown", which is honest but not the same as a
    confirmed click.
  • Iframes, shadow DOM and file uploads remain unimplemented in browser_cdp.
  • Windows only for the browser path; Linux browser discovery is still a stub.