Skip to content

feat(app-launch): support Windows and Linux application startup - #26

Merged
DeJeune merged 8 commits into
mainfrom
codex/windows-linux-app-launch
Oct 17, 2026
Merged

DeJeune merged 8 commits into
mainfrom
codex/windows-linux-app-launch

Conversation

@DeJeune

@DeJeune DeJeune commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

What this PR does

Before this PR:

Windows and Linux SDK bindings could only select running applications. Binding an
installed app such as Calculator failed with UNSUPPORTED_CAPABILITY before launch.

After this PR:

  • Resolve installed identities without launching: Windows AppsFolder/AUMIDs and
    executable paths; Linux XDG desktop names, IDs and paths.
  • Revalidate the authorized identity before native launch, preserve running-only
    reacquisition, and wait for a matching accessible window.
  • Linux launches per the Desktop Entry Specification with no GIO tools:
    DBusActivatable entries receive org.freedesktop.Application.Activate on the
    session bus (Exec only when no service is installed); other entries run their
    expanded Exec, detached from the runtime, opted into accessibility for Qt, Gecko
    and Chromium. Terminal entries are rejected.
  • Carry installed identity through app discovery, sessions and snapshots, while
    preserving possible effects after dispatch failures or cancellation. Linux maps
    owners of activatable application bus names to identities first.
  • Add native and protocol cold-launch coverage (Exec and D-Bus activation on Linux),
    CI integration, SDK guidance and a Changesets patch entry.

Fixes: None.

Why we need it and why it was done in this way

The shared Go backend explicitly rejected installed lookup and launch, while the
macOS backend already supported both. The optional application driver completes
that path without changing the wire protocol or host authorization contract.

The first Linux version delegated to gio launch. Real-machine acceptance on GNOME
showed that it exits before its D-Bus Activate call is delivered: the service of a
DBusActivatable app starts, never opens a window and quits from inactivity
(Clocks, Text Editor, Files, Settings; 17 entries on a stock Ubuntu GNOME). GLib
works around this dbus-daemon race only in gio open and gtk-launch.

The following tradeoffs were made:

  • Windows launches through native Shell/application activation APIs outside the
    temporary UIA worker's kill-on-close Job Object.
  • Linux expands Exec in Go (quoting, field codes, Path, Flatpak @@u markers
    passed through) instead of depending on GIO tools, which Debian-family KDE or
    minimal compositor installs may lack. A one-line POSIX sh shim records
    GIO_LAUNCHED_DESKTOP_FILE_PID before exec, so the runtime and desktop shells
    can associate the process with its entry. The command itself never runs in a shell.
  • A Flatpak export resolves into a directory named after its deployment commit.
    Identity uses the installation's active link instead, while that link points
    at the same deployment, so updates keep the identity. Full symlink resolution
    still prevents aliases from creating a second identity.
  • Chromium overwrites its environment to set process titles, so an Electron app's
    launch metadata cannot be read back. The runtime therefore remembers the exact
    PIDs it launched, with their start times, so a reused PID never inherits an
    identity. AppImages are matched by APPIMAGE only for processes running from
    the image's mount (APPDIR), so programs they start do not inherit the identity.
  • D-Bus-activated processes run a service command line without launch metadata,
    so discovery first asks the bus which PID owns the application's name.
  • Exec launches set QT_LINUX_ACCESSIBILITY_ALWAYS_ON, GNOME_ACCESSIBILITY and
    ACCESSIBILITY_ENABLED for the launched process only. Enabling
    org.a11y.Status.IsEnabled would also reach running apps, but GNOME persists it
    as a user setting. D-Bus-activated services do not inherit these variables.
  • Chromium/Electron programs also get --force-renderer-accessibility; no
    environment variable or session setting exposes their page content. The switch
    is added only when Chromium's chrome_100_percent.pak sits beside the resolved
    program (or one level above a wrapper script, as with VS Code), or inside an
    AppImage. The AppImage is mounted read-only with its runtime's --appimage-mount,
    without running its application. Unknown programs never get the switch.
    Retrying with the switch after a failed launch was rejected: Electron does not
    fail without it, and relaunching would replay the launch or reach a
    single-instance app that ignores new switches.
  • A Qt app with a GTK platform theme registers twice on AT-SPI. Discovery drops a
    childless registration when the same process has a populated one; otherwise
    every lookup of that app is ambiguous.
  • Windows/Linux desktop policy may foreground launched applications.

The following alternatives were considered:

  • Launching through a model terminal workaround would bypass the SDK identity and
    host stop checks.
  • gtk-launch handles the race but takes only desktop IDs and adds a GTK tool
    dependency.
  • Loading libgio through purego would reuse GIO fully but needs a GLib main loop
    and callbacks inside a CGO_ENABLED=0 runtime.
  • go-freedesktop/desktopentry expands Exec but was 12 days old with no known
    importers.

Links to places where the discussion took place: None.

Breaking changes

None. Existing running-only bindings and legacy CLI/MCP launch policy are preserved.
Hosts need matching updated native artifacts to enable the new startup behavior.

Special notes for your reviewer

Validated:

  • Shared Go resolver, desktop and app-stop regression tests, plus Go vet.
  • Windows executable/packaged identities and cold launch of a GUI executable from
    a path containing Chinese characters and spaces; Calculator cold launch through
    the TypeScript SDK, including identity mismatch and launch: false preflight.
  • Windows and Linux x64/arm64 runtime builds; Linux built with CGO_ENABLED=0.
  • Linux real machine (Ubuntu 24.04 GNOME 46 Wayland, arm64): through the SDK,
    Calculator (Exec) and Clocks, Text Editor and Files (D-Bus activation) cold-launch
    with the correct identity and an accessibility tree. So do KWrite (Qt 6, native
    Wayland) and Snap Firefox, which used to time out. Name, ID, executable and
    path queries resolve; ambiguity, mismatch and launch: false are rejected
    without effects.
  • Linux isolated sessions: the SDK cold-launch test passes for both Exec and
    D-Bus activation in X11, KDE KWin, Sway, Hyprland (nested in labwc) and headless
    GNOME Shell, each with native Wayland and XWayland apps where applicable. It
    also passes in KWin with gio, gtk-launch and gapplication hidden. CI runs
    it in every compositor job. With the old gio launch dispatch, the D-Bus case
    times out. The existing X11 engine/input suite still passes with the extended
    fixture.
  • Linux desktop-entry tests, including spec Exec expansion and bus-name rules.
  • SDK build/typecheck and five affected TypeScript client tests; Cherry Studio
    integration: 31 scoped control/REPL tests with scoped lint/format and docs checks.

The Windows executable symlink test skipped because the test host lacks symlink
privilege.

Not yet covered

Deferred to follow-ups and tracked in docs/exec-plans/tech-debt-tracker.md.

Desktops and sessions. The compositors above ran isolated, not as full logged-in
desktops, and only GNOME Wayland was tested on a real machine (arm64).

  • Full Plasma, Hyprland and Sway logins, and the KDE X11 session (kwin_x11). In
    these, the session itself must export the display to D-Bus activation.
  • XFCE, Cinnamon and MATE. No launch code is desktop-specific, and isolated X11
    passes, but these desktops were not installed.
  • A real GNOME on X11 session, an x64 real machine, and non-systemd distributions.
    The isolated sessions use a plain dbus-daemon, not systemd activation.

Launch paths:

  • No integration test for ServiceUnknown falling back to Exec, for an Activate
    error or cancellation after dispatch, for Path=, or for rejecting terminal entries.
  • Launched apps get no systemd scope of their own. They stay in the runtime host's
    scope: KWrite started this way landed in app-com.anthropic.Claude-*.scope.
    Desktops may attribute the app to the host, and stopping that scope can end the app.
  • No startup notification or Wayland activation token is passed, so focus follows
    compositor policy.
  • Only Calculator was retried while already running; D-Bus apps were not.

Applications:

  • Working: a GTK 3 fixture plus GTK 4/libadwaita apps (Calculator, Clocks, Text
    Editor, Files).
  • Qt 6 (KWrite) on GNOME Wayland works when launched by the SDK. Qt apps that are
    already running, or that are started by D-Bus activation, still expose no tree
    until accessibility is enabled session-wide.
  • Snap Firefox works when launched by the SDK from an unconfined host. Its AppArmor
    policy refuses AT-SPI to clients with another label: this test session runs as
    claude-desktop and was denied. A Firefox that is already running gets no opt-in.
  • Electron (T3 Code from /opt with a quoted Exec and duplicate entries; Obsidian
    as an AppImage): launch, identity and page content work (103 and 104 elements;
    one without the detected switch). MuseScore and Calculator launch without it.
    Electron apps from Snap or Flatpak are not detected, so they get no switch.
  • AppImage (Qt): MuseScore launches and is identified through its AppRun child.
    Its Qt Quick UI returns empty references for most of its 75 children.
  • Flatpak: GNOME Weather (D-Bus activation through Flatpak's exported service) and
    KCalc (Qt 6) cold-launch by name in isolated X11, KWin and GNOME Shell sessions
    configured as after login. Identities come from .flatpak-info, and the Qt
    opt-in reaches the sandbox. In a session started before Flatpak was installed,
    neither app resolves by name and Weather cannot be activated (no bus service)
    until the user logs in again. This matches the desktop shells. A real desktop
    after re-login is untested.
  • Not tried: Java, Wine, and KDE apps on a full KDE desktop.

Linux acceptance commands (need Xvfb or Xephyr, AT-SPI, Python GI and GTK 3 for the
fixture; the runtime needs neither Python nor GIO):

npm ci --ignore-scripts
npm run sdk:build
mkdir -p dist/native
(cd apps/OpenComputerUseLinux && go build -o ../../dist/native/open-computer-use .)
apps/OpenComputerUseLinux/testdata/x11-session.sh sh apps/OpenComputerUseLinux/testdata/app-launch-session.sh
(cd apps/OpenComputerUseLinux && COMPUTER_USE_LINUX_LAUNCH_TEST=1 testdata/x11-session.sh go test . -run SDKColdLaunchDesktopEntry -count=1 -v)

SDK/native packages have not been published.

Checklist

  • Branch: This PR targets main
  • PR: The PR description is expressive enough and will help future contributors
  • Code: Write code that humans can understand and keep it simple
  • Refactor: N/A; this is an application-launch feature
  • Upgrade: Native artifact requirements and older-runtime behavior are documented
  • Documentation: SDK/protocol guidance and an execution plan are updated
  • Self-review: Reviewed the changed code and platform effect/identity boundaries
  • Linux: Real-machine cold-launch acceptance on GNOME Wayland
  • Linux: Launch acceptance in isolated KWin, Sway, Hyprland and GNOME Shell

Release note

Support identity-authorized application startup on Windows and Linux. Action required: update matching native runtime packages. Linux launches desktop entries through D-Bus activation or Exec and needs no GIO tools.

πŸ€– Generated with Claude Code

DeJeune and others added 8 commits October 16, 2026 14:44
Signed-off-by: suyao <sy20010504@gmail.com>
`gio launch` exits before its D-Bus Activate call is delivered, so
DBusActivatable applications (GNOME Clocks, Text Editor, Files, Settings)
started their service but never opened a window and timed out.

Follow the Desktop Entry Specification natively on every desktop:
activate DBusActivatable entries over the session bus, falling back to
Exec only when no service is installed, and otherwise expand Exec in Go
and start it detached with GIO launch metadata. Terminal entries are
rejected. Discovery maps owners of activatable application bus names to
identities, since D-Bus service command lines differ from Exec.

Cover both launch paths in the Go and SDK cold-launch tests with a
D-Bus-activatable GTK fixture, and stop installing GIO tools in CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run the Exec and D-Bus activation launch test in the Sway, Hyprland, KWin
and GNOME Shell CI jobs, not only isolated X11. Isolated sessions start
D-Bus before the compositor, so the launch session now passes the display
to D-Bus activation as desktop sessions do; without it, activated services
in Wayland-only sessions could not open a window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Qt 6 on Wayland, Gecko and Chromium publish no AT-SPI tree until assistive
technology is enabled session-wide, so their launches timed out. Exec
launches now opt the launched process alone into accessibility.

A Qt application whose GTK platform theme loads GTK also registers an
empty GTK application for the same process, which made every lookup of it
ambiguous. Discovery drops a childless registration when the same process
has a populated one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Flatpak export resolves into a deployment directory named after its
commit, so every update gave the application a new identity and a host's
authorization stopped matching. Identify the launcher by the installation's
"active" link while it points at the same deployment; aliases still resolve
fully, so they cannot create a second identity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chromium overwrites its environment for process titles, so the launch
metadata of Electron apps cannot be read back, and AppImages run their
application from a private mount, often as a child of AppRun. Both timed
out after launch with no identity.

Remember launched PIDs with their start times so reused PIDs never inherit
an identity, and offer launched entries to discovery. Match an AppImage by
APPIMAGE only for processes executing from the image's mount. Parse Exec as
the specification quotes it for executable matching, so quoted program
paths associate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chromium publishes its page content to AT-SPI only with
--force-renderer-accessibility; neither ACCESSIBILITY_ENABLED nor the
session's org.a11y.Status enables it, so launched Electron apps exposed
only their window frame.

Add the switch when Chromium's resource pack sits beside the resolved
program or one level above a wrapper script, or inside an AppImage, which
is mounted read-only with its runtime's --appimage-mount without running
the application. Unknown programs never receive it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the Windows/Linux launch plan to completed and record the deferred
Linux work: per-launch systemd scopes and activation tokens, accessibility
for already-running, D-Bus-activated, Snap and Flatpak Electron apps, and
the desktops, architectures and app types not yet validated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DeJeune
DeJeune marked this pull request as ready for review October 16, 2026 09:07
@DeJeune
DeJeune merged commit cc5334e into main Oct 17, 2026
14 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 17, 2026
DeJeune added a commit to CherryHQ/cherry-studio that referenced this pull request Oct 19, 2026
### What this PR does

Before this PR: Cherry Studio pins `@cherrystudio/computer-use` and its
six native runtime packages to `0.1.7`.

After this PR: The SDK and all native runtime packages resolve to
`0.1.9`. The exact-version release-age exceptions are updated alongside
the manifest and lockfile.

Fixes # N/A

### Why we need it and why it was done in this way

Adopt upstream Windows/Linux installed-application launch support from
`0.1.8` and macOS input capability, preflight rejection, and
accessibility hit-testing fixes from `0.1.9`.

The following tradeoffs were made: Keep exact SDK/runtime version
alignment and the existing packaging path; no application integration
changes are needed.

The following alternatives were considered: Remaining on `0.1.7` would
omit these upstream improvements.

Links to places where the discussion took place: [Upstream application
launch
support](CherryHQ/cherry-computer-use#26),
[upstream macOS input
fixes](CherryHQ/cherry-computer-use#27).

### Agent Note / Spec

Agent Note: N/A β€” mechanical dependency bump with no new repository
architecture or ownership decision.

Spec PR: N/A β€” no Spec implementation layer.

Acceptance criteria: N/A β€” dependency maintenance; scoped regression
evidence below.

### Verification

- `pnpm install` β€” passed, including lockfile supply-chain checks.
- `pnpm change:scope --base origin/main` β€” confirmed only
`package.json`, `pnpm-lock.yaml`, and `pnpm-workspace.yaml` changed.
- `pnpm check --plan` β€” inspected; dependency files trigger conservative
full-gate selection. Ran focused checks locally instead.
- `pnpm docs:affected --base origin/main` β€” inspected
`docs/contrib/app-upgrade.md`; update-feed documentation is unaffected
by this dependency bump.
- `pnpm exec oxfmt --check package.json pnpm-workspace.yaml
pnpm-lock.yaml` β€” passed for the two matched files (lockfile is ignored
by the formatter).
- `git diff --check` β€” passed.
- `pnpm typecheck:node` β€” passed.
- `pnpm test:scripts scripts/__tests__/computer-use-packaging.test.ts
scripts/__tests__/computer-use-runtime-packaging.test.ts
scripts/__tests__/package-contents.test.ts` β€” 3 files, 24 tests passed,
including standalone ESM/CJS SDK loading and type resolution.
- `pnpm test:main src/main/services/__tests__/ComputerUseService.test.ts
src/main/services/__tests__/ComputerUseService.control.test.ts
src/main/services/automation/__tests__/ComputerAutomationSession.test.ts
src/main/ai/mcp/servers/__tests__/computerUse.test.ts
src/main/ai/tools/adapters/aiSdk/builtin/__tests__/ComputerUseTools.test.ts`
β€” 5 files, 51 tests passed.

### Breaking changes

None expected. No application API or configuration changes.

### Special notes for your reviewer

Only the SDK and its six platform packages changed in the lockfile. No
computer-use patches exist in this repository. Actual Electron
interaction and packaged desktop acceptance were not run; broader
validation remains with CI and platform acceptance.

### Checklist

This checklist is not enforcing, but it's a reminder of items that could
be relevant to every PR.
Approvers are expected to review this list.

- [x] Branch: This PR targets `main`
- [x] PR: The PR description is expressive enough and will help future
contributors
- [x] Code: [Write code that humans can
understand](https://en.wikiquote.org/wiki/Martin_Fowler#code-for-humans)
and [Keep it simple](https://en.wikipedia.org/wiki/KISS_principle)
- [ ] Refactor: You have [left the code cleaner than you found it (Boy
Scout
Rule)](https://learning.oreilly.com/library/view/97-things-every/9780596809515/ch08.html)
- [x] Upgrade: Impact of this change on upgrade flows was considered and
addressed if required
- [x] Documentation: A [user-guide update](https://docs.cherry-ai.com)
was considered and is present (link) or not required. Check this only
when the PR introduces or changes a user-facing feature or behavior.
- [x] Self-review: I have reviewed my own code (e.g., via
[`/gh-pr-review`](/.claude/skills/gh-pr-review/SKILL.md), `gh pr diff`,
or GitHub UI) before requesting review from others

### Release note

```release-note
Update Computer Use to 0.1.9, bringing Windows/Linux application launch support and fixes for macOS input capabilities and accessibility hit testing.
```

Signed-off-by: suyao <sy20010504@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant