Repository navigation
v0.5.0
lvt v0.5.0
v0.5.0 is a major feature release. It introduces lvt Viewer, a wholly
new graphical desktop application, and adds live subscribed MCP resources,
persistent framework connections, provider-driven typed property editing,
stronger element identity and session fencing, and complete x64, x86, and
ARM64 CLI packages.
This release contains the complete delta from v0.4.0: 164 changed files,
55,248 additions, and 3,457 deletions.
Highlights
- New lvt Viewer: inspect and edit a running application's UI through a
live graphical tree and property panel. - Live MCP resources: subscribe to mode-specific UIA or visual-tree
resources and receive orderedadded,removed, andchangedpatches. - Typed property editing: inspect, set, and clear provider-owned properties
across every built-in provider. - Persistent framework sessions: reuse XAML, WinUI 3, WPF, WinForms, UIA,
native, and capable plugin connections instead of rebuilding connection
state for every refresh. - Stronger safety and identity: reject stale, cross-session, cross-window,
and replacement-target operations instead of acting on an unintended UI. - Expanded distribution: ship CLI archives for x64, x86, and ARM64 plus a
separate complete x64 Viewer archive.
New: lvt Viewer
This is the first release of lvt Viewer. It is a standalone WPF desktop
application in the spirit of Visual Studio's Live Visual Tree and the Windows
SDK's Inspect tool.
The Viewer includes:
- A drag-and-drop crosshair for selecting the exact top-level window under the
pointer, including multi-window processes. - Live UI Automation and framework-native visual tree modes.
- Incremental in-place tree updates that preserve selection and expanded
nodes when elements change, move, appear, or disappear. - Element highlighting with target-aware z-order and occlusion handling.
- Framework-colored nodes for Win32, XAML/WinUI 3, WPF, WinForms, Avalonia,
Chromium, and UIA. - Tree search with
Ctrl+F,F3, andShift+F3. - An asynchronous property panel with a loading indicator and filtering by
property name or value. - Generic editors for strings, booleans, integers, numbers, enums, and
provider-defined commands. - Set and clear/reset operations with validation, effective-value readback,
conflict preservation, and structured retry/ownership-loss handling. - Schema caching so elements that share a provider schema reuse the same
editor metadata while live values continue to refresh.
The Viewer uses the public lvt MCP interface end to end. It starts one
long-lived lvt mcp --allow-input process, connects a fixed-mode session,
subscribes to its tree resource, and performs tree reads, actions, and property
mutations through that same session. It does not link or privately embed
lvt_core.
The Viewer is published separately as lvt-viewer-v0.5.0-x64.zip. The archive
contains LvtViewer.exe and the complete matching x64 lvt runtime, including
all TAP components and bundled providers. It requires the .NET 10 Desktop
Runtime and must be extracted as a complete directory.
MCP live resources and incremental trees
The existing MCP server now supports standards-compliant subscribed resources
for live tree updates. Every connected session exposes exactly one resource
matching the mode selected at connect:
- UIA mode:
lvt://session/<session>/uia-tree - Visual mode:
lvt://session/<session>/visual-tree
Clients can discover these resources with resources/list, read them with
resources/read, and subscribe with resources/subscribe. Subscribed clients
receive notifications/resources/updated; the next resource read drains the
already-cached patch rather than walking the application again.
The initial read is a complete snapshot expressed as added events. Later
reads return ordered added, removed, and changed events. Changes include
old/new field values and explicit relocation metadata for moves or reparenting.
Queued patches are bounded; if a client falls too far behind, lvt replaces the
queue with a recoverable current snapshot rather than dropping arbitrary
events.
New session tools expose the same change stream without resource subscription:
get_uia_tree_changesget_visual_tree_changes
Tree resources retain separate baselines per session and option set. Changing
the UIA view/property options or the visual fast setting intentionally starts
a new snapshot. Disconnect, unsubscribe, transport failure, or an unrecoverable
tree-read failure cancels the producer and clears its cached state.
Framework and UIA callbacks are treated as bounded, coalesced refresh hints.
The authoritative diff still comes from a complete tree read so that text,
property, state, and bounds changes remain observable even when a framework
does not raise a complete event set. The current scheduler remains
interval-based at roughly 500 ms.
Provider-driven typed property editing
MCP adds three provider-neutral tools:
get_editable_propertiesset_propertyclear_property
get_editable_properties returns an immutable schema plus per-element live
values. Each descriptor contains an opaque provider-owned ID, declared type,
editor kind, choices or numeric limits where applicable, writability, and
clear/reset capability. Clients do not send framework property indexes, CLR
types, Win32 messages, or conversion types back to lvt.
Successful mutations return a fresh provider readback of the effective value,
runtime type, source, override state, and clearability. Submitted text is never
echoed as proof of success. A failed readback is reported as a mutation failure.
The same generic contract is implemented by every built-in provider:
| Provider | Editable capabilities |
|---|---|
| XAML / WinUI 3 | Writable scalar dependency properties; declared property type drives the editor and conversion; runtime enum catalogs and verified flags metadata supply choices; SetProperty and ClearProperty preserve dependency-property precedence. |
| UI Automation | Value, bounded RangeValue, Toggle, ExpandCollapse, SelectionItem commands, and supported Scroll directions, derived from the element's available UIA patterns and capabilities. UIA generally has no clear/reset operation. |
| WPF | Writable scalar dependency properties only; strings, booleans, characters, numbers, nullable values, and enums; SetValue plus ClearValue to restore style, inheritance, or default precedence. Ordinary CLR object graphs are excluded. |
| WinForms | Conservative browsable, readable/writable TypeDescriptor properties; safe scalar, nullable, and enum types; SetValue and ResetValue only when the descriptor supplies real reset semantics. Arbitrary converters and object graphs are excluded. |
| Win32 / Common Controls | Curated semantic properties rather than raw messages or styles: window text/enabled state; button check state; edit text, selection, and read-only state; ComboBox/ListBox selection; scrollbar range/position; ListView view/item state and text; TreeView selection/expansion/text; toolbar state/text; status-bar text; and tab selection/text where identity is safe. |
XAML and WinUI property correctness
XAML property interpretation now distinguishes the declared property type from
the runtime type of the currently evaluated value. The declared
PropertyChainValue.Type selects the editor and conversion path; ValueType
is retained only as live diagnostic metadata. This fixes cases such as
TextBlock.Text being presented as Windows.Foundation.Object instead of a
string.
Each persistent XAML connection discovers and caches the runtime enum catalog
once. Enum editors therefore use the framework's real choices, and
comma-separated flag values are accepted only when WinRT metadata confirms
System.FlagsAttribute.
Native property safety
Native editing is deliberately allowlisted. lvt never exposes arbitrary
SendMessage, style/ex-style mutation, message IDs, wParam/lParam, or
caller-controlled pointers.
Every native write revalidates the HWND, PID, class, item or command identity,
style/capability, architecture, and value range, then reads the result back.
Pointer-bearing operations use target-process buffers and bounded
SendMessageTimeoutW calls. If a timed-out target may still own a buffer, lvt
retains it under a bounded retirement policy rather than freeing memory below
the target. Once that bound is reached, further pointer operations are refused.
Unsafe cases remain explicitly read-only, including owner-data or ambiguous
controls, owner-drawn status-bar parts, duplicate toolbar command IDs,
unverifiable tab/list identities, password text mutation, and cross-architecture
pointer layouts. Architecture-independent scalar operations remain available
where safe.
Persistent framework connections and eventing
Watch and MCP sessions now acquire provider connections once and reuse them for
tree reads, property reads, mutations, and event polling.
- XAML / WinUI 3: inject once, advise once, keep one duplex command channel,
stream structural changes, and disconnect cleanly. This eliminates repeated
per-refresh injection and the associated message-window leak. - WPF / WinForms: use persistent managed TAP command servers with stable
weak object identities, UI-thread dispatch, correlated requests, clean
disconnect, and reconnect-safe handles. - UI Automation: reuse an exact-window persistent
IUIAutomation
connection with subtree-scoped event handlers and bounded event coalescing. - Win32 / Common Controls: use persistent property connections and a
PID/root-scoped WinEvent hook for native structure, state, name, value,
selection, and location refresh hints. - Plugins: ABI v2 optionally maps persistent open/get-tree/close and event
exports onto the same provider-neutral connection interface.
One-shot CLI commands still deliberately connect, read once, and disconnect.
When a persistent watch or MCP connection is missing, dead, or incomplete,
lvt does not silently degrade into repeated one-shot injection. It preserves
the previous complete snapshot where appropriate and reacquires the provider
connection.
Managed connection startup is serialized across lvt processes and validates
the named-pipe client PID. Pipes, bootstrap mutexes, and sidecar resources are
restricted to SYSTEM and the current/target user. Timed-out asynchronous I/O
is cancelled and completed before stack-owned state is released.
Watch, identity, and snapshot reliability
Watch and MCP change tracking now use durable provider identities instead of
treating positional eN IDs as persistent object IDs.
- XAML/WinUI 3 and managed providers use compact framework-qualified handles.
- Native HWND keys include an architecture-neutral window-lifetime sentinel,
so destroying and reusing the same numeric HWND produces removal/addition
with a fresh identity. - Common-control child identities are scoped to the owning window lifetime and
are published only when uniqueness can be proved safely. - Provider-supplied identity changes, loss, or ambiguity produce removal of the
old key and addition of the new element instead of silently inheriting stale
identity. - Scoped watch reconciles the complete authoritative tree before selecting its
subtree, preventing an element scope from silently rebinding to an unrelated
sibling after structural change. - Incomplete provider refreshes retain the previous complete baseline rather
than emitting false mass removals or a sparse host-only tree.
eN references remain deterministic positions within one snapshot, but
clients should hold durable keys across updates and treat their format as
opaque.
Session authorization, target fencing, and errors
MCP sessions now fence operations to the exact target lifetime. At connection,
lvt captures the authoritative PID, process creation time, root UIA RuntimeId,
and a per-window generation identity. It revalidates that identity before and
after tree reads, actions, screenshots, connection acquisition, property
operations, and response publication.
Each accepted complete visual snapshot atomically replaces the session's
authorized provider-handle set. Failed, incomplete, superseded, scoped, or
disconnected reads cannot publish broader authorization. A compact key from a
sibling window, another session, a reparented control, a detached XAML island,
or an old connection generation is rejected.
Typed property failures have a stable structured contract:
errorCodeerrorDispositionretryableerrorhresult
Invalid input, read-only/unsupported operations, stale ordinary elements, and
bounds errors are terminal. Provider-busy, transport, and timeout failures are
transient and retryable. Process, root-window, or session identity loss is
ownershipLost and non-retryable.
Screenshots now capture through a staging file and revalidate target identity
before and after capture and before publication. A replaced target cannot leak
replacement-window pixels or overwrite the requested output path.
UI interaction and UIA hardening
The existing interaction surface is more deterministic and safer:
clickprefers UIA Invoke, legacy default-action, and Toggle behavior before
falling back to synthetic mouse input.invoke,set_value, andtoggleuse UIA patterns without moving the
pointer when the pattern is available.- Visual and UIA synthetic input paths require the original target root to
remain foreground and repeat identity/foreground checks before each input
batch. - Offscreen clicks, failed foreground activation, stale RuntimeIds, closed
targets, and recycled HWNDs are refused rather than redirected. - UIA property and action resolution preserves the underlying HRESULT so a
provider failure is not misclassified as target replacement. - Mutation results use provider readback and distinguish terminal, transient,
and ownership-loss outcomes.
Mutating MCP tools, including typed property set/clear, remain absent unless
the server is started with --allow-input. Screenshot requests that write to a
filesystem path now use the same gate because they create or overwrite files.
Performance improvements
- Added
--fastfor visual dumps and watch sessions, plusfast: truein MCP.
It skips the expensive XAML/WinUI property-chain walk while retaining bounds,
text/content, and basic state. - Visual MCP resources use fast snapshots for live updates; the selected
element's complete properties are fetched separately through
get_editable_properties. - Removed a redundant full XAML/WinUI collection that previously ran while
discovering the native host window before opening the persistent connection. - Fast XAML collection now skips empty bounds-dispatch work instead of sending
no-op batches through the target UI thread. - Persistent XAML snapshots reset per-request geometry and properties so JSON
output does not grow across refreshes or retain stale fast-mode values. - The Viewer loads and builds property rows asynchronously, caches schemas,
and updates the UI collection as one operation instead of blocking navigation
on the dispatcher.
Viewer stability and usability
The new Viewer was hardened throughout development for real, large application
trees:
- Fixed stale process-exit notifications from an old MCP process clearing a
newly connected tree. - Preserved complete trees during transient provider failures and reconnects.
- Fixed same-path moves/reparenting while retaining the correct view-model
identity. - Added bounded retry and mutation settlement without overwriting dirty edits.
- Added explicit UTF-8 decoding for lvt output and deterministic
U+XXXX
display for app-specific private-use glyphs unavailable in system fonts. - Fixed highlight ownership, z-order, target occlusion, and accidental target
reordering. - Fixed crosshair resolution so large covered windows are not selected through
the Viewer and the click-through highlight overlay does not block its target. - Added an honest indeterminate connection progress indicator rather than an
invented percentage.
Plugin architecture
The plugin ABI advances additively to v2 with optional persistent
connection and event exports.
- Existing ABI v1 plugins continue to work through one-shot
lvt_enrich_tree; no rebuild is required. - An incomplete v2 lifetime group is treated as v1 rather than partially
enabling an unsafe connection. - Event polling is an independent optional capability.
- The exact loaded plugin instance that detected a framework is retained, so
aliases or duplicate framework names cannot route a later connection to the
wrong DLL. - Persistent plugin tree/event payloads are bounded, schema-validated, and
released on all success and failure paths. - The Avalonia provider supports the persistent connection path.
- The Chromium provider remains compatible through the ABI v1 one-shot path.
External plugin property editing is intentionally not part of ABI v2. The
typed property contract remains built-in-provider-only until a real external
provider can validate an appropriate stable ABI.
Packaging, architectures, and requirements
The release publishes:
lvt-v0.5.0-x64.ziplvt-v0.5.0-x86.ziplvt-v0.5.0-arm64.ziplvt-viewer-v0.5.0-x64.zip
Each CLI archive contains the architecture-matched executable, XAML/WPF/
WinForms native TAP DLLs, managed WPF/WinForms helpers and runtime configs,
Avalonia and Chromium plugins/assets, Chromium native messaging components,
the MCP runtime, and the bundled lvt skill.
Compatibility requirements:
- UI Automation inspection works across process architectures.
- Injected XAML, WinUI 3, WPF, WinForms, Avalonia, and other visual providers
require lvt to match the target architecture. - Native Win32/Common Controls remain inspectable cross-architecture, but
ABI-sensitive pointer operations become read-only. - WPF and WinForms targets require .NET Framework 4.8 or
Microsoft.WindowsDesktop.App6.0 or newer. Active CoreCLR 3.1 and 5
runtimes are rejected. - The Viewer is x64-only and requires the .NET 10 Desktop Runtime.
- Chromium inspection requires Chrome or Edge 110 or newer plus the supplied
extension and registered native messaging host.
Release validation now rejects missing required files, wrong PE architecture,
foreign runtime identifiers, stale staging output, locked files, and debug or
symbol artifacts. The release workflow validates all three CLI architectures
and the complete x64 Viewer archive.
Bundled skill and versioning
The bundled lvt skill now:
- Checks the installed
lvt --versionagainst the latest GitHub release and
updates when needed. - Selects the exact architecture-specific CLI asset instead of accidentally
choosing the separate Viewer archive. - Documents subscribed MCP resources, fixed session modes, UI interaction,
durable references, and typed property workflows.
The product version is sourced from the root VERSION file and propagated to
the CLI, Windows resources, CMake package, and MCP server. The published plugin
manifest is version 0.3.0; its version intentionally remains independent of
the product's 0.5.0 version.
Compatibility and behavior notes
- New compact durable key formats are visible in tree/watch output. Consumers
must treat keys as opaque and must not parse their internal representation. - Positional
eNIDs remain snapshot-local and should be reacquired after
structural changes. - Typed-property descriptor IDs are connection-scoped and become invalid after
reconnecting. - Visual live resources use fast collection, so arbitrary custom XAML
properties are not included in every tick; fetch complete selected-element
properties separately. - Plugin ABI v2 is additive and retains ABI v1 compatibility.
- Product and plugin-manifest versions are intentionally decoupled.
Known limitations
- MCP resource scheduling remains interval-based at roughly 500 ms. Native,
UIA, and framework events coalesce refresh work but do not yet directly wake
the scheduler. --fastomits arbitrary custom XAML/WinUI properties.- Injected visual providers require matching target architecture.
- Unsafe or unverifiable native property operations are intentionally
read-only. - External plugin property editing is deferred.
- The Viewer is currently x64-only and has no legend for its framework color
dots. - The XAML diagnostics API has no public synchronous uninitialize contract.
Disconnect releases active lvt connection state, subscriptions, windows, and
COM references, but the injected XAML TAP module may remain mapped until the
target process exits rather than attempting unsafe self-unload.