Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
133 commits
Select commit Hold shift + click to select a range
0ab3c51
feat(harmony): add HarmonyOS emulator discovery and boot
latekvo Aug 10, 2026
83b485c
Merge remote-tracking branch 'origin/main' into feat/harmonyos-emulator
latekvo Aug 10, 2026
704f9f6
fix(harmony): drop two exports nothing imports
latekvo Aug 10, 2026
364223c
fix(harmony): bound the -list call under the discovery deadline
latekvo Aug 10, 2026
7dd0717
feat(harmony): drive real HarmonyOS devices over hdc
latekvo Aug 10, 2026
a9dd876
Merge remote-tracking branch 'origin/main' into feat/harmonyos-emulator
latekvo Aug 10, 2026
e2cc063
feat(harmony): drive a HarmonyOS device with the interaction tools
latekvo Aug 10, 2026
0a5dce5
feat(harmony): batch and diff on a HarmonyOS device
latekvo Aug 10, 2026
26b892c
fix(harmony): scope the screenshot-diff rotation guard to a live capture
latekvo Aug 10, 2026
63bba28
docs(harmony): name HarmonyOS where a tool description lists its plat…
latekvo Aug 10, 2026
afca62f
chore(harmony): trim comments that restate the error beside them
latekvo Aug 10, 2026
44e56d2
fix(harmony): say the panel is off even when the dump has windows
latekvo Aug 10, 2026
727e385
docs(harmony): name every tool that returns 501, and why each does
latekvo Aug 10, 2026
78f260c
feat(harmony): hand back the connect key a booted emulator registers as
latekvo Aug 11, 2026
af1e94e
test(harmony): pin that boot-device hands back a drivable id
latekvo Aug 11, 2026
fbbc394
chore(harmony): cut the emulator-boot comments to what the code canno…
latekvo Aug 11, 2026
cd49be2
Merge remote-tracking branch 'origin/main' into feat/harmonyos-emulator
latekvo Aug 11, 2026
de209d7
test(harmony): pin the hdc row shape against real tconn output
latekvo Aug 11, 2026
261a830
fix(harmony): stop the boot budget from running out inside the start
latekvo Aug 11, 2026
f41ae42
docs(harmony): say what the emulator was verified against
latekvo Aug 11, 2026
d677d3b
fix(harmony): reject a prototype key name instead of shelling it out
latekvo Aug 11, 2026
1840a2b
docs(harmony): say what the unsupported tools actually return, and na…
latekvo Aug 11, 2026
9f7bb55
test(harmony): cover the four tool branches that carry real logic
latekvo Aug 11, 2026
2ec6000
fix(harmony): clip describe frames, keep what a collapsed wrapper kne…
latekvo Aug 11, 2026
90be9c5
fix(harmony): stop boot-device adopting a device it did not start
latekvo Aug 11, 2026
4807a3b
fix(harmony): ask the manager whether it died when there is no connec…
latekvo Aug 11, 2026
e41cd44
fix(harmony): make the no-connector liveness check actually fire, and…
latekvo Aug 11, 2026
66a26b1
test(harmony): make the new tests hermetic, leak-free and able to fail
latekvo Aug 11, 2026
a3a5d78
fix(harmony): let a force restart outlast the shutdown it asked for
latekvo Aug 12, 2026
508bafa
docs(harmony): state the measured connect key once, not twice
latekvo Aug 12, 2026
4ade1c2
fix(harmony): close the device-found gaps the review round proved
latekvo Aug 13, 2026
95e81ad
fix(harmony): stop open-url and restart-app reporting more than the d…
latekvo Aug 14, 2026
173ffe5
fix(button): say which buttons the platform does take
latekvo Aug 14, 2026
509deac
fix(harmony): match aa's own error-code line, not the bare digits
latekvo Aug 14, 2026
64a05ba
fix(harmony): resolve the key a restarted instance comes back on
latekvo Aug 14, 2026
57a3e8e
fix(harmony): refuse a button press the suspended panel would swallow
latekvo Aug 14, 2026
bc44e3a
fix(harmony): keep a layout container that knows something itself
latekvo Aug 14, 2026
4d13ab5
fix(await-ui-element): keep a standing warning off a successful wait
latekvo Aug 14, 2026
9664b94
fix(harmony): try $DEVECO_STUDIO_HOME ahead of the default, not inste…
latekvo Aug 14, 2026
5242c03
fix(screenshot-diff): remove the capture it has copied in
latekvo Aug 14, 2026
c156057
test(harmony): pin the dispatch entry each harmony tool hangs from
latekvo Aug 14, 2026
ef98e5d
test(harmony): cover a value carrying more than one quote in shellQuote
latekvo Aug 14, 2026
afbe0ce
docs(await-screen-idle): describe the note returned with an unsettled…
latekvo Aug 14, 2026
a0a1493
fix(screenshot-diff): drop the capture even when the copy fails
latekvo Aug 14, 2026
d65017b
fix(boot-device): stop the harmony boot latching a verdict off one ea…
latekvo Aug 14, 2026
66efe56
fix(boot-device): refuse the harmony boot when the pre-start snapshot…
latekvo Aug 14, 2026
bc901b5
fix(boot-device): read a refused hdc listing as a refusal, not an emp…
latekvo Aug 14, 2026
24e7d0e
test(harmony): prime the hdc probe the dispatch cases do not depend on
latekvo Aug 14, 2026
db13bed
fix(harmony): catch the connector failure hdc reports on stderr with …
latekvo Aug 14, 2026
148402f
fix(boot-device): keep the harmony boot inside the budget it was given
latekvo Aug 14, 2026
a4dd81f
fix(harmony): report the screen-off caveat from await-screen-idle too
latekvo Aug 14, 2026
cc2a9e4
test(harmony): pin the behaviours a mutation run found nothing held
latekvo Aug 14, 2026
cd6cf1b
fix(harmony): classify hdc's unprefixed failures and the boot's own r…
latekvo Aug 14, 2026
cc87916
fix(harmony): tell a cut-off command from a connector that never reac…
latekvo Aug 14, 2026
21187f9
test(harmony): pin the uitest queue, sentinel parsing and role table
latekvo Aug 14, 2026
ed61b88
fix(harmony): keep the boot's own listings inside the caller's budget
latekvo Aug 14, 2026
5a53003
fix(await-screen-idle): report why the screen was never read
latekvo Aug 14, 2026
7e73156
test(wait): pin the caveats both wait tools fold onto a timeout
latekvo Aug 14, 2026
e8bf073
fix(harmony): give a describe read only the wait's remaining budget
latekvo Aug 14, 2026
dacdb2c
fix(harmony): refuse a boot target the host does not have
latekvo Aug 14, 2026
fda201c
fix(harmony): classify a killed CLI as a timeout, take the keys the o…
latekvo Aug 14, 2026
e981ec5
fix(harmony): only refuse a boot against a listing that has rows
latekvo Aug 14, 2026
bc7629a
fix(harmony): bound an interaction to one budget, and refuse a panel …
latekvo Aug 14, 2026
2322bfc
fix(harmony): fail a stop closed, and stop reading an unreadable list…
latekvo Aug 14, 2026
7f734eb
docs(harmony): say which tools the platform actually has, and name it…
latekvo Aug 14, 2026
54a56f6
test(harmony): pin the budget split the interaction ceiling depends on
latekvo Aug 15, 2026
56f7ef3
fix(harmony): read hdc's own words, not whatever came back beside them
latekvo Aug 17, 2026
9539893
fix(harmony): name the condition that failed, not the area it failed in
latekvo Aug 17, 2026
641b6c0
fix(describe,gesture-tap): stop offering HarmonyOS interactions it ca…
latekvo Aug 17, 2026
7d3523d
fix(flows): refuse a run device the engine has no arm for
latekvo Aug 17, 2026
e6afdfc
docs(flows): say what a run on these two platforms actually does
latekvo Aug 17, 2026
8027265
fix(harmony): cap each describe leg at its own ceiling
latekvo Aug 17, 2026
c232384
Merge remote-tracking branch 'origin/main' into feat/harmonyos-emulator
latekvo Aug 17, 2026
0eace9a
test(harmony): stop pinning budget boundaries to the millisecond
latekvo Aug 17, 2026
0922f9e
Merge origin/main into harmony branch
latekvo Aug 17, 2026
dbd37c8
fix(harmony): read the panel's size and power off one dump line
latekvo Aug 17, 2026
c4f81cd
fix(harmony): allowlist the power states a touch can land in
latekvo Aug 17, 2026
86477c0
fix(flows): refuse the gestures HarmonyOS cannot make, before dispatc…
latekvo Aug 17, 2026
72163c7
fix(harmony): name an unreadable device table, and redact a secret a …
latekvo Aug 17, 2026
b99dabd
fix(secrets): redact the pieces a backend really sends, in one pass
latekvo Aug 17, 2026
1c34379
fix(secrets): match by hand, so a large secret cannot be leaked by th…
latekvo Aug 17, 2026
7ee564b
test(harmony): pin the invariants three fixtures were too kind to catch
latekvo Aug 17, 2026
584ce22
test(harmony): refuse a silent launch and open, as the stop path alre…
latekvo Aug 18, 2026
87cb8cf
Merge branch 'main' into feat/harmonyos-emulator
latekvo Aug 21, 2026
4c43e90
Merge branch 'main' into feat/harmonyos-emulator
latekvo Aug 23, 2026
b2e7c17
Merge branch 'main' into feat/harmonyos-emulator
latekvo Aug 23, 2026
e06e562
Merge branch 'main' into feat/harmonyos-emulator
latekvo Aug 24, 2026
ff5b85a
fix(harmony): close out the device-review round
latekvo Aug 24, 2026
2c74374
fix(harmony): review-round followups
latekvo Aug 24, 2026
0131374
docs(harmony): align two comments with what the code now does
latekvo Aug 24, 2026
b65e66f
Merge origin/main into feat/harmonyos-emulator
latekvo Aug 25, 2026
03d62ab
feat(harmony): multi-touch over `uinput`, so `gesture-pinch` runs on …
latekvo Sep 2, 2026
bcb4df8
docs(harmony): tighten the uinput diagnostic comment
latekvo Sep 2, 2026
38ca868
Merge origin/main into feat/harmonyos-emulator
latekvo Sep 4, 2026
82bfe8f
Merge origin/main into feat/harmonyos-emulator
latekvo Sep 7, 2026
d165c0c
Merge remote-tracking branch 'origin/main' into feat/harmonyos-emulator
latekvo Sep 28, 2026
6a95dd7
Merge remote-tracking branch 'origin/main' into feat/harmonyos-emulator
latekvo Oct 1, 2026
6271b32
Merge remote-tracking branch 'origin/main' into feat/harmonyos-emulator
latekvo Oct 2, 2026
f6c23aa
Merge remote-tracking branch 'origin/main' into HEAD
latekvo Oct 12, 2026
c1d15c7
revert(secrets): keep the redaction rewrite out of the HarmonyOS PR
latekvo Oct 12, 2026
5eb8479
fix(await-ui-element): a suspended HarmonyOS panel times out unreadab…
latekvo Oct 12, 2026
e427a18
refactor(await-ui-element): drop the unreachable final-read stale check
latekvo Oct 12, 2026
1f0cb47
chore(harmony): trim comments to what the code cannot say
latekvo Oct 12, 2026
fdaf949
test(harmony): trim test comments; restore main's keyboard-secrets test
latekvo Oct 12, 2026
199a76c
test(harmony): mock hdcFileRecv in queue tests, sort start logs numer…
latekvo Oct 12, 2026
d0a334a
test(harmony): widen two wait budgets that flaked under load
latekvo Oct 12, 2026
ed6ae93
Merge remote-tracking branch 'origin/main' into HEAD
latekvo Oct 12, 2026
e516cf2
fix(harmony): keep wait-tool notes and capture cleanup on HarmonyOS only
latekvo Oct 12, 2026
8c76d6f
fix(harmony): inject nothing once an input call is cancelled
latekvo Oct 12, 2026
78e6533
test(await-screen-idle): pin that a degraded simulator settles withou…
latekvo Oct 12, 2026
092bca7
fix(describe): leave the Chromium header's gesture list as on main
latekvo Oct 12, 2026
18b26d1
fix(await-ui-element): keep a stale final read from timing out as unmet
latekvo Oct 12, 2026
b302c2d
revert(flow-run): leave the tool-step capability gate to #796
latekvo Oct 12, 2026
ded66f5
fix(describe): restore main's gesture-space sentence
latekvo Oct 12, 2026
5a52ca4
Merge branch 'main' into feat/harmonyos-emulator
latekvo Oct 14, 2026
8099514
Merge remote-tracking branch 'origin/main' into feat/harmonyos-emulator
latekvo Oct 20, 2026
32d13f2
feat(harmony): gate HarmonyOS support behind the harmonyos flag
latekvo Oct 20, 2026
9ce8770
fix(harmony): review fixes, and run the harmony tests with the flag on
latekvo Oct 20, 2026
37c8eb5
docs(harmony): move HarmonyOS guidance behind the harmonyos flag
latekvo Oct 20, 2026
7bedc40
feat(harmony): hide HarmonyOS text in tool descriptions behind the ha…
latekvo Oct 20, 2026
33a1715
test(harmony): pin the flag-off tool catalog and boot-device error to…
latekvo Oct 20, 2026
39dfebc
fix(harmony): scale input to the rotated display in landscape
latekvo Oct 20, 2026
1520d1d
Merge branch 'harmony-flag-tagging' into feat/harmonyos-emulator
latekvo Oct 20, 2026
92949ca
Merge branch 'harmony-flag-docs' into feat/harmonyos-emulator
latekvo Oct 20, 2026
d2144a3
fix: scrub shell-quoted secrets from errors; correct two HarmonyOS de…
latekvo Oct 20, 2026
b035135
chore(harmony): format registry types, sync the HarmonyOS reference w…
latekvo Oct 20, 2026
e1cdc74
test(harmony): pin the no-flow-tree arms of e2e step scoring and the …
latekvo Oct 21, 2026
d708762
Merge remote-tracking branch 'origin/main' into feat/harmonyos-emulator
latekvo Oct 21, 2026
8e5ce6e
Merge remote-tracking branch 'origin/main' into feat/harmonyos-emulator
latekvo Oct 22, 2026
7138191
feat(harmony): reinstall-app installs a .hap over hdc
latekvo Oct 22, 2026
daf3843
test(harmony): give the failed-dump await case room for one read unde…
latekvo Oct 22, 2026
0362e84
docs(harmony): call HarmonyOS just HarmonyOS outside its platform sec…
latekvo Oct 22, 2026
817e9d8
fix(harmony): drop the em dashes the platform added to tool descriptions
latekvo Oct 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ Argent drives a growing set of targets through a single toolkit, each with the r
| **Android** | Emulators (AVDs) and physical devices over adb | Touch / gesture |
| **TV** | Apple TV (tvOS), Android TV / Google TV, Amazon Fire TV (Vega) | D-pad / remote |
| **Desktop & web** | Electron and Chromium apps (incl. React Native Web / Expo web) over CDP | Mouse / keyboard |
| **HarmonyOS** | Phones over `hdc`, and DevEco Studio emulator instances | Touch / gesture |

> **HarmonyOS drives whatever `hdc` is connected to.** Connect a phone with USB debugging enabled and it appears as `harmony-<connectKey>` (`kind: "device"`), driven by `describe`, `screenshot`, `screenshot-diff`, `gesture-tap`, `gesture-swipe`, `keyboard`, `button`, `launch-app`, `restart-app`, `open-url`, `run-sequence` and the two `await-*` tools - all through the device's own `uitest`. DevEco Studio's emulator instances appear separately as `harmony-emulator-<name>`, which `boot-device` starts; it then waits for the instance to register with `hdc` and returns that connect key, so a booted emulator is driven by the same tools through the same id shape as a phone. Huawei restricts the emulator **image** download to mainland China, so on a host outside it no instance can be created until an image is obtained. Multi-touch and free-form touch (`gesture-pinch`, `gesture-rotate`, `gesture-custom`) return `501 NotImplementedOnPlatform`, on both device kinds: `uitest uiInput` injects only whole gestures (click / longClick / swipe / drag / fling), one contact at a time, and the level below is shut - `hdc shell` is uid 2000 and `/dev/input` is `Permission denied` on a retail phone and on Huawei's own emulator image alike, where `hdc smode` answers `Cannot set root run mode in undebuggable version`. `rotate`, the profilers and the debugger also return 501: the device exposes no orientation CLI, and argent's profiler and debugger surfaces have no HarmonyOS ingest path.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat]: Docs promise 501 for the unsupported HarmonyOS tools; every one of them actually returns 400.

The README (and two skill docs) state that the HarmonyOS-unsupported tools return 501 NotImplementedOnPlatform; every one of them returns 400 instead. Trigger: call gesture-pinch, gesture-rotate, gesture-custom, rotate, any profiler tool, or any debugger tool with a harmony-<connectKey> id. None of those tools declares a harmony block in its capability (gesture-pinch/index.ts:66, gesture-rotate/index.ts:86, gesture-custom/index.ts:51, rotate/index.ts:20, native-profiler-start.ts:51, and RN_ONLY_TOOL_CAPABILITY / DEBUGGER_TOOL_CAPABILITY in debugger/debugger-service-ref.ts:60 and :20), so the HTTP capability gate at http.ts:791-808 throws UnsupportedOperationError and answers 400 with TOOL_CAPABILITY_UNSUPPORTED_OPERATION and the message "Tool '' is not supported on harmony device." The 501 / NotImplementedOnPlatformError path is only reachable from dispatchByPlatform (utils/cross-platform-tool.ts:147) when a capability does declare harmony but wires no branch — and every tool in this PR that declares harmony: also wires a handler, so no shi …

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat]: Fixed in 1840a2b.

Confirmed against a live server, not just a read: stood up createHttpApp from this branch's dist/ with the real tool definitions and POSTed a harmony- id.

gesture-pinch           -> HTTP 400  {"error":"Tool 'gesture-pinch' is not supported on harmony device (no harmony support declared)."}
gesture-rotate          -> HTTP 400
gesture-custom          -> HTTP 400
rotate                  -> HTTP 400
native-profiler-start   -> HTTP 400
debugger-component-tree -> HTTP 400

Re-run with harmony-emulator-argent_phone: identical 400s ("…on harmony emulator"), so the README's explicit "on both device kinds" was wrong on both kinds.

I checked whether this is loose house style before changing anything, and it is not. README.md:32 was the only occurrence of 501 in any markdown in the repo, and the nearest twin describing the identical mechanism names the gate and the literal message instead: argent-metro-debugger/SKILL.md:12 — "are RN-only and reject Chromium at the capability gate with Tool 'X' is not supported on chromium app".

Worse than the wrong number, the class named is the opposite of the reason printed beside it. capability.ts:34-43 reserves NotImplementedOnPlatformError for "capability could say yes once filled in; the work just hasn't happened", and gives pinch on Android — adb has no multi-touch as the UnsupportedOperationError example. One contact and /dev/input denied is that first case verbatim. So the docs now say the tools are refused at the capability gate with the literal message they actually produce; the code is correct as it stands.


## Capabilities

Expand Down Expand Up @@ -56,6 +59,7 @@ Argent drives a growing set of targets through a single toolkit, each with the r
- For iOS / tvOS: macOS with **Xcode** installed (Apple TV uses tvOS simulators — Xcode downloads the tvOS runtime on demand)
- For Android / Android TV: **Android SDK Platform Tools** (`adb`) on `PATH`, and the **Android Emulator** package if you want to boot AVDs from Argent. Create AVDs via Android Studio or `avdmanager`.
- For Fire TV (Vega): the **Vega SDK** (`vega` CLI) on `PATH`
- For HarmonyOS: **DevEco Studio**, which ships both binaries Argent drives - `hdc` (the device connector, for phones) and the `Emulator` manager. Both are looked up inside DevEco Studio's install root - the macOS app bundle by default, `$DEVECO_STUDIO_HOME` on any other host. `hdc` also falls back to `PATH` for hosts that installed the OpenHarmony command-line tools without the IDE; `Emulator` never does, since it is too generic a name to match safely. Driving a phone needs only `hdc`. Huawei restricts the emulator image download to mainland China, so outside it no emulator instance can be created.
- For Electron / Chromium: nothing extra to control an already-running app - just launch it with `--remote-debugging-port`, or let Argent spawn your Electron app for you

##### Linux host: extra prerequisites for Android emulators
Expand Down
8 changes: 8 additions & 0 deletions packages/registry/src/failure-codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ export const FAILURE_CODES = {
VEGA_SCREENSHOT_FAILED: "VEGA_SCREENSHOT_FAILED",
VEGA_TEXT_INVALID: "VEGA_TEXT_INVALID",

HARMONY_HDC_NOT_FOUND: "HARMONY_HDC_NOT_FOUND",
HARMONY_HDC_COMMAND_FAILED: "HARMONY_HDC_COMMAND_FAILED",
HARMONY_DEVICE_UNREACHABLE: "HARMONY_DEVICE_UNREACHABLE",
HARMONY_SHELL_NO_STATUS: "HARMONY_SHELL_NO_STATUS",
HARMONY_FILE_TRANSFER_FAILED: "HARMONY_FILE_TRANSFER_FAILED",
HARMONY_UITEST_FAILED: "HARMONY_UITEST_FAILED",
HARMONY_ABILITY_START_FAILED: "HARMONY_ABILITY_START_FAILED",

ANDROID_ADB_NOT_FOUND: "ANDROID_ADB_NOT_FOUND",
ANDROID_EMULATOR_NOT_FOUND: "ANDROID_EMULATOR_NOT_FOUND",
ANDROID_ADB_COMMAND_FAILED: "ANDROID_ADB_COMMAND_FAILED",
Expand Down
21 changes: 19 additions & 2 deletions packages/registry/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export interface ToolContext extends InvokeToolOptions {

// ── Device + Capability Types ──

export type Platform = "ios" | "android" | "ios-remote" | "chromium" | "vega";
export type Platform = "ios" | "android" | "ios-remote" | "chromium" | "vega" | "harmony";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Widening Platform also admits harmony ids into subsystems that have no harmony branch and no capability gate. flow-execute accepts device: "harmony-<connectKey>", validates, and then fails per-step with ui-tree matching is not supported on platform "harmony" - verified against a live emulator with a one-step flow. Flows and the step recorder are not in the PR's supported/refused tables either way, so an agent has nothing to tell it in advance.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat]: Acknowledged, and deliberately not gated in code in 4ade1c2. A flow with a harmony device fails per-step only if a step reads the tree — a flow of pure tool: calls against the harmony tool surface runs fine, since binding is device-agnostic and the failure you measured comes from fetchFlowTree naming the platform. The gap was that the PR never documented flows either way; the passing-target docstrings now note flows have no harmony tree adapter rather than leaving an agent to discover the per-step failure.


export type DeviceKind = "simulator" | "emulator" | "vvd" | "device" | "app" | "unknown";

Expand Down Expand Up @@ -195,6 +195,16 @@ export interface ToolCapability {
vvd?: boolean;
device?: boolean;
};
/**
* HarmonyOS support. `device` is a phone reached over `hdc`; `emulator` is a
* DevEco Studio instance, which only `boot-device` addresses — every
* interaction tool drives a *connected* target, and a booted emulator presents
* as one of those, so interaction tools declare `device` alone.
*/
harmony?: {
emulator?: boolean;
device?: boolean;
};
/** Optional refiner. Returns true if this device is supported. */
supports?: (device: DeviceInfo) => boolean;
}
Expand All @@ -219,7 +229,14 @@ export interface ToolCapability {
* On a missing binary, the HTTP layer returns 424 Failed Dependency with an
* install hint the agent can surface verbatim.
*/
export type ToolDependency = "adb" | "xcrun" | "emulator" | "sim-remote" | "vega";
export type ToolDependency =
| "adb"
| "xcrun"
| "emulator"
| "sim-remote"
| "vega"
| "harmony-emulator"
| "hdc";

// ── Tool Types ──

Expand Down
4 changes: 2 additions & 2 deletions packages/skills/rules/argent.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ alwaysApply: true
---

<description>
If argent is installed and configured in this environment, its MCP tools are the preferred form of interaction with the application for iOS simulator, Android emulator, Chromium (CDP) app, and Vega (Amazon Fire TV) device control; otherwise see `<availability_check>` below before attempting any argent workflow. A "Chromium (CDP) app" is any Chromium runtime exposing a Chrome DevTools Protocol endpoint — an Electron app, or any Chromium-family browser (Chrome/Brave/Edge) launched with `--remote-debugging-port`; all are driven through the same tool surface and tagged `platform: "chromium"`. A "Vega device" is a virtual device (VVD) or physical unit — driven by tv-remote (D-pad) and tagged `platform: "vega"`.
If argent is installed and configured in this environment, its MCP tools are the preferred form of interaction with the application for iOS simulator, Android emulator, Chromium (CDP) app, Vega (Amazon Fire TV), and HarmonyOS device control; otherwise see `<availability_check>` below before attempting any argent workflow. A "Chromium (CDP) app" is any Chromium runtime exposing a Chrome DevTools Protocol endpoint — an Electron app, or any Chromium-family browser (Chrome/Brave/Edge) launched with `--remote-debugging-port`; all are driven through the same tool surface and tagged `platform: "chromium"`. A "Vega device" is a virtual device (VVD) or physical unit — driven by tv-remote (D-pad) and tagged `platform: "vega"`. A "HarmonyOS device" is a phone connected over `hdc` (id `harmony-<connectKey>`, `kind: "device"`, tagged `platform: "harmony"`) — driven by the normal touch tools, with multi-touch and free-form touch (`gesture-pinch`, `gesture-rotate`, `gesture-custom`), `rotate`, the profilers and the debugger returning 501. HarmonyOS entries with `kind: "emulator"` (id `harmony-emulator-<name>`) are DevEco Studio instances that only `boot-device` accepts, so never pick one of those as an interaction target - boot it and drive the `harmony-<connectKey>` id that `boot-device` returns.
Running MCP server and managing the Argent toolkit utilises `argent` command - if asked use `argent --help` for reference.
To check current version of MCP server run `argent --version` command.

Expand Down Expand Up @@ -102,7 +102,7 @@ Load the matching skill before starting work and executing tools from argent-mcp
procedure and edge-case handling for each workflow.

PLATFORM DETECTION
If the user did not specify a platform, call `list-devices` first and pick the booted target — do not default to iOS. Vega (Amazon Fire TV) devices appear as `platform:"vega"`, when present load `argent-tv-interact`
If the user did not specify a platform, call `list-devices` first and pick the booted target — do not default to iOS. Vega (Amazon Fire TV) devices appear as `platform:"vega"`, when present load `argent-tv-interact`. HarmonyOS entries appear as `platform:"harmony"`; pick one only when the user asked for HarmonyOS, and pick the `kind:"device"` entry (`state:"Connected"`) — a `kind:"emulator"` entry is a boot target, not an interaction target.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entry now covers HarmonyOS, but the argent-device-interact router further down the same file still reads "Phone/tablet iOS and Android only" for tapping, swiping, typing, gestures and screenshots. An agent following the router will not load the interaction skill for a HarmonyOS target even though every one of those tools now supports it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat]: Fixed in 4ade1c2. The router entry no longer reads "Phone/tablet iOS and Android only" — it now routes iOS, Android AND HarmonyOS touch interaction to argent-device-interact, with the TV carve-out intact.


iOS SIMULATOR SETUP
Skill: `argent-ios-simulator-setup`
Expand Down
4 changes: 2 additions & 2 deletions packages/skills/skills/argent-device-interact/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: argent-device-interact
description: Interact with an iOS simulator, Android emulator, or Chromium (CDP) app using argent MCP tools. Use when tapping UI elements, performing gestures, scrolling/swiping, typing text, pressing hardware buttons, launching apps, opening URLs, taking screenshots, waiting for an element to appear or disappear, or checking visible app state after interactions.
description: Interact with an iOS simulator, Android emulator, HarmonyOS device, or Chromium (CDP) app using argent MCP tools. Use when tapping UI elements, performing gestures, scrolling/swiping, typing text, pressing hardware buttons, launching apps, opening URLs, taking screenshots, waiting for an element to appear or disappear, or checking visible app state after interactions.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Unified tool surface" paragraph a few lines below still says ids auto-dispatch "iOS vs Android based on its shape (UUID -> iOS simulator, chromium-cdp-<port> -> Chromium (CDP) app, anything else -> Android adb serial)". classifyDevice now checks the harmony- prefix before the Android fallback, so the shape list is missing the two ids this PR introduces.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat]: Fixed in 4ade1c2. The unified-surface paragraph now names the two harmony id shapes the classifier checks before the Android fallback: harmony-<connectKey> → HarmonyOS device, harmony-emulator-<name> → a boot target.

---

## Unified tool surface
Expand All @@ -21,7 +21,7 @@ For platform-specific caveats (Metro `adb reverse`, locked-screen describe error

If you delegate simulator tasks to sub-agents, make sure they have MCP permissions.

Use `list-devices` to get a target id. Results are tagged with `platform` (`ios`, `android`, or `chromium`); booted/ready devices come first. Pick the first entry that matches the platform you need — if none are ready, call `boot-device` with `udid` (iOS), `avdName` (Android), or `electronAppPath` (boots an Electron app as a `chromium` device). A Chromium browser already running with a CDP port shows up directly — no `boot-device` needed. See `argent-ios-simulator-setup` / `argent-android-emulator-setup` for full setup flow.
Use `list-devices` to get a target id. Results are tagged with `platform` (`ios`, `ios-remote`, `android`, `chromium`, `vega`, or `harmony`); booted/ready devices come first. A `vega` target is a TV; drive it with `argent-tv-interact` (see the note above). A `harmony` target with `kind: "device"` is a HarmonyOS phone on `hdc`: the tools in this skill drive it, except `gesture-pinch`, `gesture-rotate`, `gesture-custom` and `rotate`, which return 501 — the device injects whole gestures one contact at a time, so there is no long-press or multi-finger path. Use `button` for home/back/power only. A `harmony` target with `kind: "emulator"` is a DevEco Studio instance that only `boot-device` accepts; boot it and drive the `harmony-<connectKey>` id it returns. Pick the first entry that matches the platform you need — if none are ready, call `boot-device` with `udid` (iOS), `avdName` (Android), or `electronAppPath` (boots an Electron app as a `chromium` device). A Chromium browser already running with a CDP port shows up directly — no `boot-device` needed. See `argent-ios-simulator-setup` / `argent-android-emulator-setup` for full setup flow.

**Load tool schemas before first use.** Gesture tools (`gesture-tap`, `gesture-swipe`, `gesture-pinch`, `gesture-rotate`, `gesture-custom`) may be deferred — their parameter schemas are not loaded until fetched. Always use ToolSearch to load the schemas of all gesture tools you plan to use **before** calling any of them. If you skip this step, parameters may be coerced to strings instead of numbers, causing validation errors.

Expand Down
1 change: 1 addition & 0 deletions packages/telemetry/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const PLATFORMS = [
"android",
"chromium",
"vega",
"harmony",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Telemetry.md still discloses the platform field as "whether Argent is used in connection with Android, iOS, tvOS (Apple TV), Android TV, VegaOS, or a Chromium-based target". With this value added, the disclosure under-declares what is sent.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat]: Fixed in 4ade1c2. Telemetry.md now discloses HarmonyOS in the platform list alongside Android, iOS, tvOS, Android TV, VegaOS and Chromium.

"tvos",
"android-tv",
] as const;
Expand Down
12 changes: 10 additions & 2 deletions packages/tool-server/src/tools/await-screen-idle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
ToolDefinition,
} from "@argent/registry";
import { chromiumCdpRef, type ChromiumCdpApi } from "../../blueprints/chromium-cdp";
import { resolveDevice } from "../../utils/device-info";
import { resolveDevice, harmonyConnectKey } from "../../utils/device-info";
import { isTvOsSimulator } from "../../utils/ios-devices";
import { isAndroidTv } from "../../utils/adb";
import { assertSupported } from "../../utils/capability";
Expand All @@ -18,6 +18,7 @@ import type { DescribeNode, DescribeTreeData } from "../describe/contract";
import { describeIos, iosRequires } from "../describe/platforms/ios";
import { describeAndroid, androidRequires } from "../describe/platforms/android";
import { describeChromium } from "../describe/platforms/chromium";
import { describeHarmony, harmonyRequires } from "../describe/platforms/harmony";

export const AWAIT_SCREEN_IDLE_TOOL_ID = "await-screen-idle";

Expand All @@ -29,7 +30,9 @@ const zodSchema = z.object({
udid: z
.string()
.min(1)
.describe("Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id)."),
.describe(
"Target device id from `list-devices` (iOS UDID, Android serial, HarmonyOS id, or Chromium id)."
),
timeoutMs: z
.number()
.int()
Expand Down Expand Up @@ -72,6 +75,7 @@ const capability: ToolCapability = {
apple: { simulator: true, device: true },
android: { emulator: true, device: true, unknown: true },
chromium: { app: true },
harmony: { device: true },
};

// A cheap fingerprint of the screen: role + label + value + frame (rounded to
Expand Down Expand Up @@ -109,6 +113,9 @@ export function createAwaitScreenIdleTool(registry: Registry): ToolDefinition<Pa
if (device.platform === "android") {
return describeAndroid(registry, device.id, undefined, androidIsTv);
}
if (device.platform === "harmony") {
return describeHarmony(harmonyConnectKey(device.id));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errors from this fetch are dropped from the result, so a device that goes away mid-wait is reported as a screen that did not settle. Against an unreachable connect key the tool returns HTTP 200 {settled: false, waitedMs: 3002, polls: 16} after 16 consecutive transport failures, with nothing in the payload naming them. await-ui-element folds the same lastError into its note. On HarmonyOS this is the routine failure mode, since each read is several hdc round trips to a device that can drop out.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat]: Fixed in 4ade1c2. A device that goes away mid-wait was indistinguishable from a screen that stayed busy. await-screen-idle now folds the poll loop's lastError into a result note (last tree fetch failed: …), the same shape await-ui-element already uses — pinned against 16 consecutive transport failures returning settled:false with the note naming them.

}
return describeChromium(services.chromium as ChromiumCdpApi);
}

Expand Down Expand Up @@ -144,6 +151,7 @@ still before the timeout. Use after a launch/navigation to wait for the UI to re
assertSupported(AWAIT_SCREEN_IDLE_TOOL_ID, capability, device);
if (device.platform === "ios") await ensureDeps(iosRequires);
else if (device.platform === "android") await ensureDeps(androidRequires);
else if (device.platform === "harmony") await ensureDeps(harmonyRequires);

// Resolved once, outside the poll loop, like `isTvOs` — an unlisted
// serial's TV probe is never cached, so leaving it inside
Expand Down
12 changes: 10 additions & 2 deletions packages/tool-server/src/tools/await-ui-element/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
ToolDefinition,
} from "@argent/registry";
import { chromiumCdpRef, type ChromiumCdpApi } from "../../blueprints/chromium-cdp";
import { resolveDevice } from "../../utils/device-info";
import { resolveDevice, harmonyConnectKey } from "../../utils/device-info";
import { isTvOsSimulator } from "../../utils/ios-devices";
import { isAndroidTv } from "../../utils/adb";
import { assertSupported } from "../../utils/capability";
Expand All @@ -19,6 +19,7 @@ import { describeIos, iosRequires } from "../describe/platforms/ios";
import { describeAndroid, androidRequires } from "../describe/platforms/android";
import { describeChromium } from "../describe/platforms/chromium";
import { describeVega, vegaRequires } from "../describe/platforms/vega";
import { describeHarmony, harmonyRequires } from "../describe/platforms/harmony";
import {
selectorSchema,
nodeText,
Expand Down Expand Up @@ -57,7 +58,9 @@ const zodSchema = z
udid: z
.string()
.min(1)
.describe("Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id)."),
.describe(
"Target device id from `list-devices` (iOS UDID, Android serial, HarmonyOS id, or Chromium id)."
),
condition: z
.enum(["exists", "visible", "hidden", "text"])
.describe(
Expand Down Expand Up @@ -136,6 +139,7 @@ const capability: ToolCapability = {
android: { emulator: true, device: true, unknown: true },
chromium: { app: true },
vega: { vvd: true },
harmony: { device: true },
};

// ── Tree matching ────────────────────────────────────────────────────────
Expand Down Expand Up @@ -243,6 +247,9 @@ export function createAwaitUiElementTool(registry: Registry): ToolDefinition<Par
if (device.platform === "vega") {
return describeVega(device.id);
}
if (device.platform === "harmony") {
return describeHarmony(harmonyConnectKey(device.id));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tree this returns carries the asleep hint when the panel is off, but the hint is only folded into the note on the timeout path - a wait that succeeds says nothing about it. With powerStatus=POWER_STATUS_OFF and the lock screen as the last composited frame, {condition:"visible", selector:{text:"Aug 13"}} returned {success: true, elapsed: 742}, and the following gesture-tap returned {tapped: true} with the panel still off. A wait-then-tap loop runs to completion against a dead screen with every call reporting success.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat]: Fixed in 4ade1c2. A successful wait against the last-composited frame was the dishonest half — the hint only rode the timeout path. A success whose tree carried the hint now surfaces it in the result's note, so a wait-then-act loop learns the panel is off before the tap. Pinned: visible on the lock-screen date with the panel off returns success:true plus the asleep caveat.

}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return describeChromium(services.chromium as ChromiumCdpApi);
}

Expand Down Expand Up @@ -298,6 +305,7 @@ or before tapping an element that appears asynchronously.`,
if (device.platform === "ios") await ensureDeps(iosRequires);
else if (device.platform === "android") await ensureDeps(androidRequires);
else if (device.platform === "vega") await ensureDeps(vegaRequires);
else if (device.platform === "harmony") await ensureDeps(harmonyRequires);

// Resolve once, outside the poll loop — re-probing `xcrun` per fetch would
// blow the per-fetch budget for a fake UDID that never caches. Same for
Expand Down
Loading
Loading