Repository navigation
feat(harmony): drive HarmonyOS devices over hdc #767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 20 commits
0ab3c51
83b485c
704f9f6
364223c
7dd0717
a9dd876
e2cc063
0a5dce5
26b892c
63bba28
afca62f
44e56d2
727e385
78f260c
af1e94e
fbbc394
cd49be2
de209d7
261a830
f41ae42
d677d3b
1840a2b
9f7bb55
2ec6000
90be9c5
4807a3b
e41cd44
66a26b1
a3a5d78
508bafa
4ade1c2
95e81ad
173ffe5
509deac
64a05ba
57a3e8e
bc44e3a
4d13ab5
9664b94
5242c03
c156057
ef98e5d
afbe0ce
a0a1493
d65017b
66efe56
bc901b5
24e7d0e
db13bed
148402f
a4dd81f
cc2a9e4
cd6cf1b
cc87916
21187f9
ed61b88
5a53003
7e73156
e8bf073
dacdb2c
fda201c
e981ec5
bc7629a
2322bfc
7f734eb
54a56f6
56f7ef3
9539893
641b6c0
7d3523d
e6afdfc
8027265
c232384
0eace9a
0922f9e
dbd37c8
c4f81cd
86477c0
72163c7
b99dabd
1c34379
7ee564b
584ce22
87cb8cf
4c43e90
b2e7c17
e06e562
ff5b85a
2c74374
0131374
b65e66f
03d62ab
bcb4df8
38ca868
82bfe8f
d165c0c
6a95dd7
6271b32
f6c23aa
c1d15c7
5eb8479
e427a18
1f0cb47
fdaf949
199a76c
d0a334a
ed6ae93
e516cf2
8c76d6f
78e6533
092bca7
18b26d1
b302c2d
ded66f5
5a52ca4
8099514
32d13f2
9ce8770
37c8eb5
7bedc40
33a1715
39dfebc
1520d1d
92949ca
d2144a3
b035135
e1cdc74
d708762
8e5ce6e
7138191
daf3843
0362e84
817e9d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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"; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Widening
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
|
||
| export type DeviceKind = "simulator" | "emulator" | "vvd" | "device" | "app" | "unknown"; | ||
|
|
||
|
|
@@ -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; | ||
| } | ||
|
|
@@ -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 ── | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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. | ||
|
|
||
|
|
@@ -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. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This entry now covers HarmonyOS, but the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| iOS SIMULATOR SETUP | ||
| Skill: `argent-ios-simulator-setup` | ||
|
|
||
| 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. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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,
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| --- | ||
|
|
||
| ## Unified tool surface | ||
|
|
@@ -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. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ export const PLATFORMS = [ | |
| "android", | ||
| "chromium", | ||
| "vega", | ||
| "harmony", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| "tvos", | ||
| "android-tv", | ||
| ] as const; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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"; | ||
|
|
@@ -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"; | ||
|
|
||
|
|
@@ -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() | ||
|
|
@@ -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 | ||
|
|
@@ -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)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| } | ||
| return describeChromium(services.chromium as ChromiumCdpApi); | ||
| } | ||
|
|
||
|
|
@@ -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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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"; | ||
|
|
@@ -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, | ||
|
|
@@ -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( | ||
|
|
@@ -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 ──────────────────────────────────────────────────────── | ||
|
|
@@ -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)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| return describeChromium(services.chromium as ChromiumCdpApi); | ||
| } | ||
|
|
||
|
|
@@ -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 | ||
|
|
||
There was a problem hiding this comment.
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 returns400instead. Trigger: callgesture-pinch,gesture-rotate,gesture-custom,rotate, any profiler tool, or any debugger tool with aharmony-<connectKey>id. None of those tools declares aharmonyblock in itscapability(gesture-pinch/index.ts:66, gesture-rotate/index.ts:86, gesture-custom/index.ts:51, rotate/index.ts:20, native-profiler-start.ts:51, andRN_ONLY_TOOL_CAPABILITY/DEBUGGER_TOOL_CAPABILITYin debugger/debugger-service-ref.ts:60 and :20), so the HTTP capability gate at http.ts:791-808 throwsUnsupportedOperationErrorand answers400withTOOL_CAPABILITY_UNSUPPORTED_OPERATIONand the message "Tool '' is not supported on harmony device." The501/NotImplementedOnPlatformErrorpath is only reachable fromdispatchByPlatform(utils/cross-platform-tool.ts:147) when a capability does declareharmonybut wires no branch — and every tool in this PR that declaresharmony:also wires a handler, so no shi …There was a problem hiding this comment.
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
createHttpAppfrom this branch'sdist/with the real tool definitions and POSTed aharmony-id.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:32was the only occurrence of501in 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 withTool '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-43reservesNotImplementedOnPlatformErrorfor "capability could say yes once filled in; the work just hasn't happened", and gives pinch on Android — adb has no multi-touch as theUnsupportedOperationErrorexample. One contact and/dev/inputdenied 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.