@swmansion/argent 0.22.1-next.1 → 0.22.1-next.3

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.
@@ -434,12 +434,13 @@ var init_artifacts = __esm({
434
434
  };
435
435
  ArtifactStore = class {
436
436
  entries = /* @__PURE__ */ new Map();
437
- async register(hostPath, opts) {
438
- const filename = opts?.filename ?? (0, import_node_path.basename)(hostPath);
439
- const mimeType = opts?.mimeType ?? inferMimeType(hostPath);
437
+ async register(opts) {
438
+ const { hostPath, kind } = opts;
439
+ const filename = opts.filename ?? (0, import_node_path.basename)(hostPath);
440
+ const mimeType = opts.mimeType ?? inferMimeType(hostPath);
440
441
  let size = 0;
441
442
  let mtimeMs;
442
- let isDirectory2 = opts?.archive === "tar.gz";
443
+ let isDirectory2 = opts.archive === "tar.gz";
443
444
  try {
444
445
  const st = await (0, import_promises.stat)(hostPath);
445
446
  size = st.size;
@@ -448,10 +449,11 @@ var init_artifacts = __esm({
448
449
  } catch {
449
450
  }
450
451
  const id = (0, import_node_crypto.randomUUID)();
451
- this.entries.set(id, { path: hostPath, filename, mimeType, size, isDirectory: isDirectory2 });
452
+ this.entries.set(id, { path: hostPath, kind, filename, mimeType, size, isDirectory: isDirectory2 });
452
453
  const handle = {
453
454
  [ARTIFACT_MARKER]: true,
454
455
  id,
456
+ kind,
455
457
  filename,
456
458
  mimeType,
457
459
  size,
@@ -459,7 +461,7 @@ var init_artifacts = __esm({
459
461
  };
460
462
  if (mtimeMs != null) handle.mtimeMs = mtimeMs;
461
463
  if (isDirectory2) handle.archive = "tar.gz";
462
- if (opts?.saveDir) handle.saveDir = opts.saveDir;
464
+ if (opts.saveDir) handle.saveDir = opts.saveDir;
463
465
  return handle;
464
466
  }
465
467
  get(id) {
@@ -468,6 +470,7 @@ var init_artifacts = __esm({
468
470
  list() {
469
471
  return [...this.entries.entries()].map(([id, entry]) => ({
470
472
  id,
473
+ kind: entry.kind,
471
474
  filename: entry.filename,
472
475
  mimeType: entry.mimeType,
473
476
  size: entry.size,
@@ -706,15 +709,12 @@ var init_failure_codes = __esm({
706
709
  NATIVE_PROFILER_SESSION_TORN_DOWN: "NATIVE_PROFILER_SESSION_TORN_DOWN",
707
710
  NATIVE_PROFILER_APP_PROCESS_NOT_FOUND: "NATIVE_PROFILER_APP_PROCESS_NOT_FOUND",
708
711
  NATIVE_PROFILER_NO_EXPORTED_TRACE: "NATIVE_PROFILER_NO_EXPORTED_TRACE",
709
- // Android perfetto start-failure modes — mirror the iOS xctrace set so a
710
- // failed recording start is classified rather than falling through to the
711
- // generic tool-execution bucket.
712
+ // Android perfetto start failures, mirroring the iOS xctrace set above.
712
713
  NATIVE_PROFILER_PERFETTO_PROCESS_ERROR: "NATIVE_PROFILER_PERFETTO_PROCESS_ERROR",
713
714
  NATIVE_PROFILER_PERFETTO_READY_TIMEOUT: "NATIVE_PROFILER_PERFETTO_READY_TIMEOUT",
714
715
  NATIVE_PROFILER_PERFETTO_READY_EXITED: "NATIVE_PROFILER_PERFETTO_READY_EXITED",
715
- // screen-recording-start / screen-recording-stop. One capture path for every
716
- // platform (simulator-server's frame stream into ffmpeg), so the stages name
717
- // the step that failed rather than the device family.
716
+ // iOS and Android share one capture path (simulator-server frames into
717
+ // ffmpeg), so these name the failing stage, not the device family.
718
718
  SCREEN_RECORDING_FACTORY_OPTIONS_MISSING: "SCREEN_RECORDING_FACTORY_OPTIONS_MISSING",
719
719
  SCREEN_RECORDING_WRONG_PLATFORM: "SCREEN_RECORDING_WRONG_PLATFORM",
720
720
  SCREEN_RECORDING_ALREADY_ACTIVE: "SCREEN_RECORDING_ALREADY_ACTIVE",
@@ -16083,22 +16083,19 @@ var init_registry = __esm({
16083
16083
  init_zod_to_json_schema();
16084
16084
  import_node_crypto2 = require("node:crypto");
16085
16085
  Registry = class {
16086
- /** Single map: URN -> ServiceNode (all instances). */
16087
16086
  services = /* @__PURE__ */ new Map();
16088
16087
  blueprints = /* @__PURE__ */ new Map();
16089
16088
  tools = /* @__PURE__ */ new Map();
16090
16089
  /**
16091
- * Predicate that decides whether a feature-flagged tool is currently enabled.
16092
- * Injected (rather than importing `@argent/cli` here) so the registry stays
16093
- * free of a CLI dependency. The default treats every flag as enabled, so
16094
- * existing `new Registry()` call sites (tests, non-flag deployments) keep
16095
- * their previous behavior. The tool-server wires the real `isFlagEnabled`.
16090
+ * Injected so the registry needs no dependency on the flag store; the
16091
+ * tool-server wires the real check. The default enables every flag, so a
16092
+ * plain `new Registry()` (tests, non-flag deployments) gates nothing.
16096
16093
  */
16097
16094
  isFlagEnabled;
16098
16095
  /**
16099
- * Host files produced by tools, registered during `execute` and served by the
16100
- * `/artifacts/:id` route. Owned here (one per registry/process) so the tool
16101
- * path and the HTTP route resolve the same instance — no module singleton.
16096
+ * Host files produced by tools, served by the tool-server's `/artifacts/:id`
16097
+ * route. Owned per registry rather than as a module singleton, so the tool
16098
+ * path and the HTTP route see the same store.
16102
16099
  */
16103
16100
  artifacts = new ArtifactStore();
16104
16101
  events = new TypedEventEmitter();
@@ -16119,8 +16116,8 @@ var init_registry = __esm({
16119
16116
  return this.tools.get(id)?.definition;
16120
16117
  }
16121
16118
  /**
16122
- * Resolve a service by URN. JIT-instantiates from blueprint if not yet created.
16123
- * Optional options are passed to the blueprint's factory (e.g. token for SimulatorServer).
16119
+ * Resolve a service by URN, JIT-instantiating from its blueprint on first use.
16120
+ * `options` reach the blueprint factory only on that first instantiation.
16124
16121
  */
16125
16122
  resolveService(urn, options) {
16126
16123
  return this._resolve(urn, [], options);
@@ -16237,14 +16234,10 @@ var init_registry = __esm({
16237
16234
  };
16238
16235
  }
16239
16236
  /**
16240
- * After a tool failed, ask each service it resolved whether the error means
16241
- * that service's instance is dead (`blueprint.recoverable(error)`). Dispose
16242
- * every one that says yes so the next `resolveService` re-creates it, and
16243
- * report whether anything was disposed (i.e. whether a retry is worthwhile).
16244
- *
16245
- * Only currently-RUNNING nodes are considered: a service that already
16246
- * errored/torn down during resolution needs no recovery here, and a URN this
16247
- * tool never resolved must not be touched.
16237
+ * Dispose every service in `refs` whose blueprint calls `error` recoverable, so
16238
+ * the next `resolveService` re-creates it; returns whether anything was
16239
+ * disposed, i.e. whether retrying the tool is worthwhile. Only RUNNING nodes
16240
+ * qualify — one that already errored or tore down needs no recovery.
16248
16241
  */
16249
16242
  async _recoverFailedServices(refs, error52) {
16250
16243
  let recoveredAny = false;
@@ -16260,10 +16253,7 @@ var init_registry = __esm({
16260
16253
  }
16261
16254
  return recoveredAny;
16262
16255
  }
16263
- /**
16264
- * Tear down a single service by URN (and cascade to its dependents).
16265
- * After disposal the service returns to IDLE and can be re-resolved.
16266
- */
16256
+ /** Tear down a service and its dependents; it returns to IDLE and can be re-resolved. */
16267
16257
  async disposeService(urn) {
16268
16258
  const node = this.services.get(urn);
16269
16259
  if (!node) throw new ServiceNotFoundError(urn);
@@ -16276,7 +16266,6 @@ var init_registry = __esm({
16276
16266
  }
16277
16267
  }
16278
16268
  }
16279
- // ── Private: Resolution ──
16280
16269
  _resolve(urn, resolutionPath, options) {
16281
16270
  let node = this.services.get(urn);
16282
16271
  if (!node) {
@@ -69925,16 +69914,12 @@ function androidRoots() {
69925
69914
  function defaultAndroidRoots() {
69926
69915
  const home = (0, import_node_os2.homedir)();
69927
69916
  const roots = [
69928
- // Android Studio defaults (the big ones — cover the majority of user
69929
- // installs that arrive without any env-var setup).
69930
69917
  (0, import_node_path3.join)(home, "Library", "Android", "sdk"),
69931
69918
  // macOS Android Studio default
69932
69919
  (0, import_node_path3.join)(home, "Android", "Sdk"),
69933
69920
  // Linux Android Studio default
69934
- // Common manual install convention. Not picked by any installer but used
69935
- // often enough in tutorials and Dockerfiles that probing it costs little.
69936
69921
  (0, import_node_path3.join)(home, "android-sdk"),
69937
- // System-wide locations (Linux package managers, Homebrew on macOS).
69922
+ // manual-install convention; no installer picks it
69938
69923
  "/opt/android-sdk",
69939
69924
  "/usr/lib/android-sdk",
69940
69925
  // Debian/Ubuntu `android-sdk` apt package
@@ -72069,9 +72054,7 @@ var require_min_release_age = __commonJS({
72069
72054
  var PROBE_TIMEOUT_MS = 3e3;
72070
72055
  var OVERRIDE_ENV = "ARGENT_MIN_RELEASE_AGE_DAYS";
72071
72056
  var PM_PROBES = {
72072
- // npm flattens `min-release-age` to an effective `before` cutoff, and some
72073
- // npm 11.x builds report `min-release-age=null` even while the policy is
72074
- // active. Probe `before`, which is what the resolver actually uses.
72057
+ // Probe `before`: that is the effective cutoff npm's resolver applies.
72075
72058
  npm: { command: "npm config get before", parse: config_parse_1.parseBeforeAgeMs },
72076
72059
  pnpm: {
72077
72060
  command: "pnpm config get minimumReleaseAge",
@@ -92082,14 +92065,12 @@ var CONFIG_SCHEMA = [
92082
92065
  description: "Whether anonymous opt-out telemetry is enabled (on by default; environment opt-outs like DO_NOT_TRACK are not reflected here \u2014 `argent telemetry status` shows effective consent).",
92083
92066
  scopes: ["global"],
92084
92067
  parse: asBoolean,
92085
- // A committed project file must never re-enable telemetry a user disabled
92086
- // globally, so the more-restrictive (opt-out) value always wins.
92087
92068
  merge: "prioritize-restrictive",
92088
- // Telemetry is opt-out: with nothing stored, consent.ts treats it as
92089
- // enabled, and the config surface must report the same instead of "(unset)".
92069
+ // Opt-out: consent.ts reads an unstored value as enabled, so the config
92070
+ // surface must show the same rather than "(unset)".
92090
92071
  default: true,
92091
- // Read-only under `argent config`: opt-in/out goes through the dedicated
92092
- // command so the live client is drained/reset, not just the file rewritten.
92072
+ // Opt-in/out goes through the dedicated command so the live client is
92073
+ // drained/reset, not just the file rewritten.
92093
92074
  manageCommand: "argent telemetry"
92094
92075
  },
92095
92076
  {
@@ -92097,8 +92078,6 @@ var CONFIG_SCHEMA = [
92097
92078
  description: "Coding-agent id remembered by `argent lens` to skip the picker.",
92098
92079
  scopes: ["project", "global"],
92099
92080
  parse: asString,
92100
- // A repo can pin the agent its screenshots should use; falls back to the
92101
- // user's global remembered choice.
92102
92081
  merge: "prioritize-local",
92103
92082
  example: "claude"
92104
92083
  },
@@ -92107,11 +92086,9 @@ var CONFIG_SCHEMA = [
92107
92086
  description: "Additional CoreSimulator device-set directories whose simulators argent should see alongside the default set. Absolute paths (or ~/\u2026); relative entries resolve against the project root (project scope) or home (global scope).",
92108
92087
  scopes: ["project", "global"],
92109
92088
  parse: asStringArray,
92110
- // Additive: the scopes extend each other rather than shadow — a repo's
92111
- // committed device sets are appended to the user's global ones (global
92112
- // baseline first, project extras after, deduplicated). Note that
92113
- // `getAdditionalIosDeviceSets` re-implements this union (path resolution
92114
- // must precede dedup) and guards on the preset staying "union".
92089
+ // Additive rather than shadowing: global baseline first, project extras
92090
+ // after, deduplicated. `getAdditionalIosDeviceSets` re-implements this union
92091
+ // (path resolution must precede dedup) and guards on the preset staying "union".
92115
92092
  merge: "union",
92116
92093
  example: '["~/DeviceSets/ci"]'
92117
92094
  },
@@ -92120,10 +92097,8 @@ var CONFIG_SCHEMA = [
92120
92097
  description: "Directory where finished screen recordings (mp4) are saved on the client host. Absolute, `~`-prefixed, or relative to the project root (home dir when not in a project). Unset \u21D2 `.argent/recordings` under the project root.",
92121
92098
  scopes: ["project", "global"],
92122
92099
  parse: asString,
92123
- // A repo can pin where its recordings land; falls back to the user's global
92124
- // preference. Resolution happens on the client (the machine the mp4 is
92125
- // persisted to), so with a remote `argent link` tool-server it is the
92126
- // *client's* config that decides.
92100
+ // Resolved on the client (the machine the mp4 is persisted to), so with a
92101
+ // remote `argent link` tool-server it is the *client's* config that decides.
92127
92102
  merge: "prioritize-local",
92128
92103
  example: "~/Movies/argent"
92129
92104
  }
@@ -94850,17 +94825,16 @@ function createExporter(config2) {
94850
94825
  // The exporter bounds a request with `req.setTimeout()`, which Node only
94851
94826
  // arms once the socket is CONNECTED — so a collector whose address drops
94852
94827
  // packets (corporate egress filter, dead host behind a firewall) leaves a
94853
- // socket stuck in the connecting state that no export deadline can reach,
94854
- // holding the process open for the OS connect timeout (~75s on macOS)
94855
- // after shutdown() has already resolved. The agent's socket timeout is
94856
- // armed when the socket is CREATED, so it also covers connect: it fires,
94857
- // the request emits 'timeout', and the exporter's handler destroys it.
94828
+ // socket stuck connecting that no export deadline can reach, holding the
94829
+ // process open for the OS connect timeout after shutdown() has resolved.
94830
+ // The agent's socket timeout is armed when the socket is CREATED, so it
94831
+ // covers connect too: it fires, the request emits 'timeout', and the
94832
+ // exporter's handler destroys it.
94858
94833
  //
94859
94834
  // keepAlive is restated because supplying httpAgentOptions at all
94860
- // replaces the agent the SDK would otherwise build, and its default is
94861
- // keepAlive: true. Without it the long-lived tool-server pays a fresh
94862
- // TCP+TLS handshake for every 10s batch — measured as one socket per
94863
- // request against a loopback collector, versus one socket shared.
94835
+ // replaces the agent the SDK would otherwise build, whose default is
94836
+ // keepAlive: true; without it the long-lived tool-server pays a fresh
94837
+ // TCP+TLS handshake for every batch.
94864
94838
  httpAgentOptions: { timeout: EXPORT_TIMEOUT_MS, keepAlive: true }
94865
94839
  });
94866
94840
  } finally {
@@ -95079,7 +95053,6 @@ var ALLOWED = {
95079
95053
  install_mode: INSTALL_MODE
95080
95054
  },
95081
95055
  "installation:global_install_decision": {
95082
- // `from_tar` is intentionally absent; the installer skips that dev path.
95083
95056
  decision: oneOf(["install", "cancel", "already_installed"])
95084
95057
  },
95085
95058
  "installation:update_decision": {
@@ -95158,9 +95131,8 @@ var ALLOWED = {
95158
95131
  tool_invocation_id: UUID,
95159
95132
  platform: PLATFORM,
95160
95133
  duration_ms: DURATION_MS,
95161
- // Schema-declared parameter names only (emit side filters against the tool's
95162
- // own zod shape and caps at 16 before this gate); the array validator also
95163
- // voids anything longer or with a non-identifier element.
95134
+ // Emit side sends only names declared in the tool's zod shape, capped at 16
95135
+ // because arrayOf voids the whole array once it is longer.
95164
95136
  invalid_params: arrayOf(matches(/^[a-z][a-z0-9_]{0,63}$/i, 64), 16),
95165
95137
  ...FAILURE_SIGNAL2,
95166
95138
  ...AI_TELEMETRY
@@ -95692,7 +95664,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
95692
95664
  var SESSION_ID = (0, import_node_crypto3.randomUUID)();
95693
95665
  function readCliVersion() {
95694
95666
  if (true) {
95695
- return "0.22.1-next.1";
95667
+ return "0.22.1-next.3";
95696
95668
  }
95697
95669
  return "0.0.0";
95698
95670
  }
@@ -96094,15 +96066,11 @@ function resolveHostFingerprint() {
96094
96066
  const out = (0, import_node_child_process.execFileSync)(simulatorServerBinaryPath(), ["fingerprint"], {
96095
96067
  encoding: "utf8",
96096
96068
  timeout: FINGERPRINT_TIMEOUT_MS,
96097
- // Reap with SIGKILL, not the default SIGTERM: a binary that ignores SIGTERM
96098
- // would otherwise block the (synchronous) event loop past the cap forever.
96099
- // SIGKILL can't be trapped, so the timeout is a genuine bound here.
96069
+ // SIGKILL, not the default SIGTERM: a binary that ignores SIGTERM would
96070
+ // otherwise block the (synchronous) event loop past the cap forever.
96100
96071
  killSignal: "SIGKILL",
96101
- // Cap captured stdout (same limit as the async variant): a binary streaming
96102
- // output is SIGKILL'd at this cap rather than filling Node's 1 MiB default.
96103
96072
  maxBuffer: FINGERPRINT_MAX_BUFFER,
96104
- // Ignore stderr so a binary that logs diagnostics doesn't pollute the
96105
- // caller's stderr; stdout (index 1) is captured as the return value.
96073
+ // Ignore stderr so the binary's diagnostics don't pollute the caller's.
96106
96074
  stdio: ["ignore", "pipe", "ignore"]
96107
96075
  }).trim();
96108
96076
  return out.length > 0 ? out : null;
@@ -96751,11 +96719,10 @@ function describeVegaFailure(args, err, kindOverride) {
96751
96719
  error_code: FAILURE_CODES.VEGA_CLI_COMMAND_FAILED,
96752
96720
  failure_stage: "vega_cli_command",
96753
96721
  failure_area: "tool_server",
96754
- // A timeout/overflow reap shapes the error with killed=true so the message reads
96755
- // correctly, but only a genuine *timeout* should classify as `error_kind:
96756
- // "timeout"` — listVegaDevices keys its skip-the-recovery-call decision off that,
96757
- // so an overflow (or other forced kill) must NOT masquerade as a wedged-agent
96758
- // timeout. Those callers pass an explicit kind; everything else uses the heuristic.
96722
+ // A forced reap shapes the error with killed=true so the message reads correctly, but
96723
+ // only a genuine *timeout* may classify as `error_kind: "timeout"` — listVegaDevices
96724
+ // keys its skip-the-recovery-call decision off that, so an overflow must not
96725
+ // masquerade as a wedged agent. Those callers pass an explicit kind.
96759
96726
  error_kind: kindOverride ?? (e.killed || e.signal ? "timeout" : "subprocess"),
96760
96727
  ...subprocessFailureMetadata(err, "vega")
96761
96728
  };
@@ -96800,7 +96767,7 @@ async function runVega(args, options = {}) {
96800
96767
  };
96801
96768
  const rejectTimeout = () => (
96802
96769
  // Shape like an execFile timeout rejection (killed=true) so it classifies as a
96803
- // timeout downstream — listVegaDevices keys its skip-the-recovery decision off it.
96770
+ // timeout downstream.
96804
96771
  settle(
96805
96772
  () => reject(
96806
96773
  describeVegaFailure(
@@ -96823,9 +96790,9 @@ async function runVega(args, options = {}) {
96823
96790
  new Error(`vega ${args.join(" ")} output exceeded ${maxOutputBytes} bytes`),
96824
96791
  { killed: true, signal: VEGA_KILL_SIGNAL, stdout, stderr }
96825
96792
  ),
96826
- // Force "subprocess": an overflow is a misbehaving child, not a wedged
96827
- // agent. Without this the killed=true shape would classify as "timeout"
96828
- // and wrongly suppress the listVegaDevices recovery call.
96793
+ // Force "subprocess": an overflow is a misbehaving child, not a wedged agent.
96794
+ // The killed=true shape would otherwise classify as "timeout" and wrongly
96795
+ // suppress the listVegaDevices recovery call.
96829
96796
  "subprocess"
96830
96797
  )
96831
96798
  )
@@ -97452,7 +97419,6 @@ async function run(args, options) {
97452
97419
  timeout: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
97453
97420
  maxBuffer: 16 * 1024 * 1024,
97454
97421
  encoding: "utf8",
97455
- // sim-remote pipes stdin through to pbcopy etc.
97456
97422
  input: options?.stdin
97457
97423
  });
97458
97424
  return { stdout: typeof stdout === "string" ? stdout : stdout.toString("utf8") };
@@ -97744,9 +97710,9 @@ async function readProcessLaunchState(pid) {
97744
97710
  ({ stdout } = await execFileAsync8(PS_BIN, ["eww", "-p", String(pid), "-o", "etime=,command="], {
97745
97711
  encoding: "utf8",
97746
97712
  timeout: PS_PROBE_TIMEOUT_MS,
97747
- // Matches the other `ps` probes (vega-process.ts). An environment can run
97713
+ // Matches the other `ps` probes (vega-process.ts): an environment can run
97748
97714
  // to `kern.argmax` (1 MiB), exactly Node's default cap, so the default
97749
- // would ENOBUFS on a maximal one instead of reading it.
97715
+ // would ENOBUFS on a maximal one.
97750
97716
  maxBuffer: 16 * 1024 * 1024
97751
97717
  }));
97752
97718
  } catch (err) {
@@ -97842,17 +97808,16 @@ var remoteIosHost = {
97842
97808
  const { stdout } = await simctlSpawn(udid, { args: ["launchctl", "list"] });
97843
97809
  return parseUIKitApplicationBundleIds(stdout);
97844
97810
  },
97845
- // App processes live on the orchestrator, so the local process table says
97846
- // nothing about how they were launched. Only running-ness is answerable; the
97847
- // null process keeps callers on their no-evidence path.
97811
+ // App processes live on the orchestrator, out of reach of the local process
97812
+ // table, so only running-ness is answerable; the null process keeps callers on
97813
+ // their no-evidence path.
97848
97814
  async inspectRunningApp(udid, bundleId) {
97849
97815
  const { stdout } = await simctlSpawn(udid, { args: ["launchctl", "list"] });
97850
97816
  return { running: parseUIKitApplicationJobs(stdout).has(bundleId), process: null };
97851
97817
  },
97852
- // Apply the accessibility defaults the tool-server needs (the local host does
97853
- // this via `defaults write`; here we run the same writes through the remote
97854
- // generic spawn). The entitlement-bypass plist is assumed active on cloud
97855
- // sims; if it isn't, describe will still surface a useful error.
97818
+ // The same accessibility `defaults write`s the local host performs, routed
97819
+ // through the remote generic spawn. The entitlement-bypass plist is assumed
97820
+ // active on cloud sims.
97856
97821
  async bootstrapAx(udid) {
97857
97822
  for (const flag of ACCESSIBILITY_DEFAULT_FLAGS) {
97858
97823
  await simctlSpawn(udid, {
@@ -98263,13 +98228,11 @@ var NotImplementedOnPlatformError = class extends Error {
98263
98228
  };
98264
98229
  var InvalidToolInputError = class extends Error {
98265
98230
  /**
98266
- * @param signal Optional telemetry-signal overrides. The HTTP 400 mapping keys
98267
- * off the error *class* (see http.ts), not the `error_code`, so a caller can
98268
- * pass a more granular `error_code` / `failure_stage` / `error_kind` — e.g.
98269
- * the keyboard backends' `KEYBOARD_KEY_UNSUPPORTED` /
98270
- * `KEYBOARD_CHARACTER_UNSUPPORTED` / `VEGA_TEXT_INVALID` classifications
98271
- * (from #420) — and keep both the granular telemetry bucket AND the 400
98272
- * status. Defaults to the generic `TOOL_INPUT_INVALID` / validation signal.
98231
+ * @param signal Telemetry-signal overrides. The HTTP 400 mapping keys off the
98232
+ * error *class* (see http.ts), not the `error_code`, so a caller can pass a
98233
+ * more granular code (e.g. the keyboard backends'
98234
+ * `KEYBOARD_CHARACTER_UNSUPPORTED`) and keep both the granular telemetry
98235
+ * bucket and the 400 status.
98273
98236
  */
98274
98237
  constructor(message, signal) {
98275
98238
  super(message);
@@ -108486,21 +108449,13 @@ var simulatorServerBlueprint = {
108486
108449
  return `${SIMULATOR_SERVER_NAMESPACE}:${device.id}`;
108487
108450
  },
108488
108451
  /**
108489
- * A cached simulator-server handle can outlive the thing it points at: when a
108490
- * simulator is un-booted (or the native server crashes) the child process may
108491
- * stay alive but stop listening on its API port, so every subsequent request
108492
- * fails with `ECONNREFUSED` against the now-dead port — the exact "worked once,
108493
- * then every call times out" symptom, unrecoverable until a human runs
108494
- * `stop-simulator-server`. The process never emitting `exit` means the
108495
- * registry's normal teardown (wired to `proc.on("exit")`) never fires.
108496
- *
108497
- * Treat a connection-refused failure as proof the instance is dead so the
108498
- * registry disposes it (killing the wedged process) and re-spawns a fresh
108499
- * simulator-server on the next call. Scoped to connection-refused only:
108500
- * ECONNREFUSED means the request never reached the server, so retrying can't
108501
- * double-apply a gesture. Timeouts and resets are deliberately excluded — the
108502
- * request there may have taken effect, and a hung-but-listening server is a
108503
- * different failure that respawning wouldn't fix.
108452
+ * A wedged simulator-server (sim un-booted, native server crashed) can keep its
108453
+ * process alive while no longer listening, so it never emits `exit`, the
108454
+ * registry's teardown never fires, and every later call gets `ECONNREFUSED`
108455
+ * until a human runs `stop-simulator-server`. Calling that recoverable makes
108456
+ * the registry dispose the instance and re-spawn. Scoped to connection-refused
108457
+ * only: the request provably never reached the server, so a retry can't
108458
+ * double-apply a gesture, whereas a timeout or reset may have taken effect.
108504
108459
  */
108505
108460
  recoverable(error52) {
108506
108461
  return getFailureSignal(error52)?.network_failure === "connection_refused";
@@ -108779,17 +108734,10 @@ var CDPClient = class {
108779
108734
  });
108780
108735
  }
108781
108736
  /**
108782
- * Re-point this client at a different CDP WebSocket target (e.g. switching
108783
- * the active browser tab) WITHOUT emitting `disconnected`.
108784
- *
108785
- * Object identity is preserved, so existing references to this client
108786
- * (`server.cdp`, `api.cdp`, and every closure that captured it) automatically
108787
- * target the new tab after the swap — no rewiring needed. Callers that wire
108788
- * `disconnected` to teardown/termination therefore do not see a tab switch as
108789
- * a device loss.
108790
- *
108791
- * In-flight requests are rejected and per-connection state (enabled domains,
108792
- * parsed scripts) is reset — the new target starts fresh, so the caller must
108737
+ * Re-point this client at another CDP target (e.g. a new browser tab) without
108738
+ * emitting `disconnected`, which callers treat as a device loss. Object
108739
+ * identity is preserved, so existing references need no rewiring. In-flight
108740
+ * requests are rejected and per-connection state is reset, so the caller must
108793
108741
  * re-enable any domains it needs.
108794
108742
  */
108795
108743
  async reconnect(newWsUrl) {
@@ -108825,10 +108773,9 @@ var CDPClient = class {
108825
108773
  const timer = setTimeout(() => {
108826
108774
  this.pending.delete(id);
108827
108775
  reject(
108828
- // The message carries its own recovery guidance so skills don't have
108829
- // to re-explain this state: the runtime is reachable but not
108830
- // answering, which agents otherwise read as a transient worth
108831
- // retry-looping (each loop iteration waits out this full timeout).
108776
+ // The message carries its own recovery guidance: agents otherwise read
108777
+ // this state as transient and retry-loop, each pass waiting out the
108778
+ // full timeout.
108832
108779
  new FailureError(
108833
108780
  `CDP request ${method} (id=${id}) timed out \u2014 the runtime accepted the connection but did not answer; it may be frozen, or paused at a breakpoint. debugger-status can still report "connected" in this state (the socket is open). Do not retry in a loop \u2014 restart the app, then reconnect and retry once.`,
108834
108781
  {
@@ -108875,8 +108822,8 @@ var CDPClient = class {
108875
108822
  this.bindingUnavailable = probe3 === "undefined";
108876
108823
  }
108877
108824
  /**
108878
- * Inject a script that will push a result via the binding using a unique requestId.
108879
- * Returns the parsed payload when the matching binding call arrives.
108825
+ * Inject a script that pushes its result over the binding tagged with a
108826
+ * requestId; resolves with the payload of the matching binding call.
108880
108827
  */
108881
108828
  evaluateWithBinding(expression, requestId, options) {
108882
108829
  const id = requestId ?? crypto3.randomUUID();
@@ -109074,9 +109021,8 @@ async function browserWebSocketUrl(port, signal) {
109074
109021
  throw new FailureError(
109075
109022
  `Chromium CDP on port ${port} did not report a browser webSocketDebuggerUrl in /json/version.`,
109076
109023
  {
109077
- // The endpoint responded but its payload was incomplete — a malformed
109078
- // response, not an unreachable port. Distinct code so telemetry doesn't
109079
- // conflate "reached but malformed" with a genuinely down debug port.
109024
+ // Reached but malformed — telemetry must not conflate this with a
109025
+ // genuinely down debug port.
109080
109026
  error_code: FAILURE_CODES.CHROMIUM_CDP_INVALID_RESPONSE,
109081
109027
  failure_stage: "chromium_cdp_browser_ws",
109082
109028
  failure_area: "tool_server",
@@ -109431,19 +109377,14 @@ var ScreencastManager = class {
109431
109377
  fps;
109432
109378
  activeCount = 0;
109433
109379
  currentOpts = null;
109434
- // The in-flight Page.startScreencast promise for the first subscriber, shared
109435
- // so concurrent joiners await the SAME start instead of assuming a live
109436
- // session (and stranding themselves on a frame-less stream if it fails). Null
109437
- // when no start is in flight (idle, or a live session already running).
109380
+ // The first subscriber's in-flight Page.startScreencast, published so joiners
109381
+ // await the SAME start instead of assuming a live session and stranding
109382
+ // themselves on a frame-less stream if it fails.
109438
109383
  startInFlight = null;
109439
109384
  lastFrame = null;
109440
109385
  cdpListenerInstalled = false;
109441
- /**
109442
- * Most-recently-received frame, or null if no screencast is active /
109443
- * Chromium hasn't pushed a frame yet. Exposed so single-shot consumers
109444
- * (preview overlay, snapshot debug tool) can grab the last frame without
109445
- * starting their own session.
109446
- */
109386
+ /** Never cleared on stop, so single-shot consumers can read a frame without
109387
+ * starting their own session. */
109447
109388
  getLastFrame() {
109448
109389
  return this.lastFrame;
109449
109390
  }
@@ -109571,7 +109512,7 @@ function warnSharpMissingOnce(reason) {
109571
109512
  var DOWNSCALER_TO_KERNEL = {
109572
109513
  lanczos3: "lanczos3",
109573
109514
  box: "mitchell",
109574
- // sharp doesn't expose a true box kernel; mitchell is the closest fast alternative
109515
+ // sharp has no box kernel
109575
109516
  bilinear: "lanczos2",
109576
109517
  nearest: "nearest"
109577
109518
  };
@@ -109786,12 +109727,8 @@ function createTabsManager(deps) {
109786
109727
  error_code: FAILURE_CODES.CHROMIUM_TAB_OPEN_FAILED,
109787
109728
  failure_stage: "chromium_tab_open",
109788
109729
  failure_area: "tool_server",
109789
- // The CDP command round-tripped but its response was missing the
109790
- // expected targetId — a malformed payload from a source we don't own,
109791
- // classified `unknown` like the other CDP-command failures in this
109792
- // server (CHROMIUM_SCREENSHOT_FAILED, the viewport/storage evals).
109793
- // `network`/`invalid_response` is reserved for the HTTP /json
109794
- // discovery layer, which has a genuine fetch transport.
109730
+ // A malformed CDP response, not a transport failure:
109731
+ // `network`/`invalid_response` is reserved for the HTTP /json layer.
109795
109732
  error_kind: "unknown"
109796
109733
  });
109797
109734
  return out.targetId;
@@ -109885,9 +109822,8 @@ function createNetworkManager(deps) {
109885
109822
  rec.resourceType = params.type ?? rec.resourceType;
109886
109823
  break;
109887
109824
  }
109888
- // The *ExtraInfo events carry the actual on-the-wire headers (Authorization,
109889
- // Set-Cookie, …) that the base events omit. Merge them in; they may arrive
109890
- // before the base event, so `record()` creates the entry if needed.
109825
+ // The *ExtraInfo events carry on-the-wire headers (Authorization, Set-Cookie, …)
109826
+ // the base events omit, and may arrive before them — hence `record()`, not `get()`.
109891
109827
  case "Network.requestWillBeSentExtraInfo": {
109892
109828
  const h = params.headers;
109893
109829
  if (h) {
@@ -110554,11 +110490,9 @@ Booted/ready devices are listed first. Platforms whose CLI is unavailable are si
110554
110490
  withDeadline(listIosSimulators(), [], "ios"),
110555
110491
  withDeadline(listRemoteIosSimulators(), [], "ios-remote"),
110556
110492
  withDeadline(
110557
- // Opt into runtimeKind enrichment (list-devices surfaces TV vs mobile to
110558
- // the agent, so the extra feature probe per device is warranted here — the
110559
- // boot-loop poller deliberately omits it), and pass the tight `adb devices`
110560
- // bound (NOT boot-device's 30s default) so the Android branch self-bounds
110561
- // under BRANCH_DEADLINE_MS — see ADB_DEVICES_TIMEOUT_MS.
110493
+ // list-devices is the one caller that surfaces TV vs mobile, so it pays for
110494
+ // runtimeKind's extra per-device probe. The explicit `adb devices` bound
110495
+ // (not runAdb's 30s default) keeps this branch under BRANCH_DEADLINE_MS.
110562
110496
  listAndroidDevices({ runtimeKind: true, devicesTimeoutMs: ADB_DEVICES_TIMEOUT_MS }).catch(
110563
110497
  () => []
110564
110498
  ),
@@ -110617,39 +110551,33 @@ var VariantProposalStore = class {
110617
110551
  consumed = false;
110618
110552
  globalComment = "";
110619
110553
  /**
110620
- * Device the variants target (last non-empty udid an agent passed to
110621
- * `propose_variant`). Persists across rounds — the agent works on one device
110622
- * — so it is intentionally NOT cleared by `reset()`.
110554
+ * Last non-empty udid an agent passed to `propose_variant`. One device spans
110555
+ * many rounds, so `reset()` intentionally does NOT clear it.
110623
110556
  */
110624
110557
  device = null;
110625
110558
  /**
110626
- * True while an `argent lens` CLI session owns the window. Set via
110627
- * `setCliSession`; deliberately NOT cleared by `reset()` — a CLI session spans
110628
- * many propose→submit rounds, like `device`.
110559
+ * True while an `argent lens` CLI session owns the window. A session spans many
110560
+ * propose→submit rounds, so `reset()` deliberately does NOT clear it.
110629
110561
  */
110630
110562
  cliSession = false;
110631
110563
  /**
110632
110564
  * The agent choices a CLI Lens session offers (when more than one is
110633
- * installed) and the one the human picked in the window. The `argent lens`
110634
- * bridge passes the choices in on begin and polls `lensAgentChoice` to learn
110635
- * which agent to spawn. Empty / null outside an unresolved pick. Like
110636
- * `cliSession`, deliberately NOT cleared by `reset()`.
110565
+ * installed) and the one the human picked. `argent lens` passes the choices in
110566
+ * on begin and learns the pick over `/preview/lens-stream`. Like `cliSession`,
110567
+ * deliberately NOT cleared by `reset()`.
110637
110568
  */
110638
110569
  lensAgents = [];
110639
110570
  lensAgentChoice = null;
110640
110571
  /**
110641
- * Whether the human asked to REMEMBER the agent pick (the picker's "Remember
110642
- * this choice" checkbox). The `argent lens` process reads this alongside the
110643
- * choice and persists it to `~/.argent/config.json` so later runs skip the
110644
- * picker. Tied to `lensAgentChoice`; reset with it.
110572
+ * Whether the human ticked the picker's "Remember this choice". `argent lens`
110573
+ * reads it alongside the pick and persists it to `~/.argent/config.json` so
110574
+ * later runs skip the picker. Tied to `lensAgentChoice`; reset with it.
110645
110575
  */
110646
110576
  lensAgentRemember = false;
110647
110577
  /**
110648
- * Devices (iOS udid / Android serial) that Lens BOOTED itself — i.e. the
110649
- * `POST /preview/boot` route started them because they were not already
110650
- * running. Tracked so the tool-server can shut them down when the CLI Lens
110651
- * session ends (`takeOwnedDevices`), without ever touching a device the user
110652
- * had already booted. Like `cliSession`, NOT cleared by `reset()`.
110578
+ * Devices `POST /preview/boot` started because they were not already running,
110579
+ * so session-end teardown (`takeOwnedDevices`) shuts down only those and never
110580
+ * one the user had booted. Like `cliSession`, NOT cleared by `reset()`.
110653
110581
  */
110654
110582
  ownedDevices = /* @__PURE__ */ new Set();
110655
110583
  submitted = [];
@@ -110660,15 +110588,12 @@ var VariantProposalStore = class {
110660
110588
  /** Frozen result of the current round once the user submits. */
110661
110589
  lastOutcome = null;
110662
110590
  /**
110663
- * Completed outcomes that finished with no `await_user_selection` parked to
110664
- * receive them directly (submitSelection's "no waiter" branch), queued in
110665
- * completion order. `autoRollIfCompleted` rolls into a fresh round on the
110666
- * very next `propose_variant` regardless of whether the outcome was ever
110667
- * retrieved — so this queue, unlike `lastOutcome`/`completed`/`consumed`
110668
- * (which describe only the CURRENT round), survives `reset()` and is
110669
- * drained first by `awaitSelection`, oldest first. Without it, a human's
110670
- * already-submitted selection is destroyed the moment the next
110671
- * `propose_variant` call rolls the round out from under it.
110591
+ * Completed outcomes that finished with no await parked to receive them, in
110592
+ * completion order. `autoRollIfCompleted` rolls on the next `propose_variant`
110593
+ * whether or not the outcome was retrieved, so this queue — unlike
110594
+ * `lastOutcome`/`completed`/`consumed`, which describe only the CURRENT round —
110595
+ * survives `reset()` and is drained first by `awaitSelection`. Without it a
110596
+ * roll destroys a human's already-submitted selection.
110672
110597
  */
110673
110598
  pendingOutcomes = [];
110674
110599
  /** Begin a fresh round, discarding the previous one's proposals/selections. */
@@ -110704,15 +110629,12 @@ var VariantProposalStore = class {
110704
110629
  this.events.emit("changed");
110705
110630
  }
110706
110631
  /**
110707
- * Emit the drop-off signal for a round still staged (proposals present, not
110708
- * completed) at process shutdown — "the server died mid-review". The
110709
- * tool-server calls this on its shutdown path, before the final telemetry
110710
- * drain, so a round the human never got to submit is counted as an abandonment
110711
- * instead of vanishing into the unattributable opens-minus-completions
110712
- * residue. Routes through reset() — the single abandonment choke point, guarded
110713
- * so a completed or empty round is a no-op — which also settles any await still
110714
- * parked on the dying round rather than letting it hang. Returns whether an
110715
- * abandonment was emitted (for the caller's logging / tests).
110632
+ * Emit the drop-off signal for a round still staged at process shutdown — "the
110633
+ * server died mid-review" — so it is counted instead of vanishing into the
110634
+ * unattributable opens-minus-completions residue. The tool-server calls this
110635
+ * before its final telemetry drain. Routes through reset(), the single
110636
+ * abandonment choke point, which also settles any await parked on the dying
110637
+ * round. Returns whether an abandonment was emitted.
110716
110638
  */
110717
110639
  flushAbandonedRound() {
110718
110640
  if (this.completed || this.proposals.length === 0) return false;
@@ -110725,13 +110647,12 @@ var VariantProposalStore = class {
110725
110647
  return p?.variants.find((v) => v.id === variantId) ?? null;
110726
110648
  }
110727
110649
  /**
110728
- * Called when the native preview window could not be launched (e.g. the
110729
- * optional `electron` dependency is absent on a headless/CI host). Settles
110730
- * every currently-parked, unsettled waiter with a `pending` outcome whose
110731
- * message points the agent at the browser fallback URL, rather than letting
110732
- * the await park for the full timeout with no window and no feedback. The
110733
- * proposals stay live, so the agent can relay the URL and re-await. No-ops
110734
- * when nothing is parked.
110650
+ * Settle every parked waiter with a `pending` outcome pointing at the browser
110651
+ * fallback URL when the native preview window could not launch (e.g. the
110652
+ * optional `electron` dependency is absent on a headless/CI host), rather than
110653
+ * letting the await run its full timeout with no window and no feedback. The
110654
+ * proposals stay live, so the agent can relay the URL and re-await. No-ops when
110655
+ * nothing is parked.
110735
110656
  */
110736
110657
  notifyWindowUnavailable(reason, url2) {
110737
110658
  const toSettle = this.waitersList.filter((w) => !w.settled);
@@ -110812,10 +110733,10 @@ var VariantProposalStore = class {
110812
110733
  }
110813
110734
  /**
110814
110735
  * Begin or end a CLI-driven Lens session (`argent lens`), optionally offering
110815
- * a set of agent choices for the window's picker. `cliSessionChanged` fires
110816
- * only on an actual begin/end transition (so the window opens/closes once),
110817
- * but the agent choices are always refreshed on a begin call — a re-begin from
110818
- * a fresh `argent lens` must replace any stale choices.
110736
+ * agent choices for the window's picker. `cliSessionChanged` fires only on an
110737
+ * actual begin/end transition (so the window opens/closes once), but the
110738
+ * choices are refreshed on every begin — a re-begin from a fresh `argent lens`
110739
+ * must replace stale ones.
110819
110740
  */
110820
110741
  setCliSession(active, agents = []) {
110821
110742
  const transitioned = this.cliSession !== active;
@@ -110833,8 +110754,7 @@ var VariantProposalStore = class {
110833
110754
  }
110834
110755
  this.events.emit("changed");
110835
110756
  }
110836
- /** Record the agent the human picked in the window's picker, and whether they
110837
- * asked to remember it. */
110757
+ /** Record the agent the human picked, and whether they asked to remember it. */
110838
110758
  setLensAgentChoice(id, remember = false) {
110839
110759
  this.lensAgentChoice = id;
110840
110760
  this.lensAgentRemember = remember;
@@ -110857,18 +110777,16 @@ var VariantProposalStore = class {
110857
110777
  if (id.trim()) this.ownedDevices.add(id.trim());
110858
110778
  }
110859
110779
  /**
110860
- * Whether Lens booted this device itself (and is therefore responsible for
110861
- * it). Test-only accessor: production code manages ownership through
110862
- * `markDeviceOwned` / `releaseDevice` / `takeOwnedDevices` and never needs to
110863
- * read a single device's ownership. Kept as a non-mutating way for tests to
110864
- * assert ownership without draining it via `takeOwnedDevices`.
110780
+ * Whether Lens booted this device itself. Test-only: production manages
110781
+ * ownership through `markDeviceOwned` / `releaseDevice` / `takeOwnedDevices`,
110782
+ * so this exists as a non-mutating way to assert it without draining the set.
110865
110783
  */
110866
110784
  isDeviceOwned(id) {
110867
110785
  return this.ownedDevices.has(id.trim());
110868
110786
  }
110869
110787
  /**
110870
- * Drop a single owned device — e.g. the user shut it down manually via the
110871
- * preview window, so session-end teardown must not try to kill it again.
110788
+ * Drop a single owned device — e.g. the user shut it down from the preview
110789
+ * window, so session-end teardown must not try to kill it again.
110872
110790
  */
110873
110791
  releaseDevice(id) {
110874
110792
  this.ownedDevices.delete(id.trim());
@@ -110881,22 +110799,21 @@ var VariantProposalStore = class {
110881
110799
  }
110882
110800
  /**
110883
110801
  * The frozen outcome of the last submitted round, or null if nothing has been
110884
- * submitted since the last reset. Read by `GET /preview/outcome` so the
110885
- * `argent lens` watcher can format the user's feedback and type it into the
110886
- * spawned `claude` terminal. Cleared (to null) when a new round begins.
110802
+ * submitted since the last reset. Served over `/preview/outcome` and
110803
+ * `/preview/lens-stream` so the `argent lens` watcher can format the user's
110804
+ * feedback and type it into the spawned agent terminal.
110887
110805
  */
110888
110806
  getLastOutcome() {
110889
110807
  return this.lastOutcome;
110890
110808
  }
110891
- /** Called by the preview UI when the human presses "Complete selection". */
110809
+ /** Called by the preview UI when the human submits their picks. */
110892
110810
  submitSelection(input) {
110893
110811
  if (typeof input.round === "number" && input.round !== this.round) {
110894
110812
  return { ok: true, round: this.round, resolved: 0, stale: true };
110895
110813
  }
110896
110814
  const cleanAnnotations = (input.annotations ?? []).filter((a) => a && typeof a.comment === "string" && a.comment.trim()).slice(0, MAX_ANNOTATIONS).map((a) => ({
110897
110815
  target: String(a.target ?? "").slice(0, 200) || "(element)",
110898
- // Cap the matcher value too — it is caller-supplied and, unlike the
110899
- // comment, was previously ingested uncapped.
110816
+ // Caller-supplied and, unlike the comment, otherwise ingested uncapped.
110900
110817
  match: {
110901
110818
  by: a.match?.by ?? "text",
110902
110819
  value: String(a.match?.value ?? "").slice(0, MAX_MATCH_VALUE_LENGTH)
@@ -111012,14 +110929,12 @@ var VariantProposalStore = class {
111012
110929
  };
111013
110930
  }
111014
110931
  /**
111015
- * Block until the user submits a selection for the current round.
111016
- *
111017
- * Resolves immediately if a selection is already waiting to be consumed.
111018
- * On `timeoutMs` elapse returns a `pending` outcome (so the agent — or the
111019
- * MCP client wrapping it — can re-await without losing the live proposals).
111020
- * Honors `signal`: a client disconnect rejects with an AbortError. Every
111021
- * await parked on a round is resolved when that round is submitted (or the
111022
- * round is superseded), so concurrent / re-entrant awaits never strand.
110932
+ * Block until the user submits a selection for the current round, resolving
110933
+ * immediately if one is already waiting to be consumed. On `timeoutMs` returns
110934
+ * a `pending` outcome so the caller can re-await without losing the live
110935
+ * proposals; a `signal` abort rejects with an AbortError. Every await parked on
110936
+ * a round is resolved when that round is submitted or superseded, so concurrent
110937
+ * / re-entrant awaits never strand.
111023
110938
  */
111024
110939
  awaitSelection(opts) {
111025
110940
  if (opts.signal?.aborted) {
@@ -112071,9 +111986,8 @@ function deriveUiAutomatorRole(className) {
112071
111986
  return short || "View";
112072
111987
  }
112073
111988
  var NOISY_CLASSES = /* @__PURE__ */ new Set([
112074
- // React Native Skia/SVG icon internals: never tappable, parent already
112075
- // carries the icon's content-desc, and a single icon can balloon a dump by
112076
- // 40+ leaf nodes. Drop the entire subtree.
111989
+ // react-native-svg icon internals: never tappable, and the parent already
111990
+ // carries the icon's content-desc.
112077
111991
  "com.horcrux.svg.PathView",
112078
111992
  "com.horcrux.svg.GroupView",
112079
111993
  "com.horcrux.svg.SvgView"
@@ -112082,10 +111996,8 @@ function isNoisyUiAutomatorClass(className) {
112082
111996
  return NOISY_CLASSES.has(className);
112083
111997
  }
112084
111998
  var SYSTEM_PACKAGES = /* @__PURE__ */ new Set([
112085
- // Status bar / nav bar / quick settings — these exist on every dump but
112086
- // rarely matter for app-level navigation. Note we deliberately do NOT drop
112087
- // the foreground-app's own package even when the foreground IS a system app
112088
- // (settings, permission dialog), so permission prompts still surface.
111999
+ // Status bar / nav bar / quick settings. Only systemui is listed on purpose:
112000
+ // a foreground system app (settings, permission dialog) must still surface.
112089
112001
  "com.android.systemui"
112090
112002
  ]);
112091
112003
  var SYSTEM_RID_PREFIXES = [
@@ -112100,8 +112012,8 @@ var LAYOUT_CONTAINERS = /* @__PURE__ */ new Set([
112100
112012
  "androidx.constraintlayout.widget.ConstraintLayout",
112101
112013
  "androidx.coordinatorlayout.widget.CoordinatorLayout",
112102
112014
  "android.view.ViewGroup",
112103
- // Bare android.view.View is what Compose emits when a semantics node has
112104
- // no widget mapping; treat it as a scaffold and walk through.
112015
+ // Compose emits bare android.view.View for semantics nodes with no widget
112016
+ // mapping.
112105
112017
  "android.view.View"
112106
112018
  ]);
112107
112019
  function isUiAutomatorLayoutContainer(className) {
@@ -112439,8 +112351,7 @@ function resolveTraceProcessorAssets() {
112439
112351
  }
112440
112352
  function patchGlueForNode(src) {
112441
112353
  const edits = [
112442
- // Force memory32: skip the memory64 feature probe entirely (avoids the
112443
- // memory64 __syscall_mprotect path; no global WebAssembly wrapper).
112354
+ // Force memory32: skip the memory64 probe and its __syscall_mprotect path.
112444
112355
  ["this.useMemory64 = hasMemory64Support();", "this.useMemory64 = false;"],
112445
112356
  // Hand the wasm bytes straight to emscripten -> it never calls readBinary/XHR.
112446
112357
  ["locateFile: (s) => s,", "locateFile: (s) => s, wasmBinary: globalThis.__TP_WASM_BYTES,"],
@@ -113219,10 +113130,8 @@ async function describeAndroid(registry2, serial, _bundleId, isTv) {
113219
113130
  throw new FailureError(
113220
113131
  `uiautomator could not capture the screen: ${trimmed}. Common causes: device locked / keyguard, DRM or secure overlay, Play Integrity screen. Unlock the device or take a screenshot as a fallback.`,
113221
113132
  {
113222
- // The adb wrapper exits 0, but the uiautomator tool it ran reported an
113223
- // in-band `ERROR:` line — a functional failure of the uiautomator
113224
- // subprocess. Classified `subprocess` to match the sibling
113225
- // ANDROID_UIAUTOMATOR_PARSE_FAILED (also adb-exit-0, unusable output).
113133
+ // adb exits 0, but uiautomator reported an in-band `ERROR:` line — same
113134
+ // adb-exit-0/unusable-output shape as ANDROID_UIAUTOMATOR_PARSE_FAILED.
113226
113135
  error_code: FAILURE_CODES.ANDROID_UIAUTOMATOR_CAPTURE_FAILED,
113227
113136
  failure_stage: "android_uiautomator_capture",
113228
113137
  failure_area: "tool_server",
@@ -113267,10 +113176,9 @@ function createPreviewRouter(registry2) {
113267
113176
  element_count: snap.proposals.length,
113268
113177
  variant_count: snap.proposals.reduce((n, p) => n + p.variants.length, 0),
113269
113178
  is_cli_session: snap.cliSession,
113270
- // Report platform ONLY for a round that actually staged proposals: the
113271
- // store's `device` deliberately survives reset(), so a CLI up-front open
113272
- // (element_count 0, no device bound THIS round) would otherwise inherit a
113273
- // prior flow's platform — a stale value next to a zero-count open.
113179
+ // `device` deliberately survives the store's reset(), so a CLI up-front
113180
+ // open (no proposals staged yet) would otherwise report a prior flow's
113181
+ // platform.
113274
113182
  platform: snap.proposals.length > 0 && snap.device ? classifyDeviceForTelemetry(snap.device) : void 0
113275
113183
  });
113276
113184
  };
@@ -113536,16 +113444,16 @@ data: ${JSON.stringify(data)}
113536
113444
  }
113537
113445
  try {
113538
113446
  const result = variantProposalStore.submitSelection({
113539
- // The round the UI built this submit against (if it sent one). The store
113540
- // rejects it as stale when the round has since rolled, so a click from a
113541
- // tab whose round already completed/rolled can't mint a phantom completion.
113447
+ // The round the UI built this submit against, if it sent one. The store
113448
+ // rejects it once the round has rolled, so a click from a stale tab can't
113449
+ // mint a phantom completion.
113542
113450
  round: typeof body.round === "number" ? body.round : void 0,
113543
113451
  selections,
113544
113452
  annotations,
113545
113453
  globalComment: typeof body.globalComment === "string" ? body.globalComment : void 0,
113546
- // Privacy-safe UI usage signals for `lens:round_completed`. Coerced to
113547
- // strict booleans so a malformed/absent field from the unauthenticated
113548
- // route can never carry anything but true/false into telemetry.
113454
+ // UI usage signals for `lens:round_completed`. Coerced to strict booleans
113455
+ // so a malformed field from this unauthenticated route can't carry
113456
+ // anything else into telemetry.
113549
113457
  inspectorUsed: body.inspectorUsed === true,
113550
113458
  offscreenRevealed: body.offscreenRevealed === true
113551
113459
  });
@@ -115062,7 +114970,7 @@ var SourceMapsRegistry = class {
115062
114970
  }
115063
114971
  /**
115064
114972
  * Begin fetching and registering a source map from a Debugger.scriptParsed event.
115065
- * Returns immediately; use `waitForPending()` to block until all maps are loaded.
114973
+ * Returns immediately; `waitForPending()` blocks until all maps are loaded.
115066
114974
  */
115067
114975
  registerFromScriptParsed(scriptUrl, scriptId, sourceMapURL) {
115068
114976
  if (!sourceMapURL) return;
@@ -115076,10 +114984,8 @@ var SourceMapsRegistry = class {
115076
114984
  /**
115077
114985
  * Resolve an original source file + line to its generated position in the bundle.
115078
114986
  *
115079
- * `filePath` can be:
115080
- * - relative to project root, e.g. "App.tsx" or "src/components/Foo.tsx"
115081
- * - absolute, e.g. "/Users/.../App.tsx"
115082
- * - aliased, e.g. "/[metro-project]/App.tsx"
114987
+ * `filePath` may be project-relative ("src/Foo.tsx"), absolute, or aliased
114988
+ * ("/[metro-project]/App.tsx").
115083
114989
  */
115084
114990
  toGeneratedPosition(filePath, line1Based, column0Based = 0) {
115085
114991
  const candidates = this.buildSourceCandidates(filePath);
@@ -115107,10 +115013,7 @@ var SourceMapsRegistry = class {
115107
115013
  }
115108
115014
  return null;
115109
115015
  }
115110
- /**
115111
- * Find which source map source path matches the given file path.
115112
- * Returns the matched source string or null.
115113
- */
115016
+ /** Matching entry in a registered map's `sources`, or null. */
115114
115017
  findMatchingSource(filePath) {
115115
115018
  const candidates = this.buildSourceCandidates(filePath);
115116
115019
  for (const map2 of this.maps) {
@@ -115453,22 +115356,12 @@ var chromiumJsRuntimeDebuggerBlueprint = {
115453
115356
  getDependencies(_payload) {
115454
115357
  return { chromium: `${CHROMIUM_CDP_NAMESPACE}:${_payload}` };
115455
115358
  },
115456
- // Deliberately NO recoverable() here. The registry's self-heal disposes the
115457
- // recovering node before it retries, and this blueprint's dispose closes the
115458
- // LogFileWriter — which UNLINKS the session's captured console log from disk
115459
- // (log-file-writer.ts) — so a recovery pass would destroy the logs whether or
115460
- // not the retry then succeeds. It would also buy nothing: the one window
115461
- // where a call fails while this node and its ChromiumCdp dependency stay
115462
- // RUNNING is a tab switch, where CDPClient.reconnect() rejects in-flight
115463
- // requests with CONNECTION_CLOSED (late sends with NOT_CONNECTED) but
115464
- // re-points the SAME client object at the new tab — the cached node heals
115465
- // itself for the next call without any dispose. The failing call surfaces a
115466
- // classified error that debugger-status maps to a structured "reconnecting"
115467
- // result with retry-once guidance. A genuinely dead Chromium socket instead
115468
- // fires ChromiumCdp's own terminated event, whose teardown cascades into this
115469
- // dependent — the node leaves RUNNING and the next call re-resolves fresh.
115470
- // (Same log-preservation reasoning as debugger-log-registry's missing socket
115471
- // gate — see that tool's comment.)
115359
+ // Deliberately NO recoverable(): the registry's self-heal disposes the node
115360
+ // before retrying, and dispose unlinks the captured console log. It would
115361
+ // also buy nothing — the only failure window while this node and ChromiumCdp
115362
+ // both stay RUNNING is a tab switch, where CDPClient.reconnect() re-points
115363
+ // the same client object and the cached node heals itself; a genuinely dead
115364
+ // socket arrives instead as ChromiumCdp's terminated cascade.
115472
115365
  async factory(deps, payload, options) {
115473
115366
  const opts = options;
115474
115367
  const device = opts?.device;
@@ -115524,14 +115417,13 @@ var chromiumJsRuntimeDebuggerBlueprint = {
115524
115417
  const sourceResolver = makeStubSourceResolver();
115525
115418
  const api = {
115526
115419
  port,
115527
- // Chromium apps have no Metro project root. Empty string keeps the
115528
- // contract type-clean; callers that care (only inspect-element via the
115529
- // source resolver) are gated out before they ever touch this field.
115420
+ // No Metro project root on Chromium; debugger-connect and debugger-status
115421
+ // document the field as empty here.
115530
115422
  projectRoot: "",
115531
115423
  deviceName: device.name ?? "Chromium",
115532
115424
  appName: "Chromium",
115533
115425
  logicalDeviceId: device.id,
115534
- // Chromium always speaks the new CDP — there is no Hermes-legacy mode.
115426
+ // No legacy RN inspector on Chromium.
115535
115427
  isNewDebugger: true,
115536
115428
  cdp,
115537
115429
  sourceResolver,
@@ -115910,14 +115802,11 @@ var ANDROID_NAMED_KEYCODES = {
115910
115802
  "esc": 111,
115911
115803
  // alias of escape
115912
115804
  "backspace": 67,
115913
- // KEYCODE_DEL (backspace: deletes the char before the cursor)
115914
- // `delete` aliases backspace, not forward-delete: the shared HID vocabulary in
115915
- // key-codes.ts (NAMED_KEYS) maps both `backspace` and `delete` to usage 42
115916
- // (Keyboard DELETE/Backspace), so iOS types `delete` as a backspace. A named
115917
- // key must mean the same thing on every platform, so map it to KEYCODE_DEL (67)
115918
- // here too rather than KEYCODE_FORWARD_DEL (112).
115805
+ // KEYCODE_DEL
115806
+ // KEYCODE_DEL, not KEYCODE_FORWARD_DEL (112): the shared HID vocabulary in
115807
+ // key-codes.ts maps both `backspace` and `delete` to usage 42, and a named key
115808
+ // must mean the same thing on every platform.
115919
115809
  "delete": 67,
115920
- // KEYCODE_DEL (alias of backspace — see note above)
115921
115810
  "tab": 61,
115922
115811
  // KEYCODE_TAB
115923
115812
  "space": 62,
@@ -115930,7 +115819,7 @@ var ANDROID_NAMED_KEYCODES = {
115930
115819
  // KEYCODE_DPAD_LEFT
115931
115820
  "arrow-right": 22,
115932
115821
  // KEYCODE_DPAD_RIGHT
115933
- // F1..F12 are KEYCODE_F1 (131) .. KEYCODE_F12 (142), contiguous.
115822
+ // KEYCODE_F1 (131) .. KEYCODE_F12 (142) are contiguous.
115934
115823
  ...Object.fromEntries(Array.from({ length: 12 }, (_, i) => [`f${i + 1}`, 131 + i]))
115935
115824
  };
115936
115825
  var ANDROID_BUTTON_KEYCODES = {
@@ -115950,9 +115839,8 @@ var ANDROID_BUTTON_KEYCODES = {
115950
115839
  function assertTypeableAndroidText(text) {
115951
115840
  if (/[\n\r]/.test(text)) {
115952
115841
  throw new InvalidToolInputError(
115953
- // Advice must hold on every path sharing this guard: named keys work on
115954
- // phones/tablets but are rejected on a TV target (typeTv), where the
115955
- // equivalent is the tv-remote select press.
115842
+ // The advice must also hold on the TV path (typeTv), which rejects named
115843
+ // keys in favour of tv-remote select.
115956
115844
  'keyboard text must not contain a newline on Android; press enter separately instead (key: "enter" on a phone or tablet, tv-remote select on a TV)',
115957
115845
  {
115958
115846
  error_code: FAILURE_CODES.KEYBOARD_CHARACTER_UNSUPPORTED,
@@ -116178,15 +116066,13 @@ var androidTvControlBlueprint = {
116178
116066
  await injectAndroidText(serial, words[i]);
116179
116067
  }
116180
116068
  },
116181
- // Android TV reads the live hierarchy on every describe (no cached
116182
- // daemon), so there is no stale-cache class of bug to recover from.
116069
+ // Android TV reads the live hierarchy on every describe — no cache to drop.
116183
116070
  async recycleAx() {
116184
116071
  }
116185
116072
  };
116186
116073
  const instance = {
116187
116074
  api,
116188
- // Stateless: every method is a fresh adb shell-out, so there is nothing to
116189
- // tear down. Present to satisfy the ServiceInstance contract.
116075
+ // Stateless: every call is a fresh adb shell-out, so nothing to tear down.
116190
116076
  dispose: async () => {
116191
116077
  },
116192
116078
  events
@@ -116213,9 +116099,8 @@ var nativeDevtoolsStatusTool = {
116213
116099
  },
116214
116100
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
116215
116101
  // The "injectable is false" recovery sentence inlines NON_INJECTABLE_RECOVERY
116216
- // verbatim: the description must stay a plain literal so scripts/extract-tools.mjs
116217
- // can read it statically for the spidershield scan. The verbatim match is pinned
116218
- // by native-devtools-status.test.ts.
116102
+ // verbatim (pinned by native-devtools-status.test.ts): the description must stay
116103
+ // a plain literal for scripts/extract-tools.mjs to read statically.
116219
116104
  description: `Check whether native devtools are connected to a specific app and whether the next launch is prepared for injection.
116220
116105
  Use when you need to verify native devtools readiness before calling native-full-hierarchy, native-describe-screen, or native-network-logs.
116221
116106
 
@@ -116294,13 +116179,13 @@ Fails if the simulator server is not running for the given UDID.`,
116294
116179
  // fresh process fixes — `indeterminate` among them, since an uninspectable
116295
116180
  // host (ios-remote) supports no finer reading. Both already carry a live
116296
116181
  // process (the settling above rewrote an empty `indeterminate` to
116297
- // `not_running`), so an `appRunning` conjunct would only restate the state.
116182
+ // `not_running`), so an `appRunning` conjunct would restate the state.
116298
116183
  requiresRestart: state3 === "stale_process" || state3 === "indeterminate",
116299
116184
  state: state3,
116300
116185
  // The booleans cannot express "one restart, then stop" — the shape
116301
116186
  // `indeterminate` needs, and the only one ios-remote can report for a
116302
- // running app. Carrying the same prose as every other consumer keeps that
116303
- // escape off the agent having read this tool's description.
116187
+ // running app. The shared prose puts that escape in front of an agent who
116188
+ // never read this tool's description.
116304
116189
  ...state3 === "connected" ? {} : { message: buildAppStateMessage(params.bundleId, state3) },
116305
116190
  nextLaunchWillBeInjected: envSetup,
116306
116191
  injectable: true
@@ -117178,22 +117063,11 @@ var jsRuntimeDebuggerBlueprint = {
117178
117063
  getURN(payload) {
117179
117064
  return `${JS_RUNTIME_DEBUGGER_NAMESPACE}:${payload}`;
117180
117065
  },
117181
- // Consulted by the registry's dispose-and-retry-once self-heal, and only for
117182
- // a node still in RUNNING state. On this blueprint a detected socket death
117183
- // tears the node down via the terminated cascade before the failing call's
117184
- // catch runs, so the only recoverable window is the send() guard rejecting
117185
- // while the WebSocket is CLOSING but the close event has not dispatched yet —
117186
- // there the request provably never left the host, making a retry safe.
117187
- // Deliberately NOT recoverable:
117188
- // - DEBUGGER_CDP_CONNECTION_CLOSED: the request was delivered and may have
117189
- // taken effect (double-execution risk); on this path the node has also
117190
- // already left RUNNING when it fires.
117191
- // - DEBUGGER_CDP_REQUEST_TIMEOUT: the request may have taken effect, and a
117192
- // hung-but-open runtime (e.g. paused at a breakpoint) is not fixed by
117193
- // reconnecting.
117194
- // - Metro discovery / target-selection codes: init-path failures — the node
117195
- // never reaches RUNNING, so recovery is never consulted, and a retry would
117196
- // be hopeless anyway.
117066
+ // Only the send() guard's DEBUGGER_CDP_NOT_CONNECTED proves the request never
117067
+ // left the host, so only it is safe to retry. CONNECTION_CLOSED and
117068
+ // REQUEST_TIMEOUT reject requests that were delivered and may have taken
117069
+ // effect; Metro discovery and target-selection codes throw before the node is
117070
+ // RUNNING, where the registry never consults this.
117197
117071
  recoverable(error52) {
117198
117072
  return getFailureSignal(error52)?.error_code === FAILURE_CODES.DEBUGGER_CDP_NOT_CONNECTED;
117199
117073
  },
@@ -118364,11 +118238,8 @@ async function waitForAdbDevice(serial, timeoutMs) {
118364
118238
  error_code: FAILURE_CODES.VEGA_DEVICE_NOT_REGISTERED,
118365
118239
  failure_stage: "vega_adb_register",
118366
118240
  failure_area: "tool_server",
118367
- // We polled until the deadline and adb never enumerated the transport,
118368
- // so the failure is structurally a timeout. The distinct error_code
118369
- // (vs VEGA_BOOT_TIMEOUT, the VVD never starting) already carries the
118370
- // "registered vs started" distinction — error_kind reflects the true
118371
- // nature (deadline expiry) rather than double-encoding that.
118241
+ // Deadline expiry, hence `timeout`; the error_code already separates this
118242
+ // from VEGA_BOOT_TIMEOUT (the VVD never starting).
118372
118243
  error_kind: "timeout"
118373
118244
  }
118374
118245
  );
@@ -118668,11 +118539,9 @@ async function bootElectronApp(options) {
118668
118539
  child = (0, import_node_child_process17.spawn)(launcher.command, args, {
118669
118540
  detached: true,
118670
118541
  stdio: ["ignore", "pipe", "pipe"],
118671
- // Strip ELECTRON_RUN_AS_NODE (see electronGuiChildEnv): if the tool-server
118672
- // inherited it from an Electron-based MCP host, the Electron binary would
118673
- // run in Node mode with no CDP endpoint — so boot-device fails below (the
118674
- // child exits early, or the readiness probe times out) instead of the app
118675
- // coming up.
118542
+ // Strip ELECTRON_RUN_AS_NODE (see electronGuiChildEnv): inherited from an
118543
+ // Electron-based MCP host it would boot the binary in Node mode with no
118544
+ // CDP endpoint, failing boot-device instead of bringing the app up.
118676
118545
  env: electronGuiChildEnv({ ELECTRON_ENABLE_LOGGING: "1" })
118677
118546
  });
118678
118547
  } catch (err) {
@@ -118835,11 +118704,9 @@ var STAGE_BUDGET = {
118835
118704
  pmReady: 45e3,
118836
118705
  // pm path android answers (retried; non-fatal on the final attempt)
118837
118706
  firstRealFrame: 9e4,
118838
- // screencap returns ≥1 non-zero pixel after cold boot
118839
118707
  firstRealFrameHot: 8e3
118840
- // tighter budget for snapshot-restore composite —
118841
- // the broken state is sticky (per assertScreencapAlive's docstring), so a
118842
- // few seconds is enough to discriminate transient blanks from genuine wedge.
118708
+ // the sticky-blank state never clears on its own, so a
118709
+ // few seconds is enough to tell it from a transient blank.
118843
118710
  };
118844
118711
  var VALID_GPU_MODES = /* @__PURE__ */ new Set([
118845
118712
  "auto",
@@ -119358,14 +119225,13 @@ async function bootAndroidImpl(params) {
119358
119225
  emulatorArgs: hotArgs,
119359
119226
  attemptDeadline: hotAttemptDeadline,
119360
119227
  serialsBefore,
119361
- // Snapshot restores register with adb within a couple of seconds;
119362
- // a minute-long register wait on the hot path would mask the
119363
- // scenario where load fails and the child silently cold-boots.
119228
+ // Snapshot restores register with adb within seconds; a minute-long
119229
+ // wait here would mask a failed load that silently cold-boots.
119364
119230
  adbRegisterBudgetMs: 3e4,
119365
119231
  deviceReadyBudgetMs: 3e4,
119366
119232
  bootCompletedBudgetMs: 3e4,
119367
- // Keep the hot path tight: a single ~10 s PM window, and tear down on
119368
- // failure so we fall through to the cold boot below.
119233
+ // Keep the hot path tight: one ~10 s PM window, tear down on failure so
119234
+ // we fall through to the cold boot below.
119369
119235
  pmProbeBudgetMs: 1e4,
119370
119236
  tearDownIfUnready: true
119371
119237
  });
@@ -119406,9 +119272,9 @@ async function bootAndroidImpl(params) {
119406
119272
  adbRegisterBudgetMs: STAGE_BUDGET.adbRegister,
119407
119273
  deviceReadyBudgetMs: STAGE_BUDGET.deviceReady,
119408
119274
  bootCompletedBudgetMs: STAGE_BUDGET.bootCompleted,
119409
- // Final attempt: retry PM for longer, and do NOT tear the emulator down
119410
- // if it stays slow — a guest that reached boot_completed is usable, and
119411
- // there is no further fallback to justify destroying it.
119275
+ // Final attempt: retry PM for longer and don't tear the emulator down if
119276
+ // it stays slow — a guest that reached boot_completed is usable, and there
119277
+ // is no further fallback to justify destroying it.
119412
119278
  pmProbeBudgetMs: STAGE_BUDGET.pmReady,
119413
119279
  tearDownIfUnready: false
119414
119280
  });
@@ -119896,11 +119762,8 @@ Common Android packages: com.android.settings, com.android.chrome, com.google.an
119896
119762
  searchHint: "open start app bundle id package simulator emulator chromium vega launch tvos apple tv fire tv",
119897
119763
  zodSchema: zodSchema10,
119898
119764
  capability: capability2,
119899
- // Chromium declares an eager CDP service; ios-remote declares an eager
119900
- // native-devtools service (its handler shares the local iOS launch path,
119901
- // which reads `services.nativeDevtools`). Local iOS resolves native-devtools
119902
- // lazily in its handler so a tvOS udid never spins up the iOS-only injection
119903
- // (see header comment); Android and Vega need no service.
119765
+ // ios-remote's handler reads `services.nativeDevtools`; local iOS resolves
119766
+ // it lazily instead (see header comment).
119904
119767
  services: (params) => {
119905
119768
  const device = resolveDevice(params.udid);
119906
119769
  if (device.platform === "ios-remote") return { nativeDevtools: nativeDevtoolsRef(device) };
@@ -120081,11 +119944,8 @@ Returns { restarted, bundleId }. Fails if the app is not installed.`,
120081
119944
  searchHint: "terminate relaunch restart reset app bundle id package simulator emulator vega tvos fire tv",
120082
119945
  zodSchema: zodSchema11,
120083
119946
  capability: capability3,
120084
- // ios-remote declares an eager native-devtools service (its handler shares
120085
- // the local iOS relaunch path, which reads `services.nativeDevtools`). Local
120086
- // iOS resolves native-devtools lazily in its handler so a tvOS udid never
120087
- // spins up the iOS-only injection (see header comment); Android and Vega
120088
- // need no service.
119947
+ // Only ios-remote's handler reads `services.nativeDevtools`, so only it
119948
+ // needs an eager declaration.
120089
119949
  services: (params) => {
120090
119950
  const device = resolveDevice(params.udid);
120091
119951
  if (device.platform === "ios-remote") return { nativeDevtools: nativeDevtoolsRef(device) };
@@ -120378,8 +120238,8 @@ function buildIosHandler(backend) {
120378
120238
  failure_stage: "ios_settings_permission_simctl_privacy",
120379
120239
  failure_area: "tool_server",
120380
120240
  error_kind: "subprocess",
120381
- // Both backends run the `simctl privacy` verb (local via xcrun,
120382
- // remote via sim-remote), so it's the same subprocess for telemetry.
120241
+ // Both backends run the same `simctl privacy` verb, so telemetry
120242
+ // uses one subprocess name.
120383
120243
  ...subprocessFailureMetadata(err, "xcrun_simctl")
120384
120244
  },
120385
120245
  { cause: err instanceof Error ? err : new Error(String(err)) }
@@ -120559,12 +120419,11 @@ var permissionAction = {
120559
120419
  reset: { started: "Resetting", completed: "Reset" }
120560
120420
  };
120561
120421
  var capability5 = {
120562
- // `simctl privacy` edits the simulator's TCC store — physical iPhones have no
120563
- // equivalent host-side switch, so no `device: true` on apple.
120422
+ // `simctl privacy` edits the simulator's TCC store; physical iPhones have no
120423
+ // equivalent host-side switch, hence no `device: true`.
120564
120424
  apple: { simulator: true },
120565
- // sim-remote runs the same `simctl privacy` verb on a remote simulator, so a
120566
- // sim-remote setup can pre-set permissions too — matching the rest of the
120567
- // launch-app / restart-app / reinstall-app / open-url family.
120425
+ // sim-remote exposes the same `simctl privacy` verb, so remote sims can
120426
+ // pre-set permissions too.
120568
120427
  appleRemote: { simulator: true },
120569
120428
  android: { emulator: true, device: true, unknown: true }
120570
120429
  };
@@ -121215,9 +121074,9 @@ Fails if the simulator-server / emulator backend / Chromium CDP is not reachable
121215
121074
  zodSchema: zodSchema15,
121216
121075
  outputHint: "image",
121217
121076
  capability: capability7,
121218
- // No eager service: a tvOS udid classifies as iOS by shape, and declaring
121219
- // simulator-server here would spawn it for the tvOS device (which it cannot
121220
- // drive) and hang on the ready timeout. Resolve the backend lazily instead.
121077
+ // No eager service: a tvOS udid classifies as iOS by shape, so declaring
121078
+ // simulator-server here would spawn it for a device it cannot drive and hang
121079
+ // on the ready timeout. Resolve the backend lazily instead.
121221
121080
  services: () => ({}),
121222
121081
  async execute(_services, params, ctx) {
121223
121082
  const signal = ctx?.signal ?? AbortSignal.timeout(16e3);
@@ -121231,17 +121090,29 @@ Fails if the simulator-server / emulator backend / Chromium CDP is not reachable
121231
121090
  scale: params.scale,
121232
121091
  downscaler: params.downscaler
121233
121092
  });
121234
- const image2 = await requireArtifacts(ctx).register(capturedPath2, { mimeType: "image/png" });
121093
+ const image2 = await requireArtifacts(ctx).register({
121094
+ hostPath: capturedPath2,
121095
+ kind: "screenshot",
121096
+ mimeType: "image/png"
121097
+ });
121235
121098
  return { image: image2 };
121236
121099
  }
121237
121100
  if (device.platform === "ios" && await isTvOsSimulator(params.udid)) {
121238
121101
  const pngPath = await tvScreenshot(params.udid, scale, signal);
121239
- const image2 = await requireArtifacts(ctx).register(pngPath, { mimeType: "image/png" });
121102
+ const image2 = await requireArtifacts(ctx).register({
121103
+ hostPath: pngPath,
121104
+ kind: "screenshot",
121105
+ mimeType: "image/png"
121106
+ });
121240
121107
  return { image: image2 };
121241
121108
  }
121242
121109
  if (device.platform === "vega") {
121243
121110
  const pngPath = await captureVegaScreenshotPng({ scale: params.scale });
121244
- const image2 = await requireArtifacts(ctx).register(pngPath, { mimeType: "image/png" });
121111
+ const image2 = await requireArtifacts(ctx).register({
121112
+ hostPath: pngPath,
121113
+ kind: "screenshot",
121114
+ mimeType: "image/png"
121115
+ });
121245
121116
  return { image: image2 };
121246
121117
  }
121247
121118
  const ref = simulatorServerRef(device);
@@ -121255,7 +121126,11 @@ Fails if the simulator-server / emulator backend / Chromium CDP is not reachable
121255
121126
  void 0,
121256
121127
  androidDevtoolsRotationPeek(registry2, device)
121257
121128
  );
121258
- const image = await requireArtifacts(ctx).register(capturedPath, { mimeType: "image/png" });
121129
+ const image = await requireArtifacts(ctx).register({
121130
+ hostPath: capturedPath,
121131
+ kind: "screenshot",
121132
+ mimeType: "image/png"
121133
+ });
121259
121134
  return { image };
121260
121135
  }
121261
121136
  };
@@ -121889,15 +121764,12 @@ var zodSchema23 = external_exports.object({
121889
121764
  });
121890
121765
  var BUTTONS_BY_PLATFORM = {
121891
121766
  "ios": /* @__PURE__ */ new Set(["home", "power", "volumeUp", "volumeDown", "appSwitch", "actionButton"]),
121892
- // Remote iOS sims expose the same hardware buttons as local iOS.
121893
121767
  "ios-remote": /* @__PURE__ */ new Set(["home", "power", "volumeUp", "volumeDown", "appSwitch", "actionButton"]),
121894
121768
  "android": /* @__PURE__ */ new Set(["home", "back", "power", "volumeUp", "volumeDown", "appSwitch"]),
121895
- // Chromium apps have no hardware buttons; the capability gate already
121896
- // excludes them, the empty set keeps the lookup total if one slips through.
121769
+ // The capability gate excludes chromium; the empty set keeps the lookup total.
121897
121770
  "chromium": /* @__PURE__ */ new Set([]),
121898
- // Vega is remote-driven: hardware buttons / D-pad go through the dedicated
121899
- // `tv-remote` tool, and this tool's capability omits `vega` so a Vega device is
121900
- // rejected before this map is consulted. Empty set keeps the record total.
121771
+ // Vega buttons / D-pad go through the `tv-remote` tool; the capability omits
121772
+ // `vega`, so this entry only keeps the record total.
121901
121773
  "vega": /* @__PURE__ */ new Set([])
121902
121774
  };
121903
121775
  var capability15 = {
@@ -121919,12 +121791,9 @@ Returns { pressed: buttonName }.
121919
121791
  Fails if the device backend is not reachable \u2014 the simulator-server for iOS, or \`adb\` for Android (Android presses are injected with \`adb shell input keyevent\`).`,
121920
121792
  zodSchema: zodSchema23,
121921
121793
  capability: capability15,
121922
- // Android presses go over `adb shell input keyevent` (see execute), not the
121923
- // simulator-server's HID transport, so declaring the service for an Android
121924
- // target would needlessly resolve + spawn a sim-server the tool never uses (up
121925
- // to a 30s ready-wait) and could throw ServiceInitializationError before the
121926
- // adb path even runs. Declare it only for the iOS / ios-remote path that
121927
- // actually consumes it (mirrors the sibling `keyboard` tool's lazy services).
121794
+ // The Android path uses `adb`, so declaring the service for an Android target
121795
+ // would spawn a sim-server the tool never uses (up to a 30s ready-wait) and
121796
+ // could throw ServiceInitializationError before the adb path even runs.
121928
121797
  services: (params) => {
121929
121798
  const device = resolveDevice(params.udid);
121930
121799
  return device.platform === "android" ? {} : { simulatorServer: simulatorServerRef(device) };
@@ -122204,12 +122073,9 @@ async function typeAndroidPhone(device, params) {
122204
122073
  }
122205
122074
  function makeAndroidImpl(registry2) {
122206
122075
  return {
122207
- // Both sub-paths shell out to `adb`: the `isAndroidTv` probe up front, then
122208
- // `adb input` either way (TV via the focus daemon, phone via `input text` /
122209
- // `input keyevent`). Declare it so `dispatchByPlatform` preflights adb and a
122210
- // missing binary fails with the clean 424 install hint rather than surfacing
122211
- // from deeper in the probe. Matches the android branch of `describe` and
122212
- // `tv-remote`.
122076
+ // Both sub-paths shell out to `adb` (the `isAndroidTv` probe, then `input`
122077
+ // either way), so declaring it makes a missing binary fail with
122078
+ // `dispatchByPlatform`'s 424 install hint instead of from inside the probe.
122213
122079
  requires: ["adb"],
122214
122080
  handler: async (_services, params, device) => await isAndroidTv(device.id) ? typeTv(registry2, device, params) : typeAndroidPhone(device, params)
122215
122081
  };
@@ -122380,11 +122246,9 @@ var REMOTE_BUTTONS = Object.keys(REMOTE_KEYCODES);
122380
122246
  var NAMED_KEYCODES = {
122381
122247
  "enter": "KEY_ENTER",
122382
122248
  "return": "KEY_ENTER",
122383
- // alias of enter (matches iOS/Android/Chromium)
122384
122249
  // Back is the TV analog of Escape; KEY_ESC is inert for the focus engine.
122385
122250
  "escape": "KEY_BACK",
122386
122251
  "esc": "KEY_BACK",
122387
- // alias of escape
122388
122252
  "backspace": "KEY_BACKSPACE",
122389
122253
  "delete": "KEY_DELETE",
122390
122254
  "tab": "KEY_TAB",
@@ -122393,8 +122257,7 @@ var NAMED_KEYCODES = {
122393
122257
  "arrow-down": "KEY_DOWN",
122394
122258
  "arrow-left": "KEY_LEFT",
122395
122259
  "arrow-right": "KEY_RIGHT",
122396
- // Vega names function keys KEY_FN_F<n> (not KEY_F<n>); KEY_FN_F1..F12 all exist
122397
- // in the device key-name table (verified against system.ext4, SDK 0.22.6759).
122260
+ // Vega names function keys KEY_FN_F<n>, not KEY_F<n> (SDK 0.22.6759 key table).
122398
122261
  ...Object.fromEntries(Array.from({ length: 12 }, (_, i) => [`f${i + 1}`, `KEY_FN_F${i + 1}`]))
122399
122262
  };
122400
122263
  var SETTLE_BETWEEN_PRESSES_S = 0.3;
@@ -122505,16 +122368,13 @@ function createKeyboardTool(registry2) {
122505
122368
  return {
122506
122369
  id: "keyboard",
122507
122370
  interaction: {
122508
- // Treat both text and key as sensitive. `key` is an unrestricted string at
122509
- // this boundary, so a value must not reach the event log before execution
122510
- // validates whether it is a supported named key.
122371
+ // Never quote the parameters: `text` may hold a plaintext credential and
122372
+ // `key` is an unvalidated free string here, yet these messages reach the
122373
+ // event log before `execute` runs.
122511
122374
  //
122512
- // `startedMsg` still words a text+key request because it renders BEFORE
122513
- // `execute` rejects the combination — and likewise words `{ key: "" }` as
122514
- // a key press, which `execute` also rejects. `completedMsg` runs only
122515
- // after a call that succeeded, so it sees neither. Each formatter
122516
- // therefore has to word a different set of shapes, and the empty
122517
- // request — neither parameter, a documented no-op — reaches both.
122375
+ // That ordering also makes `startedMsg` word requests `execute` then
122376
+ // rejects (text+key, `{ key: "" }`), while `completedMsg` runs only after
122377
+ // a success and sees neither. Both see the empty request, a no-op.
122518
122378
  startedMsg: ({ params }) => {
122519
122379
  if (params.text === void 0) return "Pressing a key";
122520
122380
  if (params.key === void 0) return "Entering text";
@@ -122534,29 +122394,21 @@ One call does one action: pass text OR key, never both. To type and then press a
122534
122394
  zodSchema: zodSchema24,
122535
122395
  capability: capability16,
122536
122396
  searchHint: "type text keyboard input named key enter escape arrow tv vega fire tv search field hid leanback",
122537
- // No eager service: each branch resolves its backend lazily (TV control,
122538
- // simulator-server, CDP, or Vega adb), since distinguishing a TV target is
122539
- // async and a tvOS udid must never resolve simulator-server.
122540
122397
  services: () => ({}),
122541
122398
  execute: async (services, params, options) => {
122542
122399
  if (params.text !== void 0 && params.key !== void 0) {
122543
122400
  throw new InvalidToolInputError(
122544
122401
  // Says what did NOT happen, so the caller retries instead of first
122545
- // inspecting the field — and spells the retry out with a literal
122546
- // example rather than an ellipsis the Android backend can't type.
122402
+ // inspecting the field. The example is literal because an ellipsis is
122403
+ // non-ASCII and the Android backend rejects it.
122547
122404
  //
122548
- // The TV caveat is carried statically rather than by probing the
122549
- // target: this guard runs above the dispatch precisely so a combined
122550
- // request reaches no device, and distinguishing a TV kind is an async
122551
- // probe. Without it the prescribed `{ key: "enter" }` is a retry that
122552
- // cannot succeed on a TV, where `key` is rejected outright
122553
- // (platforms/tv.ts) — which is the diagnosis this guard would
122554
- // otherwise pre-empt.
122555
- 'keyboard takes `text` or `key`, not both \u2014 nothing was typed. To type and then press a key, send two `keyboard` steps in one `run-sequence`: { text: "hello" } followed by { key: "enter" }. On a TV target (Apple TV / Android TV) `key` is not supported at all \u2014 type with `text` and move focus with `tv-remote` (up/down/left/right/select).' + // The one-`run-sequence` form and two bare calls are NOT equivalent
122556
- // once the text carries a placeholder, and this message is where an
122557
- // agent converts a combined secret call — the tool description's
122558
- // caveat is read long before that moment, if at all. The check is
122559
- // syntactic (the same `.includes` flow-utils.ts uses), so the guard
122405
+ // The TV caveat is static, not probed: this guard runs above the
122406
+ // dispatch, so the target kind is unknown here — yet without it the
122407
+ // prescribed `{ key: "enter" }` is a retry that cannot succeed on a
122408
+ // TV, where `key` is rejected outright (platforms/tv.ts).
122409
+ 'keyboard takes `text` or `key`, not both \u2014 nothing was typed. To type and then press a key, send two `keyboard` steps in one `run-sequence`: { text: "hello" } followed by { key: "enter" }. On a TV target (Apple TV / Android TV) `key` is not supported at all \u2014 type with `text` and move focus with `tv-remote` (up/down/left/right/select).' + // One `run-sequence` and two bare calls are NOT equivalent once the
122410
+ // text carries a placeholder, and this message is where an agent
122411
+ // converts a combined secret call. Syntactic check, so the guard
122560
122412
  // still resolves nothing.
122561
122413
  (params.text.includes(SECRET_PLACEHOLDER_MARKER) ? " This `text` carries a `" + SECRET_PLACEHOLDER_MARKER + '...}}` placeholder, so keep both steps in that ONE `run-sequence` rather than splitting them into two bare calls: the auto-screenshot skip is decided per tool call from the whole request, and a separate { key: "enter" } call carries no placeholder \u2014 its screenshot is taken after the key lands and can capture the still-visible secret.' : ""),
122562
122414
  {
@@ -122567,12 +122419,12 @@ One call does one action: pass text OR key, never both. To type and then press a
122567
122419
  }
122568
122420
  if (params.key === "") {
122569
122421
  throw new InvalidToolInputError(
122570
- // Names the omission as the alternative, because a caller that sent an
122571
- // empty string usually built the value from something absent.
122422
+ // Names the omission as the alternative: a caller that sent an empty
122423
+ // string usually built the value from something absent.
122572
122424
  "`key` is an empty string, which names no key \u2014 nothing was pressed. Pass a named key (enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1\u2013f12), or omit `key` if there is nothing to press.",
122573
122425
  {
122574
- // The same code an unknown name gets, because that is what this is:
122575
- // one telemetry bucket for every unusable `key` value.
122426
+ // The same code an unknown name gets: one telemetry bucket for
122427
+ // every unusable `key` value.
122576
122428
  error_code: FAILURE_CODES.KEYBOARD_KEY_UNSUPPORTED,
122577
122429
  failure_stage: "keyboard_named_key_empty",
122578
122430
  error_kind: "unsupported"
@@ -122629,8 +122481,8 @@ async function pasteSimulator(api, text) {
122629
122481
  }
122630
122482
  function makeIosImpl4(registry2) {
122631
122483
  return {
122632
- // `isTvOsSimulator` shells out to `simctl`; the simulator-server itself is
122633
- // resolved through the blueprint.
122484
+ // `xcrun` is for the `isTvOsSimulator` probe; simulator-server comes from
122485
+ // the blueprint.
122634
122486
  requires: ["xcrun"],
122635
122487
  async handler(_services, params, device) {
122636
122488
  if (await isTvOsSimulator(device.id)) rejectTv(device);
@@ -122683,21 +122535,16 @@ function makeAndroidImpl2(registry2) {
122683
122535
 
122684
122536
  // ../tool-server/src/tools/paste/index.ts
122685
122537
  var capability17 = {
122686
- // Simulators only. A physical iPhone's pasteboard is reachable through the
122687
- // same simulator-server endpoint, but the paste keystroke is not verified
122688
- // over the CoreDevice HID path, so `device` stays off until it is.
122689
122538
  apple: { simulator: true },
122690
- // A remote simulator pastes through the MoQ transport's existing paste
122691
- // primitive (device pasteboard + ⌘V on the remote host).
122539
+ // A remote sim pastes over the MoQ transport (`simctl pbcopy` + ⌘V); the HTTP
122540
+ // clipboard route does not exist there.
122692
122541
  appleRemote: { simulator: true },
122693
- // Emulators only: the clipboard is set through the emulator's gRPC
122694
- // endpoint, which a physical phone does not have. `unknown` is admitted
122695
- // because an unresolved serial may still be an emulator — the
122696
- // simulator-server refuses a phone either way.
122542
+ // Emulators only: the clipboard is set through the emulator's gRPC endpoint,
122543
+ // which a physical phone does not have. `unknown` is admitted because an
122544
+ // unresolved serial may still be an emulator.
122697
122545
  android: { emulator: true, unknown: true }
122698
- // No `chromium`: the renderer can only reach the HOST clipboard, so a paste
122699
- // there would overwrite whatever the user has copied. No `vega`: no
122700
- // clipboard API is exposed by the VVD tooling.
122546
+ // No `chromium`: its only clipboard is the HOST one, so a paste would
122547
+ // overwrite what the user copied. No `vega`: VVD exposes no clipboard API.
122701
122548
  //
122702
122549
  // TV targets are not separate platforms (an Apple TV simulator is
122703
122550
  // `ios`/`simulator`, an Android TV emulator `android`/`emulator`), so each
@@ -123026,9 +122873,8 @@ function shakeFailure(udid, detail, cause) {
123026
122873
  failure_stage: "ios_shake_notifyutil",
123027
122874
  failure_area: "tool_server",
123028
122875
  error_kind: "subprocess",
123029
- // Only a thrown subprocess error carries the syscall/exit metadata. The
123030
- // remote backend reports a non-zero child in its JSON payload instead of
123031
- // throwing, so there is no error object to mine there.
122876
+ // Only a thrown subprocess error carries the syscall/exit metadata; the
122877
+ // remote backend reports a failed child in its JSON payload instead.
123032
122878
  ...cause ? subprocessFailureMetadata(cause, "xcrun_simctl") : {}
123033
122879
  },
123034
122880
  cause ? { cause } : void 0
@@ -123210,26 +123056,22 @@ var androidImpl6 = {
123210
123056
 
123211
123057
  // ../tool-server/src/tools/shake/index.ts
123212
123058
  var capability19 = {
123213
- // Simulator only. A physical iPhone's motion comes from real hardware; there
123214
- // is no host-side hook to fake it, so `device` stays false and a paired phone
123215
- // gets a clean 400 instead of a silent no-op.
123059
+ // Simulator only: a physical iPhone's motion is real hardware, with no
123060
+ // host-side hook to fake it.
123216
123061
  apple: { simulator: true },
123217
- // A remote simulator is still a simulator: `sim-remote spawn` runs the same
123218
- // in-simulator `notifyutil` argv the local path does.
123062
+ // `sim-remote spawn` runs the same in-simulator `notifyutil` argv the local
123063
+ // path does.
123219
123064
  appleRemote: { simulator: true },
123220
- // Android emulators only, for the same reason as iOS: a physical phone's
123221
- // accelerometer can't be driven from the host. `unknown` is allowed through
123222
- // because a serial that didn't resolve may still be an emulator; the handler
123223
- // re-checks and rejects a non-emulator serial explicitly.
123065
+ // Emulators only, for the same reason as iOS. `unknown` is let through and
123066
+ // the handler rejects a serial with no emulator console.
123224
123067
  android: { emulator: true, unknown: true }
123225
- // No `vega`: a Fire TV is rejected here, by the absent block.
123068
+ // No `vega` block, so a Fire TV is rejected by the capability gate.
123226
123069
  //
123227
- // TV targets that are NOT separate platforms cannot be excluded here. An
123228
- // Apple TV simulator is `ios`/`simulator` and an Android TV emulator is
123229
- // `android`/`emulator` — identical to a phone by id shape and device kind, so
123230
- // the matrix admits them and each platform handler probes the runtime and
123231
- // rejects a TV. `supports` can't do it: it is synchronous, while the runtime
123232
- // kind is an async `simctl list` / `adb` probe.
123070
+ // A TV that is not a separate platform can't be excluded here: an Apple TV
123071
+ // simulator is `ios`/`simulator` and an Android TV emulator is
123072
+ // `android`/`emulator`. Each platform handler probes the runtime and rejects
123073
+ // a TV instead; `supports` can't, being synchronous while the probe is an
123074
+ // async `simctl list` / `adb` call.
123233
123075
  };
123234
123076
  var shakeTool = {
123235
123077
  id: "shake",
@@ -123248,8 +123090,8 @@ Only phone/tablet simulators and emulators are supported.`,
123248
123090
  searchHint: "shake motion accelerometer undo typing dev menu gesture",
123249
123091
  zodSchema: shakeZodSchema,
123250
123092
  capability: capability19,
123251
- // Talks to `simctl` / `adb` directly, so no simulator-server is resolved —
123252
- // avoids spawning one (and its ready-wait) for a tool that never uses it.
123093
+ // No simulator-server is resolved: `simctl`/`adb` are called directly, so a
123094
+ // tool that never uses one doesn't spawn it or wait for it to be ready.
123253
123095
  services: () => ({}),
123254
123096
  execute: dispatchByPlatform({
123255
123097
  toolId: "shake",
@@ -123376,10 +123218,8 @@ Multi-step navigation: pass a path as { button: ["up","right","right","select"]
123376
123218
  Read the screen with \`describe\` before and after to see where focus landed.
123377
123219
  Returns { pressed, count }.`,
123378
123220
  alwaysLoad: true,
123379
- // A path (≤64 buttons) × repeat (≤50) flattens to thousands of presses that
123380
- // settle apart in one held device session — minutes of wall-clock. Mark
123381
- // long-running so the MCP adapter doesn't abort it at its per-request fetch
123382
- // timeout and the idle-shutdown timer is kept warm for the call's duration.
123221
+ // A path (≤64 buttons) × repeat (≤50) flattens to thousands of presses, sent
123222
+ // one round-trip at a time on the Apple/Android path — minutes of wall-clock.
123383
123223
  longRunning: true,
123384
123224
  searchHint: "tv remote dpad d-pad navigate focus up down left right select ok back home menu play pause rewind fast forward sequence path apple tv tvos android tv leanback vega fire tv",
123385
123225
  zodSchema: zodSchema26,
@@ -129758,16 +129598,13 @@ var ALLOWED_TOOLS = /* @__PURE__ */ new Set([
129758
129598
  "gesture-rotate",
129759
129599
  "button",
129760
129600
  "keyboard",
129761
- // `paste` belongs in a sequence for the same reason `keyboard` does: the
129762
- // focus tap, the paste and the submit are one user action.
129601
+ // Sequenceable for the same reason `keyboard` is: the focus tap, the paste
129602
+ // and the submit are one user action.
129763
129603
  "paste",
129764
129604
  "rotate",
129765
- // Sequencing matters for shake: the interesting cases are races (shake while
129766
- // a sheet is dismissing, shake right after typing), which need the steps
129767
- // dispatched back-to-back rather than across separate round-trips.
129605
+ // Shake's interesting cases are races (shake while a sheet is dismissing,
129606
+ // shake right after typing), which need back-to-back dispatch.
129768
129607
  "shake",
129769
- // `tv-remote` drives the D-pad on a TV target (Apple TV / Android TV / Vega);
129770
- // `keyboard` types into the focused field there.
129771
129608
  "tv-remote",
129772
129609
  AWAIT_UI_ELEMENT_TOOL_ID
129773
129610
  ]);
@@ -129793,9 +129630,8 @@ var capability22 = {
129793
129630
  android: { emulator: true, device: true, unknown: true },
129794
129631
  chromium: { app: true },
129795
129632
  // Vega (Fire TV) is a valid target: its `tv-remote` / `keyboard` steps are
129796
- // supported, and the description advertises it. Without this key the outer
129797
- // capability gate (HTTP layer's assertSupported) would reject a Vega udid
129798
- // before any step runs — each step's own capability is still enforced below.
129633
+ // supported. Without this key the HTTP layer's `assertSupported` would reject
129634
+ // a Vega udid before any step runs.
129799
129635
  vega: { vvd: true }
129800
129636
  };
129801
129637
  function createRunSequenceTool(registry2) {
@@ -129869,15 +129705,10 @@ Stops on the first error (or unmet await-ui-element condition) and returns parti
129869
129705
  searchHint: "batch sequence multiple gesture steps sequentially",
129870
129706
  zodSchema: zodSchema28,
129871
129707
  capability: capability22,
129872
- // No eagerly-declared service: each step resolves its own services through
129873
- // `invokeSubTool` below (simulator-server for iOS/Android, CDP for
129874
- // Chromium), so run-sequence itself needs none. An eager resolver can't be
129875
- // used here because a tvOS udid shape-classifies as `ios` (there is no
129876
- // `tvos` platform) — declaring simulator-server for it would spawn a
129877
- // controller it can't drive and hang on the ready timeout before any tv-*
129878
- // step could run. The sub-tool invocations still pay only their own
129879
- // first-step spawn cost, and `ctx` is threaded through so nested steps keep
129880
- // the outer request's telemetry attribution.
129708
+ // Each step resolves its own services. An eager resolver can't be used
129709
+ // because a tvOS udid shape-classifies as `ios` (there is no `tvos`
129710
+ // platform), so declaring simulator-server would spawn a controller it
129711
+ // can't drive and hang on the ready timeout before any tv-remote step runs.
129881
129712
  services: () => ({}),
129882
129713
  async execute(_services, params, ctx) {
129883
129714
  const { udid, steps } = params;
@@ -130025,16 +129856,15 @@ var NOT_CONNECTED_CODE_MAP = {
130025
129856
  [FAILURE_CODES.DEBUGGER_CDP_SOCKET_CLOSED_BEFORE_OPEN]: "cdp_unreachable",
130026
129857
  [FAILURE_CODES.DEBUGGER_CDP_NOT_CONNECTED]: "cdp_unreachable",
130027
129858
  [FAILURE_CODES.DEBUGGER_CDP_CONNECTION_CLOSED]: "cdp_unreachable",
130028
- // Reachable from the connect pipeline's enable/binding sends when the target
130029
- // accepts the socket but its JS runtime never answers (frozen, or paused at a
130030
- // breakpoint). Post-connect hangs are different: an OPEN socket still reports
130031
- // status "connected" (see the socket-state gate comment in debugger-status).
129859
+ // Raised by the connect pipeline's enable/binding sends when the target
129860
+ // accepts the socket but its JS runtime never answers. A post-connect hang
129861
+ // differs: the OPEN socket still reports status "connected" (see the
129862
+ // socket-state gate in debugger-status).
130032
129863
  [FAILURE_CODES.DEBUGGER_CDP_REQUEST_TIMEOUT]: "runtime_unresponsive",
130033
129864
  [FAILURE_CODES.CHROMIUM_CDP_UNREACHABLE]: "cdp_unreachable",
130034
- // "Reached but not CDP / malformed answer" — a non-CDP server squatting the
130035
- // debug port, an HTTP error status, or a non-JSON body. Same precondition
130036
- // class as the Metro arm's non-Metro-port-occupant (detail names what
130037
- // actually answered), so it must not escape as a thrown tool failure.
129865
+ // Reached but not CDP: a squatter on the debug port, an HTTP error status, or
129866
+ // a non-JSON body — the same precondition class as the Metro arm's non-Metro
129867
+ // occupant, so it must not escape as a thrown tool failure.
130038
129868
  [FAILURE_CODES.CHROMIUM_CDP_INVALID_RESPONSE]: "cdp_unreachable",
130039
129869
  [FAILURE_CODES.CHROMIUM_CDP_NO_PAGE_TARGET]: "cdp_unreachable",
130040
129870
  [FAILURE_CODES.REGISTRY_SERVICE_TERMINATING]: "reconnecting"
@@ -130103,7 +129933,7 @@ Use when you need to verify connectivity before using other debugger tools. Neve
130103
129933
  zodSchema: zodSchema30,
130104
129934
  capability: DEBUGGER_TOOL_CAPABILITY,
130105
129935
  // Resolved manually in execute so a not-connected precondition becomes a
130106
- // structured result instead of a service-resolution tool failure.
129936
+ // structured result instead of a tool failure.
130107
129937
  services: () => ({}),
130108
129938
  async execute(_services, params, ctx) {
130109
129939
  try {
@@ -130226,11 +130056,8 @@ var debuggerReloadMetroTool = {
130226
130056
  description: `Restart the Metro JS bundle in the connected React Native app without restarting the native process.
130227
130057
  Use when you want to apply code changes or reset JS state. Returns { reloaded, port, method, deviceName, appName, logicalDeviceId } indicating which reload path was used and which device/app was targeted. Fails if Metro is not running on the given port.`,
130228
130058
  zodSchema: zodSchema32,
130229
- // Metro-only: Chromium loads from disk, not from a bundler. The closest
130230
- // analog (Page.reload against the renderer) would behave differently enough
130231
- // — preserving the URL but re-fetching index.html, blowing away in-memory
130232
- // app state — that calling it under the same tool name would mislead. If we
130233
- // want that on Chromium later, it deserves its own tool.
130059
+ // RN-only: reloading a Chromium page is a different operation from restarting
130060
+ // the Metro bundle, so it would need its own tool.
130234
130061
  capability: RN_ONLY_TOOL_CAPABILITY,
130235
130062
  services: (params) => ({
130236
130063
  debugger: `JsRuntimeDebugger:${params.port}:${canonicalDeviceId(params.device_id)}`
@@ -131075,9 +130902,8 @@ Use when you need tap coordinates for a React Native UI element. Returns a compa
131075
130902
  alwaysLoad: true,
131076
130903
  searchHint: "react native component tree discovery tap coordinates",
131077
130904
  zodSchema: zodSchema33,
131078
- // RN-only: depends on the React DevTools backend that ships with the JS
131079
- // bundle in dev builds. Chromium has no equivalent — use `describe` instead
131080
- // for DOM-tree discovery on Chromium.
130905
+ // RN-only: needs the React DevTools backend from the dev JS bundle. Chromium has
130906
+ // no equivalent — use `describe` there.
131081
130907
  capability: RN_ONLY_TOOL_CAPABILITY,
131082
130908
  services: (params) => ({
131083
130909
  debugger: `JsRuntimeDebugger:${params.port}:${canonicalDeviceId(params.device_id)}`
@@ -131445,7 +131271,6 @@ var SKIP_SET = /* @__PURE__ */ new Set([
131445
131271
  "TextInputLabel",
131446
131272
  "ThemeContext",
131447
131273
  "BaseHTMLEngineProvider",
131448
- // Fabric / cross-app entries
131449
131274
  "VScrollViewNativeComponent",
131450
131275
  "InnerScreen",
131451
131276
  "ScreenStackItem",
@@ -131584,10 +131409,8 @@ Set resolveSourceMaps to false to skip symbolication and get raw bundled locatio
131584
131409
  Set includeSkipped=true to see filtered items annotated with skip reasons.
131585
131410
  Use when you need the source file and line for a component at a tap coordinate. Fails if the app is not connected or the coordinate is outside the screen.`,
131586
131411
  zodSchema: zodSchema34,
131587
- // RN-only: uses React Native's internal getInspectorDataForViewAtPoint and
131588
- // Metro's /symbolicate endpoint. Chromium's CDP has DOM.getNodeForLocation
131589
- // for "what's here?" but the source-map flow would need a complete rewrite —
131590
- // out of scope for this port.
131412
+ // RN-only: needs React Native's internal getInspectorDataForViewAtPoint and
131413
+ // Metro's /symbolicate; the Chromium session has no source resolver.
131591
131414
  capability: RN_ONLY_TOOL_CAPABILITY,
131592
131415
  services: (params) => ({
131593
131416
  debugger: `JsRuntimeDebugger:${params.port}:${canonicalDeviceId(params.device_id)}`
@@ -131827,8 +131650,6 @@ On React Native (iOS / Android / Vega) interception is injected into the JS runt
131827
131650
  Use when inspecting outbound HTTP traffic or debugging API calls in the running app.
131828
131651
  Fails if the app is not connected (RN) or the device is not reachable (Chromium).`,
131829
131652
  zodSchema: zodSchema36,
131830
- // Works on RN (Metro-injected interceptor) and Chromium (native CDP Network
131831
- // domain), dispatched on the device id in `services` / `execute`.
131832
131653
  capability: DEBUGGER_TOOL_CAPABILITY,
131833
131654
  services: (params) => {
131834
131655
  const device = resolveDevice(params.device_id);
@@ -131917,8 +131738,6 @@ Returns request/response headers (sensitive headers redacted), status, timing, a
131917
131738
  Large response bodies are truncated. Use when you need headers, body, or timing for a specific request after listing logs.
131918
131739
  Returns an error message string if the requestId is not found \u2014 use view-network-logs to get valid requestId values.`,
131919
131740
  zodSchema: zodSchema37,
131920
- // Companion to view-network-logs: RN via the injected interceptor, Chromium
131921
- // via the native CDP Network recording. Dispatched on the device id.
131922
131741
  capability: DEBUGGER_TOOL_CAPABILITY,
131923
131742
  services: (params) => {
131924
131743
  const device = resolveDevice(params.device_id);
@@ -132029,10 +131848,9 @@ init_zod();
132029
131848
 
132030
131849
  // ../tool-server/src/tools/describe/format-tree.ts
132031
131850
  var CONTENT_ROLES = /* @__PURE__ */ new Set([
132032
- // iOS AX traits surfaced by mapNativeTraitsToDescribeRole. AXGroup is
132033
- // deliberately excluded: it's the catch-all wrapper, so requiring it to
132034
- // carry its own label/value before we emit a line keeps decorative
132035
- // groupings out of the output.
131851
+ // The roles mapNativeTraitsToDescribeRole can return. AXGroup is excluded on
131852
+ // purpose: as its catch-all fallback, requiring it to carry its own
131853
+ // label/value before we emit a line keeps decorative groupings out.
132036
131854
  "AXButton",
132037
131855
  "AXStaticText",
132038
131856
  "AXImage",
@@ -132261,10 +132079,9 @@ function makeDescribeExecute(registry2) {
132261
132079
  )
132262
132080
  },
132263
132081
  iosRemote: {
132264
- // describeIos already handles both ax-service (TCP) and native-devtools
132265
- // fallback — both blueprints route through sim-remote when the device is
132266
- // ios-remote. Only the preflight dep differs. Remote sims are iOS-only
132267
- // (never tvOS), so the isTvOs verdict is always false.
132082
+ // Both the ax-service and native-devtools blueprints route through
132083
+ // sim-remote for an ios-remote device, so only the preflight dep differs
132084
+ // from the ios branch.
132268
132085
  requires: ["sim-remote"],
132269
132086
  handler: async (_services, params, device) => withDescription(
132270
132087
  withBootCaveatOncePerDevice(
@@ -132276,9 +132093,8 @@ function makeDescribeExecute(registry2) {
132276
132093
  android: {
132277
132094
  requires: androidRequires,
132278
132095
  handler: async (_services, params, device) => (
132279
- // Resolve the form factor once and route on it: a TV goes to the
132280
- // focus-driven describe, a phone to the uiautomator tree — and pass the
132281
- // known `isTv: false` through so describeAndroid doesn't re-probe.
132096
+ // Resolve the form factor once and thread the known `isTv: false`
132097
+ // through so describeAndroid doesn't re-probe.
132282
132098
  await isAndroidTv(device.id) ? describeTv(registry2, device) : withDescription(await describeAndroid(registry2, params.udid, params.bundleId, false))
132283
132099
  )
132284
132100
  },
@@ -132534,9 +132350,6 @@ After starting, ask the user to perform the interaction to profile, then call re
132534
132350
  Returns { started_at, startedAtEpochMs, hermes_version, detected_architecture } on success, or the already_running payload described above.
132535
132351
  Fails if the Hermes runtime is not reachable or the Metro CDP connection cannot be established.`,
132536
132352
  zodSchema: zodSchema40,
132537
- // RN-only: bootstraps the React DevTools backend and uses Hermes'
132538
- // Profiler.start. A CDP-direct CPU profile for Chromium is tracked as a
132539
- // follow-up; the React commit recording has no Chromium analog.
132540
132353
  capability: RN_ONLY_TOOL_CAPABILITY,
132541
132354
  services: () => ({}),
132542
132355
  async execute(_services, params) {
@@ -132681,8 +132494,8 @@ Fails if the Hermes runtime is not reachable or the Metro CDP connection cannot
132681
132494
  const sessionId = crypto6.randomUUID();
132682
132495
  const ownerPayload = {
132683
132496
  sessionId,
132684
- // startedAtEpochMs/lastHeartbeatEpochMs set inside the script using
132685
- // the wrapper-captured value to eliminate clock skew.
132497
+ // Set inside the start script from the wrapper-captured clock, to
132498
+ // eliminate skew.
132686
132499
  startedAtEpochMs: 0,
132687
132500
  lastHeartbeatEpochMs: 0
132688
132501
  };
@@ -132896,7 +132709,6 @@ Returns { duration_ms, sample_count, fiber_renders_captured, total_react_commits
132896
132709
  When any commit had fibers whose display name could not be resolved at stop time (typically transient components like modals/tooltips/animations that unmounted before stop), the response also includes { unattributed_ms, unattributed_fiber_count, unattributed_commit_count } \u2014 these quantify how much work is not accounted for in the per-component breakdown (the per-commit duration itself remains correct).
132897
132710
  Fails if no active profiling session exists or the CDP connection was lost during recording.`,
132898
132711
  zodSchema: zodSchema41,
132899
- // RN-only: companion to react-profiler-start.
132900
132712
  capability: RN_ONLY_TOOL_CAPABILITY,
132901
132713
  services: () => ({}),
132902
132714
  async execute(_services, params) {
@@ -132935,10 +132747,8 @@ Fails if no active profiling session exists or the CDP connection was lost durin
132935
132747
  error_code: FAILURE_CODES.REACT_PROFILER_NO_ACTIVE_SESSION,
132936
132748
  failure_stage: "react_profiler_stop_inactive",
132937
132749
  failure_area: "tool_server",
132938
- // Internal session-state (start never reached the sampler), not caller
132939
- // input — matches the sibling session-lookup site above and the
132940
- // native NATIVE_PROFILER_NO_ACTIVE_SESSION so this code carries one
132941
- // consistent kind rather than splitting by which guard tripped.
132750
+ // Internal session-state, not caller input — matches the session-lookup
132751
+ // site above and the native NATIVE_PROFILER_NO_ACTIVE_SESSION twin.
132942
132752
  error_kind: "not_found"
132943
132753
  }
132944
132754
  );
@@ -134047,7 +133857,7 @@ async function runPipeline(input, options) {
134047
133857
  allClear: input.sessionMeta.allClear ?? false,
134048
133858
  maxCommitMs: input.sessionMeta.maxCommitMs,
134049
133859
  anyRuntimeCompilerDetected: tagOutput.anyRuntimeCompilerDetected,
134050
- // Use stored total from react-profiler-stop when available (all-clear path has no commits to count)
133860
+ // The all-clear path keeps no commits, so tagOutput.reactCommits is 0
134051
133861
  reactCommits: input.sessionMeta.totalReactCommits ?? tagOutput.reactCommits,
134052
133862
  fiberRenders: tagOutput.fiberRenders,
134053
133863
  totalFirstMounts: tagOutput.totalFirstMounts,
@@ -134743,8 +134553,8 @@ async function writeReport(path41, content) {
134743
134553
  }
134744
134554
 
134745
134555
  // ../tool-server/src/tools/profiler/react/react-profiler-analyze.ts
134746
- async function fileArtifact(store, p) {
134747
- return p ? store.register(p) : null;
134556
+ async function fileArtifact(store, p, kind) {
134557
+ return p ? store.register({ hostPath: p, kind }) : null;
134748
134558
  }
134749
134559
  var annotationSchema = external_exports.object({
134750
134560
  offsetMs: external_exports.coerce.number().describe(
@@ -134786,8 +134596,8 @@ is returned by react-profiler-start.
134786
134596
  Use when the profiling session is complete and you need to interpret the collected data.
134787
134597
  Fails if react-profiler-stop has not been called or no profiling data is stored.`,
134788
134598
  zodSchema: zodSchema43,
134789
- // RN-only: operates on profiler trace files captured via the React DevTools
134790
- // backend's commit recording, which is not present on Chromium.
134599
+ // RN-only: reads commit data captured via the React DevTools backend, which
134600
+ // Chromium does not have.
134791
134601
  capability: RN_ONLY_TOOL_CAPABILITY,
134792
134602
  services: () => ({}),
134793
134603
  async execute(_services, params, ctx) {
@@ -134903,13 +134713,13 @@ Fails if react-profiler-stop has not been called or no profiling data is stored.
134903
134713
  const artifacts = requireArtifacts(ctx);
134904
134714
  const result = {
134905
134715
  report,
134906
- reportFile: await fileArtifact(artifacts, reportFile),
134716
+ reportFile: await fileArtifact(artifacts, reportFile, "react-profile-report"),
134907
134717
  hotCommitsTotal,
134908
134718
  hotCommitsShown,
134909
134719
  sessionFiles: {
134910
134720
  sessionId: sessionPaths.sessionId,
134911
- cpuProfile: await fileArtifact(artifacts, sessionPaths.cpuProfilePath),
134912
- commits: await fileArtifact(artifacts, sessionPaths.commitsPath)
134721
+ cpuProfile: await fileArtifact(artifacts, sessionPaths.cpuProfilePath, "react-profile-cpu"),
134722
+ commits: await fileArtifact(artifacts, sessionPaths.commitsPath, "react-profile-commits")
134913
134723
  }
134914
134724
  };
134915
134725
  if (hotCommitIndices === null) {
@@ -134941,12 +134751,9 @@ Call this per-finding after react-profiler-analyze to inspect source before prop
134941
134751
  Returns found: false if the component is not found in user-owned code (e.g. lives in node_modules).
134942
134752
  When several files define a component with the same name (e.g. platform variants like List.tsx and List.web.tsx), returns the primary match and lists the rest under otherMatches[] (file/line/col) \u2014 check it before assuming the returned file is the one you meant.`,
134943
134753
  zodSchema: zodSchema44,
134944
- // Companion to react-profiler-analyze. Carries the same RN-only capability
134945
- // declaration as the rest of react-profiler-* for intent-clarity, even
134946
- // though the HTTP gate is a no-op here (the tool takes no device_id, so
134947
- // there's nothing for the gate to inspect). An LLM agent reading the tool
134948
- // catalogue should see this is paired with the other react-profiler tools
134949
- // and not reach for it on a Chromium app.
134754
+ // Declared so the tool catalogue groups this with the other react-profiler-*
134755
+ // tools; the HTTP gate itself is a no-op here, as there is no device arg to
134756
+ // inspect.
134950
134757
  capability: RN_ONLY_TOOL_CAPABILITY,
134951
134758
  fileInputs,
134952
134759
  services: () => ({}),
@@ -134980,8 +134787,8 @@ When several files define a component with the same name (e.g. platform variants
134980
134787
  }
134981
134788
  return {
134982
134789
  found: true,
134983
- // The key that actually matched, so the caller can tell which name hit
134984
- // when a wrapped name resolved through to a bare source identifier.
134790
+ // The key that matched, so the caller sees when a wrapped name resolved
134791
+ // through to a bare source identifier.
134985
134792
  component: matchedKey ?? params.component_name,
134986
134793
  requested: params.component_name,
134987
134794
  file: entry.file,
@@ -135082,9 +134889,7 @@ Call react-profiler-stop first. Reads directly from the stored cpuProfile.
135082
134889
  Returns a markdown table of the top hotspot functions with self-time, total-time, and location.
135083
134890
  Fails if react-profiler-stop has not been called or no CPU profile is stored.`,
135084
134891
  zodSchema: zodSchema45,
135085
- // RN-only: reads a Hermes CPU profile captured via the React profiler
135086
- // session. Chromium's V8 Profiler emits a different sample format — see the
135087
- // PR description for the follow-up scope.
134892
+ // RN-only: reads a Hermes CPU profile; Chromium's V8 sample format differs.
135088
134893
  capability: RN_ONLY_TOOL_CAPABILITY,
135089
134894
  services: () => ({}),
135090
134895
  async execute(_services, params) {
@@ -135793,11 +135598,8 @@ async function execFileAsyncWithTimeout(file2, args, options = {}) {
135793
135598
  const { stdout, stderr } = await execFileAsync25(file2, args, {
135794
135599
  encoding: "utf-8",
135795
135600
  ...options,
135796
- // The timeout and maxBuffer are this wrapper's whole purpose (the
135797
- // event-loop-freeze and ENOBUFS guards documented above), so they are
135798
- // applied AFTER ...options — a caller can never silently weaken them by
135799
- // passing its own. maxBuffer stays a floor: a caller may raise it for an
135800
- // even larger capture, but never drop below the 256 MiB guard.
135601
+ // Applied after ...options so a caller cannot weaken the guards; maxBuffer
135602
+ // is a floor, so a caller may still raise it for a larger capture.
135801
135603
  timeout: DEFAULT_EXEC_TIMEOUT_MS,
135802
135604
  maxBuffer: Math.max(options.maxBuffer ?? 0, DEFAULT_EXEC_MAX_BUFFER)
135803
135605
  });
@@ -135882,7 +135684,11 @@ async function tryCpuExportFallback(traceFile, outPath, diagnostics) {
135882
135684
  return false;
135883
135685
  }
135884
135686
  async function exportIosTraceData(traceFile) {
135885
- const exportedFiles = {};
135687
+ const exportedFiles = {
135688
+ cpu: null,
135689
+ hangs: null,
135690
+ leaks: null
135691
+ };
135886
135692
  const diagnostics = {
135887
135693
  tocSchemas: [],
135888
135694
  cpuSchemaUsed: null,
@@ -135890,7 +135696,8 @@ async function exportIosTraceData(traceFile) {
135890
135696
  };
135891
135697
  const dir = path23.dirname(traceFile);
135892
135698
  const baseName = path23.basename(traceFile, ".trace");
135893
- for (const [key2, config2] of Object.entries(EXPORTS)) {
135699
+ for (const key2 of Object.keys(EXPORTS)) {
135700
+ const config2 = EXPORTS[key2];
135894
135701
  const outPath = path23.join(dir, `${baseName}${config2.suffix}`);
135895
135702
  if (key2 === "cpu") {
135896
135703
  const resolvedXpath = await resolveCpuXpath(traceFile, diagnostics);
@@ -136345,8 +136152,7 @@ function aggregateCpuHotspots(rows, options) {
136345
136152
  duringHang,
136346
136153
  timeRangeMs: { first: firstMs, last: lastMs },
136347
136154
  burstWindows,
136348
- // Only emit when present (Android). Omitting the key on iOS keeps the iOS
136349
- // hotspot object shape byte-identical to before this change.
136155
+ // Omit the key on iOS rather than emitting `dominantMapping: undefined`.
136350
136156
  ...acc.dominantMapping !== void 0 ? { dominantMapping: acc.dominantMapping } : {}
136351
136157
  });
136352
136158
  }
@@ -136760,17 +136566,16 @@ var SYSTEM_FRAME_PATTERNS = [
136760
136566
  /gfxstream/i,
136761
136567
  /rcCreateSync/,
136762
136568
  /_enc\b/,
136763
- // gl*Enc / rc*_enc emulator encoder trampolines
136569
+ // emulator GL/Vulkan encoder trampolines
136764
136570
  // Linux kernel syscall entry + mm/vfs internals (no app symbol to act on).
136765
136571
  // x86-64 entry path:
136766
136572
  /\bdo_syscall_64\b/,
136767
136573
  /\bentry_SYSCALL/,
136768
136574
  /\b__x64_sys_/,
136769
136575
  /\bx64_sys_call\b/,
136770
- // arm64 (aarch64) entry path — the emulator/device the Android profiler runs
136771
- // on is arm64, so these are the leaves actually seen there. The names differ
136772
- // entirely from x86: the EL0 synchronous-exception vector dispatches to the
136773
- // SVC (syscall) handler, which calls invoke_syscall → __arm64_sys_<name>.
136576
+ // arm64 entry path, named nothing like the x86 one: the EL0
136577
+ // synchronous-exception vector dispatches to the SVC (syscall) handler, which
136578
+ // calls invoke_syscall → __arm64_sys_<name>.
136774
136579
  // arch/arm64/kernel/{entry.S,entry-common.c,syscall.c}.
136775
136580
  /\b__arm64_sys_/,
136776
136581
  /\bel0t_64_sync(_handler)?\b/,
@@ -136780,9 +136585,9 @@ var SYSTEM_FRAME_PATTERNS = [
136780
136585
  /\bdo_el0_svc\b/,
136781
136586
  /\binvoke_syscall\b/,
136782
136587
  /\bel0_(da|ia)\b/,
136783
- // data / instruction abort handlers (page-fault leaves)
136588
+ // data / instruction abort (page-fault) handlers
136784
136589
  /\b__arch_copy_(from|to)_user\b/,
136785
- // arm64 uaccess copy helpers (copy_{from,to}_user.S)
136590
+ // arm64 uaccess copy helpers
136786
136591
  /\bksys_/,
136787
136592
  /\bvfs_(read|write|fsync)\b/,
136788
136593
  /\bgup_/,
@@ -137307,8 +137112,8 @@ function resolveDefaultTemplatePath() {
137307
137112
  ${candidates.map((c) => ` - ${c}`).join("\n")}
137308
137113
  Pass template_path explicitly, or rebuild so the template is copied into place.`,
137309
137114
  {
137310
- // A required bundled asset is absent — a packaging/build problem, not a
137311
- // device or subprocess failure — so dependency_missing with no command.
137115
+ // A missing bundled asset is a packaging/build problem, not a device or
137116
+ // subprocess failure.
137312
137117
  error_code: FAILURE_CODES.NATIVE_PROFILER_TRACE_TEMPLATE_MISSING,
137313
137118
  failure_stage: "ios_native_profiler_template_resolve",
137314
137119
  failure_area: "tool_server",
@@ -137845,8 +137650,7 @@ async function stopNativeProfilerIos(api) {
137845
137650
  failure_stage: "native_profiler_stop_session_state",
137846
137651
  failure_area: "tool_server",
137847
137652
  // Internal session-state, not caller input — matches the react twin
137848
- // REACT_PROFILER_NO_ACTIVE_SESSION (not_found) and the Android site so the
137849
- // "no active session" family carries one consistent kind.
137653
+ // REACT_PROFILER_NO_ACTIVE_SESSION and the Android site.
137850
137654
  error_kind: "not_found"
137851
137655
  }
137852
137656
  );
@@ -137922,13 +137726,12 @@ async function analyzeNativeProfilerIos(api) {
137922
137726
  uiHangs,
137923
137727
  cpuHotspots,
137924
137728
  memoryLeaks,
137925
- // Freeze the exports' capture mode with the parsed data so drill-down
137926
- // consumers (leak_stacks, combined report) stay paired with it even after
137927
- // a newer capture re-stamps the session fields.
137729
+ // Freeze the capture mode with the parsed data so drill-down consumers
137730
+ // (leak_stacks, combined report) stay paired with it after a newer capture
137731
+ // re-stamps the session fields.
137928
137732
  mallocStackLogging: api.mallocStackLogging,
137929
- // Freeze the recording's start time too — the combined report anchors these
137930
- // hangs to wall-clock time, and must use THIS capture's start, not whatever
137931
- // a later native-profiler-start re-stamps onto the live session field.
137733
+ // Freeze the start time too — the combined report anchors these hangs to
137734
+ // wall-clock time and must use THIS capture's start, not a later one's.
137932
137735
  wallClockStartMs: api.wallClockStartMs
137933
137736
  };
137934
137737
  const exportErrors = {};
@@ -137957,21 +137760,17 @@ async function analyzeNativeProfilerIos(api) {
137957
137760
  payload,
137958
137761
  traceFile: api.traceFile,
137959
137762
  exportErrors,
137960
- // wallClockStartMs is the recording's start time, stamped in-memory at
137961
- // native-profiler-start. A large gap to "now" means analyze is reusing a
137962
- // trace from an earlier capture in this same process run, not a fresh one.
137763
+ // A large gap between the in-memory start time and "now" means analyze is reusing
137764
+ // a trace from an earlier capture in this same process run, not a fresh one.
137963
137765
  //
137964
- // Limitation (iOS): unlike Android, iOS has no on-disk metadata sidecar, so
137965
- // profiler-load (which restores only the raw_*.xml) cannot recover the start
137966
- // time — wallClockStartMs is null for a loaded session and this note stays
137967
- // off. The note therefore fires only for a live in-process session, never
137968
- // for one restored from disk. Restoring iOS start-time across loads needs an
137969
- // iOS sidecar this Android-scoped change does not add; formatTraceFreshness
137970
- // degrades cleanly to null in that case. See test/ios-instruments/load-freshness.test.ts.
137766
+ // iOS, unlike Android, has no on-disk metadata sidecar, so profiler-load (raw_*.xml
137767
+ // only) cannot recover the start time: wallClockStartMs is null for a loaded
137768
+ // session, formatTraceFreshness returns null, and the note stays off. See
137769
+ // test/ios-instruments/load-freshness.test.ts.
137971
137770
  freshnessNote: formatTraceFreshness(api.wallClockStartMs, Date.now()) ?? void 0,
137972
137771
  // Same live-session-only caveat as wallClockStartMs: profiler-load has no
137973
137772
  // capture-mode sidecar, so a restored session renders with null (the
137974
- // unattributed-leaks note then falls back to inferring the mode).
137773
+ // unattributed-leaks note then goes by the attributed-leak count instead).
137975
137774
  mallocStackLogging: api.mallocStackLogging
137976
137775
  });
137977
137776
  }
@@ -138819,20 +138618,18 @@ function cpuRowsToAggregatorRows(rows, traceStartNs) {
138819
138618
  const thread = normaliseAndroidThread(row.thread_name, row.is_main_thread === 1);
138820
138619
  out.push({
138821
138620
  dominantFunction: dominant,
138822
- // Mapping of the leaf frame (from cpu-hotspots.sql's MIN(spm.name)) —
138823
- // threaded through so classifyNativeFrame can recognise `/kernel` leaves.
138621
+ // Threaded through so classifyNativeFrame can recognise `/kernel` leaves.
138824
138622
  ...row.leaf_mapping != null ? { dominantMapping: row.leaf_mapping } : {},
138825
138623
  thread,
138826
138624
  weightNs: row.sample_count * SAMPLE_PERIOD_NS,
138827
- // Android ships SQL-precomputed bursts, no raw timestamps. Empty array
138828
- // keeps the aggregator's duringHang check false (Android never passes hangSampleTimestamps).
138625
+ // Bursts are precomputed in SQL, so no raw timestamps ship; the aggregator's
138626
+ // duringHang stays false (Android never passes hangSampleTimestamps).
138829
138627
  timestampsNs: [],
138830
138628
  callChains: [{ chain: [dominant], count: row.sample_count }],
138831
138629
  precomputedBursts: parseBurstWindows(row.burst_windows, traceStartMs),
138832
- // first/last_ts_ns are absolute CLOCK_MONOTONIC ns: they exceed 2^53 after
138833
- // ~104 days of device uptime, so the WASM decoder hands them back as bigint
138834
- // (see readCell). traceStartNs is already a plain Number, so coerce here to
138835
- // avoid "Cannot mix BigInt and other types" — values stay integral.
138630
+ // Absolute CLOCK_MONOTONIC ns passes 2^53 after ~104 days of uptime and
138631
+ // readCell hands those back as bigint — coerce before mixing with the
138632
+ // plain-Number traceStartNs.
138836
138633
  firstMs: Math.round((Number(row.first_ts_ns) - traceStartNs) / 1e6),
138837
138634
  lastMs: Math.round((Number(row.last_ts_ns) - traceStartNs) / 1e6),
138838
138635
  sampleCount: row.sample_count
@@ -139054,8 +138851,8 @@ async function stopNativeProfilerAndroid(api) {
139054
138851
  failure_stage: "android_native_profiler_stop",
139055
138852
  failure_area: "tool_server",
139056
138853
  // Internal session-state, not caller input — matches the react twin
139057
- // REACT_PROFILER_NO_ACTIVE_SESSION (not_found) so the "no active session"
139058
- // family carries one consistent kind across React/native/iOS/Android.
138854
+ // REACT_PROFILER_NO_ACTIVE_SESSION so the "no active session" family
138855
+ // carries one consistent kind.
139059
138856
  error_kind: "not_found"
139060
138857
  }
139061
138858
  );
@@ -139093,7 +138890,8 @@ async function stopNativeProfilerAndroid(api) {
139093
138890
  api.lastExitInfo = null;
139094
138891
  }
139095
138892
  const { hostTracePath, warning } = stopResult;
139096
- api.exportedFiles = { pftrace: hostTracePath };
138893
+ const exportedFiles = { pftrace: hostTracePath };
138894
+ api.exportedFiles = exportedFiles;
139097
138895
  if (api.appProcess) {
139098
138896
  await writeAndroidNativeProfilerMetadata(hostTracePath, {
139099
138897
  platform: "android",
@@ -139103,7 +138901,7 @@ async function stopNativeProfilerAndroid(api) {
139103
138901
  }
139104
138902
  const result = {
139105
138903
  traceFile: hostTracePath,
139106
- exportedFiles: api.exportedFiles
138904
+ exportedFiles
139107
138905
  };
139108
138906
  if (warning) result.warning = warning;
139109
138907
  return result;
@@ -139148,9 +138946,8 @@ async function analyzeNativeProfilerAndroid(api) {
139148
138946
  payload,
139149
138947
  traceFile: hostTracePath,
139150
138948
  exportErrors: pipelineResult.exportErrors,
139151
- // wallClockStartMs is the recording's start time (set at start, persisted in
139152
- // the metadata sidecar, restored by profiler-load). A large gap to "now"
139153
- // means we're analyzing a trace from an earlier session, not a fresh capture.
138949
+ // Recording start time, persisted in the metadata sidecar and restored by
138950
+ // profiler-load, so a large gap to "now" means an earlier session's trace.
139154
138951
  freshnessNote: formatTraceFreshness(api.wallClockStartMs, Date.now()) ?? void 0,
139155
138952
  // Explains an absent CPU section when samples exist but carry no stacks.
139156
138953
  cpuDiagnostic: pipelineResult.cpuDiagnostic
@@ -139224,15 +139021,22 @@ var capability26 = {
139224
139021
  apple: { simulator: true, device: true },
139225
139022
  android: { emulator: true, device: true, unknown: true }
139226
139023
  };
139024
+ var EXPORTED_FILE_KINDS = {
139025
+ cpu: "native-profile-cpu",
139026
+ hangs: "native-profile-hangs",
139027
+ leaks: "native-profile-leaks",
139028
+ pftrace: "native-profile-trace"
139029
+ };
139227
139030
  async function exportedFilesToArtifacts(store, files) {
139228
139031
  const out = {};
139229
- for (const [key2, filePath] of Object.entries(files)) {
139230
- out[key2] = filePath ? await store.register(filePath) : null;
139032
+ for (const key2 of Object.keys(files)) {
139033
+ const filePath = files[key2];
139034
+ out[key2] = filePath ? await store.register({ hostPath: filePath, kind: EXPORTED_FILE_KINDS[key2] }) : null;
139231
139035
  }
139232
139036
  return out;
139233
139037
  }
139234
139038
  function registerTrace(store, traceFile) {
139235
- return store.register(traceFile, { archive: "tar.gz" });
139039
+ return store.register({ hostPath: traceFile, kind: "native-profile-trace", archive: "tar.gz" });
139236
139040
  }
139237
139041
  var nativeProfilerStopTool = {
139238
139042
  id: "native-profiler-stop",
@@ -139242,7 +139046,7 @@ var nativeProfilerStopTool = {
139242
139046
  failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to stop native profiler: ${failureSignal2.error_code}`
139243
139047
  },
139244
139048
  capability: capability26,
139245
- // Packaging plus the export passes routinely exceed the 30s fetch timeout.
139049
+ // Packaging plus the export passes routinely exceed the 30s MCP fetch timeout.
139246
139050
  longRunning: true,
139247
139051
  description: `Stop native profiling and export trace data.
139248
139052
  iOS: sends SIGINT to xctrace, waits for packaging, then exports CPU, hangs, and leaks XML.
@@ -139329,7 +139133,10 @@ Fails if native-profiler-stop has not been called first to export trace data.`,
139329
139133
  }
139330
139134
  return {
139331
139135
  ...result,
139332
- reportFile: result.reportFile ? await requireArtifacts(ctx).register(result.reportFile) : null
139136
+ reportFile: result.reportFile ? await requireArtifacts(ctx).register({
139137
+ hostPath: result.reportFile,
139138
+ kind: "native-profile-report"
139139
+ }) : null
139333
139140
  };
139334
139141
  }
139335
139142
  };
@@ -139742,8 +139549,7 @@ function assertStoppableSession(api, stage) {
139742
139549
  error_code: reaped2 ? FAILURE_CODES.SCREEN_RECORDING_SERVER_SHUTTING_DOWN : FAILURE_CODES.SCREEN_RECORDING_NO_ACTIVE_SESSION,
139743
139550
  failure_stage: stage,
139744
139551
  failure_area: "tool_server",
139745
- // Session-state, not caller input — matches the profiler family's
139746
- // "no active session" kind.
139552
+ // Session-state, not caller input — matches the profiler family's kind.
139747
139553
  error_kind: "not_found"
139748
139554
  }
139749
139555
  );
@@ -139891,7 +139697,7 @@ function ffmpegArgs(opts) {
139891
139697
  "-loglevel",
139892
139698
  "warning",
139893
139699
  // The pump feeds whole JPEGs at a fixed cadence, so the input timeline is
139894
- // exactly OUTPUT_FPS — no timestamp guessing, no variable-framerate stutter.
139700
+ // exactly OUTPUT_FPS — no timestamp guessing.
139895
139701
  "-f",
139896
139702
  "image2pipe",
139897
139703
  "-framerate",
@@ -140319,10 +140125,9 @@ Returns { status: "recording", timeLimitSeconds, outputFile } \u2014 the video i
140319
140125
  Fails if a recording is already running on the device, the device is not booted, ffmpeg is not installed, or the platform cannot be recorded (tvOS, Chromium, Vega and remote simulators are unsupported).`,
140320
140126
  searchHint: "record video screen capture movie mp4 start filming screencast",
140321
140127
  zodSchema: zodSchema51,
140322
- // simulator-server is resolved inside execute, not declared here: a tvOS
140323
- // udid classifies as iOS by shape, and an eager service would spawn
140324
- // simulator-server for a device it cannot drive and hang on its ready
140325
- // timeout (same reasoning as `screenshot`).
140128
+ // Resolved inside execute, not declared eagerly: a tvOS udid classifies as
140129
+ // iOS by shape, so an eager service would spawn simulator-server for a
140130
+ // device it cannot drive and hang on its ready timeout (as in `screenshot`).
140326
140131
  services: (params) => ({
140327
140132
  session: screenRecordingSessionRef(resolveDevice(params.udid))
140328
140133
  }),
@@ -140432,10 +140237,11 @@ Fails if no recording (running or finished-but-unretrieved) exists for the given
140432
140237
  assertSupported("screen-recording-stop", capability29, device);
140433
140238
  const stopped = await stopCapture(api);
140434
140239
  const artifacts = requireArtifacts(ctx);
140435
- const video = await artifacts.register(stopped.outputFile, {
140240
+ const video = await artifacts.register({
140241
+ hostPath: stopped.outputFile,
140242
+ kind: "screen-recording",
140436
140243
  mimeType: "video/mp4",
140437
- // Drop the internal `argent-` temp prefix so the saved file reads cleanly
140438
- // as `.argent/recordings/screen-recording-<device>-<ts>.mp4`.
140244
+ // Drop the internal `argent-` temp-file prefix from the saved name.
140439
140245
  filename: (0, import_node_path20.basename)(stopped.outputFile).replace(/^argent-/, ""),
140440
140246
  saveDir: RECORDINGS_DIR
140441
140247
  });
@@ -140749,8 +140555,7 @@ Use when investigating JS CPU hotspots or correlating CPU cost with specific com
140749
140555
  Returns a markdown table of CPU hotspots, call tree, or per-component CPU breakdown.
140750
140556
  Fails if no CPU profile is stored \u2014 run react-profiler-stop first.`,
140751
140557
  zodSchema: zodSchema53,
140752
- // RN-only: reads Hermes-format CPU profiles. Chromium's V8 sample format is
140753
- // different — see the PR description for the follow-up scope.
140558
+ // RN-only: reads Hermes-format CPU profiles; Chromium's V8 sample format differs.
140754
140559
  capability: RN_ONLY_TOOL_CAPABILITY,
140755
140560
  services: () => ({}),
140756
140561
  async execute(_services, params) {
@@ -141109,7 +140914,7 @@ Use when drilling into specific components or time windows after react-profiler-
141109
140914
  Returns a markdown table or tree of commit data matching the requested mode.
141110
140915
  Fails if react-profiler-stop has not been called or no commit data is stored.`,
141111
140916
  zodSchema: zodSchema54,
141112
- // RN-only: reads React commit data captured via the React DevTools backend.
140917
+ // Reads React commit data captured via the React DevTools backend.
141113
140918
  capability: RN_ONLY_TOOL_CAPABILITY,
141114
140919
  services: () => ({}),
141115
140920
  async execute(_services, params) {
@@ -141497,9 +141302,7 @@ Use when drilling into native hang stacks, thread CPU breakdown, or memory leaks
141497
141302
  Returns a markdown report with native call stacks, thread weights, or leak details for the selected mode.
141498
141303
  Fails if native-profiler-analyze has not been run or no parsed trace data is in memory.`,
141499
141304
  zodSchema: zodSchema55,
141500
- // iOS: reads xctrace output. Android: queries the Perfetto .pftrace via the
141501
- // in-process trace-processor engine (see executeAndroid). Chromium has no
141502
- // native trace capture.
141305
+ // No chromium entry: it has no native trace capture.
141503
141306
  capability: {
141504
141307
  apple: { simulator: true, device: true },
141505
141308
  android: { emulator: true, device: true, unknown: true }
@@ -141581,9 +141384,8 @@ Call this tool when both profilers were run in parallel on the same session.
141581
141384
  Returns a markdown report correlating hangs with React commits, memory leaks, and investigation hints.
141582
141385
  Fails if either react-profiler-analyze or native-profiler-analyze has not been called first.`,
141583
141386
  zodSchema: zodSchema56,
141584
- // Combines React (Hermes) + native traces. iOS reads xctrace output;
141585
- // Android re-queries the Perfetto .pftrace via loadAndroidCombinedData. The
141586
- // capture half exists on neither platform's Chromium.
141387
+ // iOS reads xctrace output; Android re-queries the Perfetto .pftrace via
141388
+ // loadAndroidCombinedData. Chromium has no native trace capture.
141587
141389
  capability: {
141588
141390
  apple: { simulator: true, device: true },
141589
141391
  android: { emulator: true, device: true, unknown: true }
@@ -141830,7 +141632,7 @@ Fails if either react-profiler-analyze or native-profiler-analyze has not been c
141830
141632
  memoryLeaks,
141831
141633
  mountComponents,
141832
141634
  // From parsedData, not the live session field: a recording started
141833
- // after the analyze must not re-label the data being rendered here.
141635
+ // after analyze must not re-label the data rendered here.
141834
141636
  nativeApi.parsedData?.mallocStackLogging ?? null
141835
141637
  )
141836
141638
  );
@@ -142197,10 +141999,8 @@ async function loadNativeSession(debugDir, sessionId, api, appProcessOverride) {
142197
141999
  error_code: FAILURE_CODES.PROFILER_NATIVE_TRACE_MISSING,
142198
142000
  failure_stage: "profiler_load_native_session",
142199
142001
  failure_area: "tool_server",
142200
- // A trace file missing on disk is a not_found condition — same kind as
142201
- // the Android .pftrace-missing site above, so this shared code isn't
142202
- // split into two kinds by platform (validation stays for the
142203
- // analyze-before-stop guard, NATIVE_PROFILER_NO_EXPORTED_TRACE).
142002
+ // A trace missing on disk is not_found; validation stays for the
142003
+ // analyze-before-stop guard (NATIVE_PROFILER_NO_EXPORTED_TRACE).
142204
142004
  error_kind: "not_found"
142205
142005
  }
142206
142006
  );
@@ -142266,9 +142066,8 @@ Modes:
142266
142066
  Returns a summary of the loaded session or a session list for the list mode.
142267
142067
  Fails if the session_id is not found or required XML files are missing from disk.`,
142268
142068
  zodSchema: zodSchema57,
142269
- // Loads Hermes-format React traces or iOS xctrace XML — neither maps onto
142270
- // Chromium yet. The gate keeps the error close to the call site instead of
142271
- // letting it surface from inside the trace parser.
142069
+ // The Hermes, xctrace and perfetto formats this loads have no Chromium
142070
+ // equivalent; the gate fails at the call site, not inside the trace parser.
142272
142071
  capability: RN_ONLY_TOOL_CAPABILITY,
142273
142072
  services: (params) => {
142274
142073
  const svcs = {};
@@ -142330,12 +142129,9 @@ init_src();
142330
142129
  var URN_SUFFIXES = ["", ":tcp"];
142331
142130
  var PORT_KEYED_NAMESPACES = [
142332
142131
  JS_RUNTIME_DEBUGGER_NAMESPACE,
142333
- // Both declare `getDependencies -> JsRuntimeDebugger:<payload>`, so neither
142334
- // can be in a snapshot without it and neither adds any ownership the debugger
142335
- // entry does not already establish. They are listed for what `stopped`
142336
- // reports: a session that had a network inspector or a React profiler open is
142337
- // told those went away by name, rather than inferring it from the debugger
142338
- // line.
142132
+ // Both depend on `JsRuntimeDebugger:<payload>`, so a cascade already reaps
142133
+ // them; listed so `stopped` names them instead of leaving the caller to infer
142134
+ // them from the debugger line.
142339
142135
  NETWORK_INSPECTOR_NAMESPACE,
142340
142136
  REACT_PROFILER_SESSION_NAMESPACE
142341
142137
  ];
@@ -142441,8 +142237,7 @@ function createStopAllSimulatorServersTool(registry2) {
142441
142237
  id: "stop-all-simulator-servers",
142442
142238
  interaction: {
142443
142239
  // "all" only holds for the unscoped sweep; a scoped call touches just the
142444
- // ids it was given, and saying otherwise would misreport a teardown that
142445
- // deliberately left another agent's devices running.
142240
+ // ids it was given.
142446
142241
  startedMsg: ({ params }) => {
142447
142242
  const devices = params?.devices;
142448
142243
  return devices ? `Stopping simulator servers for ${devices.length} ${devices.length === 1 ? "device" : "devices"}` : "Stopping all simulator servers";
@@ -142538,9 +142333,8 @@ function listeningPids(port) {
142538
142333
  const output2 = (0, import_node_child_process28.execFileSync)("netstat", ["-ano"], {
142539
142334
  encoding: "utf-8",
142540
142335
  timeout: 5e3,
142541
- // `netstat -ano` dumps every socket on the host; a busy box easily
142542
- // exceeds Node's default 1 MiB maxBuffer, and the resulting ENOBUFS
142543
- // throw would misread as "port is free" (stopped:false).
142336
+ // `netstat -ano` dumps every socket on the host; overflowing Node's
142337
+ // default 1 MiB throws, which this tool would misread as "port is free".
142544
142338
  maxBuffer: 16 * 1024 * 1024
142545
142339
  });
142546
142340
  return parseNetstatListeningPids(output2, port);
@@ -144309,8 +144103,8 @@ var fileInputs2 = [
144309
144103
  var flowStartRecordingTool = {
144310
144104
  id: "flow-start-recording",
144311
144105
  interaction: {
144312
- // Name the flow: recordings are concurrent, so several of these lines can
144313
- // interleave in one log and "flow recording" would not identify which.
144106
+ // Name the flow: concurrent recordings interleave in one log, and "flow
144107
+ // recording" would not say which.
144314
144108
  startedMsg: ({ params }) => `Starting recording of flow ${params.name}`,
144315
144109
  completedMsg: ({ params, result }) => {
144316
144110
  if (!result.restarted) return `Started recording flow ${params.name}`;
@@ -144673,7 +144467,7 @@ function projectIosNode(node, screenW, screenH) {
144673
144467
  children: node.children ?? [],
144674
144468
  // Text hoists only from on-screen nodes (frame is null when the view is
144675
144469
  // scrolled off or zero-area) — otherwise a text assert against an ancestor
144676
- // would pass on content the screen doesn't show. Every labelled node is
144470
+ // would pass on content the screen doesn't show. A label makes a node
144677
144471
  // leaf-eligible, so `frame` was computed for any node with text.
144678
144472
  ownText: frame ? node.label ?? "" : "",
144679
144473
  leaf,
@@ -144714,8 +144508,8 @@ var FULL_HIERARCHY_FIELDS = [
144714
144508
  "windowFrame",
144715
144509
  "hidden",
144716
144510
  "alpha",
144717
- // The type directive's focus wait; an older injected framework ignores the
144718
- // request, which just leaves the wait's poll unconfirmed.
144511
+ // Read by the type directive's focus wait; an injected framework that omits
144512
+ // it just leaves that wait's poll unconfirmed.
144719
144513
  "firstResponder"
144720
144514
  ];
144721
144515
  async function unreadableHierarchyReason(nativeApi, bundleId) {
@@ -144843,18 +144637,17 @@ function projectAndroidNode(node, screenW, screenH) {
144843
144637
  return {
144844
144638
  skip: skip2,
144845
144639
  children: childNodes(node),
144846
- // Text hoists only from on-screen nodes (frame is null when the bounds clip
144847
- // to zero area) — otherwise a text assert against an ancestor would pass on
144848
- // content the screen doesn't show. Every text-carrying node is
144849
- // leaf-eligible (its label is non-empty), so `frame` was computed for it.
144640
+ // Off-screen text must not hoist, or an ancestor text assert would pass on
144641
+ // content the screen doesn't show. Any node with text is leaf-eligible (its
144642
+ // label is non-empty), so `frame` was computed for it.
144850
144643
  ownText: frame ? ownText2 : "",
144851
144644
  leaf,
144852
- // A password node shields its placeholder text like any identified node —
144853
- // even if it somehow lacks an id — so the secret can never bubble upward.
144645
+ // A password field shields even when it carries no id, so nothing from it
144646
+ // bubbles into an ancestor's hoisted text.
144854
144647
  shield: Boolean(identifier) || isPassword,
144855
- // Scroll-clip inputs (see `flattenHoisting`): a scrolling container's raw
144856
- // bounds clip its subtree, so a row it has scrolled out of view — still
144857
- // on the device screen — is dropped, matching the describe path's prune.
144648
+ // Scroll-clip inputs (see `flattenHoisting`): a scroller's raw bounds clip
144649
+ // its subtree, so a row scrolled out of view — but still on the device
144650
+ // screen — is dropped, matching the describe path's prune.
144858
144651
  rect,
144859
144652
  scrolls: isUiAutomatorScrollable(attrs)
144860
144653
  };
@@ -144917,9 +144710,9 @@ function projectChromiumNode(node) {
144917
144710
  return {
144918
144711
  skip: false,
144919
144712
  children: node.children,
144920
- // Text hoists only from on-screen nodes — otherwise a text assert against
144921
- // an ancestor would pass on content the screen doesn't show. A password
144922
- // field's text never bubbles up (the walker already withholds its value).
144713
+ // Off-screen text must not hoist, or a text assert against an ancestor
144714
+ // would pass on content the screen doesn't show. A password's text never
144715
+ // bubbles up.
144923
144716
  ownText: onScreen && !node.password ? nodeText(node) : "",
144924
144717
  leaf,
144925
144718
  shield: Boolean(node.identifier) || node.password === true
@@ -144950,8 +144743,8 @@ function projectVegaNode(node) {
144950
144743
  return {
144951
144744
  skip: false,
144952
144745
  children: node.children,
144953
- // Text hoists only from on-screen nodes — otherwise a text assert against
144954
- // an ancestor would pass on content the screen doesn't show.
144746
+ // Off-screen text must not hoist, or a text assert against an ancestor
144747
+ // would pass on content the screen doesn't show.
144955
144748
  ownText: onScreen ? nodeText(node) : "",
144956
144749
  leaf: { ...node, children: [] },
144957
144750
  shield: isAuthoredVegaTestId(node.identifier)
@@ -145789,10 +145582,9 @@ async function waitForIdle(env, step) {
145789
145582
  ok: false,
145790
145583
  indeterminate: true,
145791
145584
  // The underlying reader reports an instrumentation failure, whose remedy
145792
- // (relaunch the app) is the wrong repair for the commonest cause of it
145793
- // here: the app is simply not in the foreground, which reads exactly the
145794
- // same from the tree source. Name that first so the author checks it
145795
- // before relaunching anything.
145585
+ // (relaunch the app) is the wrong repair for the commonest cause here: the
145586
+ // app is simply not in the foreground, which reads exactly the same from the
145587
+ // tree source. Name that first.
145796
145588
  reason: `could not read the UI tree while waiting for the screen to settle \u2014 check the app is still in the foreground (a backgrounded app reads the same as an uninstrumented one). Underlying error: ${underlying}`
145797
145589
  });
145798
145590
  const degraded = () => ({
@@ -145939,13 +145731,12 @@ function anchoredWarnings(session, steps) {
145939
145731
  var flowFinishRecordingTool = {
145940
145732
  id: "flow-finish-recording",
145941
145733
  interaction: {
145942
- // Name the flow: other recordings stay live across this call, so an
145943
- // unqualified "Finishing flow recording" would not identify which one.
145734
+ // Name the flow: other recordings stay live, so an unqualified message would
145735
+ // not identify which one.
145944
145736
  startedMsg: ({ params }) => `Finishing recording of flow ${params.name}`,
145945
- // `params.name` rather than the basename of `result.path`: the two are the
145946
- // same string on every branch — `assertSafeFlowName` admits no dots or
145947
- // separators, so `getFlowPath` produces `<name>.yaml` and nothing else —
145948
- // and this spelling matches the two formatters either side of it.
145737
+ // `params.name` equals the basename of `result.path` on every branch
145738
+ // (`assertSafeFlowName` admits no dots or separators), and matches the two
145739
+ // formatters either side.
145949
145740
  completedMsg: ({ params }) => `Saved recorded flow ${params.name}`,
145950
145741
  failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to finish recording of flow ${params.name}: ${failureSignal2.error_code}`
145951
145742
  },
@@ -146380,7 +146171,7 @@ function createFlowAddStepTool(registry2) {
146380
146171
  id: "flow-add-step",
146381
146172
  interaction: {
146382
146173
  // Name the flow: recordings are concurrent, so several of these lines can
146383
- // interleave in one log and "the recorded flow" would not identify which.
146174
+ // interleave in one log and "the recorded flow" would not say which.
146384
146175
  startedMsg: ({ params }) => `Adding ${params.command} step to flow ${params.name}`,
146385
146176
  completedMsg: ({ params }) => `Added ${params.command} step to flow ${params.name}`,
146386
146177
  failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to add ${params.command} step to flow ${params.name}: ${failureSignal2.error_code}`
@@ -146460,9 +146251,6 @@ If a step was recorded by mistake, remove it from the .yaml after \`flow-finish-
146460
146251
  toolResult,
146461
146252
  stepCount,
146462
146253
  recorded: summarizeStep(step, stepCount),
146463
- // Host mode: a path. Client mode: the directive that carries the YAML
146464
- // to the client, which IS the persistence mechanism there — the one
146465
- // place the full file still has to travel per step.
146466
146254
  savedTo
146467
146255
  };
146468
146256
  }
@@ -146481,8 +146269,7 @@ var zodSchema64 = external_exports.object({
146481
146269
  var flowInsertEchoTool = {
146482
146270
  id: "flow-add-echo",
146483
146271
  interaction: {
146484
- // Name the flow: recordings are concurrent, so several of these lines can
146485
- // interleave in one log and "the recorded flow" would not identify which.
146272
+ // Name the flow: recordings are concurrent, so an unqualified message is ambiguous.
146486
146273
  startedMsg: ({ params }) => `Adding note to flow ${params.name}`,
146487
146274
  completedMsg: ({ params }) => `Added note to flow ${params.name}`,
146488
146275
  failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to add note to flow ${params.name}: ${failureSignal2.error_code}`
@@ -148743,7 +148530,9 @@ async function runSnapshot(env, opts) {
148743
148530
  const currentArtifact = async () => {
148744
148531
  if (cropDir === void 0) return shot.image;
148745
148532
  keepCropped = true;
148746
- return store.register(currentPath, {
148533
+ return store.register({
148534
+ hostPath: currentPath,
148535
+ kind: "screenshot",
148747
148536
  mimeType: "image/png",
148748
148537
  filename: `${snapshotKey}-current.png`
148749
148538
  });
@@ -148768,7 +148557,11 @@ async function runSnapshot(env, opts) {
148768
148557
  if (opts.updateBaselines) {
148769
148558
  await fs50.mkdir(dir, { recursive: true });
148770
148559
  await fs50.copyFile(currentPath, baselinePath);
148771
- const baseline = await store.register(baselinePath, { mimeType: "image/png" });
148560
+ const baseline = await store.register({
148561
+ hostPath: baselinePath,
148562
+ kind: "screenshot",
148563
+ mimeType: "image/png"
148564
+ });
148772
148565
  return {
148773
148566
  status: "pass",
148774
148567
  reason: exists2 ? `baseline updated (${key2})` : `baseline written (${key2})`,
@@ -148791,12 +148584,11 @@ async function runSnapshot(env, opts) {
148791
148584
  baselinePath,
148792
148585
  currentPath,
148793
148586
  outputDir,
148794
- // A crop compares EVERY pixel of the element's region — no masking,
148795
- // wherever the crop sits. Masking a crop's overlap with the screen's
148796
- // top status-bar band would degenerate into comparing nothing for an
148797
- // element inside the band (a vacuous pass); a crop overlapping the
148798
- // band leans on best-effort pinStatusBar until frame resolution
148799
- // asserts the element clears it.
148587
+ // A crop compares EVERY pixel of the element's region, wherever it
148588
+ // sits. Masking a crop's overlap with the screen's top status-bar band
148589
+ // would degenerate into comparing nothing for an element inside the
148590
+ // band (a vacuous pass); a crop overlapping the band leans on the run's
148591
+ // best-effort pinStatusBar instead.
148800
148592
  topMask: cropFrame === void 0 ? "status-bar" : "none",
148801
148593
  // Crop dimensions track the element — size drift must hard-fail below
148802
148594
  // instead of being resampled away like a full-screen scale difference.
@@ -148809,7 +148601,11 @@ async function runSnapshot(env, opts) {
148809
148601
  reason: `baseline is ${expected.width}x${expected.height} but the ${opts.cropOn ? "cropOn region" : "capture"} is ${actual.width}x${actual.height} (${key2}) \u2014 nothing was compared` + (opts.cropOn ? `. The element's size drifted \u2014 crop a fixed-size container, or re-adopt with updateBaselines` : ""),
148810
148602
  snapshotKey,
148811
148603
  artifacts: {
148812
- baseline: await store.register(baselinePath, { mimeType: "image/png" }),
148604
+ baseline: await store.register({
148605
+ hostPath: baselinePath,
148606
+ kind: "screenshot",
148607
+ mimeType: "image/png"
148608
+ }),
148813
148609
  current: await currentArtifact()
148814
148610
  }
148815
148611
  };
@@ -148820,11 +148616,17 @@ async function runSnapshot(env, opts) {
148820
148616
  return { status: "pass", reason };
148821
148617
  }
148822
148618
  const artifacts = {
148823
- baseline: await store.register(baselinePath, { mimeType: "image/png" }),
148619
+ baseline: await store.register({
148620
+ hostPath: baselinePath,
148621
+ kind: "screenshot",
148622
+ mimeType: "image/png"
148623
+ }),
148824
148624
  current: await currentArtifact()
148825
148625
  };
148826
148626
  if (result.contextDiffPath) {
148827
- artifacts.diff = await store.register(result.contextDiffPath, {
148627
+ artifacts.diff = await store.register({
148628
+ hostPath: result.contextDiffPath,
148629
+ kind: "screenshot-diff-context",
148828
148630
  mimeType: "image/png",
148829
148631
  filename: `${snapshotKey}-diff.png`
148830
148632
  });
@@ -149381,8 +149183,8 @@ Pass exactly one flow source: name for a saved flow under project_root, or flow_
149381
149183
  // One holder per ExecState, shared by nested `run:` flows: `deviceEnv`
149382
149184
  // spreads the reference, so what one step's settle learns about the
149383
149185
  // tree source the next one already has. A `tool: flow-execute` builds
149384
- // its own instead, which is why that step spends this verdict rather
149385
- // than inheriting whatever the sub-run proved.
149186
+ // its own, which is why that step spends this verdict rather than
149187
+ // inheriting whatever the sub-run proved.
149386
149188
  treeOutage: {},
149387
149189
  flowsDir,
149388
149190
  viaUpload,
@@ -149573,8 +149375,7 @@ function summarize(flowName, deviceId, executionPrerequisite, steps, aborted2) {
149573
149375
  // A cancelled run must never read as PASS — it may contain skips alone
149574
149376
  // (no fail/error report), so the verdict folds the abort in directly. A
149575
149377
  // skip by itself is NOT a failure: an unmet `when:` guard skips its block
149576
- // as a successful omission, and a hard stop already carries its own
149577
- // fail/error report.
149378
+ // as a successful omission.
149578
149379
  ok: failed === 0 && errored === 0 && !aborted2,
149579
149380
  ...aborted2 ? { aborted: true } : {},
149580
149381
  passed,
@@ -150847,9 +150648,17 @@ async function executeScreenshotDiffTool(services, params, options, captureScree
150847
150648
  const artifacts = requireArtifacts(options);
150848
150649
  return {
150849
150650
  summary: result.summary,
150850
- ...result.diffPath ? { diffPath: await artifacts.register(result.diffPath, { mimeType: "image/png" }) } : {},
150651
+ ...result.diffPath ? {
150652
+ diffPath: await artifacts.register({
150653
+ hostPath: result.diffPath,
150654
+ kind: "screenshot-diff",
150655
+ mimeType: "image/png"
150656
+ })
150657
+ } : {},
150851
150658
  ...result.contextDiffPath ? {
150852
- contextDiffPath: await artifacts.register(result.contextDiffPath, {
150659
+ contextDiffPath: await artifacts.register({
150660
+ hostPath: result.contextDiffPath,
150661
+ kind: "screenshot-diff-context",
150853
150662
  mimeType: "image/png"
150854
150663
  })
150855
150664
  } : {}
@@ -151161,11 +150970,10 @@ var awaitUserSelectionTool = {
151161
150970
  failedMsg: ({ failureSignal: failureSignal2 }) => `Failed while waiting for variant selection: ${failureSignal2.error_code}`
151162
150971
  },
151163
150972
  featureFlag: "argent-lens",
151164
- // Hidden entirely while an `argent lens` CLI session owns the preview window:
151165
- // there, the user's picks are relayed into the agent's terminal as a normal
151166
- // message (see `argent-cli/src/lens.ts`), so the agent never blocks on a pick.
151167
- // Hiding the tool (rather than telling the agent "don't call it") keeps the
151168
- // CLI-relay surface honest — propose_variant then end the turn.
150973
+ // Hidden while an `argent lens` CLI session owns the preview window: there the
150974
+ // user's picks are relayed into the agent's terminal as a normal message (see
150975
+ // `argent-cli/src/lens.ts`), so the agent ends its turn after propose_variant
150976
+ // instead of blocking.
151169
150977
  hideWhen: () => variantProposalStore.isCliSession(),
151170
150978
  description: `Block until the human finishes picking among the variants you proposed (the ONE blocking call).
151171
150979
 
@@ -151840,10 +151648,8 @@ function createPreviewWindowManager(opts = {}) {
151840
151648
  const launchBin = wrapperBin ?? electronBin;
151841
151649
  const launchArgs = wrapperBin ? ["--no-sandbox", mainScript] : [mainScript];
151842
151650
  const next = (0, import_node_child_process33.spawn)(launchBin, launchArgs, {
151843
- // Strip ELECTRON_RUN_AS_NODE so the child boots as a GUI Electron app, not
151844
- // a bare Node runtime (see electronGuiChildEnv). An Electron-based MCP
151845
- // host puts it in our env, and inheriting it makes main.cjs crash at
151846
- // app.setName() with `app` undefined — the window never opens.
151651
+ // Strips ELECTRON_RUN_AS_NODE, which an Electron-based MCP host leaves in
151652
+ // our env, so the child boots as a GUI app instead of bare Node.
151847
151653
  env: electronGuiChildEnv({ ARGENT_PREVIEW_URL: url2 }),
151848
151654
  stdio: ["pipe", "ignore", "pipe"]
151849
151655
  });
@@ -151992,11 +151798,10 @@ function start() {
151992
151798
  const { stop: stopWatcher, ready: watcherReady } = startSimulatorWatcher(registry2);
151993
151799
  let server = null;
151994
151800
  const previewWindow = createPreviewWindowManager({
151995
- // If Electron can't launch (it's an optionalDependency — absent on
151996
- // headless/CI hosts), fail fast: unblock any parked await_user_selection
151997
- // with the browser fallback URL instead of stranding it for the full
151998
- // timeout. `previewWindowBaseUrl` (declared just below) is only read when
151999
- // this fires at runtime, long after module init.
151801
+ // Electron is an optionalDependency (absent on headless/CI hosts); on a launch
151802
+ // failure unblock any parked await with the browser fallback URL rather than
151803
+ // stranding it for the full timeout. `previewWindowBaseUrl` is declared below
151804
+ // but only called at runtime.
152000
151805
  onLaunchFailure: (err) => variantProposalStore.notifyWindowUnavailable(err.message, previewWindowBaseUrl())
152001
151806
  });
152002
151807
  const previewWindowBaseUrl = () => {