The v2 Milestone
v1.0 narrowed the surface, v1.4 made delivery trustworthy, v1.8 carried it through to completion, and v1.9–v1.10 brought targeting by meaning to the browser and visual confirmation into the act. The 2.x series turns to something more basic. In human terms, it is the work of growing sensory nerves: knowing whether a thing can be touched, knowing when it cannot, and knowing what touching it was like. The nerves are still growing.
graph LR
subgraph v1x["v1.x: Learning to aim"]
A[target by meaning]
B[delivery verified]
end
subgraph v2a["2.0–2.1: Can it be touched?"]
C[refuse with a named reason]
D[deeper UIA reads]
end
subgraph v2b["2.2: Cannot be touched"]
E[frozen / covered / capped]
end
subgraph v2c["2.3: What touching felt like"]
F[may have happened]
G[read back, hit-test]
end
v1x --> v2a --> v2b --> v2c
classDef stable fill:#e8f1ff,stroke:#3b6db3,color:#183257;
classDef fresh fill:#d7f2ed,stroke:#1f8a70,color:#0e3b30;
class A,B stable;
class C,D,E,F,G fresh;
Some failures still looked like success.
By v1.16 an agent could discover a target by meaning, then act on its lease and receive confirmation in the same response. But when the screen no longer matched what desktop_discover had seen, desktop_act guessed. It pressed the remembered coordinates, typed into whatever held the focus, or treated select as a click, and answered ok: true whether or not the thing the agent asked for had happened. A button renamed since discover was pressed by position. Text meant for a check box went out as keystrokes to some other field.
In those cases, ok: true did not tell the agent whether the intended action had happened, so it could not tell the press that landed from the press that went somewhere else.
Check first, and refuse under a named reason.
2.0 changes what desktop_act does when the world has moved on: it checks first. If the window has changed, the control is gone, a dialog is in the way, or the target does not take that action, the act is refused under a named reason, with next steps, and nothing is pressed or typed.
- A lease taken before another discover of the same window answers
entity_not_found, notunknown. typeorsetValueon a button, check box, link or menu item answersaction_not_offeredinstead of typing into whatever has the focus.
Separately, keyboard with method: "background" rejects Ctrl, Shift or Alt combinations with BackgroundModifierComboUnsupported instead of sending the unmodified key; with method: "auto" they go through the foreground.
This is why 2.0 is a major version. Several replies changed shape, and agents that branch on reason had to learn new names. The trade is deliberate: a refusal you can act on, in place of a success that may not be true.
2.1 widens what the agent can feel at all. The shallow UI Automation read left browsers and Electron apps largely dependent on OCR, and missed deeper controls in Explorer, Settings and Word. In 2.1 the native UI Automation read reaches depth 64, capped at 500 elements (the PowerShell fallback keeps the old depth). On the Windows 11 machine used for the release measurements, a Chrome page went from 6 OCR elements to its 45 controls, and VS Code from 6 elements to 128. Word's body, Store Notepad's text and Calculator's digit buttons became things an act can reach.
The same release fixes the UI Automation focus handling that could leave mouse movement and window switching stuck after an acted-on window closed, though menu and fallback paths still have limitations. A sense of touch that numbs the hand is not much use.
Say so, instead of reporting it as seen.
Knowing that something cannot be touched is as much a sense as knowing that it can. 2.2 stops answering as if it had seen what it did not.
- A suspended app is not read from its last frame. Windows suspends a Store app a second or two after it is minimised or hidden. Before 2.2, discover OCRed that frozen frame, account name included, as if it were the window now. When suspension is detected, OCR and text screenshots now refuse the read with
WindowFrozen, discover reportsentityZeroReason: "window_frozen", andwindows[]marks such windowsisFrozen. - A covered window is not reported unchanged. After an act,
observationcounts only the part of the window that is on screen. If the visible part did not change and the rest was covered, the answer isindeterminate, withvisibleFraction, notno_change. - A truncated entity list says it is incomplete. When discover limits the response to
maxEntities, it returns theentities_cappedwarning and anentitiesCappedfield, instead of leaving entities out without a word.
The macOS preview in 2.3 follows the same rule from its first release. An act is refused with entity_not_found when the window or element changed since discover, and with modal_blocking when a sheet is open over it.
Carry the answer back, including "it may have happened".
The last nerve carries the answer back. Once an action has been sent, the agent needs to know what happened, including "it may have happened; look before you try again".
- A click on a list item selects it (2.3.1). A
desktop_actclick on a native list item used to go through UI Automation's Invoke, which acts as a double-click: Explorer opened the file, and a WinForms list view did nothing yet answeredok: true. The click now selects the item; useaction: "invoke"to open or run it. Chromium-based content keeps its Invoke behavior, andclick_elementis unchanged. - A press that failed after it was sent says so (2.3.1). When a
desktop_actUI Automation press reports failure after dispatch, the call returnsaim_route_failedand warns that the press may already have happened. It no longer falls back to a second mouse press at the same point. - A failed value write is checked before typing again (2.3.2). On the native UI Automation path, the field is read back after the write reports failure. If it holds the requested value, the act reports success; if it changed to another value, it returns
value_not_appliedwithout typing again. If it is unchanged or cannot be read, the keyboard fallback remains. browser_clicklands where it aims (2.3.2). After bringing the browser forward, it checks which window would receive the click before moving the cursor. A covering window, including a panel, popup or dialog of the browser itself, causesBrowserPointCovered; if Windows cannot answer the hit test, the click proceeds as before. The same release corrects clicks that landed roughly 8–12 px too low, points that drifted at any page zoom other than 100 %, and clicks that could land in another window showing the same title.
Numb spots and false alarms, written down.
This is a nervous system under construction. A few of its gaps are written down in the release notes:
desktop_discoveraimed at a Store app's frame window by its handle, rather than by its title, is not yet tested for suspension.- A control whose label changes between discover and act (a
Playbutton that turned intoPause) is refused, though it is the same control. - With DevTools or a side panel docked beside a page,
browser_clickcan still be off horizontally by half the panel's width and vertically by about 8 px.
Each one is a place where the agent cannot yet tell what it is touching. The direction does not change: when in doubt, say so and do nothing, rather than act and report success.