@swmansion/argent 0.30.2-next.0 → 0.30.2-next.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24566,6 +24566,7 @@ function spawnToolsServer(paths, port, options = {}) {
24566
24566
  stdio: ["ignore", "pipe", logFd],
24567
24567
  env: buildToolsServerEnv(paths, port, process.env, options)
24568
24568
  });
24569
+ fs.closeSync(logFd);
24569
24570
  child.unref();
24570
24571
  const pid = child.pid;
24571
24572
  if (!pid) {
@@ -25155,6 +25156,21 @@ var FAILURE_CODES = {
25155
25156
  VEGA_ALREADY_RUNNING: "VEGA_ALREADY_RUNNING",
25156
25157
  VEGA_SCREENSHOT_FAILED: "VEGA_SCREENSHOT_FAILED",
25157
25158
  VEGA_TEXT_INVALID: "VEGA_TEXT_INVALID",
25159
+ HARMONY_DISABLED: "HARMONY_DISABLED",
25160
+ HARMONY_HDC_NOT_FOUND: "HARMONY_HDC_NOT_FOUND",
25161
+ HARMONY_EMULATOR_NOT_FOUND: "HARMONY_EMULATOR_NOT_FOUND",
25162
+ HARMONY_EMULATOR_COMMAND_FAILED: "HARMONY_EMULATOR_COMMAND_FAILED",
25163
+ HARMONY_HDC_COMMAND_FAILED: "HARMONY_HDC_COMMAND_FAILED",
25164
+ HARMONY_DEVICE_ID_INVALID: "HARMONY_DEVICE_ID_INVALID",
25165
+ HARMONY_DEVICE_UNREACHABLE: "HARMONY_DEVICE_UNREACHABLE",
25166
+ HARMONY_SHELL_NO_STATUS: "HARMONY_SHELL_NO_STATUS",
25167
+ HARMONY_FILE_TRANSFER_FAILED: "HARMONY_FILE_TRANSFER_FAILED",
25168
+ HARMONY_UITEST_FAILED: "HARMONY_UITEST_FAILED",
25169
+ HARMONY_DISPLAY_UNREADABLE: "HARMONY_DISPLAY_UNREADABLE",
25170
+ HARMONY_SCREEN_OFF: "HARMONY_SCREEN_OFF",
25171
+ HARMONY_ABILITY_START_FAILED: "HARMONY_ABILITY_START_FAILED",
25172
+ HARMONY_ABILITY_STOP_FAILED: "HARMONY_ABILITY_STOP_FAILED",
25173
+ HARMONY_REINSTALL_FAILED: "HARMONY_REINSTALL_FAILED",
25158
25174
  ANDROID_ADB_NOT_FOUND: "ANDROID_ADB_NOT_FOUND",
25159
25175
  ANDROID_EMULATOR_NOT_FOUND: "ANDROID_EMULATOR_NOT_FOUND",
25160
25176
  ANDROID_ADB_COMMAND_FAILED: "ANDROID_ADB_COMMAND_FAILED",
@@ -25392,6 +25408,12 @@ var FAILURE_CODES = {
25392
25408
  BOOT_ANDROID_FIRST_FRAME_TIMEOUT: "BOOT_ANDROID_FIRST_FRAME_TIMEOUT",
25393
25409
  BOOT_ANDROID_PACKAGE_MANAGER_UNAVAILABLE: "BOOT_ANDROID_PACKAGE_MANAGER_UNAVAILABLE",
25394
25410
  BOOT_ANDROID_COLD_BOOT_FAILED: "BOOT_ANDROID_COLD_BOOT_FAILED",
25411
+ BOOT_HARMONY_INSTANCE_NOT_FOUND: "BOOT_HARMONY_INSTANCE_NOT_FOUND",
25412
+ BOOT_HARMONY_TARGET_LIST_FAILED: "BOOT_HARMONY_TARGET_LIST_FAILED",
25413
+ BOOT_HARMONY_STOP_FAILED: "BOOT_HARMONY_STOP_FAILED",
25414
+ BOOT_HARMONY_STOP_TIMEOUT: "BOOT_HARMONY_STOP_TIMEOUT",
25415
+ BOOT_HARMONY_MANAGER_EXITED: "BOOT_HARMONY_MANAGER_EXITED",
25416
+ BOOT_HARMONY_START_FAILED: "BOOT_HARMONY_START_FAILED",
25395
25417
  /**
25396
25418
  * External device providers
25397
25419
  * @see [external-devices.ts](../../tool-server/src/utils/external-devices.ts)
@@ -25421,6 +25443,8 @@ var FAILURE_COMMANDS = [
25421
25443
  "adb",
25422
25444
  "emulator",
25423
25445
  "vega",
25446
+ "hdc",
25447
+ "deveco_emulator",
25424
25448
  "xcrun_simctl",
25425
25449
  "xctrace",
25426
25450
  "native_devtools",
@@ -25554,7 +25578,7 @@ async function getResolvedToolsUrl() {
25554
25578
  const link2 = await readLinkConfig();
25555
25579
  const envToken = process.env[AUTH_TOKEN_ENV];
25556
25580
  return {
25557
- url: envUrl,
25581
+ url: envUrl.replace(/\/+$/, ""),
25558
25582
  source: "env",
25559
25583
  ...envToken ? { token: envToken } : {},
25560
25584
  ...link2 ? { shadowedLink: link2 } : {}
@@ -27406,6 +27430,7 @@ var PLATFORMS = [
27406
27430
  "android",
27407
27431
  "chromium",
27408
27432
  "vega",
27433
+ "harmony",
27409
27434
  "tvos",
27410
27435
  "android-tv"
27411
27436
  ];
@@ -28461,7 +28486,9 @@ var AUTO_SCREENSHOT_TOOLS = /* @__PURE__ */ new Set([
28461
28486
  "restart-app",
28462
28487
  "open-url",
28463
28488
  "describe",
28464
- "run-sequence"
28489
+ "run-sequence",
28490
+ // A wait is almost always followed by a look at the screen it waited for.
28491
+ "await-ui-element"
28465
28492
  ]);
28466
28493
  var AUTO_SCREENSHOT_DELAY_MS_BY_TOOL = {
28467
28494
  "launch-app": 3e3,
@@ -28482,7 +28509,10 @@ var AUTO_SCREENSHOT_DELAY_MS_BY_TOOL = {
28482
28509
  "fold": 1500,
28483
28510
  "keyboard": 300,
28484
28511
  "paste": 300,
28485
- "describe": 100
28512
+ "describe": 100,
28513
+ // The wait returns on the first match, often mid-transition; the cap must
28514
+ // leave `await-screen-idle` room to see two identical reads.
28515
+ "await-ui-element": 1500
28486
28516
  };
28487
28517
  var DEFAULT_DELAY_MS = 1400;
28488
28518
  function autoScreenshotEnabled(options) {
@@ -28504,7 +28534,8 @@ var AUTO_DESCRIBE_TOOLS = /* @__PURE__ */ new Set([
28504
28534
  "launch-app",
28505
28535
  "restart-app",
28506
28536
  "open-url",
28507
- "run-sequence"
28537
+ "run-sequence",
28538
+ "await-ui-element"
28508
28539
  ]);
28509
28540
  function autoDescribeEnabled(options) {
28510
28541
  return !isFlagEnabled("disable-auto-describe", options);
@@ -28734,11 +28765,69 @@ async function startMcpServer(options) {
28734
28765
  reconnect,
28735
28766
  extraHeaders: aiClientHeaders
28736
28767
  });
28768
+ async function awaitScreenSettled(udid, toolName) {
28769
+ const maxWaitMs = getAutoScreenshotDelayMs(toolName);
28770
+ if (maxWaitMs === 0) return true;
28771
+ try {
28772
+ const idle = await callTool("await-screen-idle", { udid, timeoutMs: maxWaitMs });
28773
+ await spyLog({
28774
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
28775
+ event: "auto_screenshot_readiness",
28776
+ name: toolName,
28777
+ maxWaitMs,
28778
+ ...idle.result
28779
+ });
28780
+ return idle.result?.settled === true;
28781
+ } catch {
28782
+ await new Promise((r) => setTimeout(r, maxWaitMs));
28783
+ return false;
28784
+ }
28785
+ }
28786
+ async function captureScreenshotBlocks(udid) {
28787
+ try {
28788
+ const { result } = await callTool("screenshot", { udid });
28789
+ const blocks = await toMcpContent(result, "image", {
28790
+ toolsUrl: TOOLS_URL,
28791
+ authToken: AUTH_TOKEN,
28792
+ deviceId: udid
28793
+ });
28794
+ if (!blocks.some((b) => b.type === "image")) return [];
28795
+ return [{ type: "text", text: "--- Screen after action ---" }, ...blocks];
28796
+ } catch {
28797
+ return [];
28798
+ }
28799
+ }
28800
+ async function captureTreeBlocks(udid, toolName) {
28801
+ const t1 = Date.now();
28802
+ try {
28803
+ const d = await callTool("describe", { udid });
28804
+ const desc = d.result?.description;
28805
+ await spyLog({
28806
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
28807
+ event: "auto_describe",
28808
+ name: toolName,
28809
+ durationMs: Date.now() - t1,
28810
+ chars: typeof desc === "string" ? desc.length : 0
28811
+ });
28812
+ if (typeof desc !== "string" || desc.length === 0) return [];
28813
+ return [{ type: "text", text: `${AUTO_DESCRIBE_HEADER}
28814
+ ${desc}` }];
28815
+ } catch (e) {
28816
+ await spyLog({
28817
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
28818
+ event: "auto_describe",
28819
+ name: toolName,
28820
+ durationMs: Date.now() - t1,
28821
+ error: String(e instanceof Error ? e.message : e)
28822
+ });
28823
+ return [];
28824
+ }
28825
+ }
28737
28826
  const server = new Server(
28738
28827
  { name: "argent", version: getInstalledVersion() },
28739
28828
  {
28740
28829
  capabilities: { tools: {} },
28741
- instructions: "Argent \u2014 iOS Simulator, Android Emulator, and Chromium app control for interacting, testing, profiling and debugging mobile and Chromium applications. Interaction tools return the screen after the action: a screenshot plus the accessibility element tree with normalized tap frames. Take coordinates from that tree; call describe (or debugger-component-tree) only when no fresh tree is available \u2014 never guess coordinates from pixels. On session end: call stop-all-simulator-servers with devices: [...] naming the devices this session used, and perform any necessary cleanup. One tool-server is shared by every agent using this argent install, so an unscoped call tears down their devices too \u2014 reserve it for a deliberate machine-wide cleanup. Full guidance is in the argent rule loaded from .claude/rules/argent.md."
28830
+ instructions: "Argent \u2014 iOS Simulator, Android Emulator, and Chromium app control for interacting, testing, profiling and debugging mobile and Chromium applications. Interaction tools return the screen after the action: a screenshot plus the accessibility element tree with normalized tap frames. Take coordinates from that tree; call describe (or debugger-component-tree) only when no fresh tree is available \u2014 never guess coordinates from pixels. Do not stop simulator servers when a task finishes: the next task reuses them. Call stop-all-simulator-servers when the user asks to clean up or ends the session, or when a skill tells you to recycle a device's services, always with devices: [...] naming the devices this session used. Never stop a Metro the user started unless they ask. One tool-server is shared by every agent using this argent install, so an unscoped call tears down their devices too \u2014 reserve it for a deliberate machine-wide cleanup. Full guidance is in the argent rule loaded from .claude/rules/argent.md."
28742
28831
  }
28743
28832
  );
28744
28833
  server.setRequestHandler(ListToolsRequestSchema, async () => {
@@ -28801,72 +28890,11 @@ async function startMcpServer(options) {
28801
28890
  }
28802
28891
  ];
28803
28892
  } else if (udid && (wantScreenshot || wantTree)) {
28804
- const maxWaitMs = getAutoScreenshotDelayMs(params.name);
28805
- if (maxWaitMs > 0) {
28806
- try {
28807
- const idle = await callTool("await-screen-idle", { udid, timeoutMs: maxWaitMs });
28808
- await spyLog({
28809
- ts: (/* @__PURE__ */ new Date()).toISOString(),
28810
- event: "auto_screenshot_readiness",
28811
- name: params.name,
28812
- maxWaitMs,
28813
- ...idle.result
28814
- });
28815
- } catch {
28816
- await new Promise((r) => setTimeout(r, maxWaitMs));
28817
- }
28818
- }
28819
- if (wantScreenshot) {
28820
- try {
28821
- const screenshotResult = await callTool("screenshot", { udid });
28822
- const screenshotContent = await toMcpContent(screenshotResult.result, "image", {
28823
- toolsUrl: TOOLS_URL,
28824
- authToken: AUTH_TOKEN,
28825
- deviceId: udid
28826
- });
28827
- const hasImage = screenshotContent.some((b) => b.type === "image");
28828
- if (hasImage) {
28829
- content = [
28830
- ...content,
28831
- {
28832
- type: "text",
28833
- text: "--- Screen after action ---"
28834
- },
28835
- ...screenshotContent
28836
- ];
28837
- }
28838
- } catch {
28839
- }
28840
- }
28841
- if (wantTree) {
28842
- const t1 = Date.now();
28843
- try {
28844
- const d = await callTool("describe", { udid });
28845
- const desc = d.result?.description;
28846
- if (typeof desc === "string" && desc.length > 0) {
28847
- content = [
28848
- ...content,
28849
- { type: "text", text: `${AUTO_DESCRIBE_HEADER}
28850
- ${desc}` }
28851
- ];
28852
- }
28853
- await spyLog({
28854
- ts: (/* @__PURE__ */ new Date()).toISOString(),
28855
- event: "auto_describe",
28856
- name: params.name,
28857
- durationMs: Date.now() - t1,
28858
- chars: typeof desc === "string" ? desc.length : 0
28859
- });
28860
- } catch (e) {
28861
- await spyLog({
28862
- ts: (/* @__PURE__ */ new Date()).toISOString(),
28863
- event: "auto_describe",
28864
- name: params.name,
28865
- durationMs: Date.now() - t1,
28866
- error: String(e instanceof Error ? e.message : e)
28867
- });
28868
- }
28869
- }
28893
+ const screenSettled = await awaitScreenSettled(udid, params.name);
28894
+ const screenshot = wantScreenshot ? captureScreenshotBlocks(udid) : Promise.resolve([]);
28895
+ if (!screenSettled) await screenshot;
28896
+ const tree = wantTree ? captureTreeBlocks(udid, params.name) : Promise.resolve([]);
28897
+ content = [...content, ...await screenshot, ...await tree];
28870
28898
  }
28871
28899
  if (note) {
28872
28900
  content = [{ type: "text", text: note }, ...content];
@@ -1,6 +1,6 @@
1
1
  export interface ArgentToolArgs {
2
2
  "await-screen-idle": {
3
- /** Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id). */
3
+ /** Target device id from `list-devices` (iOS UDID, Android serial, HarmonyOS id, or Chromium id). */
4
4
  udid: string;
5
5
  /** Max time to wait for the screen to settle before giving up (default 3000). */
6
6
  timeoutMs?: number;
@@ -10,7 +10,7 @@ export interface ArgentToolArgs {
10
10
  minStableMs?: number;
11
11
  };
12
12
  "await-ui-element": {
13
- /** Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id). */
13
+ /** Target device id from `list-devices` (iOS UDID, Android serial, HarmonyOS id, or Chromium id). */
14
14
  udid: string;
15
15
  /** What to wait for. `exists`: selector is anywhere in the tree. `visible`: selector is present with a non-zero on-screen frame. `hidden`: selector is absent or zero-area. `text`: the first visible match in reading order (topmost), falling back to the first match overall if none is visible, contains (or, with textMatch `equals`, exactly matches) expectedText. If a loose selector hits several elements, only that one is checked, so narrow it to target the intended element. */
16
16
  condition: "exists" | "visible" | "hidden" | "text";
@@ -27,7 +27,7 @@ export interface ArgentToolArgs {
27
27
  expectedText?: string;
28
28
  /** For condition `text`: how expectedText is compared. `contains` (default) is a case-insensitive substring; `equals` is a case-insensitive full-string match. */
29
29
  textMatch?: "contains" | "equals";
30
- /** Optional iOS app bundle id, passed to the describe fallback (see `describe`). Ignored on Android / Chromium, and on physical iOS. */
30
+ /** Optional iOS app bundle id, passed to the describe fallback (see `describe`). Ignored on every other platform, including physical iOS. */
31
31
  bundleId?: string;
32
32
  /** Max time to wait for the condition before giving up (default 5000). */
33
33
  timeoutMs?: number;
@@ -39,21 +39,23 @@ export interface ArgentToolArgs {
39
39
  timeoutSeconds?: number;
40
40
  };
41
41
  "boot-device": {
42
- /** iOS: simulator UDID to boot (from `list-devices`). Provide exactly one of `udid`, `avdName`, `vvdImage`, or `electronAppPath`. */
42
+ /** iOS: simulator UDID to boot (from `list-devices`). Provide exactly one of `udid`, `avdName`, `vvdImage`, `harmonyInstance`, or `electronAppPath`. */
43
43
  udid?: string;
44
- /** Android: AVD name to launch a new emulator from (from `list-devices` → `avds[].name`). Provide exactly one of `udid`, `avdName`, `vvdImage`, or `electronAppPath`. */
44
+ /** Android: AVD name to launch a new emulator from (from `list-devices` → `avds[].name`). Provide exactly one of `udid`, `avdName`, `vvdImage`, `harmonyInstance`, or `electronAppPath`. */
45
45
  avdName?: string;
46
- /** Vega (Fire TV): VVD image to boot: the `vvdImage` of a Vega device from `list-devices` (e.g. `tv`). Starts the single SDK-managed Vega Virtual Device. Provide exactly one of `udid`, `avdName`, `vvdImage`, or `electronAppPath`. */
46
+ /** Vega (Fire TV): VVD image to boot: the `vvdImage` of a Vega device from `list-devices` (e.g. `tv`). Starts the single SDK-managed Vega Virtual Device. Provide exactly one of `udid`, `avdName`, `vvdImage`, `harmonyInstance`, or `electronAppPath`. */
47
47
  vvdImage?: string;
48
- /** Android/Vega: overall budget for the boot sequence. Default 480000 (8 min) on Android, 120000 (2 min) on Vega. Clamped to [30s, 15min]. Ignored on iOS. */
48
+ /** HarmonyOS: emulator instance to start: the `name` of a `kind: "emulator"` device from `list-devices` (e.g. `Phone_1`); the `harmony-emulator-<name>` id that entry reports as its `udid` is accepted too. Provide exactly one of `udid`, `avdName`, `vvdImage`, `harmonyInstance`, or `electronAppPath`. */
49
+ harmonyInstance?: string;
50
+ /** Android/Vega/HarmonyOS: overall budget for the boot sequence. Default 480000 (8 min) on Android, 120000 (2 min) on Vega, 180000 (3 min) on HarmonyOS, where with `force` it spans the shutdown as well as the boot. Clamped to [30s, 15min]. Ignored on iOS. */
49
51
  bootTimeoutMs?: number;
50
- /** Shut down and re-boot the device even if already running. */
52
+ /** Shut down and re-boot the device even if already running. On HarmonyOS it is also what ties an already-running instance to the `hdc` connect key it registered under, since the emulator manager never reports one: without it the instance is left alone and the payload names the instance rather than a key (`list-devices` lists the keys, but not which instance each belongs to). Ignored for Electron, which always spawns a fresh process. */
51
53
  force?: boolean;
52
- /** Android only: boot the emulator with audio output enabled. Defaults to false: argent boots emulators MUTED so several agent-driven devices don't all play sound on the host machine; pass `true` when the task involves playing, hearing, or testing audio. Takes effect at boot: if the emulator is already running muted, add `force: true` to reboot it with sound. A boot snapshot saved in the other audio mode can't be reused, so the first boot after toggling is a slower cold boot. The `boot-sound` argent flag flips this default to true. Ignored on iOS/Vega/Electron, which argent never mutes. */
54
+ /** Android only: boot the emulator with audio output enabled. Defaults to false: argent boots emulators MUTED so several agent-driven devices don't all play sound on the host machine; pass `true` when the task involves playing, hearing, or testing audio. Takes effect at boot: if the emulator is already running muted, add `force: true` to reboot it with sound. A boot snapshot saved in the other audio mode can't be reused, so the first boot after toggling is a slower cold boot. The `boot-sound` argent flag flips this default to true. Ignored on iOS/Vega/HarmonyOS/Electron, which argent never mutes. */
53
55
  sound?: boolean;
54
- /** iOS only: boot the simulator core WITHOUT opening its GUI window (Simulator.app, or Device Hub on Xcode 27+). The device still streams via simulator-server; used by Argent Lens. Set the `ARGENT_SIMULATOR_NO_WINDOW` env var (1/true/yes) to force this host-wide without passing the flag per call (the iOS analog of `ARGENT_EMULATOR_NO_WINDOW`). Ignored on Android/Vega/Electron, which have no equivalent GUI step. */
56
+ /** iOS only: boot the simulator core WITHOUT opening its GUI window (Simulator.app, or Device Hub on Xcode 27+). The device still streams via simulator-server; used by Argent Lens. Set the `ARGENT_SIMULATOR_NO_WINDOW` env var (1/true/yes) to force this host-wide without passing the flag per call (the iOS analog of `ARGENT_EMULATOR_NO_WINDOW`). Ignored on Android/Vega/HarmonyOS/Electron, which have no equivalent GUI step. */
55
57
  headless?: boolean;
56
- /** Electron: path to the Electron app to launch. Either a packaged .app bundle / executable, or a project directory whose package.json points the Electron binary at the entry script. Mutually exclusive with udid/avdName. */
58
+ /** Electron: path to the Electron app to launch. Either a packaged .app bundle / executable, or a project directory whose package.json points the Electron binary at the entry script. Provide exactly one of `udid`, `avdName`, `vvdImage`, `harmonyInstance`, or `electronAppPath`. */
57
59
  electronAppPath?: string;
58
60
  /** Electron-only: CDP remote-debugging port to expose. Defaults to a free port; the resulting device id is `chromium-cdp-<port>`. */
59
61
  electronPort?: number;
@@ -61,7 +63,7 @@ export interface ArgentToolArgs {
61
63
  electronArgs?: Array<string>;
62
64
  };
63
65
  button: {
64
- /** Target device id from `list-devices` (iOS UDID or Android serial). */
66
+ /** Target device id from `list-devices` (iOS UDID, HarmonyOS id, or Android serial). */
65
67
  udid: string;
66
68
  /** Hardware button to press */
67
69
  button: "home" | "back" | "power" | "volumeUp" | "volumeDown" | "appSwitch" | "actionButton";
@@ -179,9 +181,9 @@ export interface ArgentToolArgs {
179
181
  device_id: string;
180
182
  };
181
183
  describe: {
182
- /** Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, or Chromium id). */
184
+ /** Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, HarmonyOS id, or Chromium id). */
183
185
  udid: string;
184
- /** Optional app bundle ID. Used as a target hint on iOS when the AX-service returns no elements and the describe tool falls back to native-devtools inspection. If omitted, the fallback auto-detects the frontmost connected app. Ignored on Android / Chromium, and on a physical iOS device. */
186
+ /** Optional app bundle ID. Used as a target hint on iOS when the AX-service returns no elements and the describe tool falls back to native-devtools inspection. If omitted, the fallback auto-detects the frontmost connected app. Ignored on every other platform, including a physical iOS device. */
185
187
  bundleId?: string;
186
188
  };
187
189
  "dismiss-update": {
@@ -320,7 +322,7 @@ export interface ArgentToolArgs {
320
322
  momentum?: boolean;
321
323
  };
322
324
  "gesture-pinch": {
323
- /** Target device id from `list-devices` (iOS UDID or Android serial). */
325
+ /** Target device id from `list-devices` (iOS UDID, HarmonyOS id, or Android serial). */
324
326
  udid: string;
325
327
  /** Center of pinch, horizontal: normalized 0.0–1.0 (fraction of screen width, not pixels) */
326
328
  centerX: number;
@@ -336,7 +338,7 @@ export interface ArgentToolArgs {
336
338
  endCenterY?: number;
337
339
  /** Axis angle in degrees along which the fingers are placed (default 0 = horizontal). */
338
340
  angle?: number;
339
- /** Total gesture duration in milliseconds (default 300) */
341
+ /** Total gesture duration in milliseconds (default 300). On HarmonyOS the device runs the whole move itself and takes this as wall-clock time, clamped to the 1–15000ms range `uinput` accepts. */
340
342
  durationMs?: number;
341
343
  };
342
344
  "gesture-rotate": {
@@ -374,7 +376,7 @@ export interface ArgentToolArgs {
374
376
  durationMs?: number;
375
377
  };
376
378
  "gesture-swipe": {
377
- /** Target device id from `list-devices` (iOS UDID or Android serial). */
379
+ /** Target device id from `list-devices` (iOS UDID, HarmonyOS id, or Android serial). */
378
380
  udid: string;
379
381
  /** Start x: normalized 0.0–1.0 (not pixels; same as tap) */
380
382
  fromX: number;
@@ -390,31 +392,31 @@ export interface ArgentToolArgs {
390
392
  momentum?: boolean;
391
393
  };
392
394
  "gesture-tap": {
393
- /** Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id). */
395
+ /** Target device id from `list-devices` (iOS UDID, Android serial, HarmonyOS id, or Chromium id). */
394
396
  udid: string;
395
397
  /** Normalized horizontal position 0.0–1.0 (left=0, right=1), not pixels */
396
398
  x: number;
397
399
  /** Normalized vertical position 0.0–1.0 (top=0, bottom=1), not pixels */
398
400
  y: number;
399
- /** Number of taps/clicks dispatched as ONE multi-tap gesture (2 = double-tap / double-click). The taps land inside the OS double-tap window; on Chromium each click carries an escalating CDP clickCount so dblclick actually fires; on physical iOS 2 is the native double-tap and higher counts land as separate taps. Default 1. */
401
+ /** Number of taps/clicks dispatched as ONE multi-tap gesture (2 = double-tap / double-click). The taps land inside the OS double-tap window; on Chromium each click carries an escalating CDP clickCount so dblclick actually fires; on physical iOS 2 is the native double-tap and higher counts land as separate taps. On HarmonyOS only 2 is one gesture; 3 or more are separate injections a device round trip apart, which the OS reads as single taps. Default 1. */
400
402
  clickCount?: number;
401
403
  };
402
404
  keyboard: {
403
- /** Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, or Chromium id). */
405
+ /** Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, HarmonyOS id, or Chromium id). */
404
406
  udid: string;
405
407
  /** Text to type character by character. Cannot be combined with `key` in one call (one call per action); to type and then press a key, put two `keyboard` steps in one `run-sequence`. Handles uppercase and common punctuation. To type a credential without its plaintext ever entering your context, use a secret placeholder: `{{secret:<NAME>}}`, e.g. text: "{{secret:APP_PASSWORD}}". The value is resolved on the machine running the tool-server, from the first source that defines the name: the `ARGENT_SECRET_<NAME>` environment variable, `.argent/secrets.env` in the project, the project's `.env.local` / `.env` (only their `ARGENT_SECRET_`-prefixed keys), then `~/.argent/secrets.env`. Nothing else on the host is reachable. Placeholders can be embedded in longer text and are never echoed back resolved. If the secret you need is not set, the failure lists the available names and every source it looked in; ask the user to add it to one of them (a secrets file applies immediately; an env var needs a restart), NEVER ask the user to paste the secret value into the conversation. */
406
408
  text?: string;
407
- /** Named key to press: enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1-f12. Cannot be combined with `text` in one call: one call per action; to type and then press a key, put two `keyboard` steps in one `run-sequence`. Not supported on TV targets; move focus with `tv-remote` (up/down/left/right) instead. Physical iOS: only `enter` and `backspace`. */
409
+ /** Named key to press: enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1-f12. Cannot be combined with `text` in one call: one call per action; to type and then press a key, put two `keyboard` steps in one `run-sequence`. Not supported on TV targets; move focus with `tv-remote` (up/down/left/right) instead. Physical iOS: only `enter` and `backspace`. HarmonyOS: only enter (return), backspace (delete), space, arrow-left and arrow-right; any other key fails with 400 KEYBOARD_KEY_UNSUPPORTED. */
408
410
  key?: string;
409
- /** Delay in ms between key presses (default 50). Ignored on Android phones/tablets (typed via `adb input text`, which has no per-key cadence), on Vega (text/keys injected in a single shot), on TV targets (Apple TV / Android TV type the whole string at the daemon's own cadence), and on physical iOS. */
411
+ /** Delay in ms between key presses (default 50). Ignored on Android phones/tablets (typed via `adb input text`, which has no per-key cadence), on HarmonyOS (`uitest uiInput text` types the whole string in one shot), on Vega (text/keys injected in a single shot), on TV targets (Apple TV / Android TV type the whole string at the daemon's own cadence), and on physical iOS. */
410
412
  delayMs?: number;
411
413
  };
412
414
  "launch-app": {
413
- /** Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id). */
415
+ /** Target device id from `list-devices` (iOS UDID, Android serial, HarmonyOS id, or Chromium id). */
414
416
  udid: string;
415
- /** App identifier. iOS: bundle id (e.g. com.apple.MobileSMS). Android: package name from build.gradle `applicationId` (e.g. com.android.settings). Chromium: any tag matching the same alphabet (letters, digits, '.', '_' and '-'); the call is a no-op since the renderer is already running. */
417
+ /** App identifier. iOS: bundle id (e.g. com.apple.MobileSMS). Android: package name from build.gradle `applicationId` (e.g. com.android.settings). HarmonyOS: bundle name as listed by `bm dump -a` (e.g. com.huawei.hmos.calculator). Chromium: any tag matching the same alphabet (letters, digits, '.', '_' and '-'); the call is a no-op since the renderer is already running. */
416
418
  bundleId: string;
417
- /** Android-only: fully-qualified Activity name (e.g. `.MainActivity` or `com.example/com.example.MainActivity`). If omitted on Android, the app's default launcher activity is used. Ignored on iOS / Chromium. */
419
+ /** Android-only: fully-qualified Activity name (e.g. `.MainActivity` or `com.example/com.example.MainActivity`). If omitted on Android, the app's default launcher activity is used. Ignored on iOS / Chromium / HarmonyOS (a HarmonyOS ability is resolved from the bundle's own `abilityInfos`). */
418
420
  activity?: string;
419
421
  /** Apple simulators and physical iPhones: arguments passed to the app process at launch, e.g. `["-FeatureFlag", "YES"]` to override UserDefaults. A running app is relaunched to apply them. Ignored on other targets. */
420
422
  launchArgs?: Array<string>;
@@ -545,7 +547,7 @@ export interface ArgentToolArgs {
545
547
  maxDepth?: number;
546
548
  };
547
549
  "open-url": {
548
- /** Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id). */
550
+ /** Target device id from `list-devices` (iOS UDID, Android serial, HarmonyOS id, or Chromium id). */
549
551
  udid: string;
550
552
  /** URL or scheme to open (e.g. https://example.com, messages://, tel:555, geo:37.0,-122.0). For Chromium this navigates the renderer. */
551
553
  url: string;
@@ -745,19 +747,19 @@ export interface ArgentToolArgs {
745
747
  device_id: string;
746
748
  };
747
749
  "reinstall-app": {
748
- /** Target device id from `list-devices` (iOS UDID or Android serial). */
750
+ /** Target device id from `list-devices` (iOS UDID, HarmonyOS id, or Android serial). */
749
751
  udid: string;
750
- /** App identifier that matches the bundle at `appPath`. iOS: bundle id (used to uninstall first). Android: package name (used to uninstall first; the install itself identifies the app from the APK). Vega: interactive component app id (e.g. com.example.app.main), used to uninstall first. */
752
+ /** App identifier that matches the bundle at `appPath`. iOS: bundle id (used to uninstall first). Android: package name (used to uninstall first; the install itself identifies the app from the APK). Vega: interactive component app id (e.g. com.example.app.main), used to uninstall first. HarmonyOS: bundle name, which must match `app.bundleName` in the `.hap`. */
751
753
  bundleId: string;
752
- /** Path to the app bundle. iOS: `.app` directory (e.g. ./build/.../MyApp.app). Android: `.apk` file (e.g. android/app/build/outputs/apk/debug/app-debug.apk). Vega: `.vpkg` file. Relative paths are resolved from the current working directory. */
754
+ /** Path to the app bundle. iOS: `.app` directory (e.g. ./build/.../MyApp.app). Android: `.apk` file (e.g. android/app/build/outputs/apk/debug/app-debug.apk). Vega: `.vpkg` file. HarmonyOS: `.hap` file. Relative paths are resolved from the current working directory. */
753
755
  appPath: string;
754
756
  };
755
757
  "restart-app": {
756
- /** Target device id from `list-devices` (iOS UDID or Android serial). */
758
+ /** Target device id from `list-devices` (iOS UDID, HarmonyOS id, or Android serial). */
757
759
  udid: string;
758
- /** App identifier. iOS: bundle id. Android: package name. */
760
+ /** App identifier. iOS: bundle id. Android: package name. HarmonyOS: bundle name. */
759
761
  bundleId: string;
760
- /** Android-only: relaunch a non-launcher Activity (e.g. `.SettingsActivity` or `com.example/com.example.SettingsActivity`). If omitted, the app's default launcher activity is used. Ignored on iOS. */
762
+ /** Android-only: relaunch a non-launcher Activity (e.g. `.SettingsActivity` or `com.example/com.example.SettingsActivity`). If omitted, the app's default launcher activity is used. Ignored on iOS / HarmonyOS. */
761
763
  activity?: string;
762
764
  /** Apple simulators and physical iPhones: arguments passed to the app process at launch, e.g. `["-FeatureFlag", "YES"]` to override UserDefaults. Ignored on other targets. */
763
765
  launchArgs?: Array<string>;
@@ -769,7 +771,7 @@ export interface ArgentToolArgs {
769
771
  orientation: "Portrait" | "LandscapeLeft" | "LandscapeRight" | "PortraitUpsideDown";
770
772
  };
771
773
  "run-sequence": {
772
- /** Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, or Chromium id), shared across all steps. */
774
+ /** Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, HarmonyOS id, or Chromium id), shared across all steps. */
773
775
  udid: string;
774
776
  /** Ordered list of interaction steps to execute sequentially */
775
777
  steps: Array<{
@@ -798,11 +800,11 @@ export interface ArgentToolArgs {
798
800
  udid: string;
799
801
  };
800
802
  screenshot: {
801
- /** Target device id from `list-devices` (iOS UDID, Android serial, Apple TV UDID, Vega serial, or Chromium id). */
803
+ /** Target device id from `list-devices` (iOS UDID, Android serial, Apple TV UDID, Vega serial, HarmonyOS id, or Chromium id). */
802
804
  udid: string;
803
- /** Orientation override for the screenshot (rotates the captured image after Page.captureScreenshot on Chromium). On Android the capture already follows the device's rotation. Ignored on physical iPhones. */
805
+ /** Orientation override for the screenshot (rotates the captured image after Page.captureScreenshot on Chromium). On Android the capture already follows the device's rotation. Rejected on HarmonyOS, which captures the display in its current orientation and has no override. Ignored on physical iPhones. */
804
806
  rotation?: "Portrait" | "LandscapeLeft" | "LandscapeRight" | "PortraitUpsideDown";
805
- /** Scale factor (0.01-1.0). Defaults to ARGENT_SCREENSHOT_SCALE env var, or 0.25 if unset for iOS/Android. On a local Apple TV simulator, an Android TV or Vega, with neither set, the capture is downscaled to a 576 px long side (0.15 of a 4K capture); an Android TV whose form factor or display size cannot be read in time keeps 0.25. On Chromium the default is 1.0 (no downscale); pass <1 to opt in. Downscaling on Chromium requires the optional `sharp` dependency. */
807
+ /** Scale factor (0.01-1.0). Defaults to ARGENT_SCREENSHOT_SCALE env var, or 0.25 if unset for iOS/Android/HarmonyOS. On a local Apple TV simulator, an Android TV or Vega, with neither set, the capture is downscaled to a 576 px long side (0.15 of a 4K capture); an Android TV whose form factor or display size cannot be read in time keeps 0.25. On Chromium the default is 1.0 (no downscale); pass <1 to opt in. Downscaling on Chromium requires the optional `sharp` dependency. */
806
808
  scale?: number;
807
809
  /** Default true. Set false only when capturing a full-resolution PNG (scale: 1.0) to save as a baseline/current for screenshot-diff. The file is still written, but the image bytes are not attached to the agent context. */
808
810
  includeImageInContext?: boolean;
@@ -814,13 +816,13 @@ export interface ArgentToolArgs {
814
816
  baselinePath?: string;
815
817
  /** Path to the current PNG file. Required unless captureCurrent is true. */
816
818
  currentPath?: string;
817
- /** Target device id from `list-devices` (iOS UDID or Android serial). */
819
+ /** Target device id from `list-devices` (iOS UDID, HarmonyOS id, or Android serial). */
818
820
  udid: string;
819
821
  /** Capture the baseline screenshot live at full resolution before diffing. Cannot be combined with captureCurrent. */
820
822
  captureBaseline?: boolean;
821
823
  /** Capture the current screenshot live at full resolution before diffing. Cannot be combined with captureBaseline. */
822
824
  captureCurrent?: boolean;
823
- /** Orientation override for live baseline/current captures. Ignored on physical iPhones. */
825
+ /** Orientation override for live baseline/current captures. Rejected on HarmonyOS, which captures the display in its current orientation and has no override. Ignored on physical iPhones. */
824
826
  rotation?: "Portrait" | "LandscapeLeft" | "LandscapeRight" | "PortraitUpsideDown";
825
827
  /** Directory where diff artifacts should be written. Optional: defaults to a temp directory; the diff images are returned in the result either way. */
826
828
  outputDir?: string;