Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
19bc0cd
Add neutral host preparation seam to desktop service startup
JasonVMo Nov 12, 2026
50c1f86
Add isolated AppKit fixture for native application ownership contracts
JasonVMo Nov 12, 2026
c276f21
Implement neutral macOS application ownership and exact workload iden…
JasonVMo Nov 12, 2026
f835830
Harden causal application capture and native failure evidence
JasonVMo Nov 12, 2026
56e73b6
Pin the native ownership fixture to a compatible macOS deployment target
JasonVMo Nov 12, 2026
58b2cd5
Record bounded macOS application ownership qualification and remainin…
JasonVMo Nov 12, 2026
5df4b9d
Keep package evidence references within guidance-link boundaries
JasonVMo Nov 12, 2026
ddeb328
Record final locked desktop ownership aggregate validation
JasonVMo Nov 12, 2026
ff61264
Settle rejected launches without leaking workload claims
JasonVMo Nov 12, 2026
12ec40a
Record launch settlement review qualification
JasonVMo Nov 12, 2026
9113909
Record final clean-cache ownership review validation
JasonVMo Nov 12, 2026
6690c6d
Separate macOS application identity inspection from control
JasonVMo Nov 12, 2026
49f58b4
Record macOS identity observation regression qualification
JasonVMo Nov 12, 2026
3ff7c5c
Record final identity observation regression validation
JasonVMo Nov 12, 2026
d58d473
Fix live macOS discovery and record ownership qualification
JasonVMo Nov 12, 2026
d5a7530
Qualify interrupted macOS ownership publication and survivors
JasonVMo Nov 12, 2026
ca88c28
Record rebased macOS ownership failure qualification
JasonVMo Nov 12, 2026
1655861
Record rebased Fabric and Paper ownership qualification
JasonVMo Nov 12, 2026
0fb6cd4
Bound abandoned macOS ownership qualification fixtures
JasonVMo Nov 12, 2026
507effe
Record bounded fixture review-fix qualification and remaining gates
JasonVMo Nov 12, 2026
6ee45b2
feat(desktop): extend exact application ownership to Windows
JasonVMo Nov 12, 2026
7557676
docs(desktop): record Windows ownership preparation evidence
JasonVMo Nov 12, 2026
3246c51
test(desktop-driver): retain the native release assertion result
JasonVMo Nov 12, 2026
d266a83
test(desktop-test): qualify exact concurrent Windows incarnations
JasonVMo Nov 12, 2026
83e9bcb
docs(desktop): record Windows ownership native qualification
JasonVMo Nov 12, 2026
a553feb
Fix Windows reused application and interrupted recovery ownership
JasonVMo Nov 12, 2026
bfd359b
Record Windows recovery review fixes and pending native rerun
JasonVMo Nov 12, 2026
a264503
Record default-environment Windows buildci qualification
JasonVMo Nov 12, 2026
c1c1cd5
test(desktop-test): honor the void application recovery contract
JasonVMo Nov 12, 2026
45705fd
Merge remote-tracking branch 'refs/remotes/origin/main' into user/jas…
Copilot Nov 13, 2026
cad22c8
Merge remote-tracking branch 'refs/remotes/origin/main' into user/jas…
Copilot Nov 13, 2026
20d1a61
Merge remote-tracking branch 'origin/user/jasonvmo/desktop-macos-app-…
Copilot Nov 13, 2026
ad70138
Merge remote-tracking branch 'origin/main' into user/jasonvmo/desktop…
Copilot Nov 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/windows-exact-application-ownership.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@fluentui-react-native/desktop-driver': patch
'@fluentui-react-native/desktop-test': patch
'@fluentui-react-native/storybook-desktop': patch
---

Add exact Windows and Win32 application identities, read-only inspection, cross-service application control exclusion, and bounded recovery of acquired Windows application resources from verified crashed owners. Retire reused recorded incarnations without signalling their replacements, and resume interrupted recovery barriers only after verifying their native owners exited. Keep packaged activation explicitly uncontained and uncertain launches fenced. Resolve Storybook bridge identities without acquiring another controlling attachment.
9 changes: 5 additions & 4 deletions packages/agentic/desktop-driver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ a new verified selection; finding fresh elements alone cannot restore readiness.
| React Native Windows Fabric | Windows 11 | `x64` | C++20, UI Automation, SendInput, Windows Graphics Capture |
| React Native Win32 Paper | Windows 11 | `x64` | Same Windows helper and protocol |

V1 permits one active WebDriver session per registered target in one service;
cross-service application exclusion remains work in progress. Native builds run
only on the target operating system. Installation never compiles or downloads a
helper.
V1 permits one active WebDriver session per registered target in one service.
Native helpers also exclude controlling the same exact application incarnation
across services; endpoint qualification and recovery limits are recorded in
[current status](references/current-status.md). Native builds run only on the
target operating system. Installation never compiles or downloads a helper.

## Start here

Expand Down
81 changes: 77 additions & 4 deletions packages/agentic/desktop-driver/native/PROTOCOL.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,17 +144,17 @@ never grants termination authority. Existing Storybook lease files remain a
separate, legacy identity path. Independent helpers hold a stable per-user
exclusive lock for each exact application incarnation until its lease is
released. Lock contention never permits signalling another helper's application.
The macOS-only `inspectApplication` command accepts the same configured target
parameters as `attach` and returns only `{ processId, processStartedAt }`. It
The `inspectApplication` command accepts the same configured target parameters
as `attach`. On macOS it returns only `{ processId, processStartedAt }`. It
performs the same descriptor, legacy-lease/nonce, bundle/executable, exact-window
and uniqueness checks, with the exact kernel incarnation captured before window
discovery and rechecked afterwards. It does not create an application lease,
acquire an application or physical-input lock, register windows/elements, or
grant control/termination authority. Identity observation can therefore run on
the session's helper while its controlling lease remains held, without stealing
window records or permitting another service to attach. Cancellation and
unverifiable identity/Accessibility fail closed. Other providers do not implement
this command.
unverifiable identity/Accessibility fail closed. Windows implements the same
read-only boundary with its additional native identity evidence described below.
Other lock failures after a proven launch use exact-incarnation cleanup and
report `exited` only when that primary process is confirmed gone.
Explicit app paths are authoritative; bundle-only launch rejects
Expand All @@ -179,6 +179,79 @@ uses the same exact-incarnation cleanup path. These
additive fields are described in [protocol.json](protocol.json) and retain
wire version 1.0.

### Windows application ownership

Windows and Win32 leases and inspection results retain the canonical absolute
`executablePath`, `interactiveSession: "windows-session:<id>"`, and the native
process creation timestamp with exactly seven fractional digits (100 ns), plus
`aumid` when Windows reports packaged identity. `APPMODEL_ERROR_NO_APPLICATION`
means an unpackaged process; an unreadable AUMID is unknown evidence, not proof
that a process is unpackaged. The process must belong to the helper's user and
interactive session. For REX, the canonical `ReactTest.exe` image, PID, native
creation time, and session identify one host incarnation, not just its title.

An exact external attach may supply `processIdentity` with all four required
fields (`processId`, `processStartedAt`, `executablePath`, `interactiveSession`)
and the captured `aumid` when packaged. Optional descriptor executable/AUMID
constraints must also match. Native start comparison is exact: even one 100 ns
tick differs. This identity cannot authorize a launch or be combined with a
legacy nonce lease. The legacy lease parser retains its existing timestamp
compatibility; it is not the exact-pin API. `inspectApplication` checks the same
uniqueness and descriptor constraints without creating a lease, registering
windows, or acquiring application/input control.

Independent helpers exclude control of an exact PID/start incarnation with a
session-local, nonwaiting named semaphore. A successful launch holds control of
its native launch root before discovery and, if different, of the primary
process before publishing the lease. Multiple leases within one helper share
its control; another helper receives `application-lock-busy`. Closing an
attached lease releases control without signalling the application. This lock
is separate from the physical-input mutex. Failed contenders retain no waiter
handle; after a crash, Windows destroys the semaphore when its last handle
closes.

Unpackaged leases report `launchKind: "direct-child"` and containment
`job-object`; packaged activations report `launchKind: "packaged-activation"`
and remain explicitly `uncontained`. Assigning a job after activation cannot
close the already-running process's containment race, and existing job
restrictions may forbid assignment. The helper does not promise packaged or
arbitrary-descendant containment. Attached applications remain `external` and
are never assigned to owned jobs.

Failed or cancelled Windows launches use the same `applicationSettlement`
verdicts and metadata locations as macOS. A trusted captured process is named
with its full identity, causal launch kind and containment when available.
`not-started` proves rejection before creation or refusal to adopt a
pre-existing activation. `exited` requires confirmed cleanup; unreadable or
surviving identities remain `unknown`. A pending or unleased acquisition is not
automatic recovery authority, even when a primary process was captured.

The trusted `recoverApplication` command accepts `{ endpoint, identity }`.
Only the workload owner's recovery path calls it; no WebDriver capability or
route exposes it. It first opens `PROCESS_QUERY_LIMITED_INFORMATION` and reads
native creation time. A different creation time proves the recorded incarnation
exited: it returns `{ state: "exited" }` without requesting termination access,
acquiring application control, or signalling the replacement. An absent PID
also returns `exited`. Access denied or unreadable creation time remains
`application-identity-unknown` and fenced, never guessed exit.

For a matching creation time, the query handle stays open to prevent PID reuse
while acquiring cleanup access. Recovery then verifies executable, AUMID, user
and session, acquires application control, closes/terminates only that recorded
process, and proves exit before returning `exited`. A matching start with other
identity mismatches, another controller, cancellation, or unconfirmed exit
remains fenced. Attach/inspection still reject reused PIDs; only trusted
recovery retires the absent recorded incarnation.
Mismatch reasons include `identity-start-mismatch` (attach/inspection), `identity-session-mismatch`,
`identity-executable-mismatch`, and `identity-aumid-mismatch`; ambiguous
process/window candidates report `application-identity-ambiguous`, and
unreadable candidates report `application-identity-unknown`.

These additive Windows schemas are in [protocol.json](protocol.json) and retain
FDR1 version 1.0.

### Snapshot metadata

Element snapshots can additionally include `required` as a supported Boolean
or an unsupported value with its reason. Older or non-supporting providers may
omit it; the Node boundary treats absence as unsupported. This is additive
Expand Down
58 changes: 58 additions & 0 deletions packages/agentic/desktop-driver/native/protocol.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,64 @@
"jsonType": 1,
"binaryType": 2
},
"windowsApplicationOwnership": {
"processIdentity": {
"type": "object",
"additionalProperties": false,
"required": ["processId", "processStartedAt", "executablePath", "interactiveSession"],
"properties": {
"processId": { "type": "integer", "minimum": 1, "maximum": 2147483647 },
"processStartedAt": {
"type": "string",
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{7}Z$"
},
"executablePath": { "type": "string", "minLength": 1, "maxLength": 4096 },
"interactiveSession": { "type": "string", "pattern": "^windows-session:[0-9]{1,10}$" },
"aumid": { "type": "string", "minLength": 1, "maxLength": 4096 }
}
},
"identityInspection": {
"command": "inspectApplication",
"result": { "$ref": "#/windowsApplicationOwnership/processIdentity" },
"acquiresLease": false,
"registersWindows": false
},
"survivorRecovery": {
"command": "recoverApplication",
"identity": { "$ref": "#/windowsApplicationOwnership/processIdentity" },
"result": {
"type": "object",
"additionalProperties": false,
"required": ["state"],
"properties": {
"state": {
"const": "exited",
"description": "The recorded incarnation exited, including a reused PID whose live replacement is never signalled."
}
}
},
"trustedWorkloadOwnerOnly": true,
"creationTimeQuery": "PROCESS_QUERY_LIMITED_INFORMATION",
"pidReuseSettlement": "exited-without-signalling-current-process",
"unreadableCreationTime": "fail-closed",
"holdsApplicationControlLockForCleanup": true
},
"applicationControl": "session-local-nonwaiting-semaphore-pid-and-native-start",
"launchKind": ["direct-child", "packaged-activation"],
"containment": ["job-object", "uncontained", "external"],
"applicationSettlement": ["not-started", "exited", "unknown"],
"failureMetadataFields": [
"applicationSettlement",
"processId",
"processStartedAt",
"executablePath",
"interactiveSession",
"aumid",
"launchKind",
"containment"
],
"failureMetadataLocations": ["response.error.data", "cancelled.data"]
},
"macosApplicationOwnership": {
"processIdentity": {
"type": "object",
Expand Down
52 changes: 48 additions & 4 deletions packages/agentic/desktop-driver/native/windows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,47 @@ Accessibility help text uses cached `UIA_HelpTextPropertyId` with defaults
disabled. An explicit empty string is supported; absent or non-string values
remain unsupported.

Attach accepts an owner-generated nonce-bound lease, or an exact title
qualified by the canonical executable or AUMID. Title-only attach, zero
Attach accepts an owner-generated nonce-bound lease, an exact native
`processIdentity`, or an exact title qualified by the canonical executable or
AUMID. The exact pin requires PID, seven-fractional-digit native creation time,
canonical executable, session, and AUMID when packaged; it rejects a one-tick
start mismatch instead of applying the legacy lease parser's tolerance.
Title-only attach, zero
matches, and multiple qualified matches fail. Attach fails closed: a lease's
process must be in the helper's session and match the lease's executable or
AUMID, and an unleased attach fails as ambiguous when any titled candidate's
identity cannot be read rather than skipping it.
Leases record ownership, PID, process start, executable, the primary window,
and `containment`. Attached apps survive WebDriver deletion and helper
Leases record ownership, PID, native process start, canonical executable,
`interactiveSession`, packaged AUMID when present, the primary window,
`launchKind`, and `containment`. The executable/PID/start/session tuple also
identifies an exact REX host incarnation. Unknown native evidence and multiple
matching processes/windows fail closed with fixed diagnostic reasons.
Attached apps survive WebDriver deletion and helper
disposal (`external`); launched apps are closed only by their exact recorded
process identity.

`inspectApplication` uses the same descriptor and uniqueness checks without
creating leases, application/input locks, or window records. Independent
helpers can observe a controlled application but cannot both control its exact
incarnation: a session-local nonwaiting semaphore is keyed by PID and native
creation time, and remains held through lease cleanup. A launch acquires root
control before cleanup authority or window discovery, so a losing concurrent
packaged activation cannot terminate another service's winning incarnation.
The separate input mutex still serializes
physical input across different applications and helpers.

The trusted `recoverApplication` operation reads creation time first through a
`PROCESS_QUERY_LIMITED_INFORMATION` handle. A different start proves the
recorded incarnation exited and returns `exited` without termination access,
application control, or signals to its live replacement. An unreadable start,
including access denied, remains fenced. For a matching start, the query handle
prevents PID reuse while recovery acquires cleanup access, verifies executable,
AUMID, user and session, acquires application control, and proves exact exit.
Other identity mismatches, unknown evidence, another controller, or unconfirmed
cleanup are refused without adopting a replacement process. No WebDriver route exposes
this operation. Its caller must have an acquired, owned application resource,
not an external attachment or an incomplete launch.

Unpackaged arguments use the standard `CommandLineToArgvW` quoting rules.
Launch ownership requires a non-preexisting window from the exact created
process or a verified descendant, plus a matching canonical executable or
Expand Down Expand Up @@ -134,6 +164,20 @@ or ends it. A lease is created only when the leased process's start time can be
read, because that start time is what later tells the same process from a reused
process id. The helper never places an attached application in a job.

Packaged activation deliberately remains `uncontained`. Assigning a job after
activation leaves a pre-assignment execution race and may fail under existing
job restrictions, so this backend does not advertise reliable packaged
containment. The workload claim records the exact acquired application before
session use and remains fenced after failed cleanup. A later Windows native
supervisor may reconcile that application only after proving the recorded
owner exited or its PID was reused; unknown resources/effects and unleased
launches still require operator reconciliation. Exact primary-process cleanup
does not prove arbitrary uncontained descendants exited.

Failed/cancelled launches carry `applicationSettlement` and captured native
identity in `response.error.data` or `cancelled.data`; the
[protocol](../PROTOCOL.md#windows-application-ownership) defines the allowlist.

## Automation behavior

The helper provides:
Expand Down
Loading
Loading