Phone Harness
Lets OpenClaw use a real phone: scroll, tap, type, and read the screen, with no jailbreak and nothing installed on the phone.
Install
openclaw plugins install clawhub:phone-harnessPhone Harness for OpenClaw
An OpenClaw plugin that lets your agent drive a real
phone with one tool, phone_exec. The model writes a short Python script with
the Phone Harness helpers pre-imported, you approve it, and the plugin runs it on
the phone and returns what it printed.
phone_exec(code='''
ensure_device()
open_app("Settings")
wait_stable()
print([o["text"] for o in screen()][:12])
''')
The plugin ships the phone-harness skill, which teaches the agent the helpers.
Install
You need OpenClaw 2026.10.9 or newer and uv on the machine that runs the Gateway.
openclaw plugins install clawhub:phone-harness
OpenClaw shows what the plugin can do and asks you to accept it. Where it cannot
ask, such as in a script or with /plugins install in a chat, run the command
again with --accept-capabilities once you have reviewed the plugin. A running
Gateway picks the plugin up straight away, so there is nothing to restart. The
first script fetches Phone Harness through uv, which takes a moment once.
To install from a checkout instead:
git clone https://github.com/phone-harness/phone-harness
cd phone-harness/integrations/openclaw
npm install --ignore-scripts && npm pack
openclaw plugins install npm-pack:./phone-harness-0.1.2.tgz
To use a cloud phone, sign in once on the Gateway machine:
uvx --from phone-harness==0.3.3 phone-harness cloud login
Phone Harness chooses the phone: an attached cloud phone, otherwise its default.
To force the phone on this machine, export PHONE_HARNESS_PLATFORM as ios or
android in the environment the Gateway runs in. iPhone control needs
Accessibility and Screen Recording for the app that runs the Gateway. Android
needs adb. install.md covers each.
Which agents get the tool
- The
codingandfulltool profiles includephone_exec, as does a config with no profile. - The
messagingandminimalprofiles do not, so a chat bot does not offer your phone by default. To add it, settools.alsoAllowto["phone_exec"]. - Sandboxed sessions never get the tool. A plugin runs inside the Gateway, so a script would run on the Gateway machine, outside the sandbox.
Approvals
Every phone_exec call asks you before it runs. The prompt shows the whole
script, with each run of spaces and line breaks shown as one space and any
invisible character spelled out, and you choose:
- Allow once runs this script.
- Always allow runs this script and every later script in the same session. It ends with the session, and a new conversation, a plugin reload or a Gateway restart asks again.
- Deny stops it.
Approve from the Control UI, or in a chat with /approve. If nobody can answer,
the script does not run. That includes openclaw agent from a terminal and
scheduled jobs. A prompt nobody answers expires after five minutes.
OpenClaw holds 512 characters in a prompt, so a script longer than 500 as shown is not offered for approval at all. The agent is told to split it into shorter scripts, and the phone keeps its state between them. With classifier auto-run on, the reason takes part of the prompt, so a script that asks has less room.
Only the owner can drive the phone. OpenClaw lets a chat answer its own approval prompts, so a sender it knows is not the owner is refused before any prompt.
Settings
-
Classifier auto-run is off unless you opt in. With it on, a script the classifier judges to be phone-only runs without asking. The classifier is a heuristic and can be bypassed, so a script may still reach this computer. Leave it off unless you accept that.
openclaw security auditreports it when it is on.- Plugin setting
plugins.entries.phone-harness.config.classifierAutoRun(boolean, default false). - Or export
PHONE_HARNESS_CLASSIFIER_AUTO_RUN=1in the environment the Gateway runs in. When that variable is set, it wins over the plugin setting.
While auto-run is on, a script still asks when it imports anything other than the phone helpers, reads or writes files, runs or evaluates code, uses Python internals, runs an adb shell command, or calls
send("raw", ...). What to do on the phone itself (sending, posting, buying, deleting, changing settings) is still the skill's consent rule: the agent asks you first. - Plugin setting
-
PHONE_HARNESS_PLATFORM, when you have exported it, is passed through. -
The agent workspace is
phone-harness/agent-workspacein OpenClaw's state directory, orPH_AGENT_WORKSPACEif that is set.
How it works
- The model calls
phone_exec(code, timeout_s=300). The limit is 1800 seconds. - A
before_tool_callhook shows you the whole script and asks you to approve it, or refuses a script too long to show whole. - Subagents take turns on the phone. After 120 s the tool reports it as busy.
- The plugin pipes the script to the pinned Phone Harness through
uvx, withPH_AGENT_WORKSPACEandPH_CLIENT=openclaw. The script's environment leaves outOPENCLAW_*and anything named like a credential (API keys, tokens, secrets, passwords), but keeps anyPHONE_HARNESS_*you exported. - The result is what the script printed, plus the exit code and the tail of stderr. A screenshot saved during the call is attached for the model to see. If the file is not a real image, the result says so and nothing is attached.
- A script past its timeout, or a cancelled call, is stopped with every process
it started. Each run tags its processes with an environment variable, and the
plugin notes which processes it sees under the script. Stopping kills the
script's process tree, including processes in their own session or process
group, every process that carries the tag or was noted, even after its parent
exits, and their process groups, and repeats until none is left, for up to five
seconds. The phone is free again only then. On Windows, the plugin lists the
script's process tree, runs
taskkillon it, waits for it to finish, and checks that the script and every process that was under it are gone. If a stop cannot be confirmed, the result says so and the phone stays reserved until it can be. A process that detaches before the plugin notes it can outlive the call if it drops the tag, or on macOS if it is one of Apple's own programs, whose environment macOS hides. On Windows, a process whose parent has already exited is no longer in the tree.
What it touches
- Runs model-written Python on the Gateway machine, as the user running OpenClaw.
- Taps and types into your real phone, with your apps and accounts.
- Screenshots are PNG files in the system temp folder.
- A cloud phone streams its screen to Phone Harness Cloud and uses Phone Harness's own cloud sign-in. A rented cloud Android bills by the minute.
Limits
- The approval covers
phone_exec. An agent that also has OpenClaw'sexectool and aphone-harnesscommand can reach the phone through the shell, under your exec approvals instead. Denyexecfor that agent if you want every phone script to come through this gate. - No password-vault integration: the agent never types passwords, PINs or 2FA codes. Hand the phone back to the user for those.
back()is Android-only; on an iPhone the agent taps the app's back arrow.
Development
npm install
npm run build # dist/ and skills/, built from the repo's SKILL.md
npm test
The Phone Harness release is pinned on one line: PHONE_HARNESS_VERSION at the
top of src/phone-exec.ts. The uvx commands in this README name the same
release, and tests/test_openclaw_plugin.py checks that they do. classifier.py
is the Hermes plugin's classifier, and the same tests keep the two identical.
Phone Harness sends usage data, including scripts and what they print, to PostHog.
To turn it off, set DO_NOT_TRACK=1 in the Gateway's environment, or run
uvx --from phone-harness==0.3.3 phone-harness config set telemetry false as the
user the Gateway runs as.
