feat(tool-server): add system-settings tool for device-wide display, accessibility and radio settings - #566
Draft
latekvo wants to merge 7 commits into
Draft
feat(tool-server): add system-settings tool for device-wide display, accessibility and radio settings#566latekvo wants to merge 7 commits into
latekvo wants to merge 7 commits into
Conversation
…trast, and text size Adds a cross-platform `system-settings` tool that changes device-wide display and accessibility settings directly, without navigating the Settings UI: - appearance: light / dark theme - increase-contrast: accessibility high-contrast - text-size: the 12 Dynamic Type categories (extra-small … accessibility-xxxl) iOS simulators go through `simctl ui` (appearance / increase_contrast / content_size); Android emulators and devices through `adb cmd uimode night`, the high_text_contrast_enabled flag, and font_scale (each iOS text category maps to the nearest scale). An out-of-range value is rejected up front as a 400 input error carrying the SYSTEM_SETTING_UNSUPPORTED telemetry code.
…y, a11y, and Android device state Adds reduce-motion and invert-colors (both platforms) plus the Android-only wifi, cellular, airplane-mode, location, and auto-rotate toggles alongside the existing appearance / text-size / increase-contrast. iOS reaches reduce-motion and invert-colors through the com.apple.Accessibility defaults domain (posting the change notification so running apps re-read live) and rejects the Android-only settings as invalid input. Unifies every boolean toggle on on/off.
…ings Main's interaction-messages test requires every registered tool to define started/completed/failed message formatters; add them for system-settings and bump the expected tool count to 78 for the newly added tool.
`simctl ui <udid> content_size <value>` and `increase_contrast <value>` print "Invalid argument" on stderr and still exit 0 — only `appearance` also exits non-zero. Reading the exit code alone reported `applied` for a setting that never changed, which is exactly what a runtime that does not model the option produces. Read stderr and fail on it. The boot-device hint matched only `simctl ui`'s "current state: Shutdown", so `reduce-motion` and `invert-colors` — the two settings that go through `simctl spawn`, which says "device is not booted" — lost the hint their own comment promised. Match both wordings. Verified on iOS 18.6 (39B9C547): all 16 valid (option, value) pairs exit 0 with empty stderr, so the guard cannot false-positive; every rejected argument for content_size/increase_contrast exits 0 with stderr set. `simctl spawn` propagates exit codes faithfully (42 -> 42), so the defaults path keeps using the exit code.
Setting `invert-colors` on and then reading a screenshot back is the obvious way to confirm it, and it always looks like the change failed. Verified on both platforms: with the setting reading back as on, the capture is byte-identical to the un-inverted one (Android 14 `screencap`, which composites with an identity color matrix, and iOS 18.6 `simctl io screenshot`).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A new cross-platform
system-settingstool, so the agent can change device-wide display, accessibility and radio settings without navigating the system Settings UI. It belongs to the same "system Settings, out of band" family assettings-permissions.Each setting maps to one platform command.
appearancetakeslight | dark,text-sizetakes one of the 12 Dynamic Type categories, and every other setting takeson | off:settingappearancesimctl ui appearance light|darkcmd uimode night no|yestext-sizesimctl ui content_size <category>settings put system font_scale <float>increase-contrastsimctl ui increase_contrast enabled|disabledsettings put secure high_text_contrast_enabled 0|1reduce-motiondefaults writeReduceMotionEnabled*_animation_scaleglobalsinvert-colorsdefaults writeClassicInvertColorsEnabledsettings put secure accessibility_display_inversion_enabledwifisvc wifi enable|disablecellularsvc data enable|disableairplane-modecmd connectivity airplane-mode enable|disablelocationsettings put secure location_mode 3|0auto-rotatesettings put system accelerometer_rotation 1|0Returns
{ setting, value, applied }, whereappliedis the concrete platform-level change:appearance=dark,night_mode=yes,font_scale=1.94,ReduceMotionEnabled=YES, and so on.Notes on the design
{ udid, setting, value }schema, liketv-remote. The registry's zod-to-JSON-schema helper is typed forz.ZodObject, so a top-level discriminated union isn't an option;valueis validated against the setting before dispatch instead.simctl uiis simulator-only, so there's noapple.devicesupport; theadbcommands work on emulators and real devices alike. The five radio / location / rotation settings are Android-only and the iOS handler rejects them as caller input with the list of what iOS does support.appleRemote, unlikesettings-permissions. sim-remote forwards a fixed set of simctl verbs anduiis not among them, so three of the five iOS settings have no remote path at all - declaring the capability would acceptappearanceon a remote sim and then fail inside the handler.InvalidToolInputError, which the HTTP layer maps to 400, matching how the keyboard backends reject un-typeable characters. It carries the granularSYSTEM_SETTING_UNSUPPORTEDtelemetry code.simctl uirefusals are read off stderr, not the exit code.content_sizeandincrease_contrastprintInvalid argumentand still exit 0 - onlyappearancealso exits non-zero - so an exit-code-only check would answerappliedfor a setting the runtime never changed. Thedefaultspath keeps using the exit code, becausesimctl spawnpropagates it faithfully.settings-permissionsfamily:SYSTEM_SETTING_UNSUPPORTED,IOS_SYSTEM_SETTING_FAILED,ANDROID_SYSTEM_SETTING_FAILED.Verification
tool-serversuite green (3106 passed, 296 files);tscincluding thetsconfig.test.jsongate, prettier and eslint clean.adbwith an independent read-back, then restored to baseline:font_scale,high_text_contrast_enabled,accessibility_display_inversion_enabled,accelerometer_rotationand the three animation scales viasettings get;cmd uimode nightandcmd connectivity airplane-modevia their own query form;location_modecross-checked withcmd location is-location-enabled(falseafteroff);svc wifiandsvc dataviaglobal wifi_on/global mobile_data. Failure paths confirmed non-zero (settings put system font_scale not-a-number→ 255,cmd uimode night wat→ 255), which is whatrunAdbturns into a throw.(option, value)pairs the tool can emit exit 0 with empty stderr, so the stderr guard cannot false-positive; every rejected argument forcontent_size/increase_contrastexits 0 with stderr set. Thedefaultswrites read back throughsimctl spawn defaults read. Shut-down-simulator wording captured for both mechanisms:simctl uianswersUnable to lookup in current state: Shutdown,simctl spawnanswersProcess spawn via launchd failed because device is not booted- both now carry the boot-device hint.POST /tools/system-settings) on both platforms, each change confirmed by read-back and a screenshot, then round-tripped back to defaults. An invalid value returns 400 with the list of valid values.