> **Main principle:** Do **NOT** rewrite LazyBoy into Cua. Keep LazyBoy as the agent/workspace/product layer and replace the low-level computer-control implementation with Cua Driver incrementally.
---
## 0. Agent Instructions
You are modifying **LazyBoy**, a self-hosted AI agent workspace.
The goal is to migrate the low-level GUI/browser computer-control implementation from custom CDP / AT-SPI / X11 code to **Cua Driver**, without breaking LazyBoy's existing product architecture.
### Non-negotiable rules
1.**Do not perform a Big Bang rewrite.**
2.**Do not remove the legacy computer-control implementation in the first PR.**
3. Keep LazyBoy's current public Agent tool schema stable unless absolutely necessary.
4. Keep:
-`computer_observe`
-`computer_act`
-`browser`
-`shell`
- file tools
- takeover flow
- memory
- schedule
- vault
- skills/playbooks
5. Cua must initially be an **implementation detail behind LazyBoy abstractions**.
6. Do not expose all Cua MCP tools directly to the LLM.
7. Do not replace `DockerSandbox`, `Supervisor`, noVNC, or persistent bot homes during Phase 1.
8. Add a feature flag / backend selector so legacy behavior can be restored immediately.
9. Every migrated action must have observable verification.
10. Do not guess Cua API/tool names from this document.
### Cua API freshness rule
Cua changes quickly.
Before implementing anything:
```bash
cua-driver --version
cua-driver doctor
cua-driver list-tools
```
For every Cua tool you plan to call:
```bash
cua-driver describe <tool-name>
```
Use the schema reported by the installed Cua Driver version as the source of truth.
Do not hard-code assumptions from old blog posts, old examples, or this document when the installed version differs.
---
# 1. Current LazyBoy Architecture
The current architecture is approximately:
```text
User
│
▼
React Web UI
│
▼
crates/api
│
├── Agent loop
├── Sessions / Runs
├── Memory
├── Schedule
├── Vault
├── Skills
├── MCP
└── Tool definitions
│
▼
SandboxProvider
│
▼
crates/sandbox
│
▼
DockerSandbox
│
▼
crates/supervisor
│
▼
Linux Desktop Container
│
├── XFCE
├── Chromium
├── Xvfb
├── x11vnc
├── websockify
└── controld
│
├── CDP
├── AT-SPI
├── X11
└── screenshots
```
Relevant current components:
```text
apps/web
crates/api
crates/contracts
crates/control
crates/controld
crates/harness
crates/sandbox
crates/supervisor
```
The existing `SandboxProvider` abstraction is an important migration seam and should be preserved.
Current methods include approximately:
```rust
async fn provision(...)
async fn prepare(...)
async fn capabilities(...)
async fn ensure_screen(...)
async fn reconnect(...)
async fn suspend(...)
async fn resume(...)
async fn execute(...)
async fn observe(...)
async fn act(...)
async fn connect_screen(...)
async fn list_files(...)
async fn read_file(...)
async fn write_file(...)
async fn stop(...)
async fn destroy(...)
```
Do not collapse this abstraction.
---
# 2. Target Architecture
Phase 1 target:
```text
LazyBoy Web
│
▼
LazyBoy API
│
LazyBoy Agent Loop
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
Memory Skills Schedule
│
▼
LazyBoy Tool Layer
│
computer / browser / shell
│
▼
ComputerController
abstraction
│ │
┌───────────┘ └────────────┐
▼ ▼
LegacyController CuaController
│ │
CDP / AT-SPI / X11 Cua Driver
│
┌──────────────┼──────────────┐
▼ ▼ ▼
X11 AT-SPI Browser
│
▼
Existing Desktop
Container
```
### Important
Cua is initially used as the **computer-control backend**.
It is **not** the Agent runtime.
It is **not** the memory system.
It is **not** the scheduler.
It is **not** the user-facing product.
It is **not** the first-phase sandbox lifecycle owner.
---
# 3. What Must Stay in LazyBoy
The following are LazyBoy product responsibilities and must remain owned by LazyBoy.
## Keep unchanged unless required
### Frontend
```text
apps/web
```
Keep:
- Chat UI
- remote desktop
- noVNC
- mobile controls
- takeover UI
- bot management
- session UI
### API / Agent layer
```text
crates/api
```
Keep:
- Agent loop
- model interaction
- run management
- session management
- tool policy
- takeover workflow
- credential flow
- memory
- schedules
- skills
- MCP integrations
### Harness
```text
crates/harness
```
Keep model-provider support.
Cua Driver must not decide which model LazyBoy uses.
### Supervisor
```text
crates/supervisor
```
Phase 1: keep current behavior.
Keep:
- Docker lifecycle
- pause/resume
- resource limits
- persisted bot homes
- screen lifecycle
### Sandbox
```text
crates/sandbox
```
Phase 1: keep `DockerSandbox`.
Do **not** replace it with Cua Sandbox yet.
---
# 4. What Cua Should Replace
The long-term goal is to reduce LazyBoy-owned OS automation.
Current files that are candidates for replacement or simplification:
```text
crates/control/src/a11y.py
crates/control/src/a11y.rs
crates/control/src/cdp.py
crates/control/src/cdp.rs
crates/control/src/x11.rs
crates/control/src/screen.rs
crates/control/src/overlay.rs
```
Do not delete them during the initial migration.
Instead place them behind a legacy backend.
The responsibilities that should gradually move to Cua:
- screenshots
- application/window enumeration
- accessibility tree
- native UI element actions
- pointer input
- keyboard input
- scrolling
- browser state
- browser semantic actions
- window-scoped control
- recording / trajectory capture
---
# 5. Do NOT Expose Raw Cua to the LLM
LazyBoy currently has a relatively compact Agent-facing tool surface.
Preserve this.
Preferred model:
```text
LLM
│
▼
LazyBoy tools
│
├── computer_observe
├── computer_act
├── browser
├── shell
├── file tools
└── request_takeover
│
▼
LazyBoy policy / safety / vault / state
│
▼
Cua adapter
│
▼
Cua Driver
```
Avoid this:
```text
LLM
│
▼
50+ raw Cua tools
│
▼
OS
```
Reasons:
- larger tool schemas consume context
- LazyBoy loses policy control
- LazyBoy loses stable abstraction
- Cua version changes would leak into prompts
- takeover behavior becomes harder to control
- credential handling becomes harder to constrain
- Agent behavior becomes coupled to Cua implementation details
Cua should initially behave like a device driver.
---
# 6. Introduce a ComputerController Abstraction
Create a low-level computer-control abstraction separate from sandbox lifecycle.
Suggested location:
```text
crates/control/src/controller.rs
```
Possible interface:
```rust
#[async_trait::async_trait]
pub trait ComputerController: Send + Sync {
async fn health(&self, ctx: &ControlContext)
-> Result<ControllerHealth,ControlError>;
async fn observe(
&self,
request: ObserveRequest,
ctx: &ControlContext,
) -> Result<ComputerObservation,ControlError>;
async fn act(
&self,
request: ActionRequest,
ctx: &ControlContext,
) -> Result<ActionResult,ControlError>;
async fn browser(
&self,
request: BrowserRequest,
ctx: &ControlContext,
) -> Result<BrowserResult,ControlError>;
async fn start_recording(
&self,
request: RecordingRequest,
ctx: &ControlContext,
) -> Result<RecordingSession,ControlError>;
async fn stop_recording(
&self,
ctx: &ControlContext,
) -> Result<RecordingResult,ControlError>;
}
```
Names may be adjusted to match existing LazyBoy contracts.
Do not introduce unnecessary abstractions if equivalent types already exist.
---
# 7. Backend Implementations
Implement:
```text
LegacyController
CuaController
```
Suggested layout:
```text
crates/control/src/
├── controller.rs
├── legacy/
│ ├── mod.rs
│ ├── a11y.rs
│ ├── cdp.rs
│ ├── x11.rs
│ └── screen.rs
└── cua/
├── mod.rs
├── client.rs
├── translate.rs
├── observe.rs
├── actions.rs
├── browser.rs
└── recording.rs
```
Do not spend the first PR moving all old files if it creates noisy diffs.
It is acceptable to initially keep existing file locations and only add:
```text
controller.rs
cua.rs
```
Refactor structure after behavior is stable.
---
# 8. Backend Configuration
Add a configuration value:
```bash
LAZYBOY_COMPUTER_DRIVER=legacy
```
or:
```bash
LAZYBOY_COMPUTER_DRIVER=cua
```
Default during initial rollout:
```bash
LAZYBOY_COMPUTER_DRIVER=legacy
```
After Cua passes production-equivalent validation, the default may become:
```bash
LAZYBOY_COMPUTER_DRIVER=cua
```
Do not remove the legacy option until Cua has passed the migration acceptance suite.
If configuration already has a typed settings system, add this there instead of reading environment variables throughout the code.
Suggested enum:
```rust
pub enum ComputerDriver {
Legacy,
Cua,
}
```
---
# 9. Recommended Cua Integration Mode
LazyBoy is an application with built-in computer-use.
Preferred order for experimentation:
## POC
Use the Cua Driver CLI / daemon boundary first if it allows fast validation.
Examples:
```bash
cua-driver call <tool> '<json>'
```
or a controlled local process interface.
This proves Cua works inside the existing Linux desktop container.
## Production integration
Prefer a typed/stable integration boundary.
Evaluate, based on the installed Cua release:
1. direct in-process SDK if suitable for LazyBoy's process model
2. private worker / daemon
3. CLI JSON calls as fallback
Do not choose MCP merely because Agent frameworks often use MCP.
LazyBoy is embedding computer use inside a product. The internal integration does not have to look like the Agent-facing integration.
### Rust note
Cua Driver's core runtime is Rust-based, but public application SDK support may differ by release.
Do not write custom unsafe bindings unless there is a strong reason.
Prefer an officially supported application integration surface.
---
# 10. Phase 0 — Discovery / Compatibility Check
Before changing LazyBoy behavior, create a short engineering report.
The coding agent must verify the actual runtime environment.
Inside a real LazyBoy desktop container:
```bash
echo "$DISPLAY"
ps aux
env | sort
```
Confirm:
- X11 display exists
- XFCE session exists
- AT-SPI bus is available
- Chromium exists
- Cua Driver can start
- Cua Driver can capture the current display
- Cua Driver can enumerate apps/windows
- Cua Driver can operate in Xvfb
- Cua Driver can access Chromium in this environment
Install Cua Driver using the current official installation method.
Then run:
```bash
cua-driver --version
cua-driver doctor
cua-driver list-tools
```
Save results in:
```text
docs/cua-compatibility.md
```
Include:
- installed Cua version
- Linux distribution
- display server
- Cua doctor output summary
- supported Cua tools relevant to LazyBoy
- missing dependencies
- known limitations
### Stop condition
If Cua cannot reliably control the existing `XFCE + Xvfb` environment, do **not** proceed with architecture replacement.
Instead document the blocker first.
---
# 11. Phase 1 — Five-Action POC
Do not begin by migrating the whole control crate.
First prove these five capabilities inside the existing LazyBoy desktop:
1. screenshot
2. accessibility/window observation
3. click native GUI element
4. type text
5. browser semantic action
Optional sixth:
6. scroll
Create a standalone POC path.
Example:
```text
scripts/cua-smoke-test.sh
```
or:
```text
crates/control/examples/cua_smoke.rs
```
### POC flow
Suggested test:
```text
1. Open a native/simple XFCE application.
2. Observe window state.
3. Click an accessible element.
4. Type text.
5. Launch/use Chromium.
6. Navigate to example.com.
7. Read browser state.
8. Trigger a semantic browser interaction if available.
9. Capture final screenshot.
```
### POC success criteria
All five core actions succeed 10 consecutive times without:
- wrong window actions
- stale element actions
- focus corruption
- unexplained timeouts
- leaving Cua processes behind
- breaking noVNC control
Do not migrate production paths until this passes.
---
# 12. Phase 2 — Migrate `computer_observe`
First production migration target:
```text
computer_observe
```
Current LazyBoy observation must remain compatible with the Agent.
Do not change the Agent prompt contract unless required.
Cua result must be translated into:
```rust
ComputerObservation
```
Preserve existing concepts where possible:
- screenshot/image
- dimensions
- cursor
- active window
- UI elements
### Translation layer
Implement:
```text
Cua state
↓
CuaObservationAdapter
↓
LazyBoy ComputerObservation
```
Do not expose Cua-native short-lived element identifiers directly as permanent LazyBoy identifiers.
Element references may only be valid for a specific observation.
Treat each observation as a snapshot.
### Observation ID
Strongly consider adding or preserving an observation/snapshot identifier.
Example:
```rust
pub struct ComputerObservation {
pub observation_id: String,
...
}
```
If changing the contract is too invasive, keep this internal first.
This will later help detect stale actions.
---
# 13. Phase 3 — Migrate `computer_act`
Preserve the existing LazyBoy `ComputerAction` DSL.
Current behavior such as:
- click
- move
- pointer down/up
- hover
- drag
- type
- keyboard
- wait
- semantic references
should remain Agent-facing LazyBoy concepts.
Create a translator:
```text
LazyBoy ComputerAction
│
▼
CuaActionTranslator
│
▼
Cua Driver
```
Example conceptual mapping:
```text
LazyBoy click(element)
→ semantic Cua action when possible
LazyBoy click(x, y)
→ pixel Cua action
LazyBoy type(text)
→ Cua text input
LazyBoy key(...)
→ Cua key action
LazyBoy scroll(...)
→ Cua scroll
```
Exact tool names MUST come from:
```bash
cua-driver list-tools
cua-driver describe ...
```
### Preserve semantic-first behavior
Prefer:
```text
Accessibility / browser semantic action
```
over:
```text
screen coordinate
```
Coordinate action should be fallback, not default.
---
# 14. Preserve LazyBoy Action Safety Logic
Do not delete useful behavior from:
```text
crates/control/src/actions.rs
```
Existing logic includes concepts such as:
- action batch limits
- coordinate validation
- element lookup
- stale click prevention
- browser semantic-routing preference
- double-click expansion
- drag normalization
These should become policy/translation logic above Cua.
Desired layering:
```text
Agent request
│
▼
LazyBoy validation
│
▼
LazyBoy action policy
│
▼
Cua translation
│
▼
Cua Driver
```
Cua is not a replacement for LazyBoy product policy.
---
# 15. Stale Element Handling
This is critical.
Do not assume:
```text
element 12
```
from one Cua observation refers to the same UI element later.
The migration must treat element references as snapshot-scoped.
Preferred flow:
```text
observe
↓
snapshot A
↓
Agent selects element
↓
act against snapshot A
↓
UI changes
↓
observe again
↓
snapshot B
```
If the UI changed materially, do not retry an old semantic reference blindly.
### Retry rule
When an action fails:
1. re-observe
2. re-resolve the target
3. retry with bounded count
Never loop the same stale action indefinitely.
---
# 16. Phase 4 — Migrate Browser Control
LazyBoy currently separates browser actions from generic computer actions.
Keep that separation for the Agent.
Agent-facing:
```text
browser
```
Internal:
```text
LazyBoy BrowserRequest
│
▼
Cua Browser Adapter
│
▼
Cua Driver browser tools
```
Cua browser control should replace custom CDP behavior gradually.
### Important
Preserve the current policy:
```text
When semantic browser state is available,
prefer browser semantic actions over pixel clicking Chromium.
```
Do not make browser automation less reliable during migration.
### Test cases
At minimum test:
- navigate URL
- inspect page
- click semantic element
- type into input
- scroll
- multiple tabs if LazyBoy currently depends on them
- file picker transition
- page refresh
- browser restart
- authenticated persistent profile
---
# 17. Browser Profile Compatibility
LazyBoy persists browser profiles per computer/bot.
This is product-critical.
Do not let Cua silently replace LazyBoy's profile with an ephemeral managed browser unless explicitly intended.
Verify:
- existing Chromium profile path remains usable
- cookies survive container pause/resume
- login sessions survive LazyBoy restart behavior as expected
- takeover user and Agent see the same session
- Cua attaches to the correct browser/window
If Cua requires an explicit browser preparation/attach step, integrate that into LazyBoy lifecycle.
Do not auto-create a separate profile that breaks existing saved logins.
---
# 18. Phase 5 — Recording Integration
LazyBoy has an important feature:
```text
Human demonstration
↓
record context/actions
↓
model generates playbook
↓
future run resolves current UI
```
Preserve this architecture.
Do **not** downgrade it into raw coordinate replay.
Cua recording should be used as richer source data.
Target:
```text
Human / Agent demonstration
│
▼
Cua trajectory
│
├── before state
├── action
├── after state
├── screenshots
└── optional video
│
▼
LazyBoy Skill Compiler
│
▼
Semantic Playbook
```
Cua trajectory is evidence.
LazyBoy skill/playbook is the reusable automation.
### Do not do
```text
record x=312,y=441
replay x=312,y=441 forever
```
### Do
Store semantic intent when possible:
```text
Click the "Sign in" button
```
Then resolve it on the current screen during replay.
---
# 19. Takeover Must Keep Working
User takeover is a core LazyBoy feature.
Migration acceptance requires:
```text
Agent running
↓
request_takeover
↓
Agent stops issuing input
↓
User controls noVNC desktop
↓
User releases takeover
↓
Agent re-observes
↓
Agent resumes from current state
```
### Required rule
After takeover ends:
**Always perform a fresh observation before the Agent performs another UI action.**
Never reuse pre-takeover element references.
---
# 20. Credential / Vault Boundary
Cua must not get broad access to LazyBoy secrets by default.
Keep credential policy in LazyBoy.
Preferred flow:
```text
Agent wants login
│
▼
LazyBoy checks:
- allowed domain
- saved credential exists
- HTTPS / policy
│
▼
LazyBoy authorizes injection
│
▼
Cua performs allowed typing/action
```
Do not make the Cua integration read the entire Vault.
Secrets should not appear:
- in command-line arguments
- in logs
- in Cua debug output
- in trajectory metadata
- in screenshots longer than unavoidable
- in error messages
Audit recording behavior around passwords.
If recording is active, ensure sensitive typing can be masked or recording paused.
---
# 21. `controld` Migration Strategy
Do not delete `crates/controld` during Phase 1.
It is a useful compatibility boundary.
Current conceptual API:
```text
POST /observe
POST /act
```
Recommended first migration:
```text
POST /observe
↓
selected ComputerController
↓
CuaController or LegacyController
```
```text
POST /act
↓
LazyBoy validation
↓
selected ComputerController
↓
CuaController or LegacyController
```
This keeps:
```text
API
SandboxProvider
Supervisor
```
largely unchanged.
Later, if Cua integration makes `controld` unnecessary, remove it in a dedicated architectural PR.
Do not mix that cleanup into the initial migration.
---
# 22. Docker Image Changes
Add Cua dependencies to the LazyBoy desktop image.
The coding agent must locate the actual Dockerfile(s) used for desktop computers.
Do not assume the path.
Changes may include:
- Cua Driver install
- required X11 packages
- AT-SPI dependencies
- ffmpeg if recording/video is enabled
- runtime directories
- permissions
- PATH configuration
Run:
```bash
cua-driver doctor
```
inside the built desktop container as part of the smoke test.
### Image versioning
Pin a known-working Cua version for reproducible builds.
Do not install uncontrolled nightly builds in the default production image.
Optionally allow:
```bash
LAZYBOY_CUA_CHANNEL=stable
LAZYBOY_CUA_VERSION=<version>
```
or equivalent build args.
---
# 23. Cua Process Lifecycle
Do not start one uncontrolled global Cua process for every LazyBoy computer unless architecture requires it.
Determine which process should own:
- display connection
- accessibility session
- browser connection
- recordings
- Cua lifecycle
For LazyBoy's existing architecture, the safest initial design is usually:
```text
one desktop container
│
├── XFCE/Xvfb
├── Chromium
├── noVNC
├── LazyBoy controld
└── Cua runtime/driver
```
The driver should only see/control that computer's desktop session.
### Isolation
Bot A must never control Bot B's display.
Tests must explicitly verify isolation.
---
# 24. Health Checks
Add a Cua health check.
Possible information:
```rust
pub struct ControllerHealth {
pub backend: String,
pub version: Option<String>,
pub healthy: bool,
pub degraded: bool,
pub details: Vec<String>,
}
```
Surface useful failures:
- driver not installed
- X11 unavailable
- AT-SPI unavailable
- screenshot unavailable
- browser unavailable
- incompatible Cua version
Do not return generic:
```text
computer failed
```
when a meaningful diagnosis is available.
---
# 25. Fallback Behavior
During migration:
```text
LAZYBOY_COMPUTER_DRIVER=legacy
```
must work.
For `cua` mode, avoid silent fallback for individual actions unless explicitly designed.
Bad:
```text
Cua click failed
→ silently xdotool click
```
This makes failures impossible to debug.
Preferred:
```text
Cua action failed
→ return classified error
→ Agent re-observes / retries / requests takeover
```
A feature-flag-level fallback is acceptable.
An invisible per-action fallback is not.
---
# 26. Logging / Observability
Add structured logs around Cua calls.
Include:
- operation ID
- run ID
- bot ID
- screen ID
- backend
- Cua tool
- duration
- success/failure
- action route if Cua reports it
- observation ID if available
Never log secret text.
For typing actions:
```text
text="<redacted>"
length=12
```
not:
```text
text="actual-password"
```
---
# 27. Metrics
If LazyBoy has metrics infrastructure, add:
```text
computer_action_total
computer_action_failed_total
computer_observe_duration_ms
computer_action_duration_ms
computer_browser_action_duration_ms
computer_stale_reference_total
computer_takeover_total
cua_driver_restart_total
```
Useful labels:
```text
backend
action_type
route
result
```
Avoid high-cardinality IDs such as `run_id` as metric labels.
---
# 28. Testing Strategy
## Unit tests
Test translation only.
Examples:
```text
LazyBoy click semantic ref → expected Cua request
LazyBoy coordinate click → expected Cua request
LazyBoy type → expected Cua request
Cua observation → LazyBoy ComputerObservation
unsupported Cua response → classified error
```
Use fake/mocked Cua responses.
---
## Contract tests
The Agent-facing tool results should remain equivalent between:
```text
LegacyController
CuaController
```
for common scenarios.
Test:
- observation shape
- action result shape
- error behavior
- browser results
- takeover interaction
---
## Integration tests
Run against a real desktop container.
Test native GUI:
```text
open app
observe
click
type
verify application state
```
Do not validate only that Cua returned `"ok"`.
Validate independent application state.
---
## Browser E2E
Use a deterministic local test page rather than an external website.
Create fixtures for:
- button
- input
- checkbox
- select
- scroll area
- delayed DOM update
- modal
- new tab
- canvas fallback if needed
Verify actual DOM/application state.
---
# 29. Migration Acceptance Suite
Cua backend is not considered ready until all of the following pass.
## Desktop
- [ ] screenshot works
- [ ] window enumeration works
- [ ] active window works
- [ ] accessibility elements work
- [ ] semantic click works
- [ ] coordinate click works
- [ ] type text works
- [ ] hotkey works
- [ ] scroll works
- [ ] drag works if supported
- [ ] desktop stays usable through noVNC
## Browser
- [ ] attach to correct Chromium
- [ ] use persistent LazyBoy profile
- [ ] navigate
- [ ] observe DOM/browser state
- [ ] semantic click
- [ ] type
- [ ] scroll
- [ ] refresh
- [ ] profile survives pause/resume
- [ ] same browser is visible to human takeover
## Lifecycle
- [ ] fresh container
- [ ] existing persisted container
- [ ] pause
- [ ] resume
- [ ] stop
- [ ] restart
- [ ] concurrent bots
- [ ] no cross-bot control
## Agent flow
- [ ]`computer_observe`
- [ ]`computer_act`
- [ ]`browser`
- [ ]`shell`
- [ ] takeover
- [ ] resume after takeover
- [ ] skill recording
- [ ] scheduled run
---
# 30. Performance Benchmark
Before defaulting to Cua, compare against legacy.
Create a benchmark report:
```text
docs/cua-benchmark.md
```
Measure:
| Scenario | Legacy | Cua | Winner |
| -------------------- | -----: | --: | ------ |
| screenshot | | | |
| observe desktop | | | |
| native click | | | |
| type text | | | |
| browser snapshot | | | |
| browser click | | | |
| 20-step browser task | | | |
Also measure:
- tool-call count
- bytes returned to model
- screenshot count
- total model-visible observation size
- end-to-end task completion time
- failure rate
The goal is not only faster pointer execution.
The real goal is:
```text
fewer Agent turns
+
less context
+
higher task success rate
```
---
# 31. Rollout Plan
## Step A
Add:
```text
ComputerController
LegacyController
```
No behavior change.
All tests must pass.
---
## Step B
Add Cua smoke test.
No production traffic.
---
## Step C
Implement:
```text
CuaController.observe
```
Feature flagged.
---
## Step D
Implement simple actions:
```text
click
type
keyboard
scroll
```
---
## Step E
Migrate browser state/actions.
---
## Step F
Add recording integration.
---
## Step G
Run acceptance suite and benchmark.
---
## Step H
Change default:
```text
legacy
→
cua
```
but keep legacy rollback.
---
## Step I
After stable operation, delete obsolete low-level code in separate PRs.