Skip to content

Releases

0.73.0

Choose a tag to compare

@github-actions github-actions released this 21 Oct 13:51
a21854c

0.73.0: the floor rises to invisible-playwright 0.41.0, the first wrapper sealed to firefox-51

0.72.3

Choose a tag to compare

@github-actions github-actions released this 17 Oct 17:51
a337cdf

The floor moves to invisible-playwright 0.37.0 (invisible-core 49.44.0,
firefox-49), where the snapshot and every read run in the caller's world.

  • A page that replaces getBoundingClientRect keeps its replacement in its own
    world and no longer reaches the snapshot: the element is measured with the
    real method and listed. No script can reach the unmeasurable count any
    more, the caller's included (measured), so its test drives the count
    through the snapshot's own source with the measuring made to give nothing.
  • A navigation that the previous page's own replace stops answers
    NS_BINDING_ABORTED, an honest answer upstream Playwright gives as well. The
    tests admit it only with the page then really on /landed, and the snapshot
    read right after it may meet the previous page still in place only when the
    navigation was stopped or overtaken.
  • The minimise test runs in a visible session: headless=True puts the window
    on a hidden desktop that EnumWindows does not see, and only a visible window
    can be minimised by a person. On firefox-49 the capture outlives the
    minimise, so this arm proves the window is watched again; the restart keeps
    its clock tests.
  • The server builds its engine through one function, process_engine(env), and
    the one-reader test asks it and plan.engine_here of the same environment.

0.72.2

Choose a tag to compare

@github-actions github-actions released this 13 Oct 19:26
67b7786
  • 0.72.2: the snapshot and read_html read again on the page that replaced the one they went to

invisible-playwright 0.35.2 sends the caller's code once: page.evaluate raises
Playwright's navigation error when the page moves under it, because a script
waiting on a promise ran twice when it was sent again (B285). The snapshot and
read_html are this package's fixed reads without effects, and they reached the
page through page.evaluate; they ask page.evaluate_read now, which reads again
on the page that replaced the one it went to. browser_evaluate, the model's
code, stays on page.evaluate and runs once. The floor moves to
invisible-playwright>=0.35.2.

test_read_text_after_a_page_moves_on.py reads each of them first after its own
navigation on the seven pages that replace themselves; a unit test says which
door each tool takes.

  • 0.72.2: the floor moves to invisible-playwright 0.35.4, where a page that replaces itself keeps its main world

On 0.35.2 this PR's e2e failed on /head for the snapshot and read_html: "Page.evaluate_read: the main world never
appeared for frame mainframe-9. Worlds seen: ['ctx_aux']". The engine announced only the utility world of the
document that replaced the one navigated to, and a read in the page's own world waited 30 s for one that never came
(B286). firefox-42, through invisible-core 42.39.0 and invisible-playwright 0.35.4, keeps it. Measured with 0.35.6
(core 44.39.0) under Xvfb 1920x1080: the redirect e2e 29 passed, the floor test 2 passed.

0.72.1

Choose a tag to compare

@github-actions github-actions released this 12 Oct 12:38
0d8fda4

The owner's rule of 2026-10-12: a cap only where a real constraint asks for
it, never a fixed number out of habit. Three were left that nothing asked for:

  • browser_type quoted what the field kept up to 120 characters and then
    "...". Those sentences exist to say how the field differs from what was
    typed, and a page that reformats a long value makes the difference anywhere
    in it.
  • A turn that failed said the exception's detail up to 200 characters. The
    detail is kept because it is the only clue when the cause is real, and a
    provider's JSON error names the cause past that point.
  • The four 503 answers of the panes' routes cut their reason at 200
    characters. One function writes that answer now, whole.

Each has a test that turns red when its cap is put back.

0.72.0

Choose a tag to compare

@github-actions github-actions released this 12 Oct 05:19
becf43b

No answer is cut any more. browser_read_text stopped at 6000 characters with a line asking for a larger max_chars, browser_evaluate and the browser listing became {"truncated": true, "preview": ...} above the same number, and browser_snapshot dropped elements from the end. max_chars is gone from both tools, with DEFAULT_MAX_CHARS, json_capped and capped_elements; a caller who wants less narrows the selector. The limits clean.py applies to the HTML stay.

browser_read_text right after browser_navigate, on a page that replaces itself, failed with "Failed to find execution context" 9 times in 10: it counted, then evaluated on the first match, and the page moved on between the two. It now finds and reads in one call, with the selector still resolved by the engine.

The floor moves to invisible-playwright 0.35.0 (invisible-core 41.36.0): a read caught by a navigation is made again on the page that took its place, and a navigation the previous page's own redirect beats answers at once, naming where the browser went, instead of after the 45 s timeout. browser_navigate's description says what that answer means.

The docs' tool-cost figures are measured again on 0.72.0 with their own published method: 17 tools, 9,207 characters of description, 3,497 tokens of complete definitions, median 189 per tool.

0.71.2

Choose a tag to compare

@github-actions github-actions released this 11 Oct 19:17
2f5d2e0

A host that drives the server (invisible_dots) reads the first sentence of browser_open's answer to know the browser
opened, and NOT_OPEN and GONE to know it is gone. Those two were constants it captures from the installed package;
the opened sentence was written inline in Work.open, so the host matched it by its words. OPENED is now beside them,
exported, and Work.open answers with it; the answer is otherwise unchanged.

0.71.1

Choose a tag to compare

@github-actions github-actions released this 11 Oct 00:57
332137d
  • 0.71.1: browser_evaluate says it is the last rung, and which tool reads what it is not for

A host's model (invisible_dots, z-ai/glm-5.3-flash, four tasks on 0.71.0) made 13 of its 63 calls with
browser_evaluate, most of them to read a heading that browser_read_text reads: the description named what evaluate
is for and nothing of what it is not, and the ladder's fourth rung said "what none of the above can see", which a
model did not apply to a heading. A script runs in the page and the readers do not, so reading text that way is the
less clean choice for this browser.

The description now says it comes last, and that text, a heading or a price is browser_read_text's, markup
browser_read_html's and a control browser_snapshot's, because a script runs in the page and they do not. The
fourth rung of the page rules says "last". The description is 1006 characters, inside the 1008 of headroom its gate
holds.

  • docs: the tool-surface figures of 0.71.1 in the fourteen pages that publish them

browser_evaluate's description grew by 163 characters, so the server declares 9,081 characters of description and
the content gate refused the 8,918 the pages said. Measured on 2026-10-11 the way the pages say (tiktoken
o200k_base): the descriptions alone are 2,096 tokens, 42 more than the 2,054 published, and the median per tool is
unchanged. The serialization behind the published 3,464 for the complete definitions is not recorded and four
forms tried give 3,422 to 3,675, every one of them 42 more on 0.71.1, so the pages say 3,506; forty turns of it are
about 140,000 tokens.

0.71.0

Choose a tag to compare

@github-actions github-actions released this 10 Oct 20:48
0fea81a

A host that runs one server per identity, opens and closes its browser itself and offers its model only the page
tools (invisible_dots) had two choices, both wrong: hand its model instructions about browser_open, a tool that
model does not have, and a browser argument offering a helper it cannot open; or rewrite every description and
copy the rules into a prompt of its own, where they drift from these. INVISIBLE_MCP_HOST_MANAGED=1, read once at
import like the session id, is the third: the process serves main alone (the argument leaves every schema through
SkipJsonSchema, and support is refused if sent anyway) and its instructions are the page rules alone. Opening and
closing stay tools, because such a host still needs browser_open (the engine's download is answered in steps) and
browser_close (Firefox writes its profile before the client stops the process).

INSTRUCTIONS is now three texts joined for a standalone client: the opening rule, PAGE_RULES and the two browsers.
The page rules are the same words in both modes. They gained what a host's runs measured on 2026-10-10 (45 tasks of
invisible_dots, 773 page calls, 90 that led nowhere): reach a page through a link seen or an address given, search
with Brave and read the results with browser_snapshot, and after a 404 take the next address from a page that
loaded (58 of the 90 calls were addresses from memory, 27 of the 54 pages dropped unread answer 404); answer a
consent dialog before clicking under it (four clicks waited 15 s and failed); go back or reload with a navigation,
since a key pressed on the page never reaches the browser's own shortcuts; and two rules that host's skill carried
and this server did not (a calendar wants its days clicked; a number not read on the page is not one to give). The
sentence that support is the second page moved to the paragraph about support.

browser_read_text resolved its selector with document.querySelector while every other tool goes through the engine,
so a snapshot's :nth-match(...) handle failed there while the same string clicked, and a field inside a shadow root
read as missing. It now counts and reads through page.locator, as DIAGNOSE_JS was moved off querySelectorAll on
2026-09-30. test_read_text_in_a_browser holds both on a real page (the old resolver fails those two and passes plain
CSS), and test_a_host_that_opens_the_browser holds the mode in a process of its own.

0.70.18

Choose a tag to compare

@github-actions github-actions released this 10 Oct 20:36
904f3aa
  • 0.70.18: the wrapper floor moves to 0.32.0

invisible-playwright 0.32.0, through invisible-core 39.35.0: a session ends with
its last process, so no profile is left in the temporary directory; characters
that need Shift are typed with Shift; and the hand pauses between the pointer
arriving and the click.

  • tests: Shift is a key of its own

With invisible-core 39.35.0 a character that needs Shift (@, capitals) is
preceded by a Shift keydown, as on a real keyboard. Two tests counted "one
keydown per character" and saw the Shift keys as extra: they count the keys
that are not Shift now, which is what "every character was a key" asks.

0.70.17

Choose a tag to compare

@github-actions github-actions released this 10 Oct 04:00
2093978

invisible-playwright 0.31.0, through invisible-core 39.34.0 and firefox-39: page.mouse and every element action return after the page's listeners have run, so a click and the read of the page after it see the same page; a right click no longer waits out an acknowledgement that never came.