v3.50.1
Changed
-
A replay whose same-name population merely changed SIZE no longer stops when
the recorded element can be positively identified by its container, which
turns some of the previous refusals back into successful runs. (#1185) -
browser_replsnapshots return a diff again, with the refs still
complete. Abrowser.snapshot()orbrowser.smart_snapshot()inside a
script used to be forced to render the whole tree, because the script's
refs[]was parsed out of the returned text and a diff names only what
moved — so an act-then-verify loop paid for a full listing on every
iteration (a Node.js API page cost about 78 KB and 7–8 seconds each time,
where the direct tool answered with a few lines). The handler now hands the
bridge the complete listing alongside whatever it returned, so the script's
textis the ordinary diff-or-full the direct tools give — its first line
still says[snapshot: full]or[snapshot: diff …]— whilerefs[]keeps
every element on the page.full: truestill forces the whole tree, and the
entries are unchanged:refis a string forsnapshot, a number for
smart_snapshot. (#1188) -
A touch preset now dispatches touch. Under
browser_emulate {device: "Pixel 7"}the page reported a touchscreen —maxTouchPoints: 5,
(pointer: coarse)— and then received mouse input, so anything gated on
touchstart, or readingpointerdown.pointerType, saw a desktop pressing
the button.browser_clicknow sends a realtouchStart/touchEndpair on
the session the preset is already held open on, andbrowser_dragsends
touchStart→ moves →touchEnd. Measured on the same fixture and click:
before,pointerdown: mouseand no touch events at all; after,
pointerdown: touch,touchstart,touchend, then theclickChrome
derives from them, allisTrusted. The element is still checked for
actionability exactly as a mouse click checks it, and the approach the mouse
walks is skipped — a touchscreen has no pointer resting on the page between
gestures. Two gestures a touchscreen does not have stay on the mouse and say
so in the result: a double click, andbrowser_hover, which would otherwise
turn every hover-revealed menu into a silent failure. Nothing changes without
a device preset. (#1190)
Fixed
-
A replay stops when the number of same-name elements changed, instead of
clicking by position anyway. A step recorded against the first
button "Submit order"on a page used to replay against whatever sat at that
position later, on the reasoning that the first element cannot have been
displaced. It can: a look-alike with the same accessible name inserted above
the real one takes over position one, and the run clicked the look-alike,
never fired the real submit, and still reported the step asok. A live
population whose size differs from the recorded one now stops the run at that
step — at every position, the first included — saying which way the count
moved. The count is also re-checked against the live page at the moment of
acting, so an element that appears between the internal snapshot and the click
is caught too. A stop of this kind no longer counts toward the failure streak
that retires a flow, since a page growing a banner says nothing about whether
the recording is still good. The page is left where it stopped, so the
recovery is unchanged: take a snapshot, finish the flow live, andsaveunder
the same name to re-record the healed path. A change that leaves the count
unchanged — one look-alike added above, one element removed below — is still
not detectable from what a recording stores. (#1179) -
browser_replsnapshot refs are the type their tool wants. A snapshot
inside a script handed backrefs[i].refas a number, but every browser tool
typesrefas a string — so the tool's own documented usage, passing
refs[i].refas the arg namedrefs[i].param, was rejected as "expected
string, received number" before the click ever happened. Refs now come back
as strings where the argument isref, and stay numbers forsmartRef. Note
for existing snippets: a snapshot-lanerefs[i].refis now a string, so a
comparison written asr.ref === 3has to compare'3'. (#1180) -
browser_replshows the hints its calls carried. A page with a promoted
flow advertises it to a directbrowser_navigatecall —[skill] fixture-submit — proven 4-step flow— but the same call inside a script said
nothing, exactly where a proven flow saves the most steps. A run now collects
those[replay]/[skill]hints across its calls, dedupes them, and prints a
--- hints ---block beside--- calls ---and--- console ---. (#1180) -
A navigation that carries a Referer no longer contradicts it. When the
agent left one page for another, the Referer header said a link had been
followed while Chromium's ownSec-Fetch-Site: nonesaid the URL was typed
into the address bar — a pair no real browser sends. With a referer to send,
the navigation now happens from inside the page, so the whole header set
agrees; without one,page.gotois used as before. (#1181) -
browser_emulatedevice presets apply the whole device. Choosing
iPhone 13set the User-Agent and viewport and stopped there, leaving no
touch points, a desktop pixel ratio and desktop viewport semantics behind the
mobile UA. The preset now carries device metrics, screen size, orientation
and touch on both backends, holds them across navigations, anddevice: null
clears all of it. One limit the result now states outright: a non-Chromium
preset such as an iPhone cannot fillnavigator.userAgentDataor the
Sec-CH-UA*headers, which keep answering as Chromium — a Chrome-based
preset gives a consistent mobile identity. (#1181) -
Humanlike typing holds each key for a human interval. Keys were pressed
and released within a millisecond; the dwell is now drawn from the same
log-normal rhythm as the inter-key delay (median 70 ms). (#1181) -
wmux --helpnow showsmcp register --profile. Thecoreprofile
shipped in 3.50.0 but the help text still listedmcp registerbare, so the
flag could only be found in the changelog. The row names it and says what
fullandcoremean. (#1184) -
browser_replaynow records the accessible name of the nearest named
container each element sat in, and checks it before acting. A flow whose
page was restructured underneath it — a look-alike inserted above the
recorded element and another removed below, which leaves the same-name count
unchanged and used to resolve silently by position — now stops at that step
and hands the page back live instead of clicking a stranger. Recordings made
before this release keep replaying under the previous rules. (#1185) -
A device preset now holds
navigator.platform. Underbrowser_emulate {device: "Pixel 7"}the page answeredMacIntelfrom the next tool call
onward, while its User-Agent, Client Hints, pixel ratio and touch points all
said Android. Of everything a preset installs, the platform is the one value
Chromium keeps as a page-wide setting rather than as state owned by the
session that wrote it — and every debugger session that detaches from the
page clears that setting, including a session that never set a platform of
its own. wmux opens and closes short-lived sessions on the page while
resolving which tab a tool is talking to, so the preset was being undone by
wmux's own bookkeeping. The override is now re-sent once the page has been
resolved, sonavigator.platformagrees with the rest of the identity across
navigations and tool calls, anddevice: nullstill returns the host's.
(#1186) -
wmux mcp checknow says when it is speaking for an isolated instance.
An instance launched withWMUX_DATA_SUFFIXnever registers itself in the
agent configs, butcheckstill listed~/.claude.json,~/.codex/config.toml
and~/.gemini/settings.jsonwith no hint of that — so the production entry
read like this instance's own, which is exactly the confusion that made the
original hijack hard to spot.checknow prints a heading naming the suffix
and saying the rows below belong to your production wmux (--jsongains a
matchingisolatedfield), and theregister/unregisterwarnings read
the shareddataSuffix()helper instead of the env var directly, so every
subcommand agrees with the rest of the suffix-aware paths. (#1187) -
A replay can now tell two identical siblings apart by the attribute the
page gives them. The neighbourhood check added in the previous release
abstains on the one shape it was built around: two elements with the same
role, the same accessible name, and the same container are under the same
neighbourhood, so it has nothing to say about them and the count rules decide
by position. Real pages almost always distinguish those siblings on the
element itself — adata-testid, anid, aname, or anaria-label— and
a recorded step now carries whichever of those the element had, as a second
verify-only signal. Where the neighbourhood gives no verdict, the sibling
carrying the recorded attribute wins if it is unique and still at the
recorded position; if it moved, or if the population carries attributes but
not that one, the run stops and hands the page back live instead of clicking
the twin. Nothing is ever located by the attribute, and a population that
carries none at all abstains exactly as before, so no existing flow changes
behaviour. Both recording lanes —browser_snapshotand
browser_smart_snapshot— mint the value from the same pass, so a flow
recorded on one replays against the other. Traces recorded before this
release replay unchanged. The irreducible case is now genuinely irreducible:
identical siblings that carry no identifying attribute at all. -
A flow recorded from
browser_smart_snapshotno longer stops on a page
that has not changed. A replay refuses to act when the number of
same-named elements differs from the recording, which is right — but the two
snapshot tools do not walk a page the same way:browser_snapshot's listing
is depth-capped and trimmed when the output runs long, while
browser_smart_snapshot's is neither. A step recorded from the smart
listing therefore had its count compared against a different measurement
at replay, and could stop on an untouched page, reporting a population
change that had never happened. Each step now records which listing its
numbers came from, and a disagreement is re-read from that same listing
before it is treated as a change — so a real change still stops the run, and
a difference that is only between the two ways of counting no longer does.
The extra reading happens only on a disagreement, so ordinary replays are
unaffected, and traces recorded before this release keep the behaviour they
had. (#1191)
What's Changed
- docs(cli): list --profile on mcp register in the help text by @openwong2kim in #1184
- fix(browser-replay): stop a replay when the same-name element population changed by @openwong2kim in #1179
- fix(browser_repl): ref type contract, and the hints a run was swallowing by @openwong2kim in #1180
- fix(browser): make the emulated identity agree with itself by @openwong2kim in #1181
- fix(browser): let a device preset keep navigator.platform by @openwong2kim in #1186
- fix(browser-replay): verify a replayed element by its neighbourhood, not only its position by @openwong2kim in #1185
- fix(cli): wmux mcp check names the isolated instance it is reading for by @openwong2kim in #1187
- feat(browser): dispatch real touch under a device preset by @openwong2kim in #1190
- perf(browser): keep browser_repl snapshots diffed, with a complete refs list by @openwong2kim in #1188
- fix(browser-replay): verify by the element's own attribute, and count with the recorder's enumeration by @openwong2kim in #1191
- chore(release): 3.50.1 by @openwong2kim in #1192
Full Changelog: v3.50.0...v3.50.1