@swmansion/argent 0.31.2-next.2 → 0.31.2-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.
Files changed (28) hide show
  1. package/bin/argent-android-devtools-0.3.0.apk +0 -0
  2. package/bin/darwin/ax-service +0 -0
  3. package/bin/darwin/tvos-ax-service +0 -0
  4. package/bin/darwin/tvos-hid-daemon +0 -0
  5. package/bin/tcp/ax-service +0 -0
  6. package/dist/cli-cmds.mjs +94 -24
  7. package/dist/client.js +71 -14
  8. package/dist/installer.mjs +8 -1
  9. package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/ArgentExceptionGuard.h +3 -0
  10. package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/ArgentExceptionGuard.m +11 -3
  11. package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/ArgentRunnerSession+Gestures.swift +27 -19
  12. package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/ArgentRunnerSession+Quiescence.swift +95 -0
  13. package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/ArgentRunnerSession+TextEntry.swift +2 -2
  14. package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/RunnerProtocol.swift +5 -5
  15. package/dist/mcp-server.mjs +75 -17
  16. package/dist/tool-args.generated.d.ts +16 -8
  17. package/dist/tool-server.cjs +202 -86
  18. package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
  19. package/dylibs/libKeyboardPatch.dylib +0 -0
  20. package/dylibs/libNativeDevtoolsIos.dylib +0 -0
  21. package/dylibs/tcp/libArgentInjectionBootstrap.dylib +0 -0
  22. package/dylibs/tcp/libKeyboardPatch.dylib +0 -0
  23. package/dylibs/tcp/libNativeDevtoolsIos.dylib +0 -0
  24. package/dylibs/tvos/libArgentInjectionBootstrap.dylib +0 -0
  25. package/dylibs/tvos/libKeyboardPatch.dylib +0 -0
  26. package/dylibs/tvos/libNativeDevtoolsIos.dylib +0 -0
  27. package/package.json +1 -1
  28. package/skills/argent-create-e2e/references/live-authoring.md +3 -3
@@ -1323,7 +1323,31 @@ var init_e2e_dirs = __esm({
1323
1323
  function canonicalToolId(id) {
1324
1324
  return Object.hasOwn(TOOL_ID_ALIASES, id) ? TOOL_ID_ALIASES[id] : id;
1325
1325
  }
1326
- var TOOL_ID_ALIASES;
1326
+ function toolParamAliases(tool) {
1327
+ const id = canonicalToolId(tool);
1328
+ return Object.hasOwn(TOOL_PARAM_ALIASES, id) ? TOOL_PARAM_ALIASES[id] : {};
1329
+ }
1330
+ function canonicalToolArgs(tool, args) {
1331
+ if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
1332
+ const record2 = args;
1333
+ let out;
1334
+ for (const [former, current] of Object.entries(toolParamAliases(tool))) {
1335
+ if (record2[former] === void 0) continue;
1336
+ if (record2[current] !== void 0) throw new ToolParamAliasError(former, current);
1337
+ out ??= { ...record2 };
1338
+ out[current] = out[former];
1339
+ delete out[former];
1340
+ }
1341
+ return out ?? args;
1342
+ }
1343
+ function withFormerParamSpecs(tool, specs) {
1344
+ const former = Object.entries(toolParamAliases(tool));
1345
+ const copies = specs.flatMap(
1346
+ (spec) => spec.path === `\${${spec.target}}` ? former.filter(([, current]) => current === spec.target).map(([name]) => ({ ...spec, target: name, path: `\${${name}}` })) : []
1347
+ );
1348
+ return copies.length > 0 ? [...specs, ...copies] : specs;
1349
+ }
1350
+ var TOOL_ID_ALIASES, TOOL_PARAM_ALIASES, ToolParamAliasError;
1327
1351
  var init_tool_ids = __esm({
1328
1352
  "../registry/src/tool-ids.ts"() {
1329
1353
  "use strict";
@@ -1336,6 +1360,19 @@ var init_tool_ids = __esm({
1336
1360
  "flow-read-prerequisite": "e2e-read-prerequisite",
1337
1361
  "flow-execute": "e2e-execute"
1338
1362
  });
1363
+ TOOL_PARAM_ALIASES = Object.freeze({
1364
+ "e2e-execute": Object.freeze({ flow_path: "e2e_test_path", flow_file: "e2e_test_file" }),
1365
+ "e2e-read-prerequisite": Object.freeze({
1366
+ flow_path: "e2e_test_path",
1367
+ flow_file: "e2e_test_file"
1368
+ })
1369
+ });
1370
+ ToolParamAliasError = class extends Error {
1371
+ constructor(former, current) {
1372
+ super(`Pass ${current} or its former name ${former}, not both.`);
1373
+ this.name = "ToolParamAliasError";
1374
+ }
1375
+ };
1339
1376
  }
1340
1377
  });
1341
1378
 
@@ -1382,7 +1419,13 @@ async function resolveFlowRelativeFile(anchorDir, target, addressable) {
1382
1419
  }
1383
1420
  function nestedFlowTarget(args) {
1384
1421
  if (typeof args !== "object" || args === null || Array.isArray(args)) return void 0;
1385
- const { name, project_root: projectRoot, flow_path: flowPath } = args;
1422
+ let canonical;
1423
+ try {
1424
+ canonical = canonicalToolArgs("e2e-execute", args);
1425
+ } catch {
1426
+ return void 0;
1427
+ }
1428
+ const { name, project_root: projectRoot, e2e_test_path: flowPath } = canonical;
1386
1429
  if (flowPath === void 0) {
1387
1430
  if (typeof name !== "string" || !FLOW_NAME_PATTERN.test(name)) return void 0;
1388
1431
  if (!isResolvedAbsolute(projectRoot)) return void 0;
@@ -1391,7 +1434,7 @@ function nestedFlowTarget(args) {
1391
1434
  }
1392
1435
  if (name !== void 0 || !isResolvedAbsolute(flowPath)) return void 0;
1393
1436
  if (!FLOW_FILE_NAME_PATTERN.test(path6.basename(flowPath))) return void 0;
1394
- return { kind: "flow_path", path: flowPath };
1437
+ return { kind: "e2e_test_path", path: flowPath };
1395
1438
  }
1396
1439
  function isResolvedAbsolute(value) {
1397
1440
  return typeof value === "string" && path6.isAbsolute(value) && !value.split(/[\\/]+/).includes("..");
@@ -22444,6 +22487,7 @@ var init_registry = __esm({
22444
22487
  init_artifacts();
22445
22488
  init_errors();
22446
22489
  init_failure_codes();
22490
+ init_tool_ids();
22447
22491
  init_urn();
22448
22492
  init_zod_to_json_schema();
22449
22493
  import_node_crypto2 = require("node:crypto");
@@ -22524,6 +22568,18 @@ var init_registry = __esm({
22524
22568
  );
22525
22569
  this.events.emit("toolInvoked", toolId, toolInvocationId, startedMsg, calledAs);
22526
22570
  try {
22571
+ try {
22572
+ params = canonicalToolArgs(toolId, params);
22573
+ } catch (err) {
22574
+ if (!(err instanceof ToolParamAliasError)) throw err;
22575
+ throw new FailureError(`Invalid params for tool "${toolId}": ${err.message}`, {
22576
+ error_code: FAILURE_CODES.TOOL_INPUT_INVALID,
22577
+ failure_stage: "tool_params_parse",
22578
+ failure_area: "registry",
22579
+ error_kind: "validation"
22580
+ });
22581
+ }
22582
+ effectiveParams = params;
22527
22583
  if (definition.zodSchema) {
22528
22584
  const parsed = definition.zodSchema.safeParse(params ?? {});
22529
22585
  if (!parsed.success) {
@@ -101024,7 +101080,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
101024
101080
  var SESSION_ID = (0, import_node_crypto3.randomUUID)();
101025
101081
  function readCliVersion() {
101026
101082
  if (true) {
101027
- return "0.31.2-next.2";
101083
+ return "0.31.2-next.3";
101028
101084
  }
101029
101085
  return "0.0.0";
101030
101086
  }
@@ -131011,7 +131067,9 @@ function createHttpApp(registry2, options) {
131011
131067
  )
131012
131068
  };
131013
131069
  if (def.outputHint) entry.outputHint = renderFlagGated(def.outputHint, isFlagEnabled);
131014
- if (def.fileInputs && def.fileInputs.length > 0) entry.fileInputs = def.fileInputs;
131070
+ if (def.fileInputs && def.fileInputs.length > 0) {
131071
+ entry.fileInputs = withFormerParamSpecs(def.id, def.fileInputs);
131072
+ }
131015
131073
  if (def.alwaysLoad) entry.alwaysLoad = true;
131016
131074
  if (def.searchHint) entry.searchHint = renderFlagGated(def.searchHint, isFlagEnabled);
131017
131075
  if (def.longRunning) entry.longRunning = true;
@@ -131087,11 +131145,29 @@ function createHttpApp(registry2, options) {
131087
131145
  return;
131088
131146
  }
131089
131147
  }
131148
+ let body;
131149
+ try {
131150
+ body = canonicalToolArgs(def.id, req.body);
131151
+ } catch (err) {
131152
+ if (!(err instanceof ToolParamAliasError)) throw err;
131153
+ emitHttpFailure(
131154
+ {
131155
+ error_code: FAILURE_CODES.HTTP_ZOD_VALIDATION_FAILED,
131156
+ failure_stage: "http_param_alias",
131157
+ failure_area: "http",
131158
+ error_kind: "validation"
131159
+ },
131160
+ req.body
131161
+ );
131162
+ const issues = [{ code: "custom", path: [], message: err.message }];
131163
+ res.status(400).json({ error: JSON.stringify(issues), message: err.message, issues });
131164
+ return;
131165
+ }
131090
131166
  let bodyArgs;
131091
131167
  let resolvedFileInputs;
131092
131168
  let derivedTargets;
131093
131169
  try {
131094
- const resolved = await resolveFileInputs(def, req.body, (id) => {
131170
+ const resolved = await resolveFileInputs(def, body, (id) => {
131095
131171
  const entry = uploads.get(id);
131096
131172
  if (entry) uploads.delete(id);
131097
131173
  return entry;
@@ -142060,7 +142136,7 @@ var zodSchema17 = external_exports.object({
142060
142136
  x: external_exports.number().min(0).max(1).describe("Normalized horizontal position 0.0\u20131.0 (left=0, right=1), not pixels"),
142061
142137
  y: external_exports.number().min(0).max(1).describe("Normalized vertical position 0.0\u20131.0 (top=0, bottom=1), not pixels"),
142062
142138
  clickCount: external_exports.number().int().min(1).max(10).optional().describe(
142063
- "Number of taps/clicks dispatched as ONE multi-tap gesture (2 = double-tap / double-click). The taps land inside the OS double-tap window; on Chromium each click carries an escalating CDP clickCount so dblclick actually fires; on physical iOS 2 is the native double-tap and higher counts land as separate taps. <flag:harmonyos>On HarmonyOS only 2 is one gesture; 3 or more are separate injections a device round trip apart, which the OS reads as single taps. </flag:harmonyos>Default 1."
142139
+ "Number of taps/clicks dispatched as ONE multi-tap gesture (2 = double-tap / double-click). The taps land inside the OS double-tap window; on Chromium each click carries an escalating CDP clickCount so dblclick actually fires; on physical iOS 2 is the native double-tap and higher counts loop single taps, which a double-tap recognizer may pair. <flag:harmonyos>On HarmonyOS only 2 is one gesture; 3 or more are separate injections a device round trip apart, which the OS reads as single taps. </flag:harmonyos>Default 1."
142064
142140
  )
142065
142141
  });
142066
142142
  function tapDescription(params, tense) {
@@ -142133,6 +142209,9 @@ Returns { tapped: true, timestampMs }. On physical iOS, reactivated: true = app
142133
142209
  On a physical iPhone use \`describe\`; \`native-describe-screen\` is simulator-only.
142134
142210
  Before tapping, determine the correct coordinates by using discovery tools. Pick by platform: iOS / Android use \`describe\`, \`native-describe-screen\`, or \`debugger-component-tree\`; Chromium uses \`describe\` (the DOM walker)<flag:harmonyos> and HarmonyOS uses \`describe\` (the \`uitest\` layout dump)</flag:harmonyos>, since the native and RN-specific discovery tools don't apply<flag:harmonyos> to either</flag:harmonyos>. More information in \`argent-device-interact\` skill`,
142135
142211
  alwaysLoad: true,
142212
+ // A physical iPhone's first call waits out the runner build, past the MCP fetch timeout,
142213
+ // whose abort replays the call: the gesture would land once per replay.
142214
+ longRunning: true,
142136
142215
  searchHint: "tap press button element device simulator emulator chromium touch down up click",
142137
142216
  zodSchema: zodSchema17,
142138
142217
  capability: capability9,
@@ -142260,6 +142339,9 @@ Physical iOS: an edge gesture (back-swipe) needs fromX 0 exactly; durationMs set
142260
142339
  HarmonyOS: durationMs sets drag speed, clamped to uitest's 200-40000 px/s, so a short slow swipe ends early and a long fast one runs late.</flag:harmonyos>
142261
142340
  Pass momentum:false for a momentum-free swipe that lands where the finger lifts (little to no fling at the 300 default), when you need a deterministic scroll distance; it needs durationMs >= 150 and is rejected below that, a shorter ease-out leaving the OS too little wall clock to read the deceleration as a stop. At 150 it lands short of the lift point instead, and 2 of 47 runs still flung backwards. A plain swipe takes any duration up to 10000ms and is delivered as close to the speed it was authored as a 16ms frame allows: below ~32ms the whole travel lands in one or two frames, which the OS flings as hard as it flings anything. Returns { swiped: true, timestampMs }. On physical iOS, reactivated: true = app was re-fronted; re-describe. Fails if the simulator-server / emulator backend<flag:harmonyos>, or \`hdc\` on HarmonyOS,</flag:harmonyos> is not reachable for the given device.`,
142262
142341
  alwaysLoad: true,
142342
+ // A physical iPhone's first call waits out the runner build, past the MCP fetch timeout,
142343
+ // whose abort replays the call: the gesture would land once per replay.
142344
+ longRunning: true,
142263
142345
  searchHint: "swipe scroll drag pan gesture device simulator emulator touch move",
142264
142346
  zodSchema: zodSchema18,
142265
142347
  capability: capability10,
@@ -142817,6 +142899,9 @@ Example pinch-to-zoom (with interpolate:10 for smoothness):
142817
142899
  interpolate: 10`,
142818
142900
  zodSchema: zodSchema21,
142819
142901
  capability: capability13,
142902
+ // A physical iPhone's first call waits out the runner build, past the MCP fetch timeout,
142903
+ // whose abort replays the call: the gesture would land once per replay.
142904
+ longRunning: true,
142820
142905
  // Declare the runner only for a train execute can play. A rejected train must not pay a runner cold start.
142821
142906
  services: (params) => {
142822
142907
  const device = resolveDevice(params.udid);
@@ -142904,6 +142989,9 @@ Auto-generates interpolated frames at ~60fps<flag:harmonyos> on iOS and Android;
142904
142989
  Use when you need to zoom in or out on a map, image, or zoomable view. Physical iPhones play both fingers through the runner as one event stream. Returns { pinched: true, timestampMs }. On physical iOS, reactivated: true = app was re-fronted; re-describe. Fails if the simulator-server / emulator backend<flag:harmonyos>, or \`hdc\` on HarmonyOS,</flag:harmonyos> is not reachable for the given device, or on a physical iPhone when the app is not drawn in portrait.`,
142905
142990
  zodSchema: zodSchema22,
142906
142991
  capability: capability14,
142992
+ // A physical iPhone's first call waits out the runner build, past the MCP fetch timeout,
142993
+ // whose abort replays the call: the gesture would land once per replay.
142994
+ longRunning: true,
142907
142995
  services: (params) => {
142908
142996
  const device = resolveDevice(params.udid);
142909
142997
  if (device.platform === "harmony") return {};
@@ -143039,6 +143127,9 @@ Use when you need to rotate a map, image picker, or any rotateable UI element. R
143039
143127
  Size the orbit with radius, or with radiusX and radiusY together (the pair overrides radius); one half of the pair alone, or none of the three, is rejected.`,
143040
143128
  zodSchema: zodSchema23,
143041
143129
  capability: capability15,
143130
+ // A physical iPhone's first call waits out the runner build, past the MCP fetch timeout,
143131
+ // whose abort replays the call: the gesture would land once per replay.
143132
+ longRunning: true,
143042
143133
  services: (params) => {
143043
143134
  const device = resolveDevice(params.udid);
143044
143135
  return isIosPhysicalDevice(device) ? { iosDeviceRunner: iosDeviceRunnerRef(device) } : { simulatorServer: simulatorServerRef(device) };
@@ -143162,6 +143253,9 @@ Returns { pressed: buttonName }.
143162
143253
  Fails if the device backend is not reachable: the simulator-server for iOS, <flag:harmonyos>\`hdc\` for HarmonyOS, </flag:harmonyos>or \`adb\` for Android (Android presses are injected with \`adb shell input keyevent\`).`,
143163
143254
  zodSchema: zodSchema24,
143164
143255
  capability: capability16,
143256
+ // A physical iPhone's first call waits out the runner build, past the MCP fetch timeout,
143257
+ // whose abort replays the call: the press would land once per replay.
143258
+ longRunning: true,
143165
143259
  // Declare only the service the resolved path actually consumes. The Android
143166
143260
  // path uses `adb`, so a sim-server here would spawn a service the tool never
143167
143261
  // uses (up to a 30s ready-wait) and could throw ServiceInitializationError
@@ -168118,7 +168212,13 @@ function fromYamlStep(raw, blockDepth = 0) {
168118
168212
  if ("script" in raw) return parseScriptStep(raw, raw.script);
168119
168213
  if ("tool" in raw) {
168120
168214
  const r = raw;
168121
- const step = { kind: "tool", name: r.tool, args: r.args ?? {} };
168215
+ let args = r.args ?? {};
168216
+ try {
168217
+ args = canonicalToolArgs(r.tool, args);
168218
+ } catch (err) {
168219
+ badEntry(raw, err instanceof Error ? err.message : String(err));
168220
+ }
168221
+ const step = { kind: "tool", name: r.tool, args };
168122
168222
  if (r.delayMs !== void 0) step.delayMs = r.delayMs;
168123
168223
  return step;
168124
168224
  }
@@ -168470,7 +168570,7 @@ var flowStartRecordingTool = {
168470
168570
  },
168471
168571
  description: `Start recording a new e2e test, resetting .argent/e2e/<name>.yaml to an empty e2e test and replacing any existing one.
168472
168572
  Use when you want to capture a reusable sequence of device interactions for later replay.
168473
- Returns { message, flowFile, savedTo } and optionally { restarted, discardedSteps } if a live recording of the same e2e test was discarded.
168573
+ Returns { message, e2eTestFile, savedTo } and optionally { restarted, discardedSteps } if a live recording of the same e2e test was discarded.
168474
168574
  Without a link, it creates that file and fails if the .argent/e2e/ directory cannot be created or the file cannot be written. Over a link (argent link or ARGENT_TOOLS_URL, also to 127.0.0.1), your client writes the file, and \`savedTo\` is its path on your computer, or null when that write failed.
168475
168575
 
168476
168576
  Several e2e tests can be recorded at once, each keyed by the \`name\` + \`project_root\`
@@ -168541,12 +168641,14 @@ reports for those steps.`,
168541
168641
  message: `Restarted recording "${params.name}" \u2014 ${lost} was discarded and ` + reset,
168542
168642
  restarted: true,
168543
168643
  ...discardedSteps === void 0 ? {} : { discardedSteps },
168644
+ e2eTestFile: flowFile,
168544
168645
  flowFile,
168545
168646
  savedTo
168546
168647
  };
168547
168648
  }
168548
168649
  return {
168549
168650
  message: `Started recording "${params.name}" e2e test`,
168651
+ e2eTestFile: flowFile,
168550
168652
  flowFile,
168551
168653
  savedTo
168552
168654
  };
@@ -169698,7 +169800,7 @@ async function runDirective(env, step) {
169698
169800
  return {
169699
169801
  ok: false,
169700
169802
  indeterminate: true,
169701
- reason: `\`await: { idle }\` cannot run on ${env.device.platform}: flows have no UI-tree source on this platform, so screen stillness can never be judged. Replace this step with an explicit \`wait:\`.`
169803
+ reason: `\`await: { idle }\` cannot run on ${env.device.platform}: e2e tests have no UI-tree source on this platform, so screen stillness can never be judged. Replace this step with an explicit \`wait:\`.`
169702
169804
  };
169703
169805
  }
169704
169806
  switch (step.kind) {
@@ -170647,7 +170749,7 @@ var UPLOAD_STAGE_BY_KIND = {
170647
170749
  };
170648
170750
  function nestedFlowRef(args) {
170649
170751
  if (typeof args.name === "string") return ` (name: ${args.name})`;
170650
- if (typeof args.flow_path === "string") return ` (flow_path: ${args.flow_path})`;
170752
+ if (typeof args.e2e_test_path === "string") return ` (e2e_test_path: ${args.e2e_test_path})`;
170651
170753
  return "";
170652
170754
  }
170653
170755
  function toolStepFilePaths(registry2, tool, args) {
@@ -170760,9 +170862,9 @@ async function prepareNestedFlow(registry2, project, toolId, args) {
170760
170862
  };
170761
170863
  const resolved = await resolveFileInputs(
170762
170864
  { fileInputs: registry2.getTool(toolId)?.fileInputs },
170763
- { ...args, flow_file: wire }
170865
+ { ...args, e2e_test_file: wire }
170764
170866
  );
170765
- const input2 = resolved.fileInputs?.flow_file;
170867
+ const input2 = resolved.fileInputs?.e2e_test_file;
170766
170868
  if (input2) {
170767
170869
  input2.canonical = hop.canonical;
170768
170870
  input2.spelling = hop.spelling;
@@ -170797,7 +170899,8 @@ function count(value) {
170797
170899
  return typeof value === "number" ? value : 0;
170798
170900
  }
170799
170901
  function flowExecuteOutcome(result) {
170800
- const flow = typeof result.flow === "string" ? result.flow : "the composed e2e test";
170902
+ const name = result.e2eTest ?? result.flow;
170903
+ const flow = typeof name === "string" ? name : "the composed e2e test";
170801
170904
  if (!("steps" in result) && typeof result.notice === "string") {
170802
170905
  const prerequisite = typeof result.executionPrerequisite === "string" && result.executionPrerequisite ? `: ${result.executionPrerequisite}` : "";
170803
170906
  return {
@@ -171350,7 +171453,7 @@ var DIRECTIVE_COMMAND_HINTS = {
171350
171453
  run: {
171351
171454
  tool: "e2e-execute",
171352
171455
  rewritten: true,
171353
- rewriteCondition: "when the target resolves as a sibling e2e test in this recording's folder, on the computer that has the project; a `name` that does not is kept as a raw `tool: e2e-execute` step, and a `flow_path` that is not a sibling is refused outright and records nothing"
171456
+ rewriteCondition: "when the target resolves as a sibling e2e test in this recording's folder, on the computer that has the project; a `name` that does not is kept as a raw `tool: e2e-execute` step, and an `e2e_test_path` that is not a sibling is refused outright and records nothing"
171354
171457
  },
171355
171458
  type: { tool: "keyboard", rewritten: false },
171356
171459
  await: { tool: AWAIT_UI_ELEMENT_TOOL_ID, rewritten: false },
@@ -171399,10 +171502,10 @@ function directiveCommandHint(command) {
171399
171502
  }
171400
171503
  var RUN_TARGET_COMMAND = "e2e-execute";
171401
171504
  async function rewriteSiblingFlowPath(session, args, project, linked) {
171402
- const flowPath = args.flow_path;
171505
+ const flowPath = args.e2e_test_path;
171403
171506
  if (typeof flowPath !== "string" || args.name !== void 0) return;
171404
171507
  const invalid = (detail) => new FailureError(
171405
- `Cannot record an e2e-execute of flow_path "${flowPath}": ${detail}. flow_path carries no file-input resolution through e2e-add-step's opaque args \u2014 pass name + project_root for an e2e test saved beside the recording, or add a \`run: <relative path>.yaml\` step to the e2e test YAML by hand for a cross-directory target.`,
171508
+ `Cannot record an e2e-execute of e2e_test_path "${flowPath}": ${detail}. e2e_test_path carries no file-input resolution through e2e-add-step's opaque args \u2014 pass name + project_root for an e2e test saved beside the recording, or add a \`run: <relative path>.yaml\` step to the e2e test YAML by hand for a cross-directory target.`,
171406
171509
  {
171407
171510
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
171408
171511
  failure_stage: "flow_add_step_flow_path",
@@ -171446,7 +171549,7 @@ async function rewriteSiblingFlowPath(session, args, project, linked) {
171446
171549
  const suppliedBase = path45.basename(flowPath);
171447
171550
  if (linked && project.mode === "host") {
171448
171551
  throw new FailureError(
171449
- `Cannot record an e2e-execute of flow_path "${flowPath}": the argent client sent no files with this call, so the recorder cannot check that the file is a sibling of the recording. Update the argent CLI or MCP adapter on the client.`,
171552
+ `Cannot record an e2e-execute of e2e_test_path "${flowPath}": the argent client sent no files with this call, so the recorder cannot check that the file is a sibling of the recording. Update the argent CLI or MCP adapter on the client.`,
171450
171553
  {
171451
171554
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
171452
171555
  failure_stage: "flow_add_step_flow_path",
@@ -171461,11 +171564,11 @@ async function rewriteSiblingFlowPath(session, args, project, linked) {
171461
171564
  } catch (err) {
171462
171565
  if (path45.resolve(flowDir) === path45.resolve(flowsDir)) throw err;
171463
171566
  throw invalid(
171464
- `over a link, this argent client sends a flow_path only for an e2e test beside the recording; update the argent CLI or MCP adapter on the client (${err instanceof Error ? err.message : String(err)})`
171567
+ `over a link, this argent client sends an e2e_test_path only for an e2e test beside the recording; update the argent CLI or MCP adapter on the client (${err instanceof Error ? err.message : String(err)})`
171465
171568
  );
171466
171569
  }
171467
171570
  if (spelling.state !== "listed") {
171468
- const recovery = spelling.state === "absent" ? `pass the basename exactly as it appears on disk` : spelling.addressable ? `pass flow_path with the on-disk basename "${spelling.actual}"` : `rename "${spelling.actual}" to "${suppliedBase}" to record it \u2014 e2e test files must be lowercase .yaml`;
171571
+ const recovery = spelling.state === "absent" ? `pass the basename exactly as it appears on disk` : spelling.addressable ? `pass e2e_test_path with the on-disk basename "${spelling.actual}"` : `rename "${spelling.actual}" to "${suppliedBase}" to record it \u2014 e2e test files must be lowercase .yaml`;
171469
171572
  throw invalid(
171470
171573
  `the file must be named as it appears on disk \u2014 no directory entry is named "${suppliedBase}"` + (spelling.state === "case_folded" ? ` (this filesystem matched it case-insensitively to "${spelling.actual}")` : "") + `, so the recorded run: step would name an e2e test no case-sensitive checkout can find \u2014 ` + recovery
171471
171574
  );
@@ -171476,7 +171579,7 @@ async function rewriteSiblingFlowPath(session, args, project, linked) {
171476
171579
  throw invalid(`project_root "${projectRoot}" resolves "${stem}" to "${picked.path}" instead`);
171477
171580
  }
171478
171581
  }
171479
- delete args.flow_path;
171582
+ delete args.e2e_test_path;
171480
171583
  args.name = stem;
171481
171584
  }
171482
171585
  async function captureRunTarget(session, args, project) {
@@ -171538,7 +171641,7 @@ async function captureRunTarget(session, args, project) {
171538
171641
  function replayRefusal(issue2, args) {
171539
171642
  let hint;
171540
171643
  if (issue2.kind === "nested") {
171541
- hint = nestedFlowTarget(args)?.kind === "name" ? " Over a link, a nested e2e-execute runs only when the argent client sends the e2e test it names with the call. Update the argent CLI or MCP adapter on the client." : " Over a link, a nested e2e-execute runs only when it names its e2e test with a test name in name, has an absolute project_root with no .. segment, and has no flow_path.";
171644
+ hint = nestedFlowTarget(args)?.kind === "name" ? " Over a link, a nested e2e-execute runs only when the argent client sends the e2e test it names with the call. Update the argent CLI or MCP adapter on the client." : " Over a link, a nested e2e-execute runs only when it names its e2e test with a test name in name, has an absolute project_root with no .. segment, and has no e2e_test_path.";
171542
171645
  } else if (issue2.fixes.length === 0) {
171543
171646
  hint = " A replay without a link runs this step. To keep it, add it to the YAML by hand after e2e-finish-recording.";
171544
171647
  } else {
@@ -171598,6 +171701,10 @@ Returns { message, stepCount, recorded, savedTo }; \`recorded\`, not the status,
171598
171701
  throw err;
171599
171702
  }
171600
171703
  const authoredArgs = { ...args };
171704
+ try {
171705
+ args = canonicalToolArgs(command, args);
171706
+ } catch {
171707
+ }
171601
171708
  const linked = isLinkedRecorderCall(session, ctx);
171602
171709
  const stepFiles = ctx?.fileInputs?.project_root?.members;
171603
171710
  const project = stepFiles ? new ClientProjectAccess(stepFiles) : new HostProjectAccess();
@@ -173303,7 +173410,7 @@ var flowFinishRecordingTool = {
173303
173410
  completedMsg: ({ params }) => `Saved recorded e2e test ${params.name}`,
173304
173411
  failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to finish recording of e2e test ${params.name}: ${failureSignal2.error_code}`
173305
173412
  },
173306
- description: `Finish recording the e2e test named by \`name\` + \`project_root\`, leaving recordings under any other key untouched. Returns { message, path, executionPrerequisite, steps, summary, flowFile, savedTo } - a summary of all recorded steps plus the final YAML. Use when you have added all desired steps and want to finalize the e2e test file. Fails if that e2e test has no recording in progress.
173413
+ description: `Finish recording the e2e test named by \`name\` + \`project_root\`, leaving recordings under any other key untouched. Returns { message, path, executionPrerequisite, steps, summary, e2eTestFile, savedTo } - a summary of all recorded steps plus the final YAML. Use when you have added all desired steps and want to finalize the e2e test file. Fails if that e2e test has no recording in progress.
173307
173414
  A warning e2e-add-step raised on a recorded \`await-ui-element\` is repeated in \`summary\` as a \`warning:\` line of its own, right below the step it judges, and \`message\` counts them by kind. A warning is repeated only while the step it judges is still identifiable by its number: hand-editing the .yaml during the recording moves the steps, so those warnings are DROPPED rather than pinned on whichever step inherited the number, and \`message\` says how many were dropped. A step that carries a cross-tree warning was re-probed against the runner's tree: read it before converting that wait to \`await:\`/\`assert:\`, which is what the verdict is about and what this moment is for. A step that recorded a wait which did not pass was never probed at all, and its own warning names the CAUSE, because only one of them judges the condition: an unmet wait was read and found false, and it stops the run at replay; a wait whose tree source could not be read, or one that was cancelled, observed nothing and leaves the condition UNKNOWN rather than known-bad. Read those before replaying.
173308
173415
  You can still edit the .yaml file directly afterwards to remove or reorder steps.`,
173309
173416
  zodSchema: zodSchema70,
@@ -173341,6 +173448,7 @@ You can still edit the .yaml file directly afterwards to remove or reorder steps
173341
173448
  executionPrerequisite: flow.executionPrerequisite,
173342
173449
  steps: flow.steps.length,
173343
173450
  summary,
173451
+ e2eTestFile: flowFile,
173344
173452
  flowFile,
173345
173453
  savedTo
173346
173454
  };
@@ -175786,15 +175894,15 @@ async function restoreStatusBar(device) {
175786
175894
  // ../tool-server/src/tools/flows/flow-run.ts
175787
175895
  var zodSchema71 = external_exports.object({
175788
175896
  name: external_exports.string().optional().describe(
175789
- 'Name of a saved e2e test to run from `.argent/e2e`, or `.argent/flows` when it is not there (e.g. "settings-explore"). Omit when flow_path is set.'
175897
+ 'Name of a saved e2e test to run from `.argent/e2e`, or `.argent/flows` when it is not there (e.g. "settings-explore"). Omit when e2e_test_path is set.'
175790
175898
  ),
175791
175899
  project_root: external_exports.string().describe(
175792
- "Absolute path to the calling agent's project root: the cwd it is working in. With name, the saved e2e test is read from `.argent/e2e/<name>.yaml`, else `.argent/flows/<name>.yaml`, under this root; with flow_path, the e2e test, its run: siblings, its script: paths and baselines all resolve beside the YAML instead, so pass the agent's cwd. A script still RUNS in this root whichever source was used."
175900
+ "Absolute path to the calling agent's project root: the cwd it is working in. With name, the saved e2e test is read from `.argent/e2e/<name>.yaml`, else `.argent/flows/<name>.yaml`, under this root; with e2e_test_path, the e2e test, its run: siblings, its script: paths and baselines all resolve beside the YAML instead, so pass the agent's cwd. A script still RUNS in this root whichever source was used."
175793
175901
  ),
175794
- flow_file: external_exports.string().optional().describe(
175902
+ e2e_test_file: external_exports.string().optional().describe(
175795
175903
  "Path to the e2e test .yaml as readable by the tool-server. Internal: the argent client derives it from project_root and name automatically; leave unset."
175796
175904
  ),
175797
- flow_path: external_exports.string().optional().describe(
175905
+ e2e_test_path: external_exports.string().optional().describe(
175798
175906
  "Omit when name is set. Absolute path to an e2e test .yaml on the client. Over a link (argent link or ARGENT_TOOLS_URL), the argent client uploads the file, its run: fragments and the snapshot baselines of each run, and the tool-server runs those copies. Keep run: fragments under project_root or beside the e2e test file. With updateBaselines, the client writes the new baselines when the run ends. The client also uploads each absolute .png or .yaml path that a tool: step takes as a file argument, when the file is under project_root or beside the e2e test file. A tool: e2e-execute step that names an e2e test with name and an absolute project_root without a .. segment runs: the client also uploads that e2e test and its own files. Over a link, the uploaded e2e tests must not have: script: steps; tool: steps that run an e2e test another way or record an e2e test; tool: steps that take a directory, an app or an output directory; tool: steps whose tool builds a file path from several arguments; tool: steps that name a relative path or a file other than .png or .yaml. Without a link, the tool-server reads the files in place and all step kinds run."
175799
175907
  ),
175800
175908
  device: external_exports.string().optional().describe(
@@ -175810,32 +175918,32 @@ var zodSchema71 = external_exports.object({
175810
175918
  "Set to true to confirm the execution prerequisite has been met. Required (LLM path) when a fragment defines an executionPrerequisite."
175811
175919
  )
175812
175920
  }).superRefine((params, ctx) => {
175813
- if (params.name === void 0 === (params.flow_path === void 0)) {
175921
+ if (params.name === void 0 === (params.e2e_test_path === void 0)) {
175814
175922
  ctx.addIssue({
175815
175923
  code: external_exports.ZodIssueCode.custom,
175816
- message: params.name !== void 0 ? "Pass exactly one e2e test source: name or flow_path." : "Pass exactly one e2e test source: name or flow_path. e2e-execute needs the e2e test's name in `name` \u2014 it resolves <project_root>/.argent/e2e/<name>.yaml, else .argent/flows/<name>.yaml.",
175817
- // The ROOT, not `flow_path`: the rule spans both source fields, and a
175818
- // path would prefix the message with "`flow_path`:".
175924
+ message: params.name !== void 0 ? "Pass exactly one e2e test source: name or e2e_test_path." : "Pass exactly one e2e test source: name or e2e_test_path. e2e-execute needs the e2e test's name in `name` \u2014 it resolves <project_root>/.argent/e2e/<name>.yaml, else .argent/flows/<name>.yaml.",
175925
+ // The ROOT, not `e2e_test_path`: the rule spans both source fields, and a
175926
+ // path would prefix the message with "`e2e_test_path`:".
175819
175927
  path: []
175820
175928
  });
175821
175929
  }
175822
175930
  });
175823
175931
  var fileInputs4 = [
175824
175932
  {
175825
- target: "flow_path",
175826
- path: "${flow_path}",
175933
+ target: "e2e_test_path",
175934
+ path: "${e2e_test_path}",
175827
175935
  kind: "file",
175828
175936
  optional: true,
175829
175937
  unwrapWhenSet: "name",
175830
175938
  collect: "flow"
175831
175939
  },
175832
175940
  {
175833
- target: "flow_file",
175941
+ target: "e2e_test_file",
175834
175942
  // A client that predates `paths` reads the legacy dir only.
175835
175943
  path: "${project_root}/.argent/flows/${name}.yaml",
175836
175944
  paths: E2E_FILE_TEMPLATES,
175837
175945
  kind: "file",
175838
- skipWhenSet: "flow_path",
175946
+ skipWhenSet: "e2e_test_path",
175839
175947
  collect: "flow"
175840
175948
  }
175841
175949
  ];
@@ -176126,8 +176234,8 @@ function deviceEnv(state3) {
176126
176234
  return { ...state3, device: state3.device };
176127
176235
  }
176128
176236
  function displayFlowName(params) {
176129
- const stem = params.flow_path === void 0 ? void 0 : path50.basename(params.flow_path, ".yaml");
176130
- return params.name || stem || params.flow_path || "(unspecified)";
176237
+ const stem = params.e2e_test_path === void 0 ? void 0 : path50.basename(params.e2e_test_path, ".yaml");
176238
+ return params.name || stem || params.e2e_test_path || "(unspecified)";
176131
176239
  }
176132
176240
  function* walkSteps(steps, within = "") {
176133
176241
  for (const [i, step] of steps.entries()) {
@@ -176273,7 +176381,9 @@ function assertUploadSelfContained(registry2, flow, closure, origin = {
176273
176381
  throw new FailureError(
176274
176382
  `${origin.subject} is not self-contained, and ${origin.arrival}. The steps below read or write project files, which stay on the client:
176275
176383
  ` + offending.map((o) => ` - ${o.line}`).join("\n") + `
176276
- Run the e2e test on the computer that runs the tool-server, with no link and no ARGENT_TOOLS_URL, so that the tool-server reads the files in place.${uploadUpdateHint(servable)}${toolFilePathHints(toolFileFixes)}`,
176384
+ Run the e2e test on the computer that runs the tool-server, with no link and no ARGENT_TOOLS_URL, so that the tool-server reads the files in place.${uploadUpdateHint(servable)}` + // An older client lists e2e_test_path from this schema but collects the
176385
+ // files only for flow_path.
176386
+ (servable.length > 0 ? " A client older than the e2e_test_path param sends those files for a test passed as flow_path." : "") + toolFilePathHints(toolFileFixes),
176277
176387
  {
176278
176388
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
176279
176389
  failure_stage: UPLOAD_STAGE_BY_KIND[offending[0].kind],
@@ -176294,7 +176404,7 @@ function createRunFlowTool(registry2) {
176294
176404
  description: `Run a saved YAML e2e test end to end. Use when
176295
176405
  asked to replay a recorded path, re-run a QA regression, or check that a known journey still passes; for a
176296
176406
  one-off interaction use the gesture tools instead, and to author an e2e test use e2e-start-recording. Pass
176297
- exactly one e2e test source: name (under project_root) or flow_path.
176407
+ exactly one e2e test source: name (under project_root) or e2e_test_path.
176298
176408
  Returns a per-step report: the first failure stops the run and the rest report as skipped.`,
176299
176409
  longRunning: true,
176300
176410
  zodSchema: zodSchema71,
@@ -176305,11 +176415,11 @@ Returns a per-step report: the first failure stops the run and the rest report a
176305
176415
  const signal = ctx?.signal;
176306
176416
  const { filePath, flowName, viaUpload, shadowed } = await resolveFlowSource(
176307
176417
  params,
176308
- ctx?.fileInputs?.flow_file,
176309
- ctx?.fileInputs?.flow_path
176418
+ ctx?.fileInputs?.e2e_test_file,
176419
+ ctx?.fileInputs?.e2e_test_path
176310
176420
  );
176311
176421
  const warning = shadowed ? { warning: e2eShadowWarning(filePath, shadowed) } : {};
176312
- const rootInput = viaUpload ? ctx?.fileInputs?.flow_path ?? ctx?.fileInputs?.flow_file : void 0;
176422
+ const rootInput = viaUpload ? ctx?.fileInputs?.e2e_test_path ?? ctx?.fileInputs?.e2e_test_file : void 0;
176313
176423
  const clientProject = rootInput?.members ? new ClientProjectAccess(rootInput.members) : void 0;
176314
176424
  const project = clientProject ?? new HostProjectAccess();
176315
176425
  const canonicalPath = await canonicalFlowPath(filePath);
@@ -176369,6 +176479,7 @@ Returns a per-step report: the first failure stops the run and the rest report a
176369
176479
  }
176370
176480
  if (flow.executionPrerequisite && !params.prerequisiteAcknowledged) {
176371
176481
  return {
176482
+ e2eTest: flowName,
176372
176483
  flow: flowName,
176373
176484
  notice: "This e2e test has an execution prerequisite that must be fulfilled before it can run. Verify the prerequisite is met and call e2e-execute again with prerequisiteAcknowledged set to true.",
176374
176485
  executionPrerequisite: flow.executionPrerequisite,
@@ -176622,6 +176733,7 @@ function summarize(flowName, deviceId, executionPrerequisite, steps, aborted2, t
176622
176733
  else errored++;
176623
176734
  }
176624
176735
  return {
176736
+ e2eTest: flowName,
176625
176737
  flow: flowName,
176626
176738
  device: deviceId,
176627
176739
  executionPrerequisite,
@@ -176640,6 +176752,7 @@ function summarize(flowName, deviceId, executionPrerequisite, steps, aborted2, t
176640
176752
  };
176641
176753
  }
176642
176754
  function pushReport(state3, report) {
176755
+ if (report.e2eTest !== void 0) report.flow = report.e2eTest;
176643
176756
  state3.reports.push(report);
176644
176757
  state3.onStepReport?.(report);
176645
176758
  }
@@ -176676,7 +176789,7 @@ async function execSteps(state3, steps, scope) {
176676
176789
  index,
176677
176790
  kind: step.kind,
176678
176791
  status: "skip",
176679
- flow: stepFlow(step, scope),
176792
+ e2eTest: stepFlow(step, scope),
176680
176793
  target: stepTarget(step),
176681
176794
  ...depthOf(scope),
176682
176795
  ...stopReason ? { reason: stopReason } : {},
@@ -176694,7 +176807,7 @@ async function execSteps(state3, steps, scope) {
176694
176807
  index,
176695
176808
  kind: step.kind,
176696
176809
  status: "error",
176697
- flow: scopeFlow(scope),
176810
+ e2eTest: scopeFlow(scope),
176698
176811
  target: stepTarget(step),
176699
176812
  ...depthOf(scope),
176700
176813
  reason: `step needs a device but the e2e test was resolved as device-free \u2014 pass an explicit device`
@@ -176710,7 +176823,7 @@ async function execSteps(state3, steps, scope) {
176710
176823
  kind: step.kind,
176711
176824
  status: "skip",
176712
176825
  reason: "run aborted",
176713
- flow: stepFlow(step, scope),
176826
+ e2eTest: stepFlow(step, scope),
176714
176827
  target: stepTarget(step),
176715
176828
  ...depthOf(scope),
176716
176829
  ...step.kind === "echo" ? { message: step.message } : {}
@@ -176762,7 +176875,7 @@ function reportBlockSkipped(state3, steps, scope, reason2) {
176762
176875
  kind: step.kind,
176763
176876
  status: "skip",
176764
176877
  reason: reason2,
176765
- flow: stepFlow(step, scope),
176878
+ e2eTest: stepFlow(step, scope),
176766
176879
  target: stepTarget(step),
176767
176880
  ...depthOf(scope),
176768
176881
  ...step.kind === "echo" ? { message: step.message } : {}
@@ -176788,7 +176901,7 @@ async function execWhenStep(state3, step, scope) {
176788
176901
  const marker = {
176789
176902
  index,
176790
176903
  kind: "when",
176791
- flow: scopeFlow(scope),
176904
+ e2eTest: scopeFlow(scope),
176792
176905
  target,
176793
176906
  ...depthOf(scope)
176794
176907
  };
@@ -176848,7 +176961,7 @@ async function execRunStep(state3, step, scope) {
176848
176961
  index,
176849
176962
  kind: "run",
176850
176963
  status: "error",
176851
- flow: display,
176964
+ e2eTest: display,
176852
176965
  target,
176853
176966
  reason: reason2,
176854
176967
  ...depthOf(scope),
@@ -176896,7 +177009,7 @@ async function execRunStep(state3, step, scope) {
176896
177009
  index,
176897
177010
  kind: "run",
176898
177011
  status: "pass",
176899
- flow: display,
177012
+ e2eTest: display,
176900
177013
  target,
176901
177014
  ...depthOf(scope),
176902
177015
  durationMs: Date.now() - startedAt
@@ -176950,7 +177063,7 @@ async function execLeafStep(state3, step, index, scope) {
176950
177063
  const base = {
176951
177064
  index,
176952
177065
  kind: step.kind,
176953
- flow: scopeFlow(scope),
177066
+ e2eTest: scopeFlow(scope),
176954
177067
  target: stepTarget(step),
176955
177068
  ...depthOf(scope)
176956
177069
  };
@@ -177158,7 +177271,7 @@ function errMsg4(err) {
177158
177271
  return err instanceof Error ? err.message : String(err);
177159
177272
  }
177160
177273
  function assertFlowPathShape(flowPath) {
177161
- const windows = windowsPathRefusal("flow_path", flowPath);
177274
+ const windows = windowsPathRefusal("e2e_test_path", flowPath);
177162
177275
  if (windows) {
177163
177276
  throw new FailureError(windows, {
177164
177277
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
@@ -177169,7 +177282,7 @@ function assertFlowPathShape(flowPath) {
177169
177282
  }
177170
177283
  if (!path50.isAbsolute(flowPath)) {
177171
177284
  throw new FailureError(
177172
- `Invalid flow_path "${flowPath}": e2e test paths must be absolute \u2014 a relative path is resolved against the tool server's working directory, not the caller's. Pass the absolute path to the e2e test's YAML.`,
177285
+ `Invalid e2e_test_path "${flowPath}": e2e test paths must be absolute \u2014 a relative path is resolved against the tool server's working directory, not the caller's. Pass the absolute path to the e2e test's YAML.`,
177173
177286
  {
177174
177287
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
177175
177288
  failure_stage: "flow_path_absolute",
@@ -177180,7 +177293,7 @@ function assertFlowPathShape(flowPath) {
177180
177293
  }
177181
177294
  if (flowPath.split(/[\\/]+/).includes("..")) {
177182
177295
  throw new FailureError(
177183
- `Invalid flow_path "${flowPath}": e2e test paths must not contain ".." segments \u2014 a ".." after a symlinked directory can name a different file than the spelling suggests, and the argent client always sends fully resolved paths. Pass the fully resolved absolute path to the e2e test's YAML.`,
177296
+ `Invalid e2e_test_path "${flowPath}": e2e test paths must not contain ".." segments \u2014 a ".." after a symlinked directory can name a different file than the spelling suggests, and the argent client always sends fully resolved paths. Pass the fully resolved absolute path to the e2e test's YAML.`,
177184
177297
  {
177185
177298
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
177186
177299
  failure_stage: "flow_path_dotdot",
@@ -177195,7 +177308,7 @@ function flowNameOf(clientPath) {
177195
177308
  const bareExtension = path50.basename(clientPath).toLowerCase() === ".yaml";
177196
177309
  if (!bareExtension && clientExt !== ".yaml") {
177197
177310
  const detail = clientExt.toLowerCase() === ".yaml" ? `e2e test files must use the lowercase .yaml extension, not "${clientExt}".` : `e2e test files must use the .yaml extension.`;
177198
- throw new FailureError(`Invalid flow_path "${clientPath}": ${detail}`, {
177311
+ throw new FailureError(`Invalid e2e_test_path "${clientPath}": ${detail}`, {
177199
177312
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
177200
177313
  failure_stage: "flow_path_extension",
177201
177314
  failure_area: "tool_server",
@@ -177208,9 +177321,9 @@ function flowNameOf(clientPath) {
177208
177321
  }
177209
177322
  function flowPathCasingError(clientPath, spelling) {
177210
177323
  const suppliedBase = path50.basename(clientPath);
177211
- const recovery = spelling.state === "absent" ? `Pass the basename exactly as it appears on disk.` : spelling.addressable ? `Pass flow_path with the on-disk basename "${spelling.actual}".` : `Rename "${spelling.actual}" to "${suppliedBase}" to run it \u2014 e2e test files must be lowercase .yaml.`;
177324
+ const recovery = spelling.state === "absent" ? `Pass the basename exactly as it appears on disk.` : spelling.addressable ? `Pass e2e_test_path with the on-disk basename "${spelling.actual}".` : `Rename "${spelling.actual}" to "${suppliedBase}" to run it \u2014 e2e test files must be lowercase .yaml.`;
177212
177325
  return new FailureError(
177213
- `Invalid flow_path "${clientPath}": the file must be named as it appears on disk \u2014 this filesystem matched "${suppliedBase}" case-insensitively` + (spelling.state === "case_folded" ? ` to "${spelling.actual}"` : "") + `, so the e2e test name (which keys the report and __baselines__/) would be one no directory entry carries. ${recovery}`,
177326
+ `Invalid e2e_test_path "${clientPath}": the file must be named as it appears on disk \u2014 this filesystem matched "${suppliedBase}" case-insensitively` + (spelling.state === "case_folded" ? ` to "${spelling.actual}"` : "") + `, so the e2e test name (which keys the report and __baselines__/) would be one no directory entry carries. ${recovery}`,
177214
177327
  {
177215
177328
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
177216
177329
  failure_stage: "flow_path_casing",
@@ -177220,8 +177333,8 @@ function flowPathCasingError(clientPath, spelling) {
177220
177333
  );
177221
177334
  }
177222
177335
  async function resolveFlowSource(params, fileInput, flowPathInput) {
177223
- if (params.name === void 0 === (params.flow_path === void 0)) {
177224
- throw new FailureError("Pass exactly one e2e test source: name or flow_path.", {
177336
+ if (params.name === void 0 === (params.e2e_test_path === void 0)) {
177337
+ throw new FailureError("Pass exactly one e2e test source: name or e2e_test_path.", {
177225
177338
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
177226
177339
  failure_stage: "flow_source",
177227
177340
  failure_area: "tool_server",
@@ -177229,16 +177342,16 @@ async function resolveFlowSource(params, fileInput, flowPathInput) {
177229
177342
  });
177230
177343
  }
177231
177344
  assertValidProjectRoot(params.project_root);
177232
- if (params.flow_path !== void 0) {
177345
+ if (params.e2e_test_path !== void 0) {
177233
177346
  if (flowPathInput?.viaUpload) {
177234
177347
  const clientPath2 = flowPathInput.clientPath;
177235
177348
  assertFlowPathShape(clientPath2);
177236
- return { filePath: params.flow_path, flowName: flowNameOf(clientPath2), viaUpload: true };
177349
+ return { filePath: params.e2e_test_path, flowName: flowNameOf(clientPath2), viaUpload: true };
177237
177350
  }
177238
- const isVerifiedHostPath = flowPathInput?.presentOnHost === true && flowPathInput.statVerified === true && path50.resolve(params.flow_path) === path50.resolve(flowPathInput.clientPath);
177351
+ const isVerifiedHostPath = flowPathInput?.presentOnHost === true && flowPathInput.statVerified === true && path50.resolve(params.e2e_test_path) === path50.resolve(flowPathInput.clientPath);
177239
177352
  if (!isVerifiedHostPath) {
177240
177353
  throw new FailureError(
177241
- `Invalid flow_path "${params.flow_path}": explicit e2e test paths must be supplied through the flow_path file-input boundary. Pass the client-local path and let the argent client resolve it.`,
177354
+ `Invalid e2e_test_path "${params.e2e_test_path}": explicit e2e test paths must be supplied through the e2e_test_path file-input boundary. Pass the client-local path and let the argent client resolve it.`,
177242
177355
  {
177243
177356
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
177244
177357
  failure_stage: "flow_path_boundary",
@@ -177247,24 +177360,26 @@ async function resolveFlowSource(params, fileInput, flowPathInput) {
177247
177360
  }
177248
177361
  );
177249
177362
  }
177250
- assertFlowPathShape(params.flow_path);
177363
+ assertFlowPathShape(params.e2e_test_path);
177251
177364
  const clientPath = flowPathInput.clientPath;
177252
177365
  const flowName2 = flowNameOf(clientPath);
177253
177366
  const spelling = await classifyOnDiskSpelling(
177254
- path50.dirname(params.flow_path),
177367
+ path50.dirname(params.e2e_test_path),
177255
177368
  path50.basename(clientPath)
177256
177369
  );
177257
177370
  if (spelling.state !== "listed") throw flowPathCasingError(clientPath, spelling);
177258
- return { filePath: params.flow_path, flowName: flowName2, viaUpload: false };
177371
+ return { filePath: params.e2e_test_path, flowName: flowName2, viaUpload: false };
177259
177372
  }
177260
177373
  const flowName = params.name;
177261
177374
  assertSafeFlowName(flowName);
177262
177375
  const candidates = e2eFileCandidates(params.project_root, flowName);
177263
- if (params.flow_file && fileInput?.viaUpload)
177264
- return { filePath: params.flow_file, flowName, viaUpload: true };
177265
- if (params.flow_file && (!path50.isAbsolute(params.flow_file) || params.flow_file.split(/[\\/]+/).includes("..") || !candidates.some((candidate) => path50.resolve(candidate) === path50.resolve(params.flow_file)))) {
177376
+ if (params.e2e_test_file && fileInput?.viaUpload)
177377
+ return { filePath: params.e2e_test_file, flowName, viaUpload: true };
177378
+ if (params.e2e_test_file && (!path50.isAbsolute(params.e2e_test_file) || params.e2e_test_file.split(/[\\/]+/).includes("..") || !candidates.some(
177379
+ (candidate) => path50.resolve(candidate) === path50.resolve(params.e2e_test_file)
177380
+ ))) {
177266
177381
  throw new FailureError(
177267
- `Invalid flow_file "${params.flow_file}": it must resolve to the e2e test's path under the project root ("${candidates.join('" or "')}"). flow_file is internal \u2014 leave it unset and pass project_root + name.`,
177382
+ `Invalid e2e_test_file "${params.e2e_test_file}": it must resolve to the e2e test's path under the project root ("${candidates.join('" or "')}"). e2e_test_file is internal \u2014 leave it unset and pass project_root + name.`,
177268
177383
  {
177269
177384
  error_code: FAILURE_CODES.FLOW_FILE_INVALID,
177270
177385
  failure_stage: "flow_file_containment",
@@ -177274,8 +177389,8 @@ async function resolveFlowSource(params, fileInput, flowPathInput) {
177274
177389
  );
177275
177390
  }
177276
177391
  const picked = await pickE2eFile(candidates);
177277
- const filePath = params.flow_file || picked.path;
177278
- const spellingDirs = params.flow_file || picked.exists ? [path50.dirname(filePath)] : candidates.map((candidate) => path50.dirname(candidate));
177392
+ const filePath = params.e2e_test_file || picked.path;
177393
+ const spellingDirs = params.e2e_test_file || picked.exists ? [path50.dirname(filePath)] : candidates.map((candidate) => path50.dirname(candidate));
177279
177394
  for (const dir of spellingDirs) {
177280
177395
  const spelling = await classifyOnDiskSpelling(dir, `${flowName}.yaml`);
177281
177396
  if (spelling.state === "case_folded") throw flowNameCasingError(flowName, spelling);
@@ -177290,22 +177405,22 @@ var fs66 = __toESM(require("node:fs/promises"));
177290
177405
  init_src2();
177291
177406
  var zodSchema72 = external_exports.object({
177292
177407
  name: external_exports.string().optional().describe(
177293
- 'Name of a saved e2e test to inspect from `.argent/e2e`, or `.argent/flows` when it is not there (e.g. "settings-explore"). Omit when flow_path is set.'
177408
+ 'Name of a saved e2e test to inspect from `.argent/e2e`, or `.argent/flows` when it is not there (e.g. "settings-explore"). Omit when e2e_test_path is set.'
177294
177409
  ),
177295
177410
  project_root: external_exports.string().describe(
177296
- "Absolute path to the calling agent's project root: the cwd it is working in. With name, the saved e2e test is read from `.argent/e2e/<name>.yaml`, else `.argent/flows/<name>.yaml`, under this root; with flow_path, the prerequisite is read from that YAML instead, so pass the agent's cwd."
177411
+ "Absolute path to the calling agent's project root: the cwd it is working in. With name, the saved e2e test is read from `.argent/e2e/<name>.yaml`, else `.argent/flows/<name>.yaml`, under this root; with e2e_test_path, the prerequisite is read from that YAML instead, so pass the agent's cwd."
177297
177412
  ),
177298
- flow_file: external_exports.string().optional().describe(
177413
+ e2e_test_file: external_exports.string().optional().describe(
177299
177414
  "Path to the e2e test .yaml as readable by the tool-server. Internal: the argent client derives it from project_root and name automatically; leave unset."
177300
177415
  ),
177301
- flow_path: external_exports.string().optional().describe(
177416
+ e2e_test_path: external_exports.string().optional().describe(
177302
177417
  "Omit when name is set. Absolute path to an e2e test .yaml on the client. Over a link (argent link or ARGENT_TOOLS_URL), the argent client uploads the file and the tool-server reads the uploaded copy; without a link, the tool-server reads the file in place. Pass the path through the argent client; a raw path that the file-input boundary did not verify is rejected. Pass the same e2e test source here as to e2e-execute, so the prerequisite you read belongs to the e2e test that will run."
177303
177418
  )
177304
177419
  }).superRefine((params, ctx) => {
177305
- if (params.name === void 0 === (params.flow_path === void 0)) {
177420
+ if (params.name === void 0 === (params.e2e_test_path === void 0)) {
177306
177421
  ctx.addIssue({
177307
177422
  code: external_exports.ZodIssueCode.custom,
177308
- message: params.name !== void 0 ? "Pass exactly one e2e test source: name or flow_path." : "Pass exactly one e2e test source: name or flow_path. e2e-read-prerequisite needs the e2e test's name in `name` \u2014 it resolves <project_root>/.argent/e2e/<name>.yaml, else .argent/flows/<name>.yaml.",
177423
+ message: params.name !== void 0 ? "Pass exactly one e2e test source: name or e2e_test_path." : "Pass exactly one e2e test source: name or e2e_test_path. e2e-read-prerequisite needs the e2e test's name in `name` \u2014 it resolves <project_root>/.argent/e2e/<name>.yaml, else .argent/flows/<name>.yaml.",
177309
177424
  // The ROOT, matching flow-execute: the rule spans both source fields,
177310
177425
  // so it must not be anchored on one of them.
177311
177426
  path: []
@@ -177314,18 +177429,18 @@ var zodSchema72 = external_exports.object({
177314
177429
  });
177315
177430
  var fileInputs5 = [
177316
177431
  {
177317
- target: "flow_path",
177318
- path: "${flow_path}",
177432
+ target: "e2e_test_path",
177433
+ path: "${e2e_test_path}",
177319
177434
  kind: "file",
177320
177435
  optional: true,
177321
177436
  unwrapWhenSet: "name"
177322
177437
  },
177323
177438
  {
177324
- target: "flow_file",
177439
+ target: "e2e_test_file",
177325
177440
  path: "${project_root}/.argent/flows/${name}.yaml",
177326
177441
  paths: E2E_FILE_TEMPLATES,
177327
177442
  kind: "file",
177328
- skipWhenSet: "flow_path"
177443
+ skipWhenSet: "e2e_test_path"
177329
177444
  }
177330
177445
  ];
177331
177446
  var flowReadPrerequisiteTool = {
@@ -177336,8 +177451,8 @@ var flowReadPrerequisiteTool = {
177336
177451
  completedMsg: () => "Read e2e test prerequisite",
177337
177452
  failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to read e2e test prerequisite: ${failureSignal2.error_code}`
177338
177453
  },
177339
- description: `Read the execution prerequisite of an e2e test without running it: a saved e2e test from the .argent/e2e/ directory (or the older .argent/flows/), or an explicit boundary-managed flow_path.
177340
- Returns { flow, executionPrerequisite, warning? }: the logical name, plus the precondition its author recorded
177454
+ description: `Read the execution prerequisite of an e2e test without running it: a saved e2e test from the .argent/e2e/ directory (or the older .argent/flows/), or an explicit boundary-managed e2e_test_path.
177455
+ Returns { e2eTest, executionPrerequisite, warning? }: the logical name, plus the precondition its author recorded
177341
177456
  verbatim. Empty when none was declared, which is always so for a self-contained scenario: one opening
177342
177457
  on a launch may declare no prerequisite, because it builds its own start state.
177343
177458
  Use when deciding whether the device already sits where a fragment expects it (correct app foregrounded,
@@ -177345,7 +177460,7 @@ correct account, correct screen) before committing to a run, or when relaying th
177345
177460
  Touches no device: nothing is launched, tapped, dispatched or torn down, and no simulator or emulator
177346
177461
  needs booting, so calling this costs nothing but a file read.
177347
177462
  Fails if the e2e test file does not exist.
177348
- Address the e2e test exactly as you will address it in e2e-execute: name or flow_path, one and only one; supplying both or neither is rejected. The name goes in \`name\`, which resolves <project_root>/.argent/e2e/<name>.yaml, else .argent/flows/<name>.yaml.`,
177463
+ Address the e2e test exactly as you will address it in e2e-execute: name or e2e_test_path, one and only one; supplying both or neither is rejected. The name goes in \`name\`, which resolves <project_root>/.argent/e2e/<name>.yaml, else .argent/flows/<name>.yaml.`,
177349
177464
  readOnly: true,
177350
177465
  zodSchema: zodSchema72,
177351
177466
  fileInputs: fileInputs5,
@@ -177353,11 +177468,12 @@ Address the e2e test exactly as you will address it in e2e-execute: name or flow
177353
177468
  async execute(_services, params, ctx) {
177354
177469
  const { filePath, flowName, shadowed } = await resolveFlowSource(
177355
177470
  params,
177356
- ctx?.fileInputs?.flow_file,
177357
- ctx?.fileInputs?.flow_path
177471
+ ctx?.fileInputs?.e2e_test_file,
177472
+ ctx?.fileInputs?.e2e_test_path
177358
177473
  );
177359
177474
  const flow = parseFlow(await fs66.readFile(filePath, "utf8"));
177360
177475
  return {
177476
+ e2eTest: flowName,
177361
177477
  flow: flowName,
177362
177478
  executionPrerequisite: flow.executionPrerequisite,
177363
177479
  ...shadowed ? { warning: e2eShadowWarning(filePath, shadowed) } : {}