feat(hardware-button): press phone volume and power keys once

Merged
TH
9h ago
+1340-54

Closes #3848

Summary

Adds hardware-button <volume-up|volume-down|power>: one press of a phone hardware key, never foregrounding the session app. Single press; no aliases, hold, or raw codes; an unknown/missing positional refuses INVALID_ARGS before device dispatch. The buttons join the existing SYSTEM_BUTTONS family, so every owner's typed family denial covers them until it declares a cell. Android (ADB/helper; Appium via mobile: pressKey 24/25/26; Limrun shares the same phone/TV rule) presses all three on phones/tablets. A physical iPhone/iPad presses volume via the local runner (XCUIDevice) or Appium mobile: pressButton; power is unsupported on every Apple leaf; iOS Simulators, Android TV and tvOS refuse (TV: use tv-remote — #3817's merged TV buttons stay the TV route). Only scope edit: registry line-cap ratchet 2179→2192 (chore(gates) commit, additive descriptor).

agent-device hardware-button volume-down
client.command.hardwareButton({ button: 'power' })

Latent provider bug, untouched: Android webdriver home/appSwitcher legs call mobile: pressButton, which appium-uiautomator2-driver does not implement.

Validation

Head 066cb0d742 (rebased onto 71eae2adcd; round-5: shared Limrun/local phone-TV fact builder, shared positionals parser). device-SDK build-for-testing AgentDeviceRunner -destination 'generic/platform=iOS' → TEST BUILD SUCCEEDED (exit 0). Exact-head check:affected --run receipt and live emulator transcript: evidence comment on this PR. This PR's Swift delta is unchanged since its unit+UI run (449 tests, 0 failures). Not verified live: physical iPhone volume and Appium legs (no such device/session here) — fail-closed and fact/unit-covered. CI device lanes are the remote obligation.