@swmansion/argent 0.24.1-next.9 → 0.25.0

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.
package/README.md CHANGED
@@ -1,215 +1,40 @@
1
1
  <br/>
2
- <p align="center">
3
- <a href="https://argent.swmansion.com">
4
- <img width="1100" height="382" alt="argent-header" src="https://github.com/user-attachments/assets/6cec01d5-da3c-4b6c-97c3-0374a63c213c" />
5
- </a>
6
- </p>
2
+ <p align="center"><a href="https://argent.swmansion.com"><img width="1100" height="382" alt="argent-header" src="https://github.com/user-attachments/assets/6cec01d5-da3c-4b6c-97c3-0374a63c213c" /></a></p>
7
3
 
8
4
  [![Ad](https://swm-delivery.com/www/images/zone-gh-argent-1?n=1)](https://swm-delivery.com/www/delivery/ck-slug.php?zoneid=zone-gh-argent-1&n=1)
9
5
  [![Ad](https://swm-delivery.com/www/images/zone-gh-argent-2?n=1)](https://swm-delivery.com/www/delivery/ck-slug.php?zoneid=zone-gh-argent-2&n=1)
10
6
  [![Ad](https://swm-delivery.com/www/images/zone-gh-argent-3?n=1)](https://swm-delivery.com/www/delivery/ck-slug.php?zoneid=zone-gh-argent-3&n=1)
11
7
 
12
- **[Argent](https://argent.swmansion.com)** is an **agentic toolkit** that gives your AI assistant direct access to iOS Simulators, Android emulators and physical devices, TVs (Apple TV, Android TV, Fire TV) and Electron/Chromium desktop and web apps. Ask it to tap a button, run a profiler or reproduce an issue manually - all from within your CLI, without switching context.
8
+ **[Argent](https://argent.swmansion.com)** gives your AI assistant direct control of iOS simulators, Android emulators and devices, TVs (Apple TV, Android TV, Fire TV) and Electron/Chromium apps. Tap a button, profile a screen, reproduce a bug - without leaving the CLI.
13
9
 
14
10
  ```bash
15
- npx @swmansion/argent@latest init
16
- # or, in a pnpm project (where npm's devEngines check may refuse to run npx):
17
- pnpm dlx @swmansion/argent@latest init
11
+ npx @swmansion/argent@latest init # pnpm: pnpm dlx @swmansion/argent@latest init
18
12
  ```
19
13
 
20
- ## Supported platforms
14
+ <p align="center"><img src="https://github.com/software-mansion/argent/blob/main/assets/showcase.gif" alt="argent showcase video gif" width="100%" /></p>
21
15
 
22
- Argent drives a growing set of targets through a single toolkit, each with the right interaction model - touch, remote or mouse:
16
+ ## What it does
23
17
 
24
- | Platform | Targets | Interaction |
25
- | ----------------- | ----------------------------------------------------------------------- | ---------------- |
26
- | **iOS** | Simulators and physical iPhones | Touch / gesture |
27
- | **Android** | Emulators (AVDs) and physical devices over adb | Touch / gesture |
28
- | **TV** | Apple TV (tvOS), Android TV / Google TV, Amazon Fire TV (Vega) | D-pad / remote |
29
- | **Desktop & web** | Electron and Chromium apps (incl. React Native Web / Expo web) over CDP | Mouse / keyboard |
18
+ - **Interact** - tap, swipe, gesture, type, hardware buttons; D-pad on TV; mouse and keyboard on desktop.
19
+ - **Flows** - record a path once, replay it deterministically as a repro or smoke test.
20
+ - **Visual regression** - diff a baseline against a live capture, OCR- and font-aware.
21
+ - **Profiling** - Hermes, React DevTools, Xcode Instruments, Perfetto: renders, CPU hotspots, hangs.
22
+ - **Debugging** - logs, network (`fetch` and native), JS evaluation, native and React trees.
23
+ - **React Native** - build, launch and iterate, no extra setup.
30
24
 
31
- ## Capabilities
25
+ Ask your assistant _"What can Argent do?"_ to list every tool.
32
26
 
33
- - **Autonomous mobile, TV and desktop development** - Allow your agent to work with iOS, Android, TV and Electron/web apps on its own - let it build, open, interact with the app and debug it. Ask for reproducing issues, testing features manually, profiling your app and much more, without ever interrupting your work.
34
- - **UI interaction** - Give your agent the full control toolkit - tapping, swiping, pinching, typing, gestures and hardware buttons on mobile; the directional remote on TV; mouse, scroll and drag on desktop/web. Let it navigate your app exactly as a user would, without lifting a finger.
35
- - **Record & replay flows** - Capture a sequence of interactions once and let your agent replay it deterministically, so manual repros and smoke tests become repeatable.
36
- - **Visual regression** - Diff two screenshots (or a saved baseline against a live capture) with OCR- and font-aware comparison to catch unintended UI changes.
37
- - **Profiling with batteries included** - Argent can perform and analyze React Native (Hermes), React DevTools and native (Xcode Instruments / Android Perfetto) profiling sessions - down to fiber renders, CPU hotspots and cross-correlated commit-vs-hang reports. Get comprehensive summaries and ask to optimise your app where you find fit.
38
- - **Debugging and diagnostics** - Let your agent inspect logs, capture network traffic (JS `fetch` and native), evaluate JS in the running app, walk the native UIKit and React component trees, and reproduce failing states - so you can jump straight to the fix.
39
- - **Desktop & web control** - For Electron and Chromium apps your agent can drive tabs, read and write cookies and storage, walk the DOM and inspect network over the Chrome DevTools Protocol.
40
- - **React Native out of the box** - Argent works with React Native apps natively, so your agent can build, launch, and iterate on your RN project the same way it would any native app - no extra setup required.
27
+ ## Documentation
41
28
 
42
- > **Tip:** Once installed, ask your assistant _"What can Argent do?"_ - it will walk you through all capabilities available.
43
-
44
- <br/>
45
- <p align="center">
46
- <img src="https://github.com/software-mansion/argent/blob/main/assets/showcase.gif" alt="argent showcase video gif" width="100%" />
47
- </p>
48
-
49
- ---
50
-
51
- ## Installation
52
-
53
- #### Prerequisites
54
-
55
- - **Node.js 20.12** or later
56
- - For iOS / tvOS: macOS with **Xcode** installed (Apple TV uses tvOS simulators — Xcode downloads the tvOS runtime on demand)
57
- - For Android / Android TV: **Android SDK Platform Tools** (`adb`) on `PATH`, and the **Android Emulator** package if you want to boot AVDs from Argent. Create AVDs via Android Studio or `avdmanager`.
58
- - For Fire TV (Vega): the **Vega SDK** (`vega` CLI) on `PATH`
59
- - For Electron / Chromium: nothing extra to control an already-running app - just launch it with `--remote-debugging-port`, or let Argent spawn your Electron app for you
60
-
61
- ##### Linux host: extra prerequisites for Android emulators
62
-
63
- Argent runs Android emulators on Linux but the default install can be slow if a few host-side knobs aren't right. Cover these once and the experience matches macOS:
64
-
65
- - **KVM access.** The emulator falls back to slow software emulation (TCG) without `/dev/kvm`. Make sure virtualization is enabled in BIOS/UEFI (`vmx` for Intel, `svm` for AMD in `/proc/cpuinfo`) and that your user can read/write `/dev/kvm` — on most distros that means joining the `kvm` group:
66
-
67
- ```bash
68
- sudo usermod -aG kvm "$USER"
69
- # log out and back in so the new group takes effect
70
- ```
71
-
72
- - **GPU mode (`-gpu swiftshader` on Linux, override available).** The Android emulator's Linux GPU story is messy: `-gpu auto` frequently resolves to lavapipe (slow software Vulkan via host libvulkan, ~10× cold-boot regression on flagship hardware), and `-gpu host` silently produces a corrupted or black emulator window on hosts with non-trivial GL stacks — dual-GPU / Optimus laptops, NVIDIA + Mesa coexistence via libglvnd, Wayland sessions on hybrid graphics, headless / containerized hosts. The failure mode is invisible to argent's framebuffer-based screenshot tool, so an agent reports success while the developer sees a black window.
73
-
74
- Argent picks `-gpu swiftshader` on Linux for universal compatibility: it sidesteps the host GL stack entirely and renders via the emulator's bundled SwiftShader. On modern multi-core machines this is indistinguishably smooth from hardware-accelerated `-gpu host` (and far faster than lavapipe).
75
-
76
- Override with the `ARGENT_EMULATOR_GPU_MODE` env var if you've verified `-gpu host` works on your machine (typical single-GPU Mesa box with a healthy X session):
77
-
78
- ```bash
79
- ARGENT_EMULATOR_GPU_MODE=host argent ...
80
- ```
81
-
82
- Argent's boot-device preflight prints a warning if `/dev/kvm` isn't usable — the condition that causes a 10–50× TCG-vs-KVM slowdown.
83
-
84
- - **System image.** Prefer the `default` or `google_apis` variants of `x86_64` system images for headless agent workflows; `google_apis_playstore` adds noticeable boot-time CPU churn from Play services. Always pick `x86_64` on Intel/AMD hosts — ARM images run via QEMU translation and are dramatically slower.
85
-
86
- - **AVD config.** AVDs created via `avdmanager create avd` default to `hw.gpu.enabled=no`. Argent overrides this with an explicit `-gpu` arg at launch (so the on-disk config doesn't need editing). For the smoothest experience under heavy native builds (gradle compilations alongside the AVD), bump the AVD's RAM and CPU count — edit `~/.android/avd/<name>.avd/config.ini`:
87
-
88
- ```
89
- hw.ramSize = 8192
90
- hw.cpu.ncore = 6
91
- vm.heapSize = 512
92
- ```
93
-
94
- Stock 2 GB / 4 vCPU AVDs can be CPU-starved into wedged-system_server states by a concurrent gradle/Kotlin compile.
95
-
96
- - **Headless / CI mode (`ARGENT_EMULATOR_NO_WINDOW=1`).** Argent shows the emulator window by default so a local developer can see the AVD UI. In a headless context — CI runner, container, or a Wayland-only session where the emulator's bundled Qt has no `wayland` platform plugin and SIGABRTs on the crash-consent dialog — opt out by exporting `ARGENT_EMULATOR_NO_WINDOW=1` before starting the tool-server. This appends `-no-window` to the spawn args, selecting `qemu-system-x86_64-headless` which doesn't need a Qt window. Argent's screencap-based screenshot tool reads the in-memory framebuffer correctly without a visible window.
97
-
98
- #### Run `init` in your project
99
-
100
- From your project root:
101
-
102
- ```bash
103
- npx @swmansion/argent@latest init
104
- # or, in a pnpm project (where npm's devEngines check may refuse to run npx):
105
- pnpm dlx @swmansion/argent@latest init
106
- ```
107
-
108
- This command triggers an installation wizard which:
109
-
110
- - Installs `@swmansion/argent` globally
111
- - Detects your editor and registers the MCP server
112
- - Copies skills, rules, and agent definitions into your workspace
113
-
114
- #### Prefer a manual install?
115
-
116
- ```bash
117
- npm install -g @swmansion/argent
118
- argent init
119
- ```
120
-
121
- #### Share Argent with your team (committable install)
122
-
123
- By default Argent installs **globally**. To version Argent _with your repo_ so every
124
- teammate gets the same setup on `npm install` — no per-developer global install, no
125
- `argent init` — choose the local mode:
126
-
127
- ```bash
128
- npx @swmansion/argent@latest init --local
129
- # or, in a pnpm project:
130
- pnpm dlx @swmansion/argent@latest init --local
131
- ```
132
-
133
- > Note: in a freshly `pnpm init`-ed project, `npx` itself may refuse to run
134
- > (npm's `devEngines` check) — use the `pnpm dlx` form there.
135
-
136
- This adds `@swmansion/argent` to your project's `devDependencies` and writes MCP
137
- configs that launch the project-local copy (`node node_modules/@swmansion/argent/dist/cli.js mcp`).
138
- Commit `package.json` + your lockfile, the generated MCP config (`.mcp.json`,
139
- `.cursor/mcp.json`, …), `.argent/install.json`, and the skills/rules/agents files.
140
- Teammates then just run `npm install`.
141
-
142
- Pass `--global` to force the default mode in scripts; `--local` and `--global` are
143
- mutually exclusive. A non-interactive (`--yes`) run defaults to global unless the
144
- project already opted into local mode (a committed `.argent/install.json`, or
145
- `@swmansion/argent` declared in the project's own `package.json`).
146
-
147
- > In local mode the committed MCP config runs the project-local copy, so the bare
148
- > `argent` command is **not** on teammates' `PATH`. Note that `npm install` builds
149
- > Argent's native deps (`tree-sitter`) on each machine — prebuilt for macOS, Linux
150
- > x64, and Windows x64; other targets (Linux arm64, Windows arm) compile from source
151
- > and need a C/C++ toolchain.
152
-
153
- ## CLI Reference
154
-
155
- | Command | Description |
156
- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
157
- | `argent init` | Install and configure MCP in the current workspace (`--global` default, `--local` for a committable devDependency) |
158
- | `argent install` | Alias for `init` command |
159
- | `argent update` | Pull the latest version and refresh workspace configuration (acts on the present install — both when a global install and a project devDependency coexist; `--global`/`--local` select explicitly) |
160
- | `argent uninstall` | Unregister the MCP server and uninstall the package (`--global`/`--local` choose which install — and its configs — is removed; non-interactive runs never remove a coexisting global install) |
161
- | `argent remove` | Alias for `uninstall` command |
162
- | `argent mcp` | Start MCP server instance, used internally by agent |
163
- | `argent tools` | List tools exposed by the tool-server (`describe <name>` for details) |
164
- | `argent run` | Invoke a tool by name |
165
- | `argent server` | Manage the shared tool-server: `start` / `status` / `stop` / `logs` |
166
- | `argent lens` | Open Argent Lens bound to a fresh coding-agent session — Claude by default, `--agent` selects codex/gemini/opencode/cursor (macOS; behind the `argent-lens` flag — run `argent enable argent-lens` first) |
167
- | `argent link` | Route client requests to a remote tool-server |
168
- | `argent unlink` | Remove the persisted remote tool-server link |
169
- | `argent enable` | Enable a predefined feature flag (`--scope project` for project-local) |
170
- | `argent disable` | Disable a feature flag (`--scope project` for project-local) |
171
- | `argent flags` | List available feature flags and their state |
172
- | `argent providers` | Inspect and manage external device providers: `list` / `check` / `publish` / `withdraw` / `prune` |
173
- | `argent telemetry` | Manage telemetry: `status` / `enable` / `disable` |
174
-
175
- ## Supported Editors
176
-
177
- `argent init` auto-detects and configures MCP for:
178
-
179
- | Editor | Config location |
180
- | ----------- | --------------------------------------------------------------------------- |
181
- | Claude Code | `.mcp.json` (project) or `~/.claude.json` (global) |
182
- | Cursor | `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global) |
183
- | VS Code | `.vscode/mcp.json` |
184
- | Windsurf | `~/.codeium/windsurf/mcp_config.json` (global) |
185
- | Zed | `.zed/settings.json` (project) or `~/.config/zed/settings.json` (global) |
186
- | Gemini CLI | `.gemini/settings.json` |
187
- | Codex CLI | `.codex/config.toml` (project) or `~/.codex/config.toml` (global) |
188
- | Hermes | `~/.hermes/config.yaml` (global) |
189
- | opencode | `opencode.json` (project) or `~/.config/opencode/opencode.json` (global) |
190
- | Kiro | `.kiro/settings/mcp.json` (project) or `~/.kiro/settings/mcp.json` (global) |
29
+ [Installation](https://docs.swmansion.com/argent/docs/fundamentals/installation) · [Platforms](https://docs.swmansion.com/argent/docs/fundamentals/supported-platforms) · [CLI](https://docs.swmansion.com/argent/docs/reference/cli) · [Editors](https://docs.swmansion.com/argent/docs/reference/editors)
191
30
 
192
31
  ## Privacy
193
32
 
194
- Argent collects opt-out usage and diagnostic telemetry to help us prioritise features and fix what breaks.
195
-
196
- You can opt out at any time:
197
-
198
- ```bash
199
- argent telemetry disable # check status with: argent telemetry status
200
- ```
201
-
202
- For the full details — see the [Argent Privacy Notice (Telemetry)](https://github.com/software-mansion/argent/blob/main/Telemetry.md).
33
+ Telemetry is opt-out: `argent telemetry disable`. See the [Privacy Notice](https://github.com/software-mansion/argent/blob/main/Telemetry.md).
203
34
 
204
35
  ## License
205
36
 
206
- Argent uses a mixed licensing model.
207
-
208
- **Source code** is released under the [Apache License 2.0](LICENSE.txt).
209
-
210
- **Proprietary binaries** (the per-platform `bin/<platform>/simulator-server` and `bin/darwin/ax-service` executables and the `.dylib` files in `native-devtools-ios`) are the intellectual property of Software Mansion S.A. and are licensed solely for use within this project. Decompiling, reverse-engineering, or redistributing them without explicit written permission is prohibited.
211
-
212
- By using Argent, you acknowledge and agree to this structure. See [LICENSE](https://github.com/software-mansion/argent/blob/main/LICENSE.txt) for full details.
37
+ Argent uses a mixed licensing model. **Source code** is released under the [Apache License 2.0](LICENSE.txt). **Proprietary binaries** (the per-platform `bin/<platform>/simulator-server` and `bin/darwin/ax-service` executables and the `.dylib` files in `native-devtools-ios`) are the intellectual property of Software Mansion S.A. and are licensed solely for use within this project. Decompiling, reverse-engineering, or redistributing them without explicit written permission is prohibited. By using Argent, you acknowledge and agree to this structure. See [LICENSE](https://github.com/software-mansion/argent/blob/main/LICENSE.txt) for full details.
213
38
 
214
39
  ## Argent is created by Software Mansion
215
40
 
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/cli-cmds.mjs CHANGED
@@ -20976,31 +20976,51 @@ function toAttributes2(record2) {
20976
20976
  }
20977
20977
  return attributes;
20978
20978
  }
20979
- var OTLP_HEADER_ENV_VARS = [
20979
+ var CLEARED_OTLP_ENV_VARS = [
20980
20980
  "OTEL_EXPORTER_OTLP_HEADERS",
20981
- "OTEL_EXPORTER_OTLP_LOGS_HEADERS"
20981
+ "OTEL_EXPORTER_OTLP_LOGS_HEADERS",
20982
+ "OTEL_EXPORTER_OTLP_CERTIFICATE",
20983
+ "OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE",
20984
+ "OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE",
20985
+ "OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE",
20986
+ "OTEL_EXPORTER_OTLP_CLIENT_KEY",
20987
+ "OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY"
20982
20988
  ];
20989
+ var NO_COMPRESSION = "none";
20983
20990
  function createExporter(config3) {
20984
- const saved = OTLP_HEADER_ENV_VARS.map((name) => [name, process.env[name]]);
20985
- for (const name of OTLP_HEADER_ENV_VARS) delete process.env[name];
20991
+ const saved = CLEARED_OTLP_ENV_VARS.map((name) => [name, process.env[name]]);
20992
+ for (const name of CLEARED_OTLP_ENV_VARS) delete process.env[name];
20986
20993
  try {
20987
20994
  return new OTLPLogExporter({
20988
20995
  url: config3.endpoint,
20989
20996
  headers: { authorization: `Bearer ${config3.token}` },
20990
20997
  timeoutMillis: EXPORT_TIMEOUT_MS,
20998
+ // Left unset, the SDK takes this from OTEL_EXPORTER_OTLP_COMPRESSION (or
20999
+ // its _LOGS_ variant), so a machine that already runs OpenTelemetry would
21000
+ // gzip argent's batches - a request shaped differently from the one the
21001
+ // ingestion side is sized and tested against, decided by a variable set
21002
+ // for something else. Unlike the variables cleared above, one explicit
21003
+ // value settles it: the SDK takes user-provided over env here rather than
21004
+ // merging the two or reading anything to decide.
21005
+ compression: NO_COMPRESSION,
20991
21006
  // The exporter bounds a request with `req.setTimeout()`, which Node only
20992
21007
  // arms once the socket is CONNECTED — so a collector whose address drops
20993
21008
  // packets (corporate egress filter, dead host behind a firewall) leaves a
20994
- // socket stuck connecting that no export deadline can reach, holding the
20995
- // process open for the OS connect timeout after shutdown() has resolved.
20996
- // The agent's socket timeout is armed when the socket is CREATED, so it
20997
- // covers connect too: it fires, the request emits 'timeout', and the
20998
- // exporter's handler destroys it.
21009
+ // socket stuck in the connecting state that no export deadline can reach,
21010
+ // holding the process open for the OS connect timeout (~75s on macOS)
21011
+ // after shutdown() has already resolved. The agent's socket timeout is
21012
+ // armed when the socket is CREATED, so it also covers connect: it fires,
21013
+ // the request emits 'timeout', and the exporter's handler destroys it.
20999
21014
  //
21000
- // keepAlive is restated because supplying httpAgentOptions at all
21001
- // replaces the agent the SDK would otherwise build, whose default is
21002
- // keepAlive: true; without it the long-lived tool-server pays a fresh
21003
- // TCP+TLS handshake for every batch.
21015
+ // keepAlive is restated to match the agent the SDK would otherwise build,
21016
+ // whose default is keepAlive: true supplying httpAgentOptions at all
21017
+ // replaces it, and keepAlive: false would open and close a socket per
21018
+ // request. It does NOT save the handshake across the 10s batch cadence:
21019
+ // this same timeout reaps an idle keep-alive socket, so one parked in the
21020
+ // pool between batches is destroyed at EXPORT_TIMEOUT_MS, long before the
21021
+ // next batch reaches for it. Reuse survives only for batches flushed
21022
+ // within the timeout of each other, e.g. a force-flush of a queue that
21023
+ // spans more than one batch.
21004
21024
  httpAgentOptions: { timeout: EXPORT_TIMEOUT_MS, keepAlive: true }
21005
21025
  });
21006
21026
  } finally {
@@ -21837,7 +21857,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
21837
21857
  var SESSION_ID2 = randomUUID5();
21838
21858
  function readCliVersion() {
21839
21859
  if (true) {
21840
- return "0.24.1-next.9";
21860
+ return "0.25.0";
21841
21861
  }
21842
21862
  return "0.0.0";
21843
21863
  }
@@ -15468,31 +15468,51 @@ function toAttributes2(record) {
15468
15468
  }
15469
15469
  return attributes;
15470
15470
  }
15471
- var OTLP_HEADER_ENV_VARS = [
15471
+ var CLEARED_OTLP_ENV_VARS = [
15472
15472
  "OTEL_EXPORTER_OTLP_HEADERS",
15473
- "OTEL_EXPORTER_OTLP_LOGS_HEADERS"
15473
+ "OTEL_EXPORTER_OTLP_LOGS_HEADERS",
15474
+ "OTEL_EXPORTER_OTLP_CERTIFICATE",
15475
+ "OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE",
15476
+ "OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE",
15477
+ "OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE",
15478
+ "OTEL_EXPORTER_OTLP_CLIENT_KEY",
15479
+ "OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY"
15474
15480
  ];
15481
+ var NO_COMPRESSION = "none";
15475
15482
  function createExporter(config) {
15476
- const saved = OTLP_HEADER_ENV_VARS.map((name) => [name, process.env[name]]);
15477
- for (const name of OTLP_HEADER_ENV_VARS) delete process.env[name];
15483
+ const saved = CLEARED_OTLP_ENV_VARS.map((name) => [name, process.env[name]]);
15484
+ for (const name of CLEARED_OTLP_ENV_VARS) delete process.env[name];
15478
15485
  try {
15479
15486
  return new OTLPLogExporter({
15480
15487
  url: config.endpoint,
15481
15488
  headers: { authorization: `Bearer ${config.token}` },
15482
15489
  timeoutMillis: EXPORT_TIMEOUT_MS,
15490
+ // Left unset, the SDK takes this from OTEL_EXPORTER_OTLP_COMPRESSION (or
15491
+ // its _LOGS_ variant), so a machine that already runs OpenTelemetry would
15492
+ // gzip argent's batches - a request shaped differently from the one the
15493
+ // ingestion side is sized and tested against, decided by a variable set
15494
+ // for something else. Unlike the variables cleared above, one explicit
15495
+ // value settles it: the SDK takes user-provided over env here rather than
15496
+ // merging the two or reading anything to decide.
15497
+ compression: NO_COMPRESSION,
15483
15498
  // The exporter bounds a request with `req.setTimeout()`, which Node only
15484
15499
  // arms once the socket is CONNECTED — so a collector whose address drops
15485
15500
  // packets (corporate egress filter, dead host behind a firewall) leaves a
15486
- // socket stuck connecting that no export deadline can reach, holding the
15487
- // process open for the OS connect timeout after shutdown() has resolved.
15488
- // The agent's socket timeout is armed when the socket is CREATED, so it
15489
- // covers connect too: it fires, the request emits 'timeout', and the
15490
- // exporter's handler destroys it.
15501
+ // socket stuck in the connecting state that no export deadline can reach,
15502
+ // holding the process open for the OS connect timeout (~75s on macOS)
15503
+ // after shutdown() has already resolved. The agent's socket timeout is
15504
+ // armed when the socket is CREATED, so it also covers connect: it fires,
15505
+ // the request emits 'timeout', and the exporter's handler destroys it.
15491
15506
  //
15492
- // keepAlive is restated because supplying httpAgentOptions at all
15493
- // replaces the agent the SDK would otherwise build, whose default is
15494
- // keepAlive: true; without it the long-lived tool-server pays a fresh
15495
- // TCP+TLS handshake for every batch.
15507
+ // keepAlive is restated to match the agent the SDK would otherwise build,
15508
+ // whose default is keepAlive: true supplying httpAgentOptions at all
15509
+ // replaces it, and keepAlive: false would open and close a socket per
15510
+ // request. It does NOT save the handshake across the 10s batch cadence:
15511
+ // this same timeout reaps an idle keep-alive socket, so one parked in the
15512
+ // pool between batches is destroyed at EXPORT_TIMEOUT_MS, long before the
15513
+ // next batch reaches for it. Reuse survives only for batches flushed
15514
+ // within the timeout of each other, e.g. a force-flush of a queue that
15515
+ // spans more than one batch.
15496
15516
  httpAgentOptions: { timeout: EXPORT_TIMEOUT_MS, keepAlive: true }
15497
15517
  });
15498
15518
  } finally {
@@ -16690,7 +16710,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
16690
16710
  var SESSION_ID = randomUUID4();
16691
16711
  function readCliVersion() {
16692
16712
  if (true) {
16693
- return "0.24.1-next.9";
16713
+ return "0.25.0";
16694
16714
  }
16695
16715
  return "0.0.0";
16696
16716
  }
@@ -93813,31 +93813,51 @@ function toAttributes2(record2) {
93813
93813
  }
93814
93814
  return attributes;
93815
93815
  }
93816
- var OTLP_HEADER_ENV_VARS = [
93816
+ var CLEARED_OTLP_ENV_VARS = [
93817
93817
  "OTEL_EXPORTER_OTLP_HEADERS",
93818
- "OTEL_EXPORTER_OTLP_LOGS_HEADERS"
93818
+ "OTEL_EXPORTER_OTLP_LOGS_HEADERS",
93819
+ "OTEL_EXPORTER_OTLP_CERTIFICATE",
93820
+ "OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE",
93821
+ "OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE",
93822
+ "OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE",
93823
+ "OTEL_EXPORTER_OTLP_CLIENT_KEY",
93824
+ "OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY"
93819
93825
  ];
93826
+ var NO_COMPRESSION = "none";
93820
93827
  function createExporter(config2) {
93821
- const saved = OTLP_HEADER_ENV_VARS.map((name) => [name, process.env[name]]);
93822
- for (const name of OTLP_HEADER_ENV_VARS) delete process.env[name];
93828
+ const saved = CLEARED_OTLP_ENV_VARS.map((name) => [name, process.env[name]]);
93829
+ for (const name of CLEARED_OTLP_ENV_VARS) delete process.env[name];
93823
93830
  try {
93824
93831
  return new OTLPLogExporter({
93825
93832
  url: config2.endpoint,
93826
93833
  headers: { authorization: `Bearer ${config2.token}` },
93827
93834
  timeoutMillis: EXPORT_TIMEOUT_MS,
93835
+ // Left unset, the SDK takes this from OTEL_EXPORTER_OTLP_COMPRESSION (or
93836
+ // its _LOGS_ variant), so a machine that already runs OpenTelemetry would
93837
+ // gzip argent's batches - a request shaped differently from the one the
93838
+ // ingestion side is sized and tested against, decided by a variable set
93839
+ // for something else. Unlike the variables cleared above, one explicit
93840
+ // value settles it: the SDK takes user-provided over env here rather than
93841
+ // merging the two or reading anything to decide.
93842
+ compression: NO_COMPRESSION,
93828
93843
  // The exporter bounds a request with `req.setTimeout()`, which Node only
93829
93844
  // arms once the socket is CONNECTED — so a collector whose address drops
93830
93845
  // packets (corporate egress filter, dead host behind a firewall) leaves a
93831
- // socket stuck connecting that no export deadline can reach, holding the
93832
- // process open for the OS connect timeout after shutdown() has resolved.
93833
- // The agent's socket timeout is armed when the socket is CREATED, so it
93834
- // covers connect too: it fires, the request emits 'timeout', and the
93835
- // exporter's handler destroys it.
93846
+ // socket stuck in the connecting state that no export deadline can reach,
93847
+ // holding the process open for the OS connect timeout (~75s on macOS)
93848
+ // after shutdown() has already resolved. The agent's socket timeout is
93849
+ // armed when the socket is CREATED, so it also covers connect: it fires,
93850
+ // the request emits 'timeout', and the exporter's handler destroys it.
93836
93851
  //
93837
- // keepAlive is restated because supplying httpAgentOptions at all
93838
- // replaces the agent the SDK would otherwise build, whose default is
93839
- // keepAlive: true; without it the long-lived tool-server pays a fresh
93840
- // TCP+TLS handshake for every batch.
93852
+ // keepAlive is restated to match the agent the SDK would otherwise build,
93853
+ // whose default is keepAlive: true supplying httpAgentOptions at all
93854
+ // replaces it, and keepAlive: false would open and close a socket per
93855
+ // request. It does NOT save the handshake across the 10s batch cadence:
93856
+ // this same timeout reaps an idle keep-alive socket, so one parked in the
93857
+ // pool between batches is destroyed at EXPORT_TIMEOUT_MS, long before the
93858
+ // next batch reaches for it. Reuse survives only for batches flushed
93859
+ // within the timeout of each other, e.g. a force-flush of a queue that
93860
+ // spans more than one batch.
93841
93861
  httpAgentOptions: { timeout: EXPORT_TIMEOUT_MS, keepAlive: true }
93842
93862
  });
93843
93863
  } finally {
@@ -94682,7 +94702,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
94682
94702
  var SESSION_ID = (0, import_node_crypto3.randomUUID)();
94683
94703
  function readCliVersion() {
94684
94704
  if (true) {
94685
- return "0.24.1-next.9";
94705
+ return "0.25.0";
94686
94706
  }
94687
94707
  return "0.0.0";
94688
94708
  }
@@ -96039,7 +96059,7 @@ var import_node_path5 = __toESM(require("node:path"));
96039
96059
  var import_semver2 = __toESM(require_semver2());
96040
96060
 
96041
96061
  // ../tool-server/package.json
96042
- var version2 = "0.24.0";
96062
+ var version2 = "0.25.0";
96043
96063
 
96044
96064
  // ../tool-server/src/utils/update-checker.ts
96045
96065
  var import_update_core = __toESM(require_dist4());
@@ -154349,85 +154369,11 @@ function createRunFlowTool(registry2) {
154349
154369
  completedMsg: ({ params }) => `Ran flow ${displayFlowName(params)}`,
154350
154370
  failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to run flow ${displayFlowName(params)}: ${failureSignal2.error_code}`
154351
154371
  },
154352
- description: `Run a saved flow from the .argent/flows/ directory, or an explicit boundary-managed flow_path.
154353
- Use when a scenario is already authored as YAML and the whole of it should replay in one call with a
154354
- per-step verdict; reach for the individual gesture tools when nothing is authored yet, and for
154355
- run-sequence when the steps are an ad-hoc list rather than a stored flow.
154356
- Steps run in order: \`launch\` starts an app from scratch (terminate + relaunch) and waits until it is
154357
- ready (on iOS it also pins later element lookups to that app rather than auto-detecting the frontmost
154358
- one); \`tool\` calls dispatch through the registry (a raw \`tool\` step ends that iOS pin, so lookups
154359
- auto-detect again until the next \`launch\`, though a tool that cannot change the foreground app leaves the
154360
- launched id as a fallback for a timed-out auto-detect, and \`launch-app\`/\`restart-app\` leave the id they
154361
- started as that fallback instead); \`tap\`/\`long-press\`/\`type\` resolve a selector to an
154362
- element and act on it (\`tap: { on, times: 2 }\` double-taps; \`long-press: { on, duration }\` presses and
154363
- holds; \`tap\`/\`long-press\` alternatively take a raw normalized point \u2014 bare \`{ x, y }\` or \`on: { x, y }\`;
154364
- any selector may scope its matches geometrically, the CSS combinators read off frames: \`within: <selector>\`
154365
- (descendant \u2014 inside that container's frame), \`after: <selector>\` (CSS \`~\` \u2014 following it in reading
154366
- order), \`next: <selector>\` (CSS \`+\` \u2014 the nearest such follower, which unlike CSS reaches past a
154367
- non-matching neighbour rather than failing), plus \`any: true\` (CSS \`*\` \u2014 legal only WITH a scope and
154368
- never beside text/id/role). Scopes nest to disambiguate \u2014 \`within: { id: card, within: { id: list } }\`
154369
- reads "inside card inside list", each container's frame inside the next);
154370
- \`swipe\` performs one finger flick (\`swipe: left\`, or \`swipe: { from?, direction|to|by, momentum?, duration? }\` \u2014
154371
- direction is the FINGER's travel, the opposite sense of scroll-to's content direction; \`by: { x?, y? }\` \u2014 signed
154372
- 0\u20131 screen fractions, combined length at least 0.03 (a diagonal clears it where neither axis does); duration in ms,
154373
- default 300, minimum 150, maximum 10000; each bound is a parse error that rejects the file before any step runs;
154374
- \`momentum: false\` lands exactly where the finger lifts instead of flinging);
154375
- \`scroll-to\` scrolls (momentum-free) until a target is visible; \`pinch\` zooms
154376
- (\`pinch: { on?, scale }\` \u2014 scale > 1 in, < 1 out; screen center when \`on\` is omitted); \`rotate\` is the
154377
- two-finger rotation gesture (\`rotate: { on?, by }\` \u2014 degrees, + clockwise, within \xB13000\xB0; screen center
154378
- when \`on\` is omitted; distinct from the \`rotate\` tool, which changes device orientation); \`await\` waits
154379
- for a UI condition, and additionally takes the one condition that has no selector: \`idle: true\` waits
154380
- until the screen has content and stops moving in BOTH the UI tree and the rendered pixels (it never
154381
- fails a run \u2014 a screen that never settles passes carrying a \`warning\`, which is what makes it safe to
154382
- persist; the one idle outcome that does stop the run is an \`error\` for a tree source THIS step could not
154383
- read at all \u2014 a broken window rather than a verdict about the app, which leaves the run not-ok and skips
154384
- every later step; it says nothing about WHICH screen settled \u2014 a dropped tap leaves the source screen
154385
- perfectly idle \u2014 so pair it with the element check that names the destination); \`wait\` pauses for a fixed number of milliseconds; \`assert\` checks one now; \`snapshot\`
154386
- diffs a screenshot \u2014 or, with \`cropOn: <selector>\`, one element's cropped region \u2014 against a stored
154387
- baseline (a missing baseline fails the step \u2014 set updateBaselines to adopt the current screen; a
154388
- cropped element whose size drifted fails on dimensions); \`echo\` annotates; \`run\` executes another flow
154389
- inline \u2014 a YAML path resolved against the directory of the flow file that references it (co-located
154390
- runs only); \`script\` runs a local .mjs file in a fresh Node process for setup, cleanup, or any work a device step cannot do
154391
- (\`script: { path: ../../scripts/seed.mjs, timeout?: <ms> }\` \u2014 always a map, never a bare path; the path
154392
- is resolved against the flow file that names the step, exactly as a \`run\` target is, so a saved flow
154393
- climbs out of .argent/flows/ to reach the project's own scripts/ directory. The step needs no device, so
154394
- a script-only flow runs with nothing booted \u2014 but a \`run\` or a \`when\` step beside it still resolves
154395
- one, \`when\` even when the only thing it guards is another script, so a platform-gated seed needs a
154396
- device of that platform. Its stdout and stderr come back on the step report. Co-located runs only.).
154397
- A selector-less gesture \u2014 a coordinate \`tap\`/\`long-press\`/\`swipe\`, or a \`pinch\`/\`rotate\` with no \`on\` \u2014 resolves
154398
- no frame out of the tree, so an unreadable tree source does NOT stop it the way it stops \`idle\`: it
154399
- settles best-effort, dispatches anyway, and the step PASSES carrying a \`warning\` that quotes the source's
154400
- own error. That green says the gesture was SENT, not that it landed. Restore the tree source (usually
154401
- relaunch the app so the instrumentation loads), or accept the warning where the app can serve no tree;
154402
- the first such gesture proves the outage and later ones spend that verdict without paying the settle
154403
- window again. A tree read that comes back, or a relaunch, retires that verdict \u2014 which only makes the
154404
- next gesture pay a fresh window, and it warns again if the source is still down.
154405
- A \`when:\` block (condition + \`steps:\`, no else) runs its steps only if the condition holds \u2014
154406
- checked once with the short assert grace \u2014 for one-sided divergences like interstitials and coach
154407
- marks; a skipped block reports distinctly and failures inside an entered block are real failures.
154408
- A flow is self-contained when its first non-\`echo\`/\`script\` step is \`launch\`; it must not declare
154409
- \`executionPrerequisite\`. Other flows use the device's current state. Device id is injected by the runner (flows store none) \u2014 pass \`device\` or
154410
- \`platform\` to pick one, else the single booted device is used. On Chromium a \`launch\` step's value is an
154411
- Electron app path ({ chromium: <path> | { path, args } }) the runner boots (on the tool-server host) rather
154412
- than an installed app id it relaunches. With no explicit \`device\`, a run whose leading launch is
154413
- unambiguously chromium (\`platform: chromium\`, or a lone \`{ chromium: \u2026 }\` target) boots that app and
154414
- starts there \u2014 following a leading \`run:\`, \`echo:\` or \`script:\`, so a fragment that composes a chromium
154415
- e2e flow boots too, and so does a flow that seeds a backend before it launches;
154416
- otherwise the first launch attaches to an already-running instance and never kills it. Every later
154417
- launch \u2014 a nested e2e flow's own, or a mid-flow relaunch \u2014 boots a fresh instance the run moves onto;
154418
- an instance the run already owns for that same app is killed first (its exit awaited) so the
154419
- replacement can't lose the race against its single-instance lock. Instances the runner still owns at
154420
- run end are torn down then. A launch declaring no id for the run's platform is an error, not a cue to
154421
- switch platforms. Every step hard-stops the flow on failure; later steps are reported as skipped.
154422
- Returns a structured report ({ flow, device, executionPrerequisite, ok, aborted?, passed, failed,
154423
- skipped, errored, steps }) \u2014 \`device\` is the device the run STARTED on; when launches moved it onto
154424
- runner-booted instances, each names its instance in that step's reason and marks the move \u2014 \`run moved
154425
- off <id>\`, or \`retired <id> (same app relaunched)\` when the instance it left was the one killed \u2014
154426
- a relaunch that retired an older owned instance names both.
154427
-
154428
- If a fragment has an execution prerequisite and prerequisiteAcknowledged is not set to true, the tool
154429
- returns a notice with the prerequisite instead of running.
154430
- Pass exactly one flow source: name for a saved flow under project_root, or flow_path for an explicit YAML \u2014 both together, or neither, fails the call.`,
154372
+ description: `Run a saved YAML flow end to end. Use when
154373
+ asked to replay a recorded path, re-run a QA regression, or check that a known journey still passes; for a
154374
+ one-off interaction use the gesture tools instead, and to author a flow use flow-start-recording. Pass
154375
+ exactly one flow source: name (under project_root) or flow_path.
154376
+ Returns a per-step report: the first failure stops the run and the rest report as skipped.`,
154431
154377
  longRunning: true,
154432
154378
  zodSchema: zodSchema66,
154433
154379
  fileInputs: fileInputs3,
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.24.1-next.9",
3
+ "version": "0.25.0",
4
4
  "mcpName": "io.github.software-mansion/argent",
5
5
  "description": "MCP server for iOS Simulator and Android Emulator control",
6
6
  "license": "Apache-2.0",
@@ -109,7 +109,7 @@ Scopes can combine and nest, with at most six scope keys. Use strict selectors f
109
109
 
110
110
  ## Directives
111
111
 
112
- Directives stop the flow on failure and skip later steps. `flow-execute` documents their shapes. The available directives are `launch`, `tap`, `long-press`, `swipe`, `type`, `scroll-to`, `pinch`, `rotate`, `await`, `assert`, `wait`, `snapshot`, `run`, `script`, `when`, `echo`, and `tool`.
112
+ Directives stop the flow on failure and skip later steps. The available directives are `launch`, `tap`, `long-press`, `swipe`, `type`, `scroll-to`, `pinch`, `rotate`, `await`, `assert`, `wait`, `snapshot`, `run`, `script`, `when`, `echo`, and `tool`.
113
113
 
114
114
  Use the launch map for cross-platform flows. A bare launch applies everywhere and becomes an app path on Chromium. The map takes `native:`, `ios:`, `android:`, `vega:`, and `chromium:`. `native:` is one id shared by iOS, Android, and Vega, and a per-platform key overrides it for that platform. `chromium:` accepts a relative or absolute app path. A launch that declares no id for the run's platform is an error, not a cue to switch platforms. On iOS, a successful launch also pins later tree reads to that app until the next raw `tool:` step, so read [The runner tree is not the discovery tree](#the-runner-tree-is-not-the-discovery-tree) when a read describes the wrong screen.
115
115
 
@@ -1,23 +1,13 @@
1
1
  ---
2
2
  name: argent-device-interact
3
- description: Interact with an iOS simulator, Android emulator, or Chromium (CDP) app using argent MCP tools. Use when tapping UI elements, performing gestures, scrolling/swiping, typing text, pressing hardware buttons, launching apps, opening URLs, taking screenshots, waiting for an element to appear or disappear, or checking visible app state after interactions.
3
+ description: Interact with an iOS simulator, Android emulator, or Chromium (CDP) app using argent MCP tools. Use when tapping UI elements, performing gestures, scrolling/swiping, typing text, pressing hardware buttons, launching apps, opening URLs, taking screenshots, waiting for an element to appear or disappear, or checking visible app state after interactions. Not for TV targets.
4
4
  ---
5
5
 
6
6
  ## Unified tool surface
7
7
 
8
8
  All interaction tools below accept a `udid` parameter and auto-dispatch iOS vs Android based on its shape (UUID → iOS simulator, `chromium-cdp-<port>` → Chromium (CDP) app, anything else → Android adb serial). You use the same tool names on every platform.
9
9
 
10
- **Chromium (CDP) app** = any Chromium runtime exposing a Chrome DevTools Protocol endpoint: an Electron app (boot it with `boot-device` + `electronAppPath`), or any Chromium-family browser (Chrome/Brave/Edge) launched with `--remote-debugging-port`. The latter is auto-discovered by `list-devices` on port `9222` plus anything in `ARGENT_CHROMIUM_PORTS`. The same describe/tap/swipe/keyboard/screenshot surface drives all of them.
11
-
12
- **Multi-tab / windows (Chromium only):** a Chromium device may have several tabs / BrowserWindows. Use `chromium-tabs` to `list` them (stable ids `t1`, `t2`, …, optional labels), open a `new` one, `select` which is active, or `close` one. Every other tool (`describe`, `gesture-tap`, `screenshot`, `debugger-evaluate`, `open-url`, …) acts on the **active** tab, so `chromium-tabs action=select` before driving a different tab. Note: a cross-process navigation (some redirects) can swap a tab's underlying CDP target — re-run `chromium-tabs action=list` to pick it up under a fresh id.
13
-
14
- **Cookies & storage (Chromium only):** `chromium-cookies` reads/writes cookies via the Network domain (so HttpOnly cookies are visible): `action=get` (optionally scoped by `url`), `set` (`name`, `value`, + `url`/`domain`, optional `secure`/`httpOnly`/`sameSite`/`expires`), `delete` (`name`), `clear` (all). `chromium-storage` reads/writes Web Storage for the active page: `store=local|session`, `action=get` (one `key` or all entries), `set`, `remove`, `clear`. Both are per-origin / active-tab. Handy for seeding auth before a flow or asserting app state after one.
15
-
16
- > **TV targets (Apple TV / Android TV) are not covered by this skill.** A TV target is **focus-driven, not touch-driven** — the `gesture-*` tools are the wrong tools for it. This applies to both Apple TV simulators (UUID-shaped, identical to iOS) and Android TV / leanback devices (serial-shaped, identical to a phone emulator). If `list-devices` tags your target `runtimeKind: "tv"`, stop and use the `argent-tv-interact` skill: `describe` to read focus, `tv-remote` for remote / D-pad presses, and `keyboard` to type.
17
-
18
- > **Physical iPhones (an iOS entry with kind `"device"`) follow a different contract:** automation is app-scoped and only a subset of these tools exists there. Use the `argent-ios-device-interact` skill instead.
19
-
20
- For platform-specific caveats (Metro `adb reverse`, locked-screen describe errors, etc.), see § 9 Platform-specific notes at the bottom.
10
+ **Chromium (CDP) app** = an Electron app or a Chromium-family browser (Chrome/Brave/Edge) exposing a Chrome DevTools Protocol endpoint. The same describe/tap/keyboard/screenshot surface drives it, but scrolling, tabs, cookies and storage differ **read `references/chromium.md` before driving a `chromium` target.**
21
11
 
22
12
  ## 1. Before You Start
23
13
 
@@ -98,7 +88,7 @@ Point follow-up native diagnostics after you already have a candidate point:
98
88
  - `native-user-interactable-view-at-point`: deepest native view that would receive touch at a known raw iOS point; requires `bundleId`
99
89
  - `native-view-at-point`: deepest visible native view at a known raw iOS point; requires `bundleId`
100
90
 
101
- ### If `describe` Fails
91
+ ### If `describe` Tool Fails
102
92
 
103
93
  Read the exact error and choose the action that matches it:
104
94
 
@@ -186,21 +176,7 @@ Special keys: `enter`, `escape`, `backspace`, `tab`, `space`, `arrow-up`, `arrow
186
176
  { "udid": "<UDID>", "text": "{{secret:APP_PASSWORD}}" }
187
177
  ```
188
178
 
189
- The placeholder is resolved on the machine running the tool-server, from the first of these that defines the name:
190
-
191
- | # | Source | Which keys it exposes |
192
- | --- | --------------------------------------------- | -------------------------------------------------------------------- |
193
- | 1 | `ARGENT_SECRET_<NAME>` environment variable | prefixed vars only — the CI-native path |
194
- | 2 | `<project>/.argent/secrets.env` | every key (`APP_PASSWORD=…`) — gitignore this file |
195
- | 3 | `<project>/.env.local`, then `<project>/.env` | only `ARGENT_SECRET_`-prefixed keys, so app config stays unreachable |
196
- | 4 | `~/.argent/secrets.env` | every key — per-user, works in any project |
197
-
198
- Rules:
199
-
200
- - The result echoes the placeholder, never the value. An unknown name fails with the list of available secret _names_ and every source it looked in, with paths — read that list before asking the user anything.
201
- - The auto-screenshot after the call is skipped so the typed value cannot re-enter your context as pixels. Do **not** `describe` or `screenshot` a non-secure field you just filled with a secret — submit or navigate away first, then verify the resulting screen. To submit, put the text step and the Enter step in **one `run-sequence`**. The skip covers a whole batch that contains the placeholder, but a second bare `keyboard` call gets its own screenshot of the filled field.
202
- - Nothing outside those sources is reachable; never ask the user to paste a secret value into the conversation. Ask them to put it in a secrets file instead — a file edit applies to the next call, while an exported env var only reaches a tool-server started afterwards.
203
- - The project sources are found by walking up from the tool-server's working directory. If a project file is not being picked up, the failure's source list shows the paths actually consulted; `~/.argent/secrets.env` needs no project and always applies.
179
+ Where the value is read from, and the rules for using a placeholder — including not screenshotting the field afterwards are in `references/secrets.md`. Read it before typing any credential.
204
180
 
205
181
  ### paste — Paste text into the focused field
206
182
 
@@ -224,20 +200,18 @@ Values: `Portrait`, `LandscapeLeft`, `LandscapeRight`, `PortraitUpsideDown`
224
200
 
225
201
  ### await-ui-element — Block until a UI element reaches a state
226
202
 
227
- Instead of polling `screenshot`/`describe` in a loop, use `await-ui-element` to block server-side until an element reaches an expected state (or `timeoutMs`, default 5000ms, elapses). It polls the same accessibility/DOM tree as `describe`. (For a plain pause, use your own harness sleep — this tool deliberately has no bare-timer mode.)
203
+ **Never poll `screenshot`/`describe` in a loop to wait for something.** Use `await-ui-element`: it blocks server-side on the same tree `describe` reads. It has no bare-timer mode by design for a plain pause, use your own harness sleep.
228
204
 
229
205
  ```json
230
206
  { "udid": "<UDID>", "condition": "visible", "selector": { "text": "Continue" } }
231
207
  ```
232
208
 
233
- - `condition`: `exists`, `visible`, `hidden`, or `text`.
234
- - `selector`: `{ text?, identifier?, role? }` — every provided field must match. `text` matches the element's label or value and `role` its element role (e.g. `AXButton`, `button`, `TextView`, `StaticText`), both as case-insensitive substrings; `identifier` matches its accessibility id / resource-id / testID **exactly** (case-insensitive), also accepting the unqualified Android resource-id name (`submit` matches `com.example.app:id/submit`). The synthetic `ROOT` container `describe` prints is never matched, so a `role` like `AXGroup`/`html` won't trivially "match the screen".
235
- - Prefer a **specific** selector. A loose substring can match several elements, and the tool may then key off one you didn't mean: `text` reads the first **visible** match in **reading order** (top-to-bottom, left-to-right — the same order `describe` lists them, so it's the one you saw first; when no match is visible, the first match overall), while `visible`/`exists` are satisfied by **any** match. Disambiguate with a longer or more exact string, an `identifier`, or a `role` (e.g. pin to a text role like `StaticText` to skip a same-named button). On a `text` timeout the `note` quotes the matched element's text, so you can see which one it landed on.
236
- - `text` condition also needs `expectedText` (substring the matched element must contain).
237
- - `hidden` passes when the selector matches nothing. If `note` says it never matched, treat the check as failed and fix the selector. On iOS, a degraded empty tree does not report `hidden` success; the note gives the recovery hint.
238
- - Optional `timeoutMs` (default 5000) and `pollIntervalMs` (default 400).
209
+ The tool's own description carries the conditions, selector matching, defaults and return shape. What it does not tell you:
239
210
 
240
- Returns `{ success, elapsed, note?, cause? }`. On failure, `note` describes the result. `cause` is `unmet`, `unreadable`, or `cancelled`. Only `unmet` means the tree was readable and the condition was false.
211
+ - A `hidden` check that succeeds **immediately** may be a false pass — its `note` then says the selector never matched anything at all. Treat that as a failed check and fix the selector; do not read it as "the element went away".
212
+ - The synthetic `ROOT` container `describe` prints is never matched, so a `role` like `AXGroup`/`html` won't trivially "match the screen".
213
+ - To disambiguate a loose selector, pin the `role` to a text role like `StaticText` — that skips a same-named button.
214
+ - On a `text` timeout the `note` quotes the text of the element the check actually read, so you can see which match it landed on.
241
215
 
242
216
  ### await-screen-idle — Block until the screen stops changing
243
217
 
@@ -297,18 +271,14 @@ For visual regression checks, before/after screenshot comparisons, and detailed
297
271
 
298
272
  ## 8. Action Sequencing with `run-sequence`
299
273
 
300
- Use `run-sequence` to batch multiple interaction steps into **a single tool call**. Only one screenshot is returned — after all steps complete. Use cases:
301
- scrolling multiple times, typing and submitting automatically, known sequence of multiple taps, rotating device back and forth.
274
+ Use `run-sequence` to batch multiple interaction steps into **a single tool call**. Only one screenshot is returned — after all steps complete.
302
275
 
303
- Do **not** use `run-sequence` when any step depends on observing the result of a previous step
276
+ Do **not** use `run-sequence` when any step depends on observing the result of a previous step.
304
277
 
305
278
  ### Use cases
306
279
 
307
- Use the sequencing when:
308
-
309
- - Knowing that some action needs multiple steps without necessarily immediate insight of screenshot
310
- - "scroll to bottom", "scroll to top", "scroll to do X" -> sequence scroll 3-5 times
311
- - form interactions, "clear and retype field" -> you may use triple-tap to select all, type new value
280
+ - "scroll to bottom", "scroll to top", "scroll until X" -> sequence 3-5 scrolls
281
+ - form interactions, "clear and retype field" -> triple-tap to select all, then type the new value
312
282
  - "submit form" → fill all fields in sequence, tap submit
313
283
  - "go back to X" → defined tap sequence for the navigation
314
284
 
@@ -394,6 +364,10 @@ Stops on the first error (or unmet `await-ui-element` condition) and returns par
394
364
  - **Locked screen / secure surfaces**: `describe` throws a clear error if it can't capture (keyguard, DRM, Play Integrity). Unlock the device or fall back to `screenshot`.
395
365
  - **APK vs .app in `reinstall-app`**: pass `.apk` absolute path on Android; `.app` directory on iOS.
396
366
 
367
+ ### Chromium
368
+
369
+ See `references/chromium.md` — tabs, cookies/storage.
370
+
397
371
  ### iOS
398
372
 
399
373
  _(no iOS-only gotchas collected here yet — add them as they come up)_
@@ -0,0 +1,24 @@
1
+ # Chromium (CDP) Targets
2
+
3
+ Everything in the related SKILL.md still applies; this page covers what differs.
4
+
5
+ ## Multi-tab / windows
6
+
7
+ A Chromium device may have several tabs / BrowserWindows. Use `chromium-tabs` to `list` them (stable ids `t1`, `t2`, …, optional labels), open a `new` one, `select` which is active, or `close` one.
8
+
9
+ Every other tool (`describe`, `gesture-tap`, `screenshot`, `debugger-evaluate`, `open-url`, …) acts on the **active** tab, so `chromium-tabs action=select` before driving a different tab.
10
+
11
+ Note: a cross-process navigation (some redirects) can swap a tab's underlying CDP target — re-run `chromium-tabs action=list` to pick it up under a fresh id.
12
+
13
+ ## Cookies & storage
14
+
15
+ `chromium-cookies` reads/writes cookies via the Network domain (so HttpOnly cookies are visible):
16
+
17
+ - `action=get` (optionally scoped by `url`)
18
+ - `set` (`name`, `value`, + `url`/`domain`, optional `secure`/`httpOnly`/`sameSite`/`expires`)
19
+ - `delete` (`name`)
20
+ - `clear` (all)
21
+
22
+ `chromium-storage` reads/writes Web Storage for the active page: `store=local|session`, `action=get` (one `key` or all entries), `set`, `remove`, `clear`.
23
+
24
+ Both are per-origin / active-tab. Handy for seeding auth before a flow or asserting app state after one.
@@ -0,0 +1,21 @@
1
+ # Typing Secrets
2
+
3
+ Where a `{{secret:<NAME>}}` placeholder gets its value, and the rules for using one. See the `keyboard` section of SKILL.md for the placeholder syntax itself.
4
+
5
+ ## Where the value comes from
6
+
7
+ The placeholder is resolved on the machine running the tool-server, from the first of these that defines the name:
8
+
9
+ | # | Source | Which keys it exposes |
10
+ | --- | --------------------------------------------- | -------------------------------------------------------------------- |
11
+ | 1 | `ARGENT_SECRET_<NAME>` environment variable | prefixed vars only — the CI-native path |
12
+ | 2 | `<project>/.argent/secrets.env` | every key (`APP_PASSWORD=…`) — gitignore this file |
13
+ | 3 | `<project>/.env.local`, then `<project>/.env` | only `ARGENT_SECRET_`-prefixed keys, so app config stays unreachable |
14
+ | 4 | `~/.argent/secrets.env` | every key — per-user, works in any project |
15
+
16
+ ## Rules
17
+
18
+ - The result echoes the placeholder, never the value. An unknown name fails with the list of available secret _names_ and every source it looked in, with paths — read that list before asking the user anything.
19
+ - The auto-screenshot after the call is skipped so the typed value cannot re-enter your context as pixels. Do **not** `describe` or `screenshot` a non-secure field you just filled with a secret — submit or navigate away first, then verify the resulting screen. To submit, put the text step and the Enter step in **one `run-sequence`**. The skip covers a whole batch that contains the placeholder, but a second bare `keyboard` call gets its own screenshot of the filled field.
20
+ - Nothing outside those sources is reachable; never ask the user to paste a secret value into the conversation. Ask them to put it in a secrets file instead — a file edit applies to the next call, while an exported env var only reaches a tool-server started afterwards.
21
+ - The project sources are found by walking up from the tool-server's working directory. If a project file is not being picked up, the failure's source list shows the paths actually consulted; `~/.argent/secrets.env` needs no project and always applies.