fix: retry observation after navigation, and read bundled assets as UTF-8

GI
8d ago
+51-4

Two fixes found while running the agent end-to-end on Windows 11.

1. tick() ended the run whenever an action triggered a navigation

tick() recovers from a StalePage by re-observing:

except StalePage:
    state["decision"] = None
    state["status"] = "ready"
    state["page"] = state["browser"].observe(...)   # called exactly once

A click that starts a navigation raises StalePage from act() while the
document is still loading, so the recovery path raised StalePage a second
time and it propagated straight out of run(). Any task whose action
navigates - i.e. most real tasks - died there instead of resuming.

Observed on main (1231850) with the Wikipedia demo task:

 2806 ms  0 actions  ready
11164 ms  1 actions  ready
jev_ultrafast.browser.StalePage: Document is navigating

Re-observe in a bounded retry loop instead (40 attempts, 250 ms apart, ~10s
budget). The same task then completes:

 1445 ms  0 actions  ready
 3225 ms  1 actions  ready
 5659 ms  2 actions  ready
 6993 ms  2 actions  ready
 7869 ms  2 actions  ready
 9149 ms  2 actions  ready
10194 ms  2 actions  done
https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems

2. The package could not be imported on Windows

browser.py and demo.py call Path.read_text() without an encoding, so the
default locale codec (GBK here) decodes snapshot.js:

UnicodeDecodeError: 'gbk' codec can't decode byte 0x92 in position 4299

That makes import jev_ultrafast fail before anything else can run, and the
bundled demo page fails the same way. Both files are UTF-8; pin them.

Tests

tests/test_agent.py gets two offline tests:

  • test_navigation_recovery_retries_until_the_document_settles - the recovery
    path resumes and yields a fresh page. On main it fails with
    StalePage: Document is navigating escaping tick().
  • test_navigation_recovery_gives_up_after_the_retry_budget - a document that
    never settles still raises, so the wait stays bounded.
$ uv run pytest -q
33 passed
$ uv run ruff check .
All checks passed!

31 tests pass before this change, 33 after. The two new ones fail on main.


Summary by cubic

Fixes two Windows 11 failures: agent runs no longer crash when an action triggers navigation, and the package imports correctly.

Bug Fixes

  • tick() now re-observes in a bounded retry loop (40 attempts, 250 ms apart) instead of a single call, so a navigation that outlives the click no longer raises StalePage out of run().
  • Bundled assets are now read as UTF-8 in browser.py and demo.py, fixing UnicodeDecodeError on Windows where the default locale codec is GBK.