lazyBoy/docs/cua-compatibility.md

160 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Cua Driver compatibility (LazyBoy desktop)
> 歷史紀錄:本文描述 2026-09-07 的雙後端驗證,已非現況。目前只保留 Cua操作方式見 [operations.md](operations.md),最新驗證狀態見 [cua-migration-progress.md](cua-migration-progress.md)。
This report answers one question, from a real `make cua-smoke` run on 2026-09-07:
> Can Cua Driver reliably control the existing LazyBoy XFCE + Xvfb desktop container?
**Yes, as an opt-in backend.** A disposable `lazyboy/computer:local` desktop on 2026-09-07 (linux/arm64, Cua Driver 0.23.2) passed raw Driver smoke 10/10, the LazyBoy adapter 10/10, dual-display isolation, and Chromium cookie persistence across `docker pause` and `docker restart`. Production still defaults to `legacy`. See [cua-review.md](cua-review.md) and [cua-benchmark.md](cua-benchmark.md).
## Environment
- Image: `lazyboy/computer:local` (`image/computer/Dockerfile`)
- Distro: Debian bookworm; Driver binary follows `TARGETARCH` (`linux-arm64` or `linux-x86_64`)
- Display: Xvfb `DISPLAY=:1` at 1280×800, XFCE (`xfwm4` compositor off, `xfce4-panel`, `xfdesktop`)
- Accessibility: AT-SPI 2 per screen (`at-spi-bus-launcher` + `at-spi2-registryd`)
- Browser: Debian `chromium` via `lazyboy-browser` (persistent profile, `--remote-debugging-port=9221+display`, `--force-renderer-accessibility`, `--lang=zh-TW`)
- Cua Driver: **0.23.2** (`cua-driver-rs-v0.23.2`; linux-arm64 SHA256 `be22768a207796a4bc1de50c52f32f9ef680b5e86e58c059e02eec2caba2e7bb`, linux-x86_64 SHA256 `01bf8339ec129cc00f4b4b2c6056ef1a7c5b52df39ff83ad17c9b16818aec500`)
- Install path: `/usr/local/lib/cua-driver` + `/usr/local/bin/cua-driver` (not under the persisted `/home/lazyboy` bind)
- Daemon: `cua-driver serve --grant existing-profile --socket /tmp/lazyboy/cua.sock --no-overlay` on the primary display only
- Telemetry: disabled
- How to reproduce: `make cua-smoke` (artifacts in `/tmp/lazyboy-cua-smoke-last/`)
## Doctor
`cua-driver doctor --json` exit 0, `ok: true`.
| Probe | Status | Note |
| --- | --- | --- |
| binary | ok | `cua-driver 0.23.2 (x86_64-linux)` |
| install dir | ok | `/usr/local/lib/cua-driver/cua-driver` |
| telemetry | ok | disabled |
| display server | ok | X11 `DISPLAY=:1` |
| X11 connection | ok | connected, visible top-level windows |
| AT-SPI | **warn** | CLI `doctor` (docker exec) does not always see the XFCE session bus. The **daemon** started from `lazyboy-screen` does: native `get_window_state` + AT-SPI click/type worked 10/10. |
`cua-driver status --socket /tmp/lazyboy/cua.sock`: daemon running, permission mode `standard`. Unix socket rejects uid 0; smoke and future controld calls must run as uid 1000 (`lazyboy`).
## Results (10 consecutive iterations)
Independent application state, not Cua `"ok"`:
| Check | Result |
| --- | --- |
| `cua-driver --version` | `cua-driver 0.23.2` |
| screenshot (`get_desktop_state`) | 10/10 PNG of the XFCE desktop |
| window / accessibility observation | 10/10 `list_windows` + GTK `get_window_state` |
| native click | 10/10 GTK `Smoke Click` wrote `/tmp/lazyboy/cua-smoke-clicked` |
| native type | 10/10 GTK entry + `Smoke Save` wrote `hello-cua` |
| Chromium attach (existing window/profile) | 10/10 `browser_prepare` `attached_existing_profile` |
| browser semantic click / type | 10/10 local `http://127.0.0.1:8765/cua-smoke.html`; DOM became `clicked-ok` then `typed:hello-cua` |
| noVNC `:6080` still up | 10/10 |
| leftover Cua processes | none (only `cua-driver serve`) |
Same Chromium **pid 334** / **window_id 29360131** across all ten iterations. `browser_prepare` side effects were all false: no isolated profile, no copy, no restart, no extra remote-debugging toggle (LazyBoy already exposes loopback CDP).
Element refs are snapshot-scoped (`p1:1`, `p4:1`, … `p28:1`). Reusing an old ref would be wrong; the smoke re-snapshots every action.
## Relevant tools (0.23.2 `list-tools`)
Observation / native input used by `controld`: `get_desktop_state`, `list_windows`,
`get_window_state`, `click`, `type_text`, `press_key`, `hotkey`, `scroll`, `drag`,
`move_cursor`, `set_value`, `bring_to_front`, `get_cursor_position`, `get_screen_size`.
Browser (attach only): `browser_prepare` (`strategy.kind=existing_profile`,
`allow_launch=false`), `get_browser_state` (`semantic_v2`), `browser_navigate`
(http/https/about only), `browser_click`, `browser_type`.
Lifecycle / diagnostics: `start_session`, `end_session`, `health_report`. Skill
teaching also uses `start_recording` / `stop_recording`.
Not used: `mouse_button_down`, `mouse_button_up`, `mouse_drag` (held-button
background X11 tools). LazyBoy's action DSL has no partial-pointer state, so
`Pointer{Down}` / `Pointer{Up}` translate to `ControlError::Unsupported` instead
of a half-pressed button nobody releases. Also unused: isolated `launch_app`
browsers, Wayland helpers, and the deprecated `get_session_state` /
`escalate_session` aliases. None of these names may be exposed to the LLM.
## Driver contract rules (enforced in `crates/control/src/cua`)
Each of these was confirmed against a real 0.23.2 daemon, and each one fails
silently (exit 0) if violated:
1. **Repeat the `session` label on every call.** `CuaClient::call` injects
`lazyboy-<display>` unless the caller already set one. Without it each CLI
process gets an ephemeral `cli-<uuid>` session, so trajectory turns,
snapshots, and browser binds never line up, and every call also emits a bogus
`end_session` turn.
2. **Never send `target: {kind: "desktop", display_id: "primary"}`.** The Linux
driver rejects it with `invalid_action_target` (exit 0). Omit `target` to use
the global input route.
3. **Desktop `scroll` needs a point.** With `scope: "desktop"`, `x`/`y` are
required (`missing field x`); `dispatch` aims at the pointer and falls back to
the screen centre. `amount` is clamped to the schema range `1..=50`.
4. **Only a JSON object proves the tool ran.** A refusal or prose banner that
arrives with exit 0 is a failure (`decode_stdout`), never an empty success.
5. **One escalation retry.** `background_unavailable` carries
`escalation.recommended`; the client retries once with that `delivery_mode`
and never loops.
6. **`get_window_state` can be degraded.** AT-SPI intermittently answers with
`degraded: true` and a root-only tree. Such windows are skipped and the
observation reports `native_observation_complete: false` rather than failing
the whole `observe`.
7. **Nothing is validated for you.** The Linux schemas declare
`additionalProperties: false` and numeric bounds, but 0.23.2 accepts unknown
keys and out-of-range values anyway (a bogus key on `list_windows` and
`scroll amount: 0` both return exit 0 with `effect: unverifiable`), so the
bounds in `docs/cua-schemas/0.23.2/` are enforced here, by the client.
`session` is accepted by every tool, including the ones whose own schema
omits it (`list_windows`, `bring_to_front`, `health_report`,
`start_recording`), which is what lets rule 1 be applied uniformly.
8. **Socket peer uid.** `/tmp/lazyboy/cua*.sock` rejects uid 0; `controld` runs as
the desktop user (uid 1000).
## Integration notes for the next PR
- Call Cua as uid 1000 via `cua-driver call --socket /tmp/lazyboy/cua.sock`. Root is rejected (`reject Unix peer uid 0 for runtime owned by uid 1000`).
- `get_browser_state` on a live LazyBoy Chromium first returns `browser_consent_required` / `consumer_profile_endpoint_requires_grant`. Then `browser_prepare` with `existing_profile` attaches. Serve must keep `--grant existing-profile`. Never `allow_launch`.
- Linux Chromium trusted CDP pointer is unavailable; smoke used `browser_click` `input_route=dom_event` and verified the DOM. Production adapter should prefer that route on this platform and treat `browser_input_trust_unavailable` as classified, not a silent xdotool fallback.
- Extra Team screens are extra Xvfb `DISPLAY`s. This POC only runs a daemon on `:1`. Later: one socket per slot.
- `browser_navigate` refuses `file://`; local fixtures need `http://127.0.0.1`.
- `get_window_state` on Linux is `additionalProperties: false` — do not send macOS-only fields such as `include_accessibility_tree`.
## Known limits
- Doctor AT-SPI warn from a non-desktop D-Bus is not a daemon failure.
- Overlay warnings (`X11 channel rejected command`) appeared in the daemon log with `--no-overlay`; they did not block actions.
- Debian Chromium + zh-TW UI: existing-profile attach worked because CDP was already open, so Cua did not need the English setup-checkbox path.
- Multi-screen, pause/resume, takeover, and skill recording were **not** in this POC; later PRs added controller routing, browser attach, takeover re-observe, and dual-source skill recording.
## Conclusion
Cua Driver 0.23.2 **can** control the existing LazyBoy XFCE + Xvfb container: screenshot, window/AT-SPI observation, native click/type, and Chromium semantic click/type, 10/10, without replacing the browser profile or breaking noVNC.
`ComputerController` is in place. Production defaults to `legacy` pending the full acceptance suite and benchmark. Set `LAZYBOY_COMPUTER_DRIVER=cua` only for explicit testing. Set `LAZYBOY_COMPUTER_DRIVER=legacy` on the supervisor (passed into each desktop container) to roll back to CDP/AT-SPI/xdotool. Recreate desktop containers after changing the flag.
With `cua`: `POST /observe`, `POST /act`, and `POST /browser` go through Cua Driver. The Agent-facing `browser` schema is unchanged (`snapshot` / `click` / `type` / `press` / `navigate` / `wait`); Cua attaches with `existing_profile` and maps `semantic_v2` refs (`pN:M`) onto the existing element list. After human takeover ends, the run forces a fresh `computer_observe` and drops pre-handoff ids/refs. Skill teaching starts Cua `start_recording` (no video) plus the existing CDP DOM recorder so a human noVNC demo still yields semantic click/type/navigate events; Cua trajectory turns are ingested as extra evidence and password-labelled typing is masked. `use_saved_login` still fills via CDP stdin so passwords never appear on argv. `cdp.py` / AT-SPI remain for login fill, human browser recording, and the `legacy` rollback.
## Review of the current checkout (2026-09-07)
The locally tagged `lazyboy/computer:local` image now installs Cua Driver 0.23.2
for the build architecture (`cua-driver 0.23.2` on linux/arm64 in this run).
`make cua-smoke` is the acceptance entry: raw Driver smoke, adapter E2E,
isolation, and pause/restart persistence. Production defaults remain `legacy`.
See [the migration audit](cua-review.md).
One upstream caveat about that persistence claim: Chromium writes its cookie
database on a ~30 s timer and does not flush on `SIGTERM`. A profile survives
`docker pause` / `docker restart` once that write has landed; stopping a desktop
seconds after a login can still lose the cookie, and no LazyBoy code controls
the timer. `scripts/cua-smoke-test.sh` waits for the fixture cookie to reach the
profile before it restarts, so the check measures profile persistence instead of
the flush timer.
Image architectures are capped at `linux/amd64` and `linux/arm64` by upstream
binaries: the Cua Driver ships only `linux-x86_64` / `linux-arm64` and ONNX
Runtime only `linux-x64` / `linux-aarch64`. See
[development.md](development.md#映像與-cpu-架構).