Skip to content

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 31 Aug 23:12
· 2 commits to main since this release
aba1273

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 ordered added, removed, and changed patches.
  • 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, and Shift+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_changes
  • get_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_properties
  • set_property
  • clear_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:

  • errorCode
  • errorDisposition
  • retryable
  • error
  • hresult

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:

  • click prefers UIA Invoke, legacy default-action, and Toggle behavior before
    falling back to synthetic mouse input.
  • invoke, set_value, and toggle use 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 --fast for visual dumps and watch sessions, plus fast: true in 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.zip
  • lvt-v0.5.0-x86.zip
  • lvt-v0.5.0-arm64.zip
  • lvt-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.App 6.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 --version against 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 eN IDs 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.
  • --fast omits 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.