@swmansion/argent 0.22.1-next.2 → 0.22.1-next.4

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.
@@ -709,15 +709,12 @@ var init_failure_codes = __esm({
709
709
  NATIVE_PROFILER_SESSION_TORN_DOWN: "NATIVE_PROFILER_SESSION_TORN_DOWN",
710
710
  NATIVE_PROFILER_APP_PROCESS_NOT_FOUND: "NATIVE_PROFILER_APP_PROCESS_NOT_FOUND",
711
711
  NATIVE_PROFILER_NO_EXPORTED_TRACE: "NATIVE_PROFILER_NO_EXPORTED_TRACE",
712
- // Android perfetto start-failure modes — mirror the iOS xctrace set so a
713
- // failed recording start is classified rather than falling through to the
714
- // generic tool-execution bucket.
712
+ // Android perfetto start failures, mirroring the iOS xctrace set above.
715
713
  NATIVE_PROFILER_PERFETTO_PROCESS_ERROR: "NATIVE_PROFILER_PERFETTO_PROCESS_ERROR",
716
714
  NATIVE_PROFILER_PERFETTO_READY_TIMEOUT: "NATIVE_PROFILER_PERFETTO_READY_TIMEOUT",
717
715
  NATIVE_PROFILER_PERFETTO_READY_EXITED: "NATIVE_PROFILER_PERFETTO_READY_EXITED",
718
- // screen-recording-start / screen-recording-stop. One capture path for every
719
- // platform (simulator-server's frame stream into ffmpeg), so the stages name
720
- // 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.
721
718
  SCREEN_RECORDING_FACTORY_OPTIONS_MISSING: "SCREEN_RECORDING_FACTORY_OPTIONS_MISSING",
722
719
  SCREEN_RECORDING_WRONG_PLATFORM: "SCREEN_RECORDING_WRONG_PLATFORM",
723
720
  SCREEN_RECORDING_ALREADY_ACTIVE: "SCREEN_RECORDING_ALREADY_ACTIVE",
@@ -16086,22 +16083,19 @@ var init_registry = __esm({
16086
16083
  init_zod_to_json_schema();
16087
16084
  import_node_crypto2 = require("node:crypto");
16088
16085
  Registry = class {
16089
- /** Single map: URN -> ServiceNode (all instances). */
16090
16086
  services = /* @__PURE__ */ new Map();
16091
16087
  blueprints = /* @__PURE__ */ new Map();
16092
16088
  tools = /* @__PURE__ */ new Map();
16093
16089
  /**
16094
- * Predicate that decides whether a feature-flagged tool is currently enabled.
16095
- * Injected (rather than importing `@argent/cli` here) so the registry stays
16096
- * free of a CLI dependency. The default treats every flag as enabled, so
16097
- * existing `new Registry()` call sites (tests, non-flag deployments) keep
16098
- * 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.
16099
16093
  */
16100
16094
  isFlagEnabled;
16101
16095
  /**
16102
- * Host files produced by tools, registered during `execute` and served by the
16103
- * `/artifacts/:id` route. Owned here (one per registry/process) so the tool
16104
- * 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.
16105
16099
  */
16106
16100
  artifacts = new ArtifactStore();
16107
16101
  events = new TypedEventEmitter();
@@ -16122,8 +16116,8 @@ var init_registry = __esm({
16122
16116
  return this.tools.get(id)?.definition;
16123
16117
  }
16124
16118
  /**
16125
- * Resolve a service by URN. JIT-instantiates from blueprint if not yet created.
16126
- * 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.
16127
16121
  */
16128
16122
  resolveService(urn, options) {
16129
16123
  return this._resolve(urn, [], options);
@@ -16240,14 +16234,10 @@ var init_registry = __esm({
16240
16234
  };
16241
16235
  }
16242
16236
  /**
16243
- * After a tool failed, ask each service it resolved whether the error means
16244
- * that service's instance is dead (`blueprint.recoverable(error)`). Dispose
16245
- * every one that says yes so the next `resolveService` re-creates it, and
16246
- * report whether anything was disposed (i.e. whether a retry is worthwhile).
16247
- *
16248
- * Only currently-RUNNING nodes are considered: a service that already
16249
- * errored/torn down during resolution needs no recovery here, and a URN this
16250
- * 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.
16251
16241
  */
16252
16242
  async _recoverFailedServices(refs, error52) {
16253
16243
  let recoveredAny = false;
@@ -16263,10 +16253,7 @@ var init_registry = __esm({
16263
16253
  }
16264
16254
  return recoveredAny;
16265
16255
  }
16266
- /**
16267
- * Tear down a single service by URN (and cascade to its dependents).
16268
- * After disposal the service returns to IDLE and can be re-resolved.
16269
- */
16256
+ /** Tear down a service and its dependents; it returns to IDLE and can be re-resolved. */
16270
16257
  async disposeService(urn) {
16271
16258
  const node = this.services.get(urn);
16272
16259
  if (!node) throw new ServiceNotFoundError(urn);
@@ -16279,7 +16266,6 @@ var init_registry = __esm({
16279
16266
  }
16280
16267
  }
16281
16268
  }
16282
- // ── Private: Resolution ──
16283
16269
  _resolve(urn, resolutionPath, options) {
16284
16270
  let node = this.services.get(urn);
16285
16271
  if (!node) {
@@ -69928,16 +69914,12 @@ function androidRoots() {
69928
69914
  function defaultAndroidRoots() {
69929
69915
  const home = (0, import_node_os2.homedir)();
69930
69916
  const roots = [
69931
- // Android Studio defaults (the big ones — cover the majority of user
69932
- // installs that arrive without any env-var setup).
69933
69917
  (0, import_node_path3.join)(home, "Library", "Android", "sdk"),
69934
69918
  // macOS Android Studio default
69935
69919
  (0, import_node_path3.join)(home, "Android", "Sdk"),
69936
69920
  // Linux Android Studio default
69937
- // Common manual install convention. Not picked by any installer but used
69938
- // often enough in tutorials and Dockerfiles that probing it costs little.
69939
69921
  (0, import_node_path3.join)(home, "android-sdk"),
69940
- // System-wide locations (Linux package managers, Homebrew on macOS).
69922
+ // manual-install convention; no installer picks it
69941
69923
  "/opt/android-sdk",
69942
69924
  "/usr/lib/android-sdk",
69943
69925
  // Debian/Ubuntu `android-sdk` apt package
@@ -72072,9 +72054,7 @@ var require_min_release_age = __commonJS({
72072
72054
  var PROBE_TIMEOUT_MS = 3e3;
72073
72055
  var OVERRIDE_ENV = "ARGENT_MIN_RELEASE_AGE_DAYS";
72074
72056
  var PM_PROBES = {
72075
- // npm flattens `min-release-age` to an effective `before` cutoff, and some
72076
- // npm 11.x builds report `min-release-age=null` even while the policy is
72077
- // active. Probe `before`, which is what the resolver actually uses.
72057
+ // Probe `before`: that is the effective cutoff npm's resolver applies.
72078
72058
  npm: { command: "npm config get before", parse: config_parse_1.parseBeforeAgeMs },
72079
72059
  pnpm: {
72080
72060
  command: "pnpm config get minimumReleaseAge",
@@ -92085,14 +92065,12 @@ var CONFIG_SCHEMA = [
92085
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).",
92086
92066
  scopes: ["global"],
92087
92067
  parse: asBoolean,
92088
- // A committed project file must never re-enable telemetry a user disabled
92089
- // globally, so the more-restrictive (opt-out) value always wins.
92090
92068
  merge: "prioritize-restrictive",
92091
- // Telemetry is opt-out: with nothing stored, consent.ts treats it as
92092
- // 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)".
92093
92071
  default: true,
92094
- // Read-only under `argent config`: opt-in/out goes through the dedicated
92095
- // 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.
92096
92074
  manageCommand: "argent telemetry"
92097
92075
  },
92098
92076
  {
@@ -92100,8 +92078,6 @@ var CONFIG_SCHEMA = [
92100
92078
  description: "Coding-agent id remembered by `argent lens` to skip the picker.",
92101
92079
  scopes: ["project", "global"],
92102
92080
  parse: asString,
92103
- // A repo can pin the agent its screenshots should use; falls back to the
92104
- // user's global remembered choice.
92105
92081
  merge: "prioritize-local",
92106
92082
  example: "claude"
92107
92083
  },
@@ -92110,11 +92086,9 @@ var CONFIG_SCHEMA = [
92110
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).",
92111
92087
  scopes: ["project", "global"],
92112
92088
  parse: asStringArray,
92113
- // Additive: the scopes extend each other rather than shadow — a repo's
92114
- // committed device sets are appended to the user's global ones (global
92115
- // baseline first, project extras after, deduplicated). Note that
92116
- // `getAdditionalIosDeviceSets` re-implements this union (path resolution
92117
- // 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".
92118
92092
  merge: "union",
92119
92093
  example: '["~/DeviceSets/ci"]'
92120
92094
  },
@@ -92123,10 +92097,8 @@ var CONFIG_SCHEMA = [
92123
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.",
92124
92098
  scopes: ["project", "global"],
92125
92099
  parse: asString,
92126
- // A repo can pin where its recordings land; falls back to the user's global
92127
- // preference. Resolution happens on the client (the machine the mp4 is
92128
- // persisted to), so with a remote `argent link` tool-server it is the
92129
- // *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.
92130
92102
  merge: "prioritize-local",
92131
92103
  example: "~/Movies/argent"
92132
92104
  }
@@ -94853,17 +94825,16 @@ function createExporter(config2) {
94853
94825
  // The exporter bounds a request with `req.setTimeout()`, which Node only
94854
94826
  // arms once the socket is CONNECTED — so a collector whose address drops
94855
94827
  // packets (corporate egress filter, dead host behind a firewall) leaves a
94856
- // socket stuck in the connecting state that no export deadline can reach,
94857
- // holding the process open for the OS connect timeout (~75s on macOS)
94858
- // after shutdown() has already resolved. The agent's socket timeout is
94859
- // armed when the socket is CREATED, so it also covers connect: it fires,
94860
- // 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.
94861
94833
  //
94862
94834
  // keepAlive is restated because supplying httpAgentOptions at all
94863
- // replaces the agent the SDK would otherwise build, and its default is
94864
- // keepAlive: true. Without it the long-lived tool-server pays a fresh
94865
- // TCP+TLS handshake for every 10s batch — measured as one socket per
94866
- // 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.
94867
94838
  httpAgentOptions: { timeout: EXPORT_TIMEOUT_MS, keepAlive: true }
94868
94839
  });
94869
94840
  } finally {
@@ -95082,7 +95053,6 @@ var ALLOWED = {
95082
95053
  install_mode: INSTALL_MODE
95083
95054
  },
95084
95055
  "installation:global_install_decision": {
95085
- // `from_tar` is intentionally absent; the installer skips that dev path.
95086
95056
  decision: oneOf(["install", "cancel", "already_installed"])
95087
95057
  },
95088
95058
  "installation:update_decision": {
@@ -95161,9 +95131,8 @@ var ALLOWED = {
95161
95131
  tool_invocation_id: UUID,
95162
95132
  platform: PLATFORM,
95163
95133
  duration_ms: DURATION_MS,
95164
- // Schema-declared parameter names only (emit side filters against the tool's
95165
- // own zod shape and caps at 16 before this gate); the array validator also
95166
- // 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.
95167
95136
  invalid_params: arrayOf(matches(/^[a-z][a-z0-9_]{0,63}$/i, 64), 16),
95168
95137
  ...FAILURE_SIGNAL2,
95169
95138
  ...AI_TELEMETRY
@@ -95695,7 +95664,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
95695
95664
  var SESSION_ID = (0, import_node_crypto3.randomUUID)();
95696
95665
  function readCliVersion() {
95697
95666
  if (true) {
95698
- return "0.22.1-next.2";
95667
+ return "0.22.1-next.4";
95699
95668
  }
95700
95669
  return "0.0.0";
95701
95670
  }
@@ -96097,15 +96066,11 @@ function resolveHostFingerprint() {
96097
96066
  const out = (0, import_node_child_process.execFileSync)(simulatorServerBinaryPath(), ["fingerprint"], {
96098
96067
  encoding: "utf8",
96099
96068
  timeout: FINGERPRINT_TIMEOUT_MS,
96100
- // Reap with SIGKILL, not the default SIGTERM: a binary that ignores SIGTERM
96101
- // would otherwise block the (synchronous) event loop past the cap forever.
96102
- // 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.
96103
96071
  killSignal: "SIGKILL",
96104
- // Cap captured stdout (same limit as the async variant): a binary streaming
96105
- // output is SIGKILL'd at this cap rather than filling Node's 1 MiB default.
96106
96072
  maxBuffer: FINGERPRINT_MAX_BUFFER,
96107
- // Ignore stderr so a binary that logs diagnostics doesn't pollute the
96108
- // 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.
96109
96074
  stdio: ["ignore", "pipe", "ignore"]
96110
96075
  }).trim();
96111
96076
  return out.length > 0 ? out : null;
@@ -96754,11 +96719,10 @@ function describeVegaFailure(args, err, kindOverride) {
96754
96719
  error_code: FAILURE_CODES.VEGA_CLI_COMMAND_FAILED,
96755
96720
  failure_stage: "vega_cli_command",
96756
96721
  failure_area: "tool_server",
96757
- // A timeout/overflow reap shapes the error with killed=true so the message reads
96758
- // correctly, but only a genuine *timeout* should classify as `error_kind:
96759
- // "timeout"` — listVegaDevices keys its skip-the-recovery-call decision off that,
96760
- // so an overflow (or other forced kill) must NOT masquerade as a wedged-agent
96761
- // 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.
96762
96726
  error_kind: kindOverride ?? (e.killed || e.signal ? "timeout" : "subprocess"),
96763
96727
  ...subprocessFailureMetadata(err, "vega")
96764
96728
  };
@@ -96803,7 +96767,7 @@ async function runVega(args, options = {}) {
96803
96767
  };
96804
96768
  const rejectTimeout = () => (
96805
96769
  // Shape like an execFile timeout rejection (killed=true) so it classifies as a
96806
- // timeout downstream — listVegaDevices keys its skip-the-recovery decision off it.
96770
+ // timeout downstream.
96807
96771
  settle(
96808
96772
  () => reject(
96809
96773
  describeVegaFailure(
@@ -96826,9 +96790,9 @@ async function runVega(args, options = {}) {
96826
96790
  new Error(`vega ${args.join(" ")} output exceeded ${maxOutputBytes} bytes`),
96827
96791
  { killed: true, signal: VEGA_KILL_SIGNAL, stdout, stderr }
96828
96792
  ),
96829
- // Force "subprocess": an overflow is a misbehaving child, not a wedged
96830
- // agent. Without this the killed=true shape would classify as "timeout"
96831
- // 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.
96832
96796
  "subprocess"
96833
96797
  )
96834
96798
  )
@@ -97455,7 +97419,6 @@ async function run(args, options) {
97455
97419
  timeout: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
97456
97420
  maxBuffer: 16 * 1024 * 1024,
97457
97421
  encoding: "utf8",
97458
- // sim-remote pipes stdin through to pbcopy etc.
97459
97422
  input: options?.stdin
97460
97423
  });
97461
97424
  return { stdout: typeof stdout === "string" ? stdout : stdout.toString("utf8") };
@@ -97747,9 +97710,9 @@ async function readProcessLaunchState(pid) {
97747
97710
  ({ stdout } = await execFileAsync8(PS_BIN, ["eww", "-p", String(pid), "-o", "etime=,command="], {
97748
97711
  encoding: "utf8",
97749
97712
  timeout: PS_PROBE_TIMEOUT_MS,
97750
- // 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
97751
97714
  // to `kern.argmax` (1 MiB), exactly Node's default cap, so the default
97752
- // would ENOBUFS on a maximal one instead of reading it.
97715
+ // would ENOBUFS on a maximal one.
97753
97716
  maxBuffer: 16 * 1024 * 1024
97754
97717
  }));
97755
97718
  } catch (err) {
@@ -97845,17 +97808,16 @@ var remoteIosHost = {
97845
97808
  const { stdout } = await simctlSpawn(udid, { args: ["launchctl", "list"] });
97846
97809
  return parseUIKitApplicationBundleIds(stdout);
97847
97810
  },
97848
- // App processes live on the orchestrator, so the local process table says
97849
- // nothing about how they were launched. Only running-ness is answerable; the
97850
- // 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.
97851
97814
  async inspectRunningApp(udid, bundleId) {
97852
97815
  const { stdout } = await simctlSpawn(udid, { args: ["launchctl", "list"] });
97853
97816
  return { running: parseUIKitApplicationJobs(stdout).has(bundleId), process: null };
97854
97817
  },
97855
- // Apply the accessibility defaults the tool-server needs (the local host does
97856
- // this via `defaults write`; here we run the same writes through the remote
97857
- // generic spawn). The entitlement-bypass plist is assumed active on cloud
97858
- // 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.
97859
97821
  async bootstrapAx(udid) {
97860
97822
  for (const flag of ACCESSIBILITY_DEFAULT_FLAGS) {
97861
97823
  await simctlSpawn(udid, {
@@ -98266,13 +98228,11 @@ var NotImplementedOnPlatformError = class extends Error {
98266
98228
  };
98267
98229
  var InvalidToolInputError = class extends Error {
98268
98230
  /**
98269
- * @param signal Optional telemetry-signal overrides. The HTTP 400 mapping keys
98270
- * off the error *class* (see http.ts), not the `error_code`, so a caller can
98271
- * pass a more granular `error_code` / `failure_stage` / `error_kind` — e.g.
98272
- * the keyboard backends' `KEYBOARD_KEY_UNSUPPORTED` /
98273
- * `KEYBOARD_CHARACTER_UNSUPPORTED` / `VEGA_TEXT_INVALID` classifications
98274
- * (from #420) — and keep both the granular telemetry bucket AND the 400
98275
- * 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.
98276
98236
  */
98277
98237
  constructor(message, signal) {
98278
98238
  super(message);
@@ -108489,21 +108449,13 @@ var simulatorServerBlueprint = {
108489
108449
  return `${SIMULATOR_SERVER_NAMESPACE}:${device.id}`;
108490
108450
  },
108491
108451
  /**
108492
- * A cached simulator-server handle can outlive the thing it points at: when a
108493
- * simulator is un-booted (or the native server crashes) the child process may
108494
- * stay alive but stop listening on its API port, so every subsequent request
108495
- * fails with `ECONNREFUSED` against the now-dead port — the exact "worked once,
108496
- * then every call times out" symptom, unrecoverable until a human runs
108497
- * `stop-simulator-server`. The process never emitting `exit` means the
108498
- * registry's normal teardown (wired to `proc.on("exit")`) never fires.
108499
- *
108500
- * Treat a connection-refused failure as proof the instance is dead so the
108501
- * registry disposes it (killing the wedged process) and re-spawns a fresh
108502
- * simulator-server on the next call. Scoped to connection-refused only:
108503
- * ECONNREFUSED means the request never reached the server, so retrying can't
108504
- * double-apply a gesture. Timeouts and resets are deliberately excluded — the
108505
- * request there may have taken effect, and a hung-but-listening server is a
108506
- * 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.
108507
108459
  */
108508
108460
  recoverable(error52) {
108509
108461
  return getFailureSignal(error52)?.network_failure === "connection_refused";
@@ -108782,17 +108734,10 @@ var CDPClient = class {
108782
108734
  });
108783
108735
  }
108784
108736
  /**
108785
- * Re-point this client at a different CDP WebSocket target (e.g. switching
108786
- * the active browser tab) WITHOUT emitting `disconnected`.
108787
- *
108788
- * Object identity is preserved, so existing references to this client
108789
- * (`server.cdp`, `api.cdp`, and every closure that captured it) automatically
108790
- * target the new tab after the swap — no rewiring needed. Callers that wire
108791
- * `disconnected` to teardown/termination therefore do not see a tab switch as
108792
- * a device loss.
108793
- *
108794
- * In-flight requests are rejected and per-connection state (enabled domains,
108795
- * 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
108796
108741
  * re-enable any domains it needs.
108797
108742
  */
108798
108743
  async reconnect(newWsUrl) {
@@ -108828,10 +108773,9 @@ var CDPClient = class {
108828
108773
  const timer = setTimeout(() => {
108829
108774
  this.pending.delete(id);
108830
108775
  reject(
108831
- // The message carries its own recovery guidance so skills don't have
108832
- // to re-explain this state: the runtime is reachable but not
108833
- // answering, which agents otherwise read as a transient worth
108834
- // 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.
108835
108779
  new FailureError(
108836
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.`,
108837
108781
  {
@@ -108878,8 +108822,8 @@ var CDPClient = class {
108878
108822
  this.bindingUnavailable = probe3 === "undefined";
108879
108823
  }
108880
108824
  /**
108881
- * Inject a script that will push a result via the binding using a unique requestId.
108882
- * 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.
108883
108827
  */
108884
108828
  evaluateWithBinding(expression, requestId, options) {
108885
108829
  const id = requestId ?? crypto3.randomUUID();
@@ -109077,9 +109021,8 @@ async function browserWebSocketUrl(port, signal) {
109077
109021
  throw new FailureError(
109078
109022
  `Chromium CDP on port ${port} did not report a browser webSocketDebuggerUrl in /json/version.`,
109079
109023
  {
109080
- // The endpoint responded but its payload was incomplete — a malformed
109081
- // response, not an unreachable port. Distinct code so telemetry doesn't
109082
- // 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.
109083
109026
  error_code: FAILURE_CODES.CHROMIUM_CDP_INVALID_RESPONSE,
109084
109027
  failure_stage: "chromium_cdp_browser_ws",
109085
109028
  failure_area: "tool_server",
@@ -109434,19 +109377,14 @@ var ScreencastManager = class {
109434
109377
  fps;
109435
109378
  activeCount = 0;
109436
109379
  currentOpts = null;
109437
- // The in-flight Page.startScreencast promise for the first subscriber, shared
109438
- // so concurrent joiners await the SAME start instead of assuming a live
109439
- // session (and stranding themselves on a frame-less stream if it fails). Null
109440
- // 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.
109441
109383
  startInFlight = null;
109442
109384
  lastFrame = null;
109443
109385
  cdpListenerInstalled = false;
109444
- /**
109445
- * Most-recently-received frame, or null if no screencast is active /
109446
- * Chromium hasn't pushed a frame yet. Exposed so single-shot consumers
109447
- * (preview overlay, snapshot debug tool) can grab the last frame without
109448
- * starting their own session.
109449
- */
109386
+ /** Never cleared on stop, so single-shot consumers can read a frame without
109387
+ * starting their own session. */
109450
109388
  getLastFrame() {
109451
109389
  return this.lastFrame;
109452
109390
  }
@@ -109574,7 +109512,7 @@ function warnSharpMissingOnce(reason) {
109574
109512
  var DOWNSCALER_TO_KERNEL = {
109575
109513
  lanczos3: "lanczos3",
109576
109514
  box: "mitchell",
109577
- // sharp doesn't expose a true box kernel; mitchell is the closest fast alternative
109515
+ // sharp has no box kernel
109578
109516
  bilinear: "lanczos2",
109579
109517
  nearest: "nearest"
109580
109518
  };
@@ -109789,12 +109727,8 @@ function createTabsManager(deps) {
109789
109727
  error_code: FAILURE_CODES.CHROMIUM_TAB_OPEN_FAILED,
109790
109728
  failure_stage: "chromium_tab_open",
109791
109729
  failure_area: "tool_server",
109792
- // The CDP command round-tripped but its response was missing the
109793
- // expected targetId — a malformed payload from a source we don't own,
109794
- // classified `unknown` like the other CDP-command failures in this
109795
- // server (CHROMIUM_SCREENSHOT_FAILED, the viewport/storage evals).
109796
- // `network`/`invalid_response` is reserved for the HTTP /json
109797
- // 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.
109798
109732
  error_kind: "unknown"
109799
109733
  });
109800
109734
  return out.targetId;
@@ -109888,9 +109822,8 @@ function createNetworkManager(deps) {
109888
109822
  rec.resourceType = params.type ?? rec.resourceType;
109889
109823
  break;
109890
109824
  }
109891
- // The *ExtraInfo events carry the actual on-the-wire headers (Authorization,
109892
- // Set-Cookie, …) that the base events omit. Merge them in; they may arrive
109893
- // 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()`.
109894
109827
  case "Network.requestWillBeSentExtraInfo": {
109895
109828
  const h = params.headers;
109896
109829
  if (h) {
@@ -110557,11 +110490,9 @@ Booted/ready devices are listed first. Platforms whose CLI is unavailable are si
110557
110490
  withDeadline(listIosSimulators(), [], "ios"),
110558
110491
  withDeadline(listRemoteIosSimulators(), [], "ios-remote"),
110559
110492
  withDeadline(
110560
- // Opt into runtimeKind enrichment (list-devices surfaces TV vs mobile to
110561
- // the agent, so the extra feature probe per device is warranted here — the
110562
- // boot-loop poller deliberately omits it), and pass the tight `adb devices`
110563
- // bound (NOT boot-device's 30s default) so the Android branch self-bounds
110564
- // 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.
110565
110496
  listAndroidDevices({ runtimeKind: true, devicesTimeoutMs: ADB_DEVICES_TIMEOUT_MS }).catch(
110566
110497
  () => []
110567
110498
  ),
@@ -110620,39 +110551,33 @@ var VariantProposalStore = class {
110620
110551
  consumed = false;
110621
110552
  globalComment = "";
110622
110553
  /**
110623
- * Device the variants target (last non-empty udid an agent passed to
110624
- * `propose_variant`). Persists across rounds — the agent works on one device
110625
- * — 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.
110626
110556
  */
110627
110557
  device = null;
110628
110558
  /**
110629
- * True while an `argent lens` CLI session owns the window. Set via
110630
- * `setCliSession`; deliberately NOT cleared by `reset()` — a CLI session spans
110631
- * 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.
110632
110561
  */
110633
110562
  cliSession = false;
110634
110563
  /**
110635
110564
  * The agent choices a CLI Lens session offers (when more than one is
110636
- * installed) and the one the human picked in the window. The `argent lens`
110637
- * bridge passes the choices in on begin and polls `lensAgentChoice` to learn
110638
- * which agent to spawn. Empty / null outside an unresolved pick. Like
110639
- * `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()`.
110640
110568
  */
110641
110569
  lensAgents = [];
110642
110570
  lensAgentChoice = null;
110643
110571
  /**
110644
- * Whether the human asked to REMEMBER the agent pick (the picker's "Remember
110645
- * this choice" checkbox). The `argent lens` process reads this alongside the
110646
- * choice and persists it to `~/.argent/config.json` so later runs skip the
110647
- * 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.
110648
110575
  */
110649
110576
  lensAgentRemember = false;
110650
110577
  /**
110651
- * Devices (iOS udid / Android serial) that Lens BOOTED itself — i.e. the
110652
- * `POST /preview/boot` route started them because they were not already
110653
- * running. Tracked so the tool-server can shut them down when the CLI Lens
110654
- * session ends (`takeOwnedDevices`), without ever touching a device the user
110655
- * 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()`.
110656
110581
  */
110657
110582
  ownedDevices = /* @__PURE__ */ new Set();
110658
110583
  submitted = [];
@@ -110663,15 +110588,12 @@ var VariantProposalStore = class {
110663
110588
  /** Frozen result of the current round once the user submits. */
110664
110589
  lastOutcome = null;
110665
110590
  /**
110666
- * Completed outcomes that finished with no `await_user_selection` parked to
110667
- * receive them directly (submitSelection's "no waiter" branch), queued in
110668
- * completion order. `autoRollIfCompleted` rolls into a fresh round on the
110669
- * very next `propose_variant` regardless of whether the outcome was ever
110670
- * retrieved — so this queue, unlike `lastOutcome`/`completed`/`consumed`
110671
- * (which describe only the CURRENT round), survives `reset()` and is
110672
- * drained first by `awaitSelection`, oldest first. Without it, a human's
110673
- * already-submitted selection is destroyed the moment the next
110674
- * `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.
110675
110597
  */
110676
110598
  pendingOutcomes = [];
110677
110599
  /** Begin a fresh round, discarding the previous one's proposals/selections. */
@@ -110707,15 +110629,12 @@ var VariantProposalStore = class {
110707
110629
  this.events.emit("changed");
110708
110630
  }
110709
110631
  /**
110710
- * Emit the drop-off signal for a round still staged (proposals present, not
110711
- * completed) at process shutdown — "the server died mid-review". The
110712
- * tool-server calls this on its shutdown path, before the final telemetry
110713
- * drain, so a round the human never got to submit is counted as an abandonment
110714
- * instead of vanishing into the unattributable opens-minus-completions
110715
- * residue. Routes through reset() — the single abandonment choke point, guarded
110716
- * so a completed or empty round is a no-op — which also settles any await still
110717
- * parked on the dying round rather than letting it hang. Returns whether an
110718
- * 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.
110719
110638
  */
110720
110639
  flushAbandonedRound() {
110721
110640
  if (this.completed || this.proposals.length === 0) return false;
@@ -110728,13 +110647,12 @@ var VariantProposalStore = class {
110728
110647
  return p?.variants.find((v) => v.id === variantId) ?? null;
110729
110648
  }
110730
110649
  /**
110731
- * Called when the native preview window could not be launched (e.g. the
110732
- * optional `electron` dependency is absent on a headless/CI host). Settles
110733
- * every currently-parked, unsettled waiter with a `pending` outcome whose
110734
- * message points the agent at the browser fallback URL, rather than letting
110735
- * the await park for the full timeout with no window and no feedback. The
110736
- * proposals stay live, so the agent can relay the URL and re-await. No-ops
110737
- * 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.
110738
110656
  */
110739
110657
  notifyWindowUnavailable(reason, url2) {
110740
110658
  const toSettle = this.waitersList.filter((w) => !w.settled);
@@ -110815,10 +110733,10 @@ var VariantProposalStore = class {
110815
110733
  }
110816
110734
  /**
110817
110735
  * Begin or end a CLI-driven Lens session (`argent lens`), optionally offering
110818
- * a set of agent choices for the window's picker. `cliSessionChanged` fires
110819
- * only on an actual begin/end transition (so the window opens/closes once),
110820
- * but the agent choices are always refreshed on a begin call — a re-begin from
110821
- * 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.
110822
110740
  */
110823
110741
  setCliSession(active, agents = []) {
110824
110742
  const transitioned = this.cliSession !== active;
@@ -110836,8 +110754,7 @@ var VariantProposalStore = class {
110836
110754
  }
110837
110755
  this.events.emit("changed");
110838
110756
  }
110839
- /** Record the agent the human picked in the window's picker, and whether they
110840
- * asked to remember it. */
110757
+ /** Record the agent the human picked, and whether they asked to remember it. */
110841
110758
  setLensAgentChoice(id, remember = false) {
110842
110759
  this.lensAgentChoice = id;
110843
110760
  this.lensAgentRemember = remember;
@@ -110860,18 +110777,16 @@ var VariantProposalStore = class {
110860
110777
  if (id.trim()) this.ownedDevices.add(id.trim());
110861
110778
  }
110862
110779
  /**
110863
- * Whether Lens booted this device itself (and is therefore responsible for
110864
- * it). Test-only accessor: production code manages ownership through
110865
- * `markDeviceOwned` / `releaseDevice` / `takeOwnedDevices` and never needs to
110866
- * read a single device's ownership. Kept as a non-mutating way for tests to
110867
- * 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.
110868
110783
  */
110869
110784
  isDeviceOwned(id) {
110870
110785
  return this.ownedDevices.has(id.trim());
110871
110786
  }
110872
110787
  /**
110873
- * Drop a single owned device — e.g. the user shut it down manually via the
110874
- * 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.
110875
110790
  */
110876
110791
  releaseDevice(id) {
110877
110792
  this.ownedDevices.delete(id.trim());
@@ -110884,22 +110799,21 @@ var VariantProposalStore = class {
110884
110799
  }
110885
110800
  /**
110886
110801
  * The frozen outcome of the last submitted round, or null if nothing has been
110887
- * submitted since the last reset. Read by `GET /preview/outcome` so the
110888
- * `argent lens` watcher can format the user's feedback and type it into the
110889
- * 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.
110890
110805
  */
110891
110806
  getLastOutcome() {
110892
110807
  return this.lastOutcome;
110893
110808
  }
110894
- /** Called by the preview UI when the human presses "Complete selection". */
110809
+ /** Called by the preview UI when the human submits their picks. */
110895
110810
  submitSelection(input) {
110896
110811
  if (typeof input.round === "number" && input.round !== this.round) {
110897
110812
  return { ok: true, round: this.round, resolved: 0, stale: true };
110898
110813
  }
110899
110814
  const cleanAnnotations = (input.annotations ?? []).filter((a) => a && typeof a.comment === "string" && a.comment.trim()).slice(0, MAX_ANNOTATIONS).map((a) => ({
110900
110815
  target: String(a.target ?? "").slice(0, 200) || "(element)",
110901
- // Cap the matcher value too — it is caller-supplied and, unlike the
110902
- // comment, was previously ingested uncapped.
110816
+ // Caller-supplied and, unlike the comment, otherwise ingested uncapped.
110903
110817
  match: {
110904
110818
  by: a.match?.by ?? "text",
110905
110819
  value: String(a.match?.value ?? "").slice(0, MAX_MATCH_VALUE_LENGTH)
@@ -111015,14 +110929,12 @@ var VariantProposalStore = class {
111015
110929
  };
111016
110930
  }
111017
110931
  /**
111018
- * Block until the user submits a selection for the current round.
111019
- *
111020
- * Resolves immediately if a selection is already waiting to be consumed.
111021
- * On `timeoutMs` elapse returns a `pending` outcome (so the agent — or the
111022
- * MCP client wrapping it — can re-await without losing the live proposals).
111023
- * Honors `signal`: a client disconnect rejects with an AbortError. Every
111024
- * await parked on a round is resolved when that round is submitted (or the
111025
- * 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.
111026
110938
  */
111027
110939
  awaitSelection(opts) {
111028
110940
  if (opts.signal?.aborted) {
@@ -112074,9 +111986,8 @@ function deriveUiAutomatorRole(className) {
112074
111986
  return short || "View";
112075
111987
  }
112076
111988
  var NOISY_CLASSES = /* @__PURE__ */ new Set([
112077
- // React Native Skia/SVG icon internals: never tappable, parent already
112078
- // carries the icon's content-desc, and a single icon can balloon a dump by
112079
- // 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.
112080
111991
  "com.horcrux.svg.PathView",
112081
111992
  "com.horcrux.svg.GroupView",
112082
111993
  "com.horcrux.svg.SvgView"
@@ -112085,10 +111996,8 @@ function isNoisyUiAutomatorClass(className) {
112085
111996
  return NOISY_CLASSES.has(className);
112086
111997
  }
112087
111998
  var SYSTEM_PACKAGES = /* @__PURE__ */ new Set([
112088
- // Status bar / nav bar / quick settings — these exist on every dump but
112089
- // rarely matter for app-level navigation. Note we deliberately do NOT drop
112090
- // the foreground-app's own package even when the foreground IS a system app
112091
- // (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.
112092
112001
  "com.android.systemui"
112093
112002
  ]);
112094
112003
  var SYSTEM_RID_PREFIXES = [
@@ -112103,8 +112012,8 @@ var LAYOUT_CONTAINERS = /* @__PURE__ */ new Set([
112103
112012
  "androidx.constraintlayout.widget.ConstraintLayout",
112104
112013
  "androidx.coordinatorlayout.widget.CoordinatorLayout",
112105
112014
  "android.view.ViewGroup",
112106
- // Bare android.view.View is what Compose emits when a semantics node has
112107
- // 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.
112108
112017
  "android.view.View"
112109
112018
  ]);
112110
112019
  function isUiAutomatorLayoutContainer(className) {
@@ -112442,8 +112351,7 @@ function resolveTraceProcessorAssets() {
112442
112351
  }
112443
112352
  function patchGlueForNode(src) {
112444
112353
  const edits = [
112445
- // Force memory32: skip the memory64 feature probe entirely (avoids the
112446
- // memory64 __syscall_mprotect path; no global WebAssembly wrapper).
112354
+ // Force memory32: skip the memory64 probe and its __syscall_mprotect path.
112447
112355
  ["this.useMemory64 = hasMemory64Support();", "this.useMemory64 = false;"],
112448
112356
  // Hand the wasm bytes straight to emscripten -> it never calls readBinary/XHR.
112449
112357
  ["locateFile: (s) => s,", "locateFile: (s) => s, wasmBinary: globalThis.__TP_WASM_BYTES,"],
@@ -113222,10 +113130,8 @@ async function describeAndroid(registry2, serial, _bundleId, isTv) {
113222
113130
  throw new FailureError(
113223
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.`,
113224
113132
  {
113225
- // The adb wrapper exits 0, but the uiautomator tool it ran reported an
113226
- // in-band `ERROR:` line — a functional failure of the uiautomator
113227
- // subprocess. Classified `subprocess` to match the sibling
113228
- // 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.
113229
113135
  error_code: FAILURE_CODES.ANDROID_UIAUTOMATOR_CAPTURE_FAILED,
113230
113136
  failure_stage: "android_uiautomator_capture",
113231
113137
  failure_area: "tool_server",
@@ -113270,10 +113176,9 @@ function createPreviewRouter(registry2) {
113270
113176
  element_count: snap.proposals.length,
113271
113177
  variant_count: snap.proposals.reduce((n, p) => n + p.variants.length, 0),
113272
113178
  is_cli_session: snap.cliSession,
113273
- // Report platform ONLY for a round that actually staged proposals: the
113274
- // store's `device` deliberately survives reset(), so a CLI up-front open
113275
- // (element_count 0, no device bound THIS round) would otherwise inherit a
113276
- // 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.
113277
113182
  platform: snap.proposals.length > 0 && snap.device ? classifyDeviceForTelemetry(snap.device) : void 0
113278
113183
  });
113279
113184
  };
@@ -113539,16 +113444,16 @@ data: ${JSON.stringify(data)}
113539
113444
  }
113540
113445
  try {
113541
113446
  const result = variantProposalStore.submitSelection({
113542
- // The round the UI built this submit against (if it sent one). The store
113543
- // rejects it as stale when the round has since rolled, so a click from a
113544
- // 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.
113545
113450
  round: typeof body.round === "number" ? body.round : void 0,
113546
113451
  selections,
113547
113452
  annotations,
113548
113453
  globalComment: typeof body.globalComment === "string" ? body.globalComment : void 0,
113549
- // Privacy-safe UI usage signals for `lens:round_completed`. Coerced to
113550
- // strict booleans so a malformed/absent field from the unauthenticated
113551
- // 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.
113552
113457
  inspectorUsed: body.inspectorUsed === true,
113553
113458
  offscreenRevealed: body.offscreenRevealed === true
113554
113459
  });
@@ -115065,7 +114970,7 @@ var SourceMapsRegistry = class {
115065
114970
  }
115066
114971
  /**
115067
114972
  * Begin fetching and registering a source map from a Debugger.scriptParsed event.
115068
- * Returns immediately; use `waitForPending()` to block until all maps are loaded.
114973
+ * Returns immediately; `waitForPending()` blocks until all maps are loaded.
115069
114974
  */
115070
114975
  registerFromScriptParsed(scriptUrl, scriptId, sourceMapURL) {
115071
114976
  if (!sourceMapURL) return;
@@ -115079,10 +114984,8 @@ var SourceMapsRegistry = class {
115079
114984
  /**
115080
114985
  * Resolve an original source file + line to its generated position in the bundle.
115081
114986
  *
115082
- * `filePath` can be:
115083
- * - relative to project root, e.g. "App.tsx" or "src/components/Foo.tsx"
115084
- * - absolute, e.g. "/Users/.../App.tsx"
115085
- * - aliased, e.g. "/[metro-project]/App.tsx"
114987
+ * `filePath` may be project-relative ("src/Foo.tsx"), absolute, or aliased
114988
+ * ("/[metro-project]/App.tsx").
115086
114989
  */
115087
114990
  toGeneratedPosition(filePath, line1Based, column0Based = 0) {
115088
114991
  const candidates = this.buildSourceCandidates(filePath);
@@ -115110,10 +115013,7 @@ var SourceMapsRegistry = class {
115110
115013
  }
115111
115014
  return null;
115112
115015
  }
115113
- /**
115114
- * Find which source map source path matches the given file path.
115115
- * Returns the matched source string or null.
115116
- */
115016
+ /** Matching entry in a registered map's `sources`, or null. */
115117
115017
  findMatchingSource(filePath) {
115118
115018
  const candidates = this.buildSourceCandidates(filePath);
115119
115019
  for (const map2 of this.maps) {
@@ -115456,22 +115356,12 @@ var chromiumJsRuntimeDebuggerBlueprint = {
115456
115356
  getDependencies(_payload) {
115457
115357
  return { chromium: `${CHROMIUM_CDP_NAMESPACE}:${_payload}` };
115458
115358
  },
115459
- // Deliberately NO recoverable() here. The registry's self-heal disposes the
115460
- // recovering node before it retries, and this blueprint's dispose closes the
115461
- // LogFileWriter — which UNLINKS the session's captured console log from disk
115462
- // (log-file-writer.ts) — so a recovery pass would destroy the logs whether or
115463
- // not the retry then succeeds. It would also buy nothing: the one window
115464
- // where a call fails while this node and its ChromiumCdp dependency stay
115465
- // RUNNING is a tab switch, where CDPClient.reconnect() rejects in-flight
115466
- // requests with CONNECTION_CLOSED (late sends with NOT_CONNECTED) but
115467
- // re-points the SAME client object at the new tab — the cached node heals
115468
- // itself for the next call without any dispose. The failing call surfaces a
115469
- // classified error that debugger-status maps to a structured "reconnecting"
115470
- // result with retry-once guidance. A genuinely dead Chromium socket instead
115471
- // fires ChromiumCdp's own terminated event, whose teardown cascades into this
115472
- // dependent — the node leaves RUNNING and the next call re-resolves fresh.
115473
- // (Same log-preservation reasoning as debugger-log-registry's missing socket
115474
- // 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.
115475
115365
  async factory(deps, payload, options) {
115476
115366
  const opts = options;
115477
115367
  const device = opts?.device;
@@ -115527,14 +115417,13 @@ var chromiumJsRuntimeDebuggerBlueprint = {
115527
115417
  const sourceResolver = makeStubSourceResolver();
115528
115418
  const api = {
115529
115419
  port,
115530
- // Chromium apps have no Metro project root. Empty string keeps the
115531
- // contract type-clean; callers that care (only inspect-element via the
115532
- // 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.
115533
115422
  projectRoot: "",
115534
115423
  deviceName: device.name ?? "Chromium",
115535
115424
  appName: "Chromium",
115536
115425
  logicalDeviceId: device.id,
115537
- // Chromium always speaks the new CDP — there is no Hermes-legacy mode.
115426
+ // No legacy RN inspector on Chromium.
115538
115427
  isNewDebugger: true,
115539
115428
  cdp,
115540
115429
  sourceResolver,
@@ -115913,14 +115802,11 @@ var ANDROID_NAMED_KEYCODES = {
115913
115802
  "esc": 111,
115914
115803
  // alias of escape
115915
115804
  "backspace": 67,
115916
- // KEYCODE_DEL (backspace: deletes the char before the cursor)
115917
- // `delete` aliases backspace, not forward-delete: the shared HID vocabulary in
115918
- // key-codes.ts (NAMED_KEYS) maps both `backspace` and `delete` to usage 42
115919
- // (Keyboard DELETE/Backspace), so iOS types `delete` as a backspace. A named
115920
- // key must mean the same thing on every platform, so map it to KEYCODE_DEL (67)
115921
- // 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.
115922
115809
  "delete": 67,
115923
- // KEYCODE_DEL (alias of backspace — see note above)
115924
115810
  "tab": 61,
115925
115811
  // KEYCODE_TAB
115926
115812
  "space": 62,
@@ -115933,7 +115819,7 @@ var ANDROID_NAMED_KEYCODES = {
115933
115819
  // KEYCODE_DPAD_LEFT
115934
115820
  "arrow-right": 22,
115935
115821
  // KEYCODE_DPAD_RIGHT
115936
- // F1..F12 are KEYCODE_F1 (131) .. KEYCODE_F12 (142), contiguous.
115822
+ // KEYCODE_F1 (131) .. KEYCODE_F12 (142) are contiguous.
115937
115823
  ...Object.fromEntries(Array.from({ length: 12 }, (_, i) => [`f${i + 1}`, 131 + i]))
115938
115824
  };
115939
115825
  var ANDROID_BUTTON_KEYCODES = {
@@ -115953,9 +115839,8 @@ var ANDROID_BUTTON_KEYCODES = {
115953
115839
  function assertTypeableAndroidText(text) {
115954
115840
  if (/[\n\r]/.test(text)) {
115955
115841
  throw new InvalidToolInputError(
115956
- // Advice must hold on every path sharing this guard: named keys work on
115957
- // phones/tablets but are rejected on a TV target (typeTv), where the
115958
- // 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.
115959
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)',
115960
115845
  {
115961
115846
  error_code: FAILURE_CODES.KEYBOARD_CHARACTER_UNSUPPORTED,
@@ -116181,15 +116066,13 @@ var androidTvControlBlueprint = {
116181
116066
  await injectAndroidText(serial, words[i]);
116182
116067
  }
116183
116068
  },
116184
- // Android TV reads the live hierarchy on every describe (no cached
116185
- // 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.
116186
116070
  async recycleAx() {
116187
116071
  }
116188
116072
  };
116189
116073
  const instance = {
116190
116074
  api,
116191
- // Stateless: every method is a fresh adb shell-out, so there is nothing to
116192
- // tear down. Present to satisfy the ServiceInstance contract.
116075
+ // Stateless: every call is a fresh adb shell-out, so nothing to tear down.
116193
116076
  dispose: async () => {
116194
116077
  },
116195
116078
  events
@@ -116216,9 +116099,8 @@ var nativeDevtoolsStatusTool = {
116216
116099
  },
116217
116100
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
116218
116101
  // The "injectable is false" recovery sentence inlines NON_INJECTABLE_RECOVERY
116219
- // verbatim: the description must stay a plain literal so scripts/extract-tools.mjs
116220
- // can read it statically for the spidershield scan. The verbatim match is pinned
116221
- // 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.
116222
116104
  description: `Check whether native devtools are connected to a specific app and whether the next launch is prepared for injection.
116223
116105
  Use when you need to verify native devtools readiness before calling native-full-hierarchy, native-describe-screen, or native-network-logs.
116224
116106
 
@@ -116297,13 +116179,13 @@ Fails if the simulator server is not running for the given UDID.`,
116297
116179
  // fresh process fixes — `indeterminate` among them, since an uninspectable
116298
116180
  // host (ios-remote) supports no finer reading. Both already carry a live
116299
116181
  // process (the settling above rewrote an empty `indeterminate` to
116300
- // `not_running`), so an `appRunning` conjunct would only restate the state.
116182
+ // `not_running`), so an `appRunning` conjunct would restate the state.
116301
116183
  requiresRestart: state3 === "stale_process" || state3 === "indeterminate",
116302
116184
  state: state3,
116303
116185
  // The booleans cannot express "one restart, then stop" — the shape
116304
116186
  // `indeterminate` needs, and the only one ios-remote can report for a
116305
- // running app. Carrying the same prose as every other consumer keeps that
116306
- // 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.
116307
116189
  ...state3 === "connected" ? {} : { message: buildAppStateMessage(params.bundleId, state3) },
116308
116190
  nextLaunchWillBeInjected: envSetup,
116309
116191
  injectable: true
@@ -117181,22 +117063,11 @@ var jsRuntimeDebuggerBlueprint = {
117181
117063
  getURN(payload) {
117182
117064
  return `${JS_RUNTIME_DEBUGGER_NAMESPACE}:${payload}`;
117183
117065
  },
117184
- // Consulted by the registry's dispose-and-retry-once self-heal, and only for
117185
- // a node still in RUNNING state. On this blueprint a detected socket death
117186
- // tears the node down via the terminated cascade before the failing call's
117187
- // catch runs, so the only recoverable window is the send() guard rejecting
117188
- // while the WebSocket is CLOSING but the close event has not dispatched yet —
117189
- // there the request provably never left the host, making a retry safe.
117190
- // Deliberately NOT recoverable:
117191
- // - DEBUGGER_CDP_CONNECTION_CLOSED: the request was delivered and may have
117192
- // taken effect (double-execution risk); on this path the node has also
117193
- // already left RUNNING when it fires.
117194
- // - DEBUGGER_CDP_REQUEST_TIMEOUT: the request may have taken effect, and a
117195
- // hung-but-open runtime (e.g. paused at a breakpoint) is not fixed by
117196
- // reconnecting.
117197
- // - Metro discovery / target-selection codes: init-path failures — the node
117198
- // never reaches RUNNING, so recovery is never consulted, and a retry would
117199
- // 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.
117200
117071
  recoverable(error52) {
117201
117072
  return getFailureSignal(error52)?.error_code === FAILURE_CODES.DEBUGGER_CDP_NOT_CONNECTED;
117202
117073
  },
@@ -118367,11 +118238,8 @@ async function waitForAdbDevice(serial, timeoutMs) {
118367
118238
  error_code: FAILURE_CODES.VEGA_DEVICE_NOT_REGISTERED,
118368
118239
  failure_stage: "vega_adb_register",
118369
118240
  failure_area: "tool_server",
118370
- // We polled until the deadline and adb never enumerated the transport,
118371
- // so the failure is structurally a timeout. The distinct error_code
118372
- // (vs VEGA_BOOT_TIMEOUT, the VVD never starting) already carries the
118373
- // "registered vs started" distinction — error_kind reflects the true
118374
- // 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).
118375
118243
  error_kind: "timeout"
118376
118244
  }
118377
118245
  );
@@ -118671,11 +118539,9 @@ async function bootElectronApp(options) {
118671
118539
  child = (0, import_node_child_process17.spawn)(launcher.command, args, {
118672
118540
  detached: true,
118673
118541
  stdio: ["ignore", "pipe", "pipe"],
118674
- // Strip ELECTRON_RUN_AS_NODE (see electronGuiChildEnv): if the tool-server
118675
- // inherited it from an Electron-based MCP host, the Electron binary would
118676
- // run in Node mode with no CDP endpoint — so boot-device fails below (the
118677
- // child exits early, or the readiness probe times out) instead of the app
118678
- // 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.
118679
118545
  env: electronGuiChildEnv({ ELECTRON_ENABLE_LOGGING: "1" })
118680
118546
  });
118681
118547
  } catch (err) {
@@ -118838,11 +118704,9 @@ var STAGE_BUDGET = {
118838
118704
  pmReady: 45e3,
118839
118705
  // pm path android answers (retried; non-fatal on the final attempt)
118840
118706
  firstRealFrame: 9e4,
118841
- // screencap returns ≥1 non-zero pixel after cold boot
118842
118707
  firstRealFrameHot: 8e3
118843
- // tighter budget for snapshot-restore composite —
118844
- // the broken state is sticky (per assertScreencapAlive's docstring), so a
118845
- // 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.
118846
118710
  };
118847
118711
  var VALID_GPU_MODES = /* @__PURE__ */ new Set([
118848
118712
  "auto",
@@ -119361,14 +119225,13 @@ async function bootAndroidImpl(params) {
119361
119225
  emulatorArgs: hotArgs,
119362
119226
  attemptDeadline: hotAttemptDeadline,
119363
119227
  serialsBefore,
119364
- // Snapshot restores register with adb within a couple of seconds;
119365
- // a minute-long register wait on the hot path would mask the
119366
- // 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.
119367
119230
  adbRegisterBudgetMs: 3e4,
119368
119231
  deviceReadyBudgetMs: 3e4,
119369
119232
  bootCompletedBudgetMs: 3e4,
119370
- // Keep the hot path tight: a single ~10 s PM window, and tear down on
119371
- // 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.
119372
119235
  pmProbeBudgetMs: 1e4,
119373
119236
  tearDownIfUnready: true
119374
119237
  });
@@ -119409,9 +119272,9 @@ async function bootAndroidImpl(params) {
119409
119272
  adbRegisterBudgetMs: STAGE_BUDGET.adbRegister,
119410
119273
  deviceReadyBudgetMs: STAGE_BUDGET.deviceReady,
119411
119274
  bootCompletedBudgetMs: STAGE_BUDGET.bootCompleted,
119412
- // Final attempt: retry PM for longer, and do NOT tear the emulator down
119413
- // if it stays slow — a guest that reached boot_completed is usable, and
119414
- // 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.
119415
119278
  pmProbeBudgetMs: STAGE_BUDGET.pmReady,
119416
119279
  tearDownIfUnready: false
119417
119280
  });
@@ -119899,11 +119762,8 @@ Common Android packages: com.android.settings, com.android.chrome, com.google.an
119899
119762
  searchHint: "open start app bundle id package simulator emulator chromium vega launch tvos apple tv fire tv",
119900
119763
  zodSchema: zodSchema10,
119901
119764
  capability: capability2,
119902
- // Chromium declares an eager CDP service; ios-remote declares an eager
119903
- // native-devtools service (its handler shares the local iOS launch path,
119904
- // which reads `services.nativeDevtools`). Local iOS resolves native-devtools
119905
- // lazily in its handler so a tvOS udid never spins up the iOS-only injection
119906
- // (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).
119907
119767
  services: (params) => {
119908
119768
  const device = resolveDevice(params.udid);
119909
119769
  if (device.platform === "ios-remote") return { nativeDevtools: nativeDevtoolsRef(device) };
@@ -120084,11 +119944,8 @@ Returns { restarted, bundleId }. Fails if the app is not installed.`,
120084
119944
  searchHint: "terminate relaunch restart reset app bundle id package simulator emulator vega tvos fire tv",
120085
119945
  zodSchema: zodSchema11,
120086
119946
  capability: capability3,
120087
- // ios-remote declares an eager native-devtools service (its handler shares
120088
- // the local iOS relaunch path, which reads `services.nativeDevtools`). Local
120089
- // iOS resolves native-devtools lazily in its handler so a tvOS udid never
120090
- // spins up the iOS-only injection (see header comment); Android and Vega
120091
- // need no service.
119947
+ // Only ios-remote's handler reads `services.nativeDevtools`, so only it
119948
+ // needs an eager declaration.
120092
119949
  services: (params) => {
120093
119950
  const device = resolveDevice(params.udid);
120094
119951
  if (device.platform === "ios-remote") return { nativeDevtools: nativeDevtoolsRef(device) };
@@ -120381,8 +120238,8 @@ function buildIosHandler(backend) {
120381
120238
  failure_stage: "ios_settings_permission_simctl_privacy",
120382
120239
  failure_area: "tool_server",
120383
120240
  error_kind: "subprocess",
120384
- // Both backends run the `simctl privacy` verb (local via xcrun,
120385
- // 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.
120386
120243
  ...subprocessFailureMetadata(err, "xcrun_simctl")
120387
120244
  },
120388
120245
  { cause: err instanceof Error ? err : new Error(String(err)) }
@@ -120562,12 +120419,11 @@ var permissionAction = {
120562
120419
  reset: { started: "Resetting", completed: "Reset" }
120563
120420
  };
120564
120421
  var capability5 = {
120565
- // `simctl privacy` edits the simulator's TCC store — physical iPhones have no
120566
- // 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`.
120567
120424
  apple: { simulator: true },
120568
- // sim-remote runs the same `simctl privacy` verb on a remote simulator, so a
120569
- // sim-remote setup can pre-set permissions too — matching the rest of the
120570
- // 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.
120571
120427
  appleRemote: { simulator: true },
120572
120428
  android: { emulator: true, device: true, unknown: true }
120573
120429
  };
@@ -121218,9 +121074,9 @@ Fails if the simulator-server / emulator backend / Chromium CDP is not reachable
121218
121074
  zodSchema: zodSchema15,
121219
121075
  outputHint: "image",
121220
121076
  capability: capability7,
121221
- // No eager service: a tvOS udid classifies as iOS by shape, and declaring
121222
- // simulator-server here would spawn it for the tvOS device (which it cannot
121223
- // 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.
121224
121080
  services: () => ({}),
121225
121081
  async execute(_services, params, ctx) {
121226
121082
  const signal = ctx?.signal ?? AbortSignal.timeout(16e3);
@@ -121908,15 +121764,12 @@ var zodSchema23 = external_exports.object({
121908
121764
  });
121909
121765
  var BUTTONS_BY_PLATFORM = {
121910
121766
  "ios": /* @__PURE__ */ new Set(["home", "power", "volumeUp", "volumeDown", "appSwitch", "actionButton"]),
121911
- // Remote iOS sims expose the same hardware buttons as local iOS.
121912
121767
  "ios-remote": /* @__PURE__ */ new Set(["home", "power", "volumeUp", "volumeDown", "appSwitch", "actionButton"]),
121913
121768
  "android": /* @__PURE__ */ new Set(["home", "back", "power", "volumeUp", "volumeDown", "appSwitch"]),
121914
- // Chromium apps have no hardware buttons; the capability gate already
121915
- // 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.
121916
121770
  "chromium": /* @__PURE__ */ new Set([]),
121917
- // Vega is remote-driven: hardware buttons / D-pad go through the dedicated
121918
- // `tv-remote` tool, and this tool's capability omits `vega` so a Vega device is
121919
- // 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.
121920
121773
  "vega": /* @__PURE__ */ new Set([])
121921
121774
  };
121922
121775
  var capability15 = {
@@ -121938,12 +121791,9 @@ Returns { pressed: buttonName }.
121938
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\`).`,
121939
121792
  zodSchema: zodSchema23,
121940
121793
  capability: capability15,
121941
- // Android presses go over `adb shell input keyevent` (see execute), not the
121942
- // simulator-server's HID transport, so declaring the service for an Android
121943
- // target would needlessly resolve + spawn a sim-server the tool never uses (up
121944
- // to a 30s ready-wait) and could throw ServiceInitializationError before the
121945
- // adb path even runs. Declare it only for the iOS / ios-remote path that
121946
- // 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.
121947
121797
  services: (params) => {
121948
121798
  const device = resolveDevice(params.udid);
121949
121799
  return device.platform === "android" ? {} : { simulatorServer: simulatorServerRef(device) };
@@ -122223,12 +122073,9 @@ async function typeAndroidPhone(device, params) {
122223
122073
  }
122224
122074
  function makeAndroidImpl(registry2) {
122225
122075
  return {
122226
- // Both sub-paths shell out to `adb`: the `isAndroidTv` probe up front, then
122227
- // `adb input` either way (TV via the focus daemon, phone via `input text` /
122228
- // `input keyevent`). Declare it so `dispatchByPlatform` preflights adb and a
122229
- // missing binary fails with the clean 424 install hint rather than surfacing
122230
- // from deeper in the probe. Matches the android branch of `describe` and
122231
- // `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.
122232
122079
  requires: ["adb"],
122233
122080
  handler: async (_services, params, device) => await isAndroidTv(device.id) ? typeTv(registry2, device, params) : typeAndroidPhone(device, params)
122234
122081
  };
@@ -122399,11 +122246,9 @@ var REMOTE_BUTTONS = Object.keys(REMOTE_KEYCODES);
122399
122246
  var NAMED_KEYCODES = {
122400
122247
  "enter": "KEY_ENTER",
122401
122248
  "return": "KEY_ENTER",
122402
- // alias of enter (matches iOS/Android/Chromium)
122403
122249
  // Back is the TV analog of Escape; KEY_ESC is inert for the focus engine.
122404
122250
  "escape": "KEY_BACK",
122405
122251
  "esc": "KEY_BACK",
122406
- // alias of escape
122407
122252
  "backspace": "KEY_BACKSPACE",
122408
122253
  "delete": "KEY_DELETE",
122409
122254
  "tab": "KEY_TAB",
@@ -122412,8 +122257,7 @@ var NAMED_KEYCODES = {
122412
122257
  "arrow-down": "KEY_DOWN",
122413
122258
  "arrow-left": "KEY_LEFT",
122414
122259
  "arrow-right": "KEY_RIGHT",
122415
- // Vega names function keys KEY_FN_F<n> (not KEY_F<n>); KEY_FN_F1..F12 all exist
122416
- // 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).
122417
122261
  ...Object.fromEntries(Array.from({ length: 12 }, (_, i) => [`f${i + 1}`, `KEY_FN_F${i + 1}`]))
122418
122262
  };
122419
122263
  var SETTLE_BETWEEN_PRESSES_S = 0.3;
@@ -122524,16 +122368,13 @@ function createKeyboardTool(registry2) {
122524
122368
  return {
122525
122369
  id: "keyboard",
122526
122370
  interaction: {
122527
- // Treat both text and key as sensitive. `key` is an unrestricted string at
122528
- // this boundary, so a value must not reach the event log before execution
122529
- // 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.
122530
122374
  //
122531
- // `startedMsg` still words a text+key request because it renders BEFORE
122532
- // `execute` rejects the combination — and likewise words `{ key: "" }` as
122533
- // a key press, which `execute` also rejects. `completedMsg` runs only
122534
- // after a call that succeeded, so it sees neither. Each formatter
122535
- // therefore has to word a different set of shapes, and the empty
122536
- // 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.
122537
122378
  startedMsg: ({ params }) => {
122538
122379
  if (params.text === void 0) return "Pressing a key";
122539
122380
  if (params.key === void 0) return "Entering text";
@@ -122553,29 +122394,21 @@ One call does one action: pass text OR key, never both. To type and then press a
122553
122394
  zodSchema: zodSchema24,
122554
122395
  capability: capability16,
122555
122396
  searchHint: "type text keyboard input named key enter escape arrow tv vega fire tv search field hid leanback",
122556
- // No eager service: each branch resolves its backend lazily (TV control,
122557
- // simulator-server, CDP, or Vega adb), since distinguishing a TV target is
122558
- // async and a tvOS udid must never resolve simulator-server.
122559
122397
  services: () => ({}),
122560
122398
  execute: async (services, params, options) => {
122561
122399
  if (params.text !== void 0 && params.key !== void 0) {
122562
122400
  throw new InvalidToolInputError(
122563
122401
  // Says what did NOT happen, so the caller retries instead of first
122564
- // inspecting the field — and spells the retry out with a literal
122565
- // 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.
122566
122404
  //
122567
- // The TV caveat is carried statically rather than by probing the
122568
- // target: this guard runs above the dispatch precisely so a combined
122569
- // request reaches no device, and distinguishing a TV kind is an async
122570
- // probe. Without it the prescribed `{ key: "enter" }` is a retry that
122571
- // cannot succeed on a TV, where `key` is rejected outright
122572
- // (platforms/tv.ts) — which is the diagnosis this guard would
122573
- // otherwise pre-empt.
122574
- '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
122575
- // once the text carries a placeholder, and this message is where an
122576
- // agent converts a combined secret call — the tool description's
122577
- // caveat is read long before that moment, if at all. The check is
122578
- // 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
122579
122412
  // still resolves nothing.
122580
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.' : ""),
122581
122414
  {
@@ -122586,12 +122419,12 @@ One call does one action: pass text OR key, never both. To type and then press a
122586
122419
  }
122587
122420
  if (params.key === "") {
122588
122421
  throw new InvalidToolInputError(
122589
- // Names the omission as the alternative, because a caller that sent an
122590
- // 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.
122591
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.",
122592
122425
  {
122593
- // The same code an unknown name gets, because that is what this is:
122594
- // 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.
122595
122428
  error_code: FAILURE_CODES.KEYBOARD_KEY_UNSUPPORTED,
122596
122429
  failure_stage: "keyboard_named_key_empty",
122597
122430
  error_kind: "unsupported"
@@ -122648,8 +122481,8 @@ async function pasteSimulator(api, text) {
122648
122481
  }
122649
122482
  function makeIosImpl4(registry2) {
122650
122483
  return {
122651
- // `isTvOsSimulator` shells out to `simctl`; the simulator-server itself is
122652
- // resolved through the blueprint.
122484
+ // `xcrun` is for the `isTvOsSimulator` probe; simulator-server comes from
122485
+ // the blueprint.
122653
122486
  requires: ["xcrun"],
122654
122487
  async handler(_services, params, device) {
122655
122488
  if (await isTvOsSimulator(device.id)) rejectTv(device);
@@ -122702,21 +122535,16 @@ function makeAndroidImpl2(registry2) {
122702
122535
 
122703
122536
  // ../tool-server/src/tools/paste/index.ts
122704
122537
  var capability17 = {
122705
- // Simulators only. A physical iPhone's pasteboard is reachable through the
122706
- // same simulator-server endpoint, but the paste keystroke is not verified
122707
- // over the CoreDevice HID path, so `device` stays off until it is.
122708
122538
  apple: { simulator: true },
122709
- // A remote simulator pastes through the MoQ transport's existing paste
122710
- // 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.
122711
122541
  appleRemote: { simulator: true },
122712
- // Emulators only: the clipboard is set through the emulator's gRPC
122713
- // endpoint, which a physical phone does not have. `unknown` is admitted
122714
- // because an unresolved serial may still be an emulator — the
122715
- // 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.
122716
122545
  android: { emulator: true, unknown: true }
122717
- // No `chromium`: the renderer can only reach the HOST clipboard, so a paste
122718
- // there would overwrite whatever the user has copied. No `vega`: no
122719
- // 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.
122720
122548
  //
122721
122549
  // TV targets are not separate platforms (an Apple TV simulator is
122722
122550
  // `ios`/`simulator`, an Android TV emulator `android`/`emulator`), so each
@@ -123045,9 +122873,8 @@ function shakeFailure(udid, detail, cause) {
123045
122873
  failure_stage: "ios_shake_notifyutil",
123046
122874
  failure_area: "tool_server",
123047
122875
  error_kind: "subprocess",
123048
- // Only a thrown subprocess error carries the syscall/exit metadata. The
123049
- // remote backend reports a non-zero child in its JSON payload instead of
123050
- // 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.
123051
122878
  ...cause ? subprocessFailureMetadata(cause, "xcrun_simctl") : {}
123052
122879
  },
123053
122880
  cause ? { cause } : void 0
@@ -123229,26 +123056,22 @@ var androidImpl6 = {
123229
123056
 
123230
123057
  // ../tool-server/src/tools/shake/index.ts
123231
123058
  var capability19 = {
123232
- // Simulator only. A physical iPhone's motion comes from real hardware; there
123233
- // is no host-side hook to fake it, so `device` stays false and a paired phone
123234
- // 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.
123235
123061
  apple: { simulator: true },
123236
- // A remote simulator is still a simulator: `sim-remote spawn` runs the same
123237
- // in-simulator `notifyutil` argv the local path does.
123062
+ // `sim-remote spawn` runs the same in-simulator `notifyutil` argv the local
123063
+ // path does.
123238
123064
  appleRemote: { simulator: true },
123239
- // Android emulators only, for the same reason as iOS: a physical phone's
123240
- // accelerometer can't be driven from the host. `unknown` is allowed through
123241
- // because a serial that didn't resolve may still be an emulator; the handler
123242
- // 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.
123243
123067
  android: { emulator: true, unknown: true }
123244
- // 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.
123245
123069
  //
123246
- // TV targets that are NOT separate platforms cannot be excluded here. An
123247
- // Apple TV simulator is `ios`/`simulator` and an Android TV emulator is
123248
- // `android`/`emulator` — identical to a phone by id shape and device kind, so
123249
- // the matrix admits them and each platform handler probes the runtime and
123250
- // rejects a TV. `supports` can't do it: it is synchronous, while the runtime
123251
- // 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.
123252
123075
  };
123253
123076
  var shakeTool = {
123254
123077
  id: "shake",
@@ -123267,8 +123090,8 @@ Only phone/tablet simulators and emulators are supported.`,
123267
123090
  searchHint: "shake motion accelerometer undo typing dev menu gesture",
123268
123091
  zodSchema: shakeZodSchema,
123269
123092
  capability: capability19,
123270
- // Talks to `simctl` / `adb` directly, so no simulator-server is resolved —
123271
- // 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.
123272
123095
  services: () => ({}),
123273
123096
  execute: dispatchByPlatform({
123274
123097
  toolId: "shake",
@@ -123395,10 +123218,8 @@ Multi-step navigation: pass a path as { button: ["up","right","right","select"]
123395
123218
  Read the screen with \`describe\` before and after to see where focus landed.
123396
123219
  Returns { pressed, count }.`,
123397
123220
  alwaysLoad: true,
123398
- // A path (≤64 buttons) × repeat (≤50) flattens to thousands of presses that
123399
- // settle apart in one held device session — minutes of wall-clock. Mark
123400
- // long-running so the MCP adapter doesn't abort it at its per-request fetch
123401
- // 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.
123402
123223
  longRunning: true,
123403
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",
123404
123225
  zodSchema: zodSchema26,
@@ -129777,16 +129598,13 @@ var ALLOWED_TOOLS = /* @__PURE__ */ new Set([
129777
129598
  "gesture-rotate",
129778
129599
  "button",
129779
129600
  "keyboard",
129780
- // `paste` belongs in a sequence for the same reason `keyboard` does: the
129781
- // 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.
129782
129603
  "paste",
129783
129604
  "rotate",
129784
- // Sequencing matters for shake: the interesting cases are races (shake while
129785
- // a sheet is dismissing, shake right after typing), which need the steps
129786
- // 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.
129787
129607
  "shake",
129788
- // `tv-remote` drives the D-pad on a TV target (Apple TV / Android TV / Vega);
129789
- // `keyboard` types into the focused field there.
129790
129608
  "tv-remote",
129791
129609
  AWAIT_UI_ELEMENT_TOOL_ID
129792
129610
  ]);
@@ -129812,9 +129630,8 @@ var capability22 = {
129812
129630
  android: { emulator: true, device: true, unknown: true },
129813
129631
  chromium: { app: true },
129814
129632
  // Vega (Fire TV) is a valid target: its `tv-remote` / `keyboard` steps are
129815
- // supported, and the description advertises it. Without this key the outer
129816
- // capability gate (HTTP layer's assertSupported) would reject a Vega udid
129817
- // 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.
129818
129635
  vega: { vvd: true }
129819
129636
  };
129820
129637
  function createRunSequenceTool(registry2) {
@@ -129888,15 +129705,10 @@ Stops on the first error (or unmet await-ui-element condition) and returns parti
129888
129705
  searchHint: "batch sequence multiple gesture steps sequentially",
129889
129706
  zodSchema: zodSchema28,
129890
129707
  capability: capability22,
129891
- // No eagerly-declared service: each step resolves its own services through
129892
- // `invokeSubTool` below (simulator-server for iOS/Android, CDP for
129893
- // Chromium), so run-sequence itself needs none. An eager resolver can't be
129894
- // used here because a tvOS udid shape-classifies as `ios` (there is no
129895
- // `tvos` platform) — declaring simulator-server for it would spawn a
129896
- // controller it can't drive and hang on the ready timeout before any tv-*
129897
- // step could run. The sub-tool invocations still pay only their own
129898
- // first-step spawn cost, and `ctx` is threaded through so nested steps keep
129899
- // 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.
129900
129712
  services: () => ({}),
129901
129713
  async execute(_services, params, ctx) {
129902
129714
  const { udid, steps } = params;
@@ -130044,16 +129856,15 @@ var NOT_CONNECTED_CODE_MAP = {
130044
129856
  [FAILURE_CODES.DEBUGGER_CDP_SOCKET_CLOSED_BEFORE_OPEN]: "cdp_unreachable",
130045
129857
  [FAILURE_CODES.DEBUGGER_CDP_NOT_CONNECTED]: "cdp_unreachable",
130046
129858
  [FAILURE_CODES.DEBUGGER_CDP_CONNECTION_CLOSED]: "cdp_unreachable",
130047
- // Reachable from the connect pipeline's enable/binding sends when the target
130048
- // accepts the socket but its JS runtime never answers (frozen, or paused at a
130049
- // breakpoint). Post-connect hangs are different: an OPEN socket still reports
130050
- // 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).
130051
129863
  [FAILURE_CODES.DEBUGGER_CDP_REQUEST_TIMEOUT]: "runtime_unresponsive",
130052
129864
  [FAILURE_CODES.CHROMIUM_CDP_UNREACHABLE]: "cdp_unreachable",
130053
- // "Reached but not CDP / malformed answer" — a non-CDP server squatting the
130054
- // debug port, an HTTP error status, or a non-JSON body. Same precondition
130055
- // class as the Metro arm's non-Metro-port-occupant (detail names what
130056
- // 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.
130057
129868
  [FAILURE_CODES.CHROMIUM_CDP_INVALID_RESPONSE]: "cdp_unreachable",
130058
129869
  [FAILURE_CODES.CHROMIUM_CDP_NO_PAGE_TARGET]: "cdp_unreachable",
130059
129870
  [FAILURE_CODES.REGISTRY_SERVICE_TERMINATING]: "reconnecting"
@@ -130122,7 +129933,7 @@ Use when you need to verify connectivity before using other debugger tools. Neve
130122
129933
  zodSchema: zodSchema30,
130123
129934
  capability: DEBUGGER_TOOL_CAPABILITY,
130124
129935
  // Resolved manually in execute so a not-connected precondition becomes a
130125
- // structured result instead of a service-resolution tool failure.
129936
+ // structured result instead of a tool failure.
130126
129937
  services: () => ({}),
130127
129938
  async execute(_services, params, ctx) {
130128
129939
  try {
@@ -130245,11 +130056,8 @@ var debuggerReloadMetroTool = {
130245
130056
  description: `Restart the Metro JS bundle in the connected React Native app without restarting the native process.
130246
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.`,
130247
130058
  zodSchema: zodSchema32,
130248
- // Metro-only: Chromium loads from disk, not from a bundler. The closest
130249
- // analog (Page.reload against the renderer) would behave differently enough
130250
- // — preserving the URL but re-fetching index.html, blowing away in-memory
130251
- // app state — that calling it under the same tool name would mislead. If we
130252
- // 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.
130253
130061
  capability: RN_ONLY_TOOL_CAPABILITY,
130254
130062
  services: (params) => ({
130255
130063
  debugger: `JsRuntimeDebugger:${params.port}:${canonicalDeviceId(params.device_id)}`
@@ -131094,9 +130902,8 @@ Use when you need tap coordinates for a React Native UI element. Returns a compa
131094
130902
  alwaysLoad: true,
131095
130903
  searchHint: "react native component tree discovery tap coordinates",
131096
130904
  zodSchema: zodSchema33,
131097
- // RN-only: depends on the React DevTools backend that ships with the JS
131098
- // bundle in dev builds. Chromium has no equivalent — use `describe` instead
131099
- // 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.
131100
130907
  capability: RN_ONLY_TOOL_CAPABILITY,
131101
130908
  services: (params) => ({
131102
130909
  debugger: `JsRuntimeDebugger:${params.port}:${canonicalDeviceId(params.device_id)}`
@@ -131464,7 +131271,6 @@ var SKIP_SET = /* @__PURE__ */ new Set([
131464
131271
  "TextInputLabel",
131465
131272
  "ThemeContext",
131466
131273
  "BaseHTMLEngineProvider",
131467
- // Fabric / cross-app entries
131468
131274
  "VScrollViewNativeComponent",
131469
131275
  "InnerScreen",
131470
131276
  "ScreenStackItem",
@@ -131603,10 +131409,8 @@ Set resolveSourceMaps to false to skip symbolication and get raw bundled locatio
131603
131409
  Set includeSkipped=true to see filtered items annotated with skip reasons.
131604
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.`,
131605
131411
  zodSchema: zodSchema34,
131606
- // RN-only: uses React Native's internal getInspectorDataForViewAtPoint and
131607
- // Metro's /symbolicate endpoint. Chromium's CDP has DOM.getNodeForLocation
131608
- // for "what's here?" but the source-map flow would need a complete rewrite —
131609
- // 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.
131610
131414
  capability: RN_ONLY_TOOL_CAPABILITY,
131611
131415
  services: (params) => ({
131612
131416
  debugger: `JsRuntimeDebugger:${params.port}:${canonicalDeviceId(params.device_id)}`
@@ -131846,8 +131650,6 @@ On React Native (iOS / Android / Vega) interception is injected into the JS runt
131846
131650
  Use when inspecting outbound HTTP traffic or debugging API calls in the running app.
131847
131651
  Fails if the app is not connected (RN) or the device is not reachable (Chromium).`,
131848
131652
  zodSchema: zodSchema36,
131849
- // Works on RN (Metro-injected interceptor) and Chromium (native CDP Network
131850
- // domain), dispatched on the device id in `services` / `execute`.
131851
131653
  capability: DEBUGGER_TOOL_CAPABILITY,
131852
131654
  services: (params) => {
131853
131655
  const device = resolveDevice(params.device_id);
@@ -131936,8 +131738,6 @@ Returns request/response headers (sensitive headers redacted), status, timing, a
131936
131738
  Large response bodies are truncated. Use when you need headers, body, or timing for a specific request after listing logs.
131937
131739
  Returns an error message string if the requestId is not found \u2014 use view-network-logs to get valid requestId values.`,
131938
131740
  zodSchema: zodSchema37,
131939
- // Companion to view-network-logs: RN via the injected interceptor, Chromium
131940
- // via the native CDP Network recording. Dispatched on the device id.
131941
131741
  capability: DEBUGGER_TOOL_CAPABILITY,
131942
131742
  services: (params) => {
131943
131743
  const device = resolveDevice(params.device_id);
@@ -132048,10 +131848,9 @@ init_zod();
132048
131848
 
132049
131849
  // ../tool-server/src/tools/describe/format-tree.ts
132050
131850
  var CONTENT_ROLES = /* @__PURE__ */ new Set([
132051
- // iOS AX traits surfaced by mapNativeTraitsToDescribeRole. AXGroup is
132052
- // deliberately excluded: it's the catch-all wrapper, so requiring it to
132053
- // carry its own label/value before we emit a line keeps decorative
132054
- // 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.
132055
131854
  "AXButton",
132056
131855
  "AXStaticText",
132057
131856
  "AXImage",
@@ -132280,10 +132079,9 @@ function makeDescribeExecute(registry2) {
132280
132079
  )
132281
132080
  },
132282
132081
  iosRemote: {
132283
- // describeIos already handles both ax-service (TCP) and native-devtools
132284
- // fallback — both blueprints route through sim-remote when the device is
132285
- // ios-remote. Only the preflight dep differs. Remote sims are iOS-only
132286
- // (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.
132287
132085
  requires: ["sim-remote"],
132288
132086
  handler: async (_services, params, device) => withDescription(
132289
132087
  withBootCaveatOncePerDevice(
@@ -132295,9 +132093,8 @@ function makeDescribeExecute(registry2) {
132295
132093
  android: {
132296
132094
  requires: androidRequires,
132297
132095
  handler: async (_services, params, device) => (
132298
- // Resolve the form factor once and route on it: a TV goes to the
132299
- // focus-driven describe, a phone to the uiautomator tree — and pass the
132300
- // 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.
132301
132098
  await isAndroidTv(device.id) ? describeTv(registry2, device) : withDescription(await describeAndroid(registry2, params.udid, params.bundleId, false))
132302
132099
  )
132303
132100
  },
@@ -132553,9 +132350,6 @@ After starting, ask the user to perform the interaction to profile, then call re
132553
132350
  Returns { started_at, startedAtEpochMs, hermes_version, detected_architecture } on success, or the already_running payload described above.
132554
132351
  Fails if the Hermes runtime is not reachable or the Metro CDP connection cannot be established.`,
132555
132352
  zodSchema: zodSchema40,
132556
- // RN-only: bootstraps the React DevTools backend and uses Hermes'
132557
- // Profiler.start. A CDP-direct CPU profile for Chromium is tracked as a
132558
- // follow-up; the React commit recording has no Chromium analog.
132559
132353
  capability: RN_ONLY_TOOL_CAPABILITY,
132560
132354
  services: () => ({}),
132561
132355
  async execute(_services, params) {
@@ -132700,8 +132494,8 @@ Fails if the Hermes runtime is not reachable or the Metro CDP connection cannot
132700
132494
  const sessionId = crypto6.randomUUID();
132701
132495
  const ownerPayload = {
132702
132496
  sessionId,
132703
- // startedAtEpochMs/lastHeartbeatEpochMs set inside the script using
132704
- // the wrapper-captured value to eliminate clock skew.
132497
+ // Set inside the start script from the wrapper-captured clock, to
132498
+ // eliminate skew.
132705
132499
  startedAtEpochMs: 0,
132706
132500
  lastHeartbeatEpochMs: 0
132707
132501
  };
@@ -132915,7 +132709,6 @@ Returns { duration_ms, sample_count, fiber_renders_captured, total_react_commits
132915
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).
132916
132710
  Fails if no active profiling session exists or the CDP connection was lost during recording.`,
132917
132711
  zodSchema: zodSchema41,
132918
- // RN-only: companion to react-profiler-start.
132919
132712
  capability: RN_ONLY_TOOL_CAPABILITY,
132920
132713
  services: () => ({}),
132921
132714
  async execute(_services, params) {
@@ -132954,10 +132747,8 @@ Fails if no active profiling session exists or the CDP connection was lost durin
132954
132747
  error_code: FAILURE_CODES.REACT_PROFILER_NO_ACTIVE_SESSION,
132955
132748
  failure_stage: "react_profiler_stop_inactive",
132956
132749
  failure_area: "tool_server",
132957
- // Internal session-state (start never reached the sampler), not caller
132958
- // input — matches the sibling session-lookup site above and the
132959
- // native NATIVE_PROFILER_NO_ACTIVE_SESSION so this code carries one
132960
- // 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.
132961
132752
  error_kind: "not_found"
132962
132753
  }
132963
132754
  );
@@ -134066,7 +133857,7 @@ async function runPipeline(input, options) {
134066
133857
  allClear: input.sessionMeta.allClear ?? false,
134067
133858
  maxCommitMs: input.sessionMeta.maxCommitMs,
134068
133859
  anyRuntimeCompilerDetected: tagOutput.anyRuntimeCompilerDetected,
134069
- // 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
134070
133861
  reactCommits: input.sessionMeta.totalReactCommits ?? tagOutput.reactCommits,
134071
133862
  fiberRenders: tagOutput.fiberRenders,
134072
133863
  totalFirstMounts: tagOutput.totalFirstMounts,
@@ -134805,8 +134596,8 @@ is returned by react-profiler-start.
134805
134596
  Use when the profiling session is complete and you need to interpret the collected data.
134806
134597
  Fails if react-profiler-stop has not been called or no profiling data is stored.`,
134807
134598
  zodSchema: zodSchema43,
134808
- // RN-only: operates on profiler trace files captured via the React DevTools
134809
- // 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.
134810
134601
  capability: RN_ONLY_TOOL_CAPABILITY,
134811
134602
  services: () => ({}),
134812
134603
  async execute(_services, params, ctx) {
@@ -134960,12 +134751,9 @@ Call this per-finding after react-profiler-analyze to inspect source before prop
134960
134751
  Returns found: false if the component is not found in user-owned code (e.g. lives in node_modules).
134961
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.`,
134962
134753
  zodSchema: zodSchema44,
134963
- // Companion to react-profiler-analyze. Carries the same RN-only capability
134964
- // declaration as the rest of react-profiler-* for intent-clarity, even
134965
- // though the HTTP gate is a no-op here (the tool takes no device_id, so
134966
- // there's nothing for the gate to inspect). An LLM agent reading the tool
134967
- // catalogue should see this is paired with the other react-profiler tools
134968
- // 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.
134969
134757
  capability: RN_ONLY_TOOL_CAPABILITY,
134970
134758
  fileInputs,
134971
134759
  services: () => ({}),
@@ -134999,8 +134787,8 @@ When several files define a component with the same name (e.g. platform variants
134999
134787
  }
135000
134788
  return {
135001
134789
  found: true,
135002
- // The key that actually matched, so the caller can tell which name hit
135003
- // 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.
135004
134792
  component: matchedKey ?? params.component_name,
135005
134793
  requested: params.component_name,
135006
134794
  file: entry.file,
@@ -135101,9 +134889,7 @@ Call react-profiler-stop first. Reads directly from the stored cpuProfile.
135101
134889
  Returns a markdown table of the top hotspot functions with self-time, total-time, and location.
135102
134890
  Fails if react-profiler-stop has not been called or no CPU profile is stored.`,
135103
134891
  zodSchema: zodSchema45,
135104
- // RN-only: reads a Hermes CPU profile captured via the React profiler
135105
- // session. Chromium's V8 Profiler emits a different sample format — see the
135106
- // PR description for the follow-up scope.
134892
+ // RN-only: reads a Hermes CPU profile; Chromium's V8 sample format differs.
135107
134893
  capability: RN_ONLY_TOOL_CAPABILITY,
135108
134894
  services: () => ({}),
135109
134895
  async execute(_services, params) {
@@ -135812,11 +135598,8 @@ async function execFileAsyncWithTimeout(file2, args, options = {}) {
135812
135598
  const { stdout, stderr } = await execFileAsync25(file2, args, {
135813
135599
  encoding: "utf-8",
135814
135600
  ...options,
135815
- // The timeout and maxBuffer are this wrapper's whole purpose (the
135816
- // event-loop-freeze and ENOBUFS guards documented above), so they are
135817
- // applied AFTER ...options — a caller can never silently weaken them by
135818
- // passing its own. maxBuffer stays a floor: a caller may raise it for an
135819
- // 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.
135820
135603
  timeout: DEFAULT_EXEC_TIMEOUT_MS,
135821
135604
  maxBuffer: Math.max(options.maxBuffer ?? 0, DEFAULT_EXEC_MAX_BUFFER)
135822
135605
  });
@@ -136369,8 +136152,7 @@ function aggregateCpuHotspots(rows, options) {
136369
136152
  duringHang,
136370
136153
  timeRangeMs: { first: firstMs, last: lastMs },
136371
136154
  burstWindows,
136372
- // Only emit when present (Android). Omitting the key on iOS keeps the iOS
136373
- // hotspot object shape byte-identical to before this change.
136155
+ // Omit the key on iOS rather than emitting `dominantMapping: undefined`.
136374
136156
  ...acc.dominantMapping !== void 0 ? { dominantMapping: acc.dominantMapping } : {}
136375
136157
  });
136376
136158
  }
@@ -136784,17 +136566,16 @@ var SYSTEM_FRAME_PATTERNS = [
136784
136566
  /gfxstream/i,
136785
136567
  /rcCreateSync/,
136786
136568
  /_enc\b/,
136787
- // gl*Enc / rc*_enc emulator encoder trampolines
136569
+ // emulator GL/Vulkan encoder trampolines
136788
136570
  // Linux kernel syscall entry + mm/vfs internals (no app symbol to act on).
136789
136571
  // x86-64 entry path:
136790
136572
  /\bdo_syscall_64\b/,
136791
136573
  /\bentry_SYSCALL/,
136792
136574
  /\b__x64_sys_/,
136793
136575
  /\bx64_sys_call\b/,
136794
- // arm64 (aarch64) entry path — the emulator/device the Android profiler runs
136795
- // on is arm64, so these are the leaves actually seen there. The names differ
136796
- // entirely from x86: the EL0 synchronous-exception vector dispatches to the
136797
- // 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>.
136798
136579
  // arch/arm64/kernel/{entry.S,entry-common.c,syscall.c}.
136799
136580
  /\b__arm64_sys_/,
136800
136581
  /\bel0t_64_sync(_handler)?\b/,
@@ -136804,9 +136585,9 @@ var SYSTEM_FRAME_PATTERNS = [
136804
136585
  /\bdo_el0_svc\b/,
136805
136586
  /\binvoke_syscall\b/,
136806
136587
  /\bel0_(da|ia)\b/,
136807
- // data / instruction abort handlers (page-fault leaves)
136588
+ // data / instruction abort (page-fault) handlers
136808
136589
  /\b__arch_copy_(from|to)_user\b/,
136809
- // arm64 uaccess copy helpers (copy_{from,to}_user.S)
136590
+ // arm64 uaccess copy helpers
136810
136591
  /\bksys_/,
136811
136592
  /\bvfs_(read|write|fsync)\b/,
136812
136593
  /\bgup_/,
@@ -137331,8 +137112,8 @@ function resolveDefaultTemplatePath() {
137331
137112
  ${candidates.map((c) => ` - ${c}`).join("\n")}
137332
137113
  Pass template_path explicitly, or rebuild so the template is copied into place.`,
137333
137114
  {
137334
- // A required bundled asset is absent — a packaging/build problem, not a
137335
- // 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.
137336
137117
  error_code: FAILURE_CODES.NATIVE_PROFILER_TRACE_TEMPLATE_MISSING,
137337
137118
  failure_stage: "ios_native_profiler_template_resolve",
137338
137119
  failure_area: "tool_server",
@@ -137869,8 +137650,7 @@ async function stopNativeProfilerIos(api) {
137869
137650
  failure_stage: "native_profiler_stop_session_state",
137870
137651
  failure_area: "tool_server",
137871
137652
  // Internal session-state, not caller input — matches the react twin
137872
- // REACT_PROFILER_NO_ACTIVE_SESSION (not_found) and the Android site so the
137873
- // "no active session" family carries one consistent kind.
137653
+ // REACT_PROFILER_NO_ACTIVE_SESSION and the Android site.
137874
137654
  error_kind: "not_found"
137875
137655
  }
137876
137656
  );
@@ -137946,13 +137726,12 @@ async function analyzeNativeProfilerIos(api) {
137946
137726
  uiHangs,
137947
137727
  cpuHotspots,
137948
137728
  memoryLeaks,
137949
- // Freeze the exports' capture mode with the parsed data so drill-down
137950
- // consumers (leak_stacks, combined report) stay paired with it even after
137951
- // 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.
137952
137732
  mallocStackLogging: api.mallocStackLogging,
137953
- // Freeze the recording's start time too — the combined report anchors these
137954
- // hangs to wall-clock time, and must use THIS capture's start, not whatever
137955
- // 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.
137956
137735
  wallClockStartMs: api.wallClockStartMs
137957
137736
  };
137958
137737
  const exportErrors = {};
@@ -137981,21 +137760,17 @@ async function analyzeNativeProfilerIos(api) {
137981
137760
  payload,
137982
137761
  traceFile: api.traceFile,
137983
137762
  exportErrors,
137984
- // wallClockStartMs is the recording's start time, stamped in-memory at
137985
- // native-profiler-start. A large gap to "now" means analyze is reusing a
137986
- // 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.
137987
137765
  //
137988
- // Limitation (iOS): unlike Android, iOS has no on-disk metadata sidecar, so
137989
- // profiler-load (which restores only the raw_*.xml) cannot recover the start
137990
- // time — wallClockStartMs is null for a loaded session and this note stays
137991
- // off. The note therefore fires only for a live in-process session, never
137992
- // for one restored from disk. Restoring iOS start-time across loads needs an
137993
- // iOS sidecar this Android-scoped change does not add; formatTraceFreshness
137994
- // 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.
137995
137770
  freshnessNote: formatTraceFreshness(api.wallClockStartMs, Date.now()) ?? void 0,
137996
137771
  // Same live-session-only caveat as wallClockStartMs: profiler-load has no
137997
137772
  // capture-mode sidecar, so a restored session renders with null (the
137998
- // unattributed-leaks note then falls back to inferring the mode).
137773
+ // unattributed-leaks note then goes by the attributed-leak count instead).
137999
137774
  mallocStackLogging: api.mallocStackLogging
138000
137775
  });
138001
137776
  }
@@ -138843,20 +138618,18 @@ function cpuRowsToAggregatorRows(rows, traceStartNs) {
138843
138618
  const thread = normaliseAndroidThread(row.thread_name, row.is_main_thread === 1);
138844
138619
  out.push({
138845
138620
  dominantFunction: dominant,
138846
- // Mapping of the leaf frame (from cpu-hotspots.sql's MIN(spm.name)) —
138847
- // threaded through so classifyNativeFrame can recognise `/kernel` leaves.
138621
+ // Threaded through so classifyNativeFrame can recognise `/kernel` leaves.
138848
138622
  ...row.leaf_mapping != null ? { dominantMapping: row.leaf_mapping } : {},
138849
138623
  thread,
138850
138624
  weightNs: row.sample_count * SAMPLE_PERIOD_NS,
138851
- // Android ships SQL-precomputed bursts, no raw timestamps. Empty array
138852
- // 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).
138853
138627
  timestampsNs: [],
138854
138628
  callChains: [{ chain: [dominant], count: row.sample_count }],
138855
138629
  precomputedBursts: parseBurstWindows(row.burst_windows, traceStartMs),
138856
- // first/last_ts_ns are absolute CLOCK_MONOTONIC ns: they exceed 2^53 after
138857
- // ~104 days of device uptime, so the WASM decoder hands them back as bigint
138858
- // (see readCell). traceStartNs is already a plain Number, so coerce here to
138859
- // 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.
138860
138633
  firstMs: Math.round((Number(row.first_ts_ns) - traceStartNs) / 1e6),
138861
138634
  lastMs: Math.round((Number(row.last_ts_ns) - traceStartNs) / 1e6),
138862
138635
  sampleCount: row.sample_count
@@ -139078,8 +138851,8 @@ async function stopNativeProfilerAndroid(api) {
139078
138851
  failure_stage: "android_native_profiler_stop",
139079
138852
  failure_area: "tool_server",
139080
138853
  // Internal session-state, not caller input — matches the react twin
139081
- // REACT_PROFILER_NO_ACTIVE_SESSION (not_found) so the "no active session"
139082
- // 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.
139083
138856
  error_kind: "not_found"
139084
138857
  }
139085
138858
  );
@@ -139173,9 +138946,8 @@ async function analyzeNativeProfilerAndroid(api) {
139173
138946
  payload,
139174
138947
  traceFile: hostTracePath,
139175
138948
  exportErrors: pipelineResult.exportErrors,
139176
- // wallClockStartMs is the recording's start time (set at start, persisted in
139177
- // the metadata sidecar, restored by profiler-load). A large gap to "now"
139178
- // 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.
139179
138951
  freshnessNote: formatTraceFreshness(api.wallClockStartMs, Date.now()) ?? void 0,
139180
138952
  // Explains an absent CPU section when samples exist but carry no stacks.
139181
138953
  cpuDiagnostic: pipelineResult.cpuDiagnostic
@@ -139274,7 +139046,7 @@ var nativeProfilerStopTool = {
139274
139046
  failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to stop native profiler: ${failureSignal2.error_code}`
139275
139047
  },
139276
139048
  capability: capability26,
139277
- // Packaging plus the export passes routinely exceed the 30s fetch timeout.
139049
+ // Packaging plus the export passes routinely exceed the 30s MCP fetch timeout.
139278
139050
  longRunning: true,
139279
139051
  description: `Stop native profiling and export trace data.
139280
139052
  iOS: sends SIGINT to xctrace, waits for packaging, then exports CPU, hangs, and leaks XML.
@@ -139777,8 +139549,7 @@ function assertStoppableSession(api, stage) {
139777
139549
  error_code: reaped2 ? FAILURE_CODES.SCREEN_RECORDING_SERVER_SHUTTING_DOWN : FAILURE_CODES.SCREEN_RECORDING_NO_ACTIVE_SESSION,
139778
139550
  failure_stage: stage,
139779
139551
  failure_area: "tool_server",
139780
- // Session-state, not caller input — matches the profiler family's
139781
- // "no active session" kind.
139552
+ // Session-state, not caller input — matches the profiler family's kind.
139782
139553
  error_kind: "not_found"
139783
139554
  }
139784
139555
  );
@@ -139926,7 +139697,7 @@ function ffmpegArgs(opts) {
139926
139697
  "-loglevel",
139927
139698
  "warning",
139928
139699
  // The pump feeds whole JPEGs at a fixed cadence, so the input timeline is
139929
- // exactly OUTPUT_FPS — no timestamp guessing, no variable-framerate stutter.
139700
+ // exactly OUTPUT_FPS — no timestamp guessing.
139930
139701
  "-f",
139931
139702
  "image2pipe",
139932
139703
  "-framerate",
@@ -140354,10 +140125,9 @@ Returns { status: "recording", timeLimitSeconds, outputFile } \u2014 the video i
140354
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).`,
140355
140126
  searchHint: "record video screen capture movie mp4 start filming screencast",
140356
140127
  zodSchema: zodSchema51,
140357
- // simulator-server is resolved inside execute, not declared here: a tvOS
140358
- // udid classifies as iOS by shape, and an eager service would spawn
140359
- // simulator-server for a device it cannot drive and hang on its ready
140360
- // 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`).
140361
140131
  services: (params) => ({
140362
140132
  session: screenRecordingSessionRef(resolveDevice(params.udid))
140363
140133
  }),
@@ -140471,8 +140241,7 @@ Fails if no recording (running or finished-but-unretrieved) exists for the given
140471
140241
  hostPath: stopped.outputFile,
140472
140242
  kind: "screen-recording",
140473
140243
  mimeType: "video/mp4",
140474
- // Drop the internal `argent-` temp prefix so the saved file reads cleanly
140475
- // as `.argent/recordings/screen-recording-<device>-<ts>.mp4`.
140244
+ // Drop the internal `argent-` temp-file prefix from the saved name.
140476
140245
  filename: (0, import_node_path20.basename)(stopped.outputFile).replace(/^argent-/, ""),
140477
140246
  saveDir: RECORDINGS_DIR
140478
140247
  });
@@ -140786,8 +140555,7 @@ Use when investigating JS CPU hotspots or correlating CPU cost with specific com
140786
140555
  Returns a markdown table of CPU hotspots, call tree, or per-component CPU breakdown.
140787
140556
  Fails if no CPU profile is stored \u2014 run react-profiler-stop first.`,
140788
140557
  zodSchema: zodSchema53,
140789
- // RN-only: reads Hermes-format CPU profiles. Chromium's V8 sample format is
140790
- // different — see the PR description for the follow-up scope.
140558
+ // RN-only: reads Hermes-format CPU profiles; Chromium's V8 sample format differs.
140791
140559
  capability: RN_ONLY_TOOL_CAPABILITY,
140792
140560
  services: () => ({}),
140793
140561
  async execute(_services, params) {
@@ -141146,7 +140914,7 @@ Use when drilling into specific components or time windows after react-profiler-
141146
140914
  Returns a markdown table or tree of commit data matching the requested mode.
141147
140915
  Fails if react-profiler-stop has not been called or no commit data is stored.`,
141148
140916
  zodSchema: zodSchema54,
141149
- // RN-only: reads React commit data captured via the React DevTools backend.
140917
+ // Reads React commit data captured via the React DevTools backend.
141150
140918
  capability: RN_ONLY_TOOL_CAPABILITY,
141151
140919
  services: () => ({}),
141152
140920
  async execute(_services, params) {
@@ -141534,9 +141302,7 @@ Use when drilling into native hang stacks, thread CPU breakdown, or memory leaks
141534
141302
  Returns a markdown report with native call stacks, thread weights, or leak details for the selected mode.
141535
141303
  Fails if native-profiler-analyze has not been run or no parsed trace data is in memory.`,
141536
141304
  zodSchema: zodSchema55,
141537
- // iOS: reads xctrace output. Android: queries the Perfetto .pftrace via the
141538
- // in-process trace-processor engine (see executeAndroid). Chromium has no
141539
- // native trace capture.
141305
+ // No chromium entry: it has no native trace capture.
141540
141306
  capability: {
141541
141307
  apple: { simulator: true, device: true },
141542
141308
  android: { emulator: true, device: true, unknown: true }
@@ -141618,9 +141384,8 @@ Call this tool when both profilers were run in parallel on the same session.
141618
141384
  Returns a markdown report correlating hangs with React commits, memory leaks, and investigation hints.
141619
141385
  Fails if either react-profiler-analyze or native-profiler-analyze has not been called first.`,
141620
141386
  zodSchema: zodSchema56,
141621
- // Combines React (Hermes) + native traces. iOS reads xctrace output;
141622
- // Android re-queries the Perfetto .pftrace via loadAndroidCombinedData. The
141623
- // 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.
141624
141389
  capability: {
141625
141390
  apple: { simulator: true, device: true },
141626
141391
  android: { emulator: true, device: true, unknown: true }
@@ -141867,7 +141632,7 @@ Fails if either react-profiler-analyze or native-profiler-analyze has not been c
141867
141632
  memoryLeaks,
141868
141633
  mountComponents,
141869
141634
  // From parsedData, not the live session field: a recording started
141870
- // after the analyze must not re-label the data being rendered here.
141635
+ // after analyze must not re-label the data rendered here.
141871
141636
  nativeApi.parsedData?.mallocStackLogging ?? null
141872
141637
  )
141873
141638
  );
@@ -142234,10 +141999,8 @@ async function loadNativeSession(debugDir, sessionId, api, appProcessOverride) {
142234
141999
  error_code: FAILURE_CODES.PROFILER_NATIVE_TRACE_MISSING,
142235
142000
  failure_stage: "profiler_load_native_session",
142236
142001
  failure_area: "tool_server",
142237
- // A trace file missing on disk is a not_found condition — same kind as
142238
- // the Android .pftrace-missing site above, so this shared code isn't
142239
- // split into two kinds by platform (validation stays for the
142240
- // 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).
142241
142004
  error_kind: "not_found"
142242
142005
  }
142243
142006
  );
@@ -142303,9 +142066,8 @@ Modes:
142303
142066
  Returns a summary of the loaded session or a session list for the list mode.
142304
142067
  Fails if the session_id is not found or required XML files are missing from disk.`,
142305
142068
  zodSchema: zodSchema57,
142306
- // Loads Hermes-format React traces or iOS xctrace XML — neither maps onto
142307
- // Chromium yet. The gate keeps the error close to the call site instead of
142308
- // 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.
142309
142071
  capability: RN_ONLY_TOOL_CAPABILITY,
142310
142072
  services: (params) => {
142311
142073
  const svcs = {};
@@ -142367,12 +142129,9 @@ init_src();
142367
142129
  var URN_SUFFIXES = ["", ":tcp"];
142368
142130
  var PORT_KEYED_NAMESPACES = [
142369
142131
  JS_RUNTIME_DEBUGGER_NAMESPACE,
142370
- // Both declare `getDependencies -> JsRuntimeDebugger:<payload>`, so neither
142371
- // can be in a snapshot without it and neither adds any ownership the debugger
142372
- // entry does not already establish. They are listed for what `stopped`
142373
- // reports: a session that had a network inspector or a React profiler open is
142374
- // told those went away by name, rather than inferring it from the debugger
142375
- // 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.
142376
142135
  NETWORK_INSPECTOR_NAMESPACE,
142377
142136
  REACT_PROFILER_SESSION_NAMESPACE
142378
142137
  ];
@@ -142478,8 +142237,7 @@ function createStopAllSimulatorServersTool(registry2) {
142478
142237
  id: "stop-all-simulator-servers",
142479
142238
  interaction: {
142480
142239
  // "all" only holds for the unscoped sweep; a scoped call touches just the
142481
- // ids it was given, and saying otherwise would misreport a teardown that
142482
- // deliberately left another agent's devices running.
142240
+ // ids it was given.
142483
142241
  startedMsg: ({ params }) => {
142484
142242
  const devices = params?.devices;
142485
142243
  return devices ? `Stopping simulator servers for ${devices.length} ${devices.length === 1 ? "device" : "devices"}` : "Stopping all simulator servers";
@@ -142575,9 +142333,8 @@ function listeningPids(port) {
142575
142333
  const output2 = (0, import_node_child_process28.execFileSync)("netstat", ["-ano"], {
142576
142334
  encoding: "utf-8",
142577
142335
  timeout: 5e3,
142578
- // `netstat -ano` dumps every socket on the host; a busy box easily
142579
- // exceeds Node's default 1 MiB maxBuffer, and the resulting ENOBUFS
142580
- // 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".
142581
142338
  maxBuffer: 16 * 1024 * 1024
142582
142339
  });
142583
142340
  return parseNetstatListeningPids(output2, port);
@@ -144346,8 +144103,8 @@ var fileInputs2 = [
144346
144103
  var flowStartRecordingTool = {
144347
144104
  id: "flow-start-recording",
144348
144105
  interaction: {
144349
- // Name the flow: recordings are concurrent, so several of these lines can
144350
- // 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.
144351
144108
  startedMsg: ({ params }) => `Starting recording of flow ${params.name}`,
144352
144109
  completedMsg: ({ params, result }) => {
144353
144110
  if (!result.restarted) return `Started recording flow ${params.name}`;
@@ -144710,7 +144467,7 @@ function projectIosNode(node, screenW, screenH) {
144710
144467
  children: node.children ?? [],
144711
144468
  // Text hoists only from on-screen nodes (frame is null when the view is
144712
144469
  // scrolled off or zero-area) — otherwise a text assert against an ancestor
144713
- // 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
144714
144471
  // leaf-eligible, so `frame` was computed for any node with text.
144715
144472
  ownText: frame ? node.label ?? "" : "",
144716
144473
  leaf,
@@ -144751,8 +144508,8 @@ var FULL_HIERARCHY_FIELDS = [
144751
144508
  "windowFrame",
144752
144509
  "hidden",
144753
144510
  "alpha",
144754
- // The type directive's focus wait; an older injected framework ignores the
144755
- // 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.
144756
144513
  "firstResponder"
144757
144514
  ];
144758
144515
  async function unreadableHierarchyReason(nativeApi, bundleId) {
@@ -144880,18 +144637,17 @@ function projectAndroidNode(node, screenW, screenH) {
144880
144637
  return {
144881
144638
  skip: skip2,
144882
144639
  children: childNodes(node),
144883
- // Text hoists only from on-screen nodes (frame is null when the bounds clip
144884
- // to zero area) — otherwise a text assert against an ancestor would pass on
144885
- // content the screen doesn't show. Every text-carrying node is
144886
- // 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.
144887
144643
  ownText: frame ? ownText2 : "",
144888
144644
  leaf,
144889
- // A password node shields its placeholder text like any identified node —
144890
- // 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.
144891
144647
  shield: Boolean(identifier) || isPassword,
144892
- // Scroll-clip inputs (see `flattenHoisting`): a scrolling container's raw
144893
- // bounds clip its subtree, so a row it has scrolled out of view — still
144894
- // 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.
144895
144651
  rect,
144896
144652
  scrolls: isUiAutomatorScrollable(attrs)
144897
144653
  };
@@ -144954,9 +144710,9 @@ function projectChromiumNode(node) {
144954
144710
  return {
144955
144711
  skip: false,
144956
144712
  children: node.children,
144957
- // Text hoists only from on-screen nodes — otherwise a text assert against
144958
- // an ancestor would pass on content the screen doesn't show. A password
144959
- // 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.
144960
144716
  ownText: onScreen && !node.password ? nodeText(node) : "",
144961
144717
  leaf,
144962
144718
  shield: Boolean(node.identifier) || node.password === true
@@ -144987,8 +144743,8 @@ function projectVegaNode(node) {
144987
144743
  return {
144988
144744
  skip: false,
144989
144745
  children: node.children,
144990
- // Text hoists only from on-screen nodes — otherwise a text assert against
144991
- // 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.
144992
144748
  ownText: onScreen ? nodeText(node) : "",
144993
144749
  leaf: { ...node, children: [] },
144994
144750
  shield: isAuthoredVegaTestId(node.identifier)
@@ -145826,10 +145582,9 @@ async function waitForIdle(env, step) {
145826
145582
  ok: false,
145827
145583
  indeterminate: true,
145828
145584
  // The underlying reader reports an instrumentation failure, whose remedy
145829
- // (relaunch the app) is the wrong repair for the commonest cause of it
145830
- // here: the app is simply not in the foreground, which reads exactly the
145831
- // same from the tree source. Name that first so the author checks it
145832
- // 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.
145833
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}`
145834
145589
  });
145835
145590
  const degraded = () => ({
@@ -145976,13 +145731,12 @@ function anchoredWarnings(session, steps) {
145976
145731
  var flowFinishRecordingTool = {
145977
145732
  id: "flow-finish-recording",
145978
145733
  interaction: {
145979
- // Name the flow: other recordings stay live across this call, so an
145980
- // 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.
145981
145736
  startedMsg: ({ params }) => `Finishing recording of flow ${params.name}`,
145982
- // `params.name` rather than the basename of `result.path`: the two are the
145983
- // same string on every branch — `assertSafeFlowName` admits no dots or
145984
- // separators, so `getFlowPath` produces `<name>.yaml` and nothing else —
145985
- // 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.
145986
145740
  completedMsg: ({ params }) => `Saved recorded flow ${params.name}`,
145987
145741
  failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to finish recording of flow ${params.name}: ${failureSignal2.error_code}`
145988
145742
  },
@@ -146417,7 +146171,7 @@ function createFlowAddStepTool(registry2) {
146417
146171
  id: "flow-add-step",
146418
146172
  interaction: {
146419
146173
  // Name the flow: recordings are concurrent, so several of these lines can
146420
- // 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.
146421
146175
  startedMsg: ({ params }) => `Adding ${params.command} step to flow ${params.name}`,
146422
146176
  completedMsg: ({ params }) => `Added ${params.command} step to flow ${params.name}`,
146423
146177
  failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to add ${params.command} step to flow ${params.name}: ${failureSignal2.error_code}`
@@ -146497,9 +146251,6 @@ If a step was recorded by mistake, remove it from the .yaml after \`flow-finish-
146497
146251
  toolResult,
146498
146252
  stepCount,
146499
146253
  recorded: summarizeStep(step, stepCount),
146500
- // Host mode: a path. Client mode: the directive that carries the YAML
146501
- // to the client, which IS the persistence mechanism there — the one
146502
- // place the full file still has to travel per step.
146503
146254
  savedTo
146504
146255
  };
146505
146256
  }
@@ -146518,8 +146269,7 @@ var zodSchema64 = external_exports.object({
146518
146269
  var flowInsertEchoTool = {
146519
146270
  id: "flow-add-echo",
146520
146271
  interaction: {
146521
- // Name the flow: recordings are concurrent, so several of these lines can
146522
- // 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.
146523
146273
  startedMsg: ({ params }) => `Adding note to flow ${params.name}`,
146524
146274
  completedMsg: ({ params }) => `Added note to flow ${params.name}`,
146525
146275
  failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to add note to flow ${params.name}: ${failureSignal2.error_code}`
@@ -148834,12 +148584,11 @@ async function runSnapshot(env, opts) {
148834
148584
  baselinePath,
148835
148585
  currentPath,
148836
148586
  outputDir,
148837
- // A crop compares EVERY pixel of the element's region — no masking,
148838
- // wherever the crop sits. Masking a crop's overlap with the screen's
148839
- // top status-bar band would degenerate into comparing nothing for an
148840
- // element inside the band (a vacuous pass); a crop overlapping the
148841
- // band leans on best-effort pinStatusBar until frame resolution
148842
- // 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.
148843
148592
  topMask: cropFrame === void 0 ? "status-bar" : "none",
148844
148593
  // Crop dimensions track the element — size drift must hard-fail below
148845
148594
  // instead of being resampled away like a full-screen scale difference.
@@ -149434,8 +149183,8 @@ Pass exactly one flow source: name for a saved flow under project_root, or flow_
149434
149183
  // One holder per ExecState, shared by nested `run:` flows: `deviceEnv`
149435
149184
  // spreads the reference, so what one step's settle learns about the
149436
149185
  // tree source the next one already has. A `tool: flow-execute` builds
149437
- // its own instead, which is why that step spends this verdict rather
149438
- // 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.
149439
149188
  treeOutage: {},
149440
149189
  flowsDir,
149441
149190
  viaUpload,
@@ -149626,8 +149375,7 @@ function summarize(flowName, deviceId, executionPrerequisite, steps, aborted2) {
149626
149375
  // A cancelled run must never read as PASS — it may contain skips alone
149627
149376
  // (no fail/error report), so the verdict folds the abort in directly. A
149628
149377
  // skip by itself is NOT a failure: an unmet `when:` guard skips its block
149629
- // as a successful omission, and a hard stop already carries its own
149630
- // fail/error report.
149378
+ // as a successful omission.
149631
149379
  ok: failed === 0 && errored === 0 && !aborted2,
149632
149380
  ...aborted2 ? { aborted: true } : {},
149633
149381
  passed,
@@ -151222,11 +150970,10 @@ var awaitUserSelectionTool = {
151222
150970
  failedMsg: ({ failureSignal: failureSignal2 }) => `Failed while waiting for variant selection: ${failureSignal2.error_code}`
151223
150971
  },
151224
150972
  featureFlag: "argent-lens",
151225
- // Hidden entirely while an `argent lens` CLI session owns the preview window:
151226
- // there, the user's picks are relayed into the agent's terminal as a normal
151227
- // message (see `argent-cli/src/lens.ts`), so the agent never blocks on a pick.
151228
- // Hiding the tool (rather than telling the agent "don't call it") keeps the
151229
- // 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.
151230
150977
  hideWhen: () => variantProposalStore.isCliSession(),
151231
150978
  description: `Block until the human finishes picking among the variants you proposed (the ONE blocking call).
151232
150979
 
@@ -151901,10 +151648,8 @@ function createPreviewWindowManager(opts = {}) {
151901
151648
  const launchBin = wrapperBin ?? electronBin;
151902
151649
  const launchArgs = wrapperBin ? ["--no-sandbox", mainScript] : [mainScript];
151903
151650
  const next = (0, import_node_child_process33.spawn)(launchBin, launchArgs, {
151904
- // Strip ELECTRON_RUN_AS_NODE so the child boots as a GUI Electron app, not
151905
- // a bare Node runtime (see electronGuiChildEnv). An Electron-based MCP
151906
- // host puts it in our env, and inheriting it makes main.cjs crash at
151907
- // 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.
151908
151653
  env: electronGuiChildEnv({ ARGENT_PREVIEW_URL: url2 }),
151909
151654
  stdio: ["pipe", "ignore", "pipe"]
151910
151655
  });
@@ -152053,11 +151798,10 @@ function start() {
152053
151798
  const { stop: stopWatcher, ready: watcherReady } = startSimulatorWatcher(registry2);
152054
151799
  let server = null;
152055
151800
  const previewWindow = createPreviewWindowManager({
152056
- // If Electron can't launch (it's an optionalDependency — absent on
152057
- // headless/CI hosts), fail fast: unblock any parked await_user_selection
152058
- // with the browser fallback URL instead of stranding it for the full
152059
- // timeout. `previewWindowBaseUrl` (declared just below) is only read when
152060
- // 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.
152061
151805
  onLaunchFailure: (err) => variantProposalStore.notifyWindowUnavailable(err.message, previewWindowBaseUrl())
152062
151806
  });
152063
151807
  const previewWindowBaseUrl = () => {