@exodus/xqa 1.13.0 → 1.14.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/dist/xqa.cjs +161 -154
- package/package.json +2 -2
package/dist/xqa.cjs
CHANGED
|
@@ -52247,7 +52247,6 @@ function createAccessibilitySnapshotTool(udid = "booted") {
|
|
|
52247
52247
|
}
|
|
52248
52248
|
);
|
|
52249
52249
|
}
|
|
52250
|
-
var accessibilitySnapshotTool = createAccessibilitySnapshotTool();
|
|
52251
52250
|
var LAUNCH_APP_TOOL_NAME = "launch_app";
|
|
52252
52251
|
var TERMINATE_APP_TOOL_NAME = "terminate_app";
|
|
52253
52252
|
var LIST_APPS_TOOL_NAME = "list_apps";
|
|
@@ -52348,9 +52347,6 @@ function createListAppsTool(udid = "booted") {
|
|
|
52348
52347
|
async () => handleListApps(udid)
|
|
52349
52348
|
);
|
|
52350
52349
|
}
|
|
52351
|
-
var launchAppTool = createLaunchAppTool();
|
|
52352
|
-
var terminateAppTool = createTerminateAppTool();
|
|
52353
|
-
var listAppsTool = createListAppsTool();
|
|
52354
52350
|
var DEFAULT_LONG_PRESS_DURATION_MS = 500;
|
|
52355
52351
|
var MS_PER_SECOND = 1e3;
|
|
52356
52352
|
var ENTER_KEY_CODE = "0x28";
|
|
@@ -52539,12 +52535,6 @@ function createPressButtonTool(udid = "booted") {
|
|
|
52539
52535
|
async ({ button }) => handlePressButton(udid, button)
|
|
52540
52536
|
);
|
|
52541
52537
|
}
|
|
52542
|
-
var tapTool = createTapTool();
|
|
52543
|
-
var doubleTapTool = createDoubleTapTool();
|
|
52544
|
-
var longPressTool = createLongPressTool();
|
|
52545
|
-
var swipeTool = createSwipeTool();
|
|
52546
|
-
var typeTextTool = createTypeTextTool();
|
|
52547
|
-
var pressButtonTool = createPressButtonTool();
|
|
52548
52538
|
var cache2 = /* @__PURE__ */ new Map();
|
|
52549
52539
|
function parseDeviceInfo(raw) {
|
|
52550
52540
|
const parsed = JSON.parse(raw);
|
|
@@ -52620,7 +52610,6 @@ function createScreenshotTool(udid = "booted") {
|
|
|
52620
52610
|
}
|
|
52621
52611
|
);
|
|
52622
52612
|
}
|
|
52623
|
-
var screenshotTool = createScreenshotTool();
|
|
52624
52613
|
var MIN_WAIT_SECONDS = 1;
|
|
52625
52614
|
var MAX_WAIT_SECONDS = 10;
|
|
52626
52615
|
var MS_PER_SECOND2 = 1e3;
|
|
@@ -62198,121 +62187,24 @@ var import_neverthrow33 = __toESM(require_index_cjs(), 1);
|
|
|
62198
62187
|
var import_promises16 = require("node:timers/promises");
|
|
62199
62188
|
|
|
62200
62189
|
// ../../agents/explorer/dist/index.js
|
|
62201
|
-
var import_node_child_process3 = require("node:child_process");
|
|
62202
|
-
var import_node_fs = require("node:fs");
|
|
62203
|
-
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
62204
|
-
var import_neverthrow15 = __toESM(require_index_cjs(), 1);
|
|
62205
62190
|
var import_promises9 = require("node:timers/promises");
|
|
62191
|
+
var import_neverthrow15 = __toESM(require_index_cjs(), 1);
|
|
62206
62192
|
var import_neverthrow16 = __toESM(require_index_cjs(), 1);
|
|
62207
|
-
var import_neverthrow17 = __toESM(require_index_cjs(), 1);
|
|
62208
62193
|
var import_promises10 = require("node:fs/promises");
|
|
62209
|
-
var
|
|
62210
|
-
var
|
|
62211
|
-
var
|
|
62194
|
+
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
62195
|
+
var import_neverthrow17 = __toESM(require_index_cjs(), 1);
|
|
62196
|
+
var import_node_child_process3 = require("node:child_process");
|
|
62212
62197
|
var import_promises11 = require("node:fs/promises");
|
|
62198
|
+
var import_neverthrow18 = __toESM(require_index_cjs(), 1);
|
|
62213
62199
|
var import_neverthrow19 = __toESM(require_index_cjs(), 1);
|
|
62200
|
+
var import_node_child_process4 = require("node:child_process");
|
|
62201
|
+
var import_node_fs = require("node:fs");
|
|
62202
|
+
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
62214
62203
|
var import_neverthrow20 = __toESM(require_index_cjs(), 1);
|
|
62215
62204
|
var import_neverthrow21 = __toESM(require_index_cjs(), 1);
|
|
62216
62205
|
var import_promises12 = require("node:fs/promises");
|
|
62217
62206
|
var import_node_path6 = __toESM(require("node:path"), 1);
|
|
62218
62207
|
var import_neverthrow22 = __toESM(require_index_cjs(), 1);
|
|
62219
|
-
async function runFfmpeg(arguments_) {
|
|
62220
|
-
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
62221
|
-
(0, import_node_child_process3.execFile)("ffmpeg", arguments_, (error48) => {
|
|
62222
|
-
if (error48) {
|
|
62223
|
-
reject(error48);
|
|
62224
|
-
} else {
|
|
62225
|
-
resolve(true);
|
|
62226
|
-
}
|
|
62227
|
-
});
|
|
62228
|
-
await promise2;
|
|
62229
|
-
}
|
|
62230
|
-
function spawnRecorder(outputPath) {
|
|
62231
|
-
const safeMkdirSync = (0, import_neverthrow15.fromThrowable)(import_node_fs.mkdirSync, (cause) => cause);
|
|
62232
|
-
const safeSpawn2 = (0, import_neverthrow15.fromThrowable)(
|
|
62233
|
-
(command, arguments_) => (0, import_node_child_process3.spawn)(command, arguments_),
|
|
62234
|
-
(cause) => cause
|
|
62235
|
-
);
|
|
62236
|
-
const mkdirResult = safeMkdirSync(import_node_path4.default.dirname(outputPath), { recursive: true });
|
|
62237
|
-
if (mkdirResult.isErr()) {
|
|
62238
|
-
return mkdirResult.error;
|
|
62239
|
-
}
|
|
62240
|
-
const spawnResult = safeSpawn2("xcrun", [
|
|
62241
|
-
"simctl",
|
|
62242
|
-
"io",
|
|
62243
|
-
"booted",
|
|
62244
|
-
"recordVideo",
|
|
62245
|
-
"--force",
|
|
62246
|
-
outputPath
|
|
62247
|
-
]);
|
|
62248
|
-
if (spawnResult.isErr()) {
|
|
62249
|
-
return spawnResult.error;
|
|
62250
|
-
}
|
|
62251
|
-
return spawnResult.value;
|
|
62252
|
-
}
|
|
62253
|
-
function earlyExitError(code) {
|
|
62254
|
-
return new RangeError(`simctl exited with code ${String(code)} before recording started`);
|
|
62255
|
-
}
|
|
62256
|
-
function waitForRecordingStart(proc) {
|
|
62257
|
-
const { stderr } = proc;
|
|
62258
|
-
if (!stderr) {
|
|
62259
|
-
return (0, import_neverthrow15.errAsync)({ type: "SPAWN_FAILED", cause: new RangeError("proc has no stderr pipe") });
|
|
62260
|
-
}
|
|
62261
|
-
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
62262
|
-
proc.once("error", reject);
|
|
62263
|
-
proc.once("exit", (code) => {
|
|
62264
|
-
if (code !== 0) {
|
|
62265
|
-
reject(earlyExitError(code));
|
|
62266
|
-
}
|
|
62267
|
-
});
|
|
62268
|
-
let accumulated = "";
|
|
62269
|
-
stderr.on("data", (chunk) => {
|
|
62270
|
-
accumulated += chunk.toString();
|
|
62271
|
-
if (accumulated.includes("Recording started")) {
|
|
62272
|
-
resolve({ process: proc, startedAt: Date.now() });
|
|
62273
|
-
}
|
|
62274
|
-
});
|
|
62275
|
-
return import_neverthrow15.ResultAsync.fromPromise(
|
|
62276
|
-
promise2,
|
|
62277
|
-
(cause) => ({ type: "SPAWN_FAILED", cause })
|
|
62278
|
-
);
|
|
62279
|
-
}
|
|
62280
|
-
function startRecording(outputPath) {
|
|
62281
|
-
const procOrError = spawnRecorder(outputPath);
|
|
62282
|
-
if (procOrError instanceof Error) {
|
|
62283
|
-
return (0, import_neverthrow15.errAsync)({ type: "SPAWN_FAILED", cause: procOrError });
|
|
62284
|
-
}
|
|
62285
|
-
return waitForRecordingStart(procOrError);
|
|
62286
|
-
}
|
|
62287
|
-
function stopRecording(handle) {
|
|
62288
|
-
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
62289
|
-
handle.process.once("exit", () => {
|
|
62290
|
-
resolve(true);
|
|
62291
|
-
});
|
|
62292
|
-
handle.process.once("error", reject);
|
|
62293
|
-
if (handle.process.exitCode == void 0) {
|
|
62294
|
-
handle.process.kill("SIGINT");
|
|
62295
|
-
} else {
|
|
62296
|
-
resolve(true);
|
|
62297
|
-
}
|
|
62298
|
-
return import_neverthrow15.ResultAsync.fromPromise(
|
|
62299
|
-
promise2,
|
|
62300
|
-
(cause) => ({ type: "STOP_FAILED", cause })
|
|
62301
|
-
);
|
|
62302
|
-
}
|
|
62303
|
-
function speedUpVideo({
|
|
62304
|
-
inputPath,
|
|
62305
|
-
factor,
|
|
62306
|
-
outputPath
|
|
62307
|
-
}) {
|
|
62308
|
-
const pts = String(1 / factor);
|
|
62309
|
-
return (0, import_neverthrow15.fromAsyncThrowable)(
|
|
62310
|
-
runFfmpeg,
|
|
62311
|
-
(cause) => ({ type: "PROCESS_FAILED", cause })
|
|
62312
|
-
)(["-i", inputPath, "-filter:v", `setpts=${pts}*PTS`, "-an", "-y", outputPath]).map(
|
|
62313
|
-
() => outputPath
|
|
62314
|
-
);
|
|
62315
|
-
}
|
|
62316
62208
|
var VIEW_UI_TOOL_NAME = "mcp__mobile-ios__view_ui";
|
|
62317
62209
|
var VIEW_UI_DESCRIPTION = `Capture current screen state: accessibility tree (element labels, positions, attributes) and screenshot in one call. Use when you need to tap an element, assert element presence or labels, check attributes, or track screen identity via <screen_id>.
|
|
62318
62210
|
|
|
@@ -62329,8 +62221,8 @@ function deriveScreenLabel(tree, stepIndex) {
|
|
|
62329
62221
|
return match[1].replaceAll(/([A-Z])/g, "-$1").toLowerCase().replaceAll(/[^a-z\d]+/g, "-").replaceAll(/^-|-$/g, "");
|
|
62330
62222
|
}
|
|
62331
62223
|
async function persistScreenshot(params) {
|
|
62332
|
-
const screenshotPath =
|
|
62333
|
-
const safeWriteFile = (0,
|
|
62224
|
+
const screenshotPath = import_node_path4.default.join(params.screenshotsDirectory, `${params.screenLabel}.png`);
|
|
62225
|
+
const safeWriteFile = (0, import_neverthrow17.fromAsyncThrowable)(
|
|
62334
62226
|
import_promises10.writeFile,
|
|
62335
62227
|
(cause) => ({ type: "WRITE_FAILED", cause })
|
|
62336
62228
|
);
|
|
@@ -62395,11 +62287,11 @@ function buildSnapshotRecord(rawElements, { stepIndex, context }) {
|
|
|
62395
62287
|
}
|
|
62396
62288
|
async function handleScreenshotData(data, params) {
|
|
62397
62289
|
if (params.context.screenshotsDir !== void 0) {
|
|
62398
|
-
const safeWrite2 = (0,
|
|
62290
|
+
const safeWrite2 = (0, import_neverthrow17.fromAsyncThrowable)(
|
|
62399
62291
|
import_promises10.writeFile,
|
|
62400
62292
|
(cause) => ({ type: "WRITE_FAILED", cause })
|
|
62401
62293
|
);
|
|
62402
|
-
const snapshotPath =
|
|
62294
|
+
const snapshotPath = import_node_path4.default.join(params.context.screenshotsDir, `${params.screenLabel}.txt`);
|
|
62403
62295
|
await safeWrite2(snapshotPath, params.formatted).match(
|
|
62404
62296
|
() => true,
|
|
62405
62297
|
() => false
|
|
@@ -62562,23 +62454,23 @@ function processMessage(message, state) {
|
|
|
62562
62454
|
}
|
|
62563
62455
|
if (message.type === "result") {
|
|
62564
62456
|
if (message.subtype !== "success" && !state.timedOut.value && !state.aborted.value) {
|
|
62565
|
-
return (0,
|
|
62457
|
+
return (0, import_neverthrow16.err)(message.errors.join("; "));
|
|
62566
62458
|
}
|
|
62567
|
-
return (0,
|
|
62459
|
+
return (0, import_neverthrow16.ok)(true);
|
|
62568
62460
|
}
|
|
62569
|
-
return (0,
|
|
62461
|
+
return (0, import_neverthrow16.ok)(false);
|
|
62570
62462
|
}
|
|
62571
62463
|
async function processMessages(queryRunner, state) {
|
|
62572
62464
|
for await (const message of queryRunner) {
|
|
62573
62465
|
const result = processMessage(message, state);
|
|
62574
62466
|
if (result.isErr()) {
|
|
62575
|
-
return (0,
|
|
62467
|
+
return (0, import_neverthrow16.err)(result.error);
|
|
62576
62468
|
}
|
|
62577
62469
|
if (result.value) {
|
|
62578
62470
|
break;
|
|
62579
62471
|
}
|
|
62580
62472
|
}
|
|
62581
|
-
return (0,
|
|
62473
|
+
return (0, import_neverthrow16.ok)(null);
|
|
62582
62474
|
}
|
|
62583
62475
|
var MessageQueue = class {
|
|
62584
62476
|
pending = [];
|
|
@@ -62620,7 +62512,7 @@ var MessageQueue = class {
|
|
|
62620
62512
|
}
|
|
62621
62513
|
};
|
|
62622
62514
|
function spawnDetached(options) {
|
|
62623
|
-
const child = (0,
|
|
62515
|
+
const child = (0, import_node_child_process3.spawn)(options.command, options.args, {
|
|
62624
62516
|
cwd: options.cwd,
|
|
62625
62517
|
env: options.env,
|
|
62626
62518
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -62688,7 +62580,6 @@ function buildAgentState({
|
|
|
62688
62580
|
pendingToolCallNames: /* @__PURE__ */ new Map(),
|
|
62689
62581
|
snapshots: [],
|
|
62690
62582
|
stepCounter: { value: 0 },
|
|
62691
|
-
lastEmittedHash: void 0,
|
|
62692
62583
|
captureInput: outputTools.captureInput,
|
|
62693
62584
|
sendMessage: buildSendMessageFunction(inputQueue),
|
|
62694
62585
|
closeQueue: () => {
|
|
@@ -62790,7 +62681,7 @@ var INTERRUPT_DRAIN_TIMEOUT_MS = 1e4;
|
|
|
62790
62681
|
async function interruptOrTimeout(queryRunner) {
|
|
62791
62682
|
await Promise.race([queryRunner.interrupt(), (0, import_promises9.setTimeout)(INTERRUPT_DRAIN_TIMEOUT_MS)]);
|
|
62792
62683
|
}
|
|
62793
|
-
var safeInterruptOrTimeout =
|
|
62684
|
+
var safeInterruptOrTimeout = import_neverthrow15.ResultAsync.fromThrowable(
|
|
62794
62685
|
interruptOrTimeout,
|
|
62795
62686
|
(error48) => error48
|
|
62796
62687
|
);
|
|
@@ -62841,18 +62732,18 @@ function startQueryTimers(config3, context) {
|
|
|
62841
62732
|
}
|
|
62842
62733
|
function awaitMessagesAndResolve({ queryRunner, state }, { cleanup: cleanup3, getOutput }) {
|
|
62843
62734
|
const messagesPromise = processMessages(queryRunner, state);
|
|
62844
|
-
return
|
|
62735
|
+
return import_neverthrow15.ResultAsync.fromPromise(messagesPromise, String).andThen((innerResult) => {
|
|
62845
62736
|
cleanup3();
|
|
62846
62737
|
if (innerResult.isErr()) {
|
|
62847
|
-
return (0,
|
|
62738
|
+
return (0, import_neverthrow15.err)(innerResult.error);
|
|
62848
62739
|
}
|
|
62849
|
-
return (0,
|
|
62740
|
+
return (0, import_neverthrow15.ok)(getOutput());
|
|
62850
62741
|
}).orElse((sdkError) => {
|
|
62851
62742
|
cleanup3();
|
|
62852
62743
|
if (!state.timedOut.value && !state.aborted.value) {
|
|
62853
|
-
return (0,
|
|
62744
|
+
return (0, import_neverthrow15.err)(sdkError);
|
|
62854
62745
|
}
|
|
62855
|
-
return (0,
|
|
62746
|
+
return (0, import_neverthrow15.ok)(getOutput());
|
|
62856
62747
|
});
|
|
62857
62748
|
}
|
|
62858
62749
|
function executeQuery({
|
|
@@ -62866,9 +62757,9 @@ function executeQuery({
|
|
|
62866
62757
|
message: { role: "user", content: prompt },
|
|
62867
62758
|
parent_tool_use_id: null
|
|
62868
62759
|
});
|
|
62869
|
-
const queryRunnerResult = (0,
|
|
62760
|
+
const queryRunnerResult = (0, import_neverthrow15.fromThrowable)(Qs, String)({ prompt: inputQueue, options });
|
|
62870
62761
|
if (queryRunnerResult.isErr()) {
|
|
62871
|
-
return (0,
|
|
62762
|
+
return (0, import_neverthrow15.errAsync)(queryRunnerResult.error);
|
|
62872
62763
|
}
|
|
62873
62764
|
const queryRunner = queryRunnerResult.value;
|
|
62874
62765
|
const cleanup3 = startQueryTimers(config3, { state, queryRunner, inputQueue, linkedController });
|
|
@@ -62888,6 +62779,103 @@ function runQuery(prompt, config3) {
|
|
|
62888
62779
|
function collectAgentOutput(prompt, config3) {
|
|
62889
62780
|
return runQuery(prompt, config3).mapErr((cause) => ({ type: "QUERY_FAILED", cause }));
|
|
62890
62781
|
}
|
|
62782
|
+
async function runFfmpeg(arguments_) {
|
|
62783
|
+
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
62784
|
+
(0, import_node_child_process4.execFile)("ffmpeg", arguments_, (error48) => {
|
|
62785
|
+
if (error48) {
|
|
62786
|
+
reject(error48);
|
|
62787
|
+
} else {
|
|
62788
|
+
resolve(true);
|
|
62789
|
+
}
|
|
62790
|
+
});
|
|
62791
|
+
await promise2;
|
|
62792
|
+
}
|
|
62793
|
+
function spawnRecorder(outputPath) {
|
|
62794
|
+
const safeMkdirSync = (0, import_neverthrow20.fromThrowable)(import_node_fs.mkdirSync, (cause) => cause);
|
|
62795
|
+
const safeSpawn2 = (0, import_neverthrow20.fromThrowable)(
|
|
62796
|
+
(command, arguments_) => (0, import_node_child_process4.spawn)(command, arguments_),
|
|
62797
|
+
(cause) => cause
|
|
62798
|
+
);
|
|
62799
|
+
const mkdirResult = safeMkdirSync(import_node_path5.default.dirname(outputPath), { recursive: true });
|
|
62800
|
+
if (mkdirResult.isErr()) {
|
|
62801
|
+
return mkdirResult.error;
|
|
62802
|
+
}
|
|
62803
|
+
const spawnResult = safeSpawn2("xcrun", [
|
|
62804
|
+
"simctl",
|
|
62805
|
+
"io",
|
|
62806
|
+
"booted",
|
|
62807
|
+
"recordVideo",
|
|
62808
|
+
"--force",
|
|
62809
|
+
outputPath
|
|
62810
|
+
]);
|
|
62811
|
+
if (spawnResult.isErr()) {
|
|
62812
|
+
return spawnResult.error;
|
|
62813
|
+
}
|
|
62814
|
+
return spawnResult.value;
|
|
62815
|
+
}
|
|
62816
|
+
function earlyExitError(code) {
|
|
62817
|
+
return new RangeError(`simctl exited with code ${String(code)} before recording started`);
|
|
62818
|
+
}
|
|
62819
|
+
function waitForRecordingStart(proc) {
|
|
62820
|
+
const { stderr } = proc;
|
|
62821
|
+
if (!stderr) {
|
|
62822
|
+
return (0, import_neverthrow20.errAsync)({ type: "SPAWN_FAILED", cause: new RangeError("proc has no stderr pipe") });
|
|
62823
|
+
}
|
|
62824
|
+
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
62825
|
+
proc.once("error", reject);
|
|
62826
|
+
proc.once("exit", (code) => {
|
|
62827
|
+
if (code !== 0) {
|
|
62828
|
+
reject(earlyExitError(code));
|
|
62829
|
+
}
|
|
62830
|
+
});
|
|
62831
|
+
let accumulated = "";
|
|
62832
|
+
stderr.on("data", (chunk) => {
|
|
62833
|
+
accumulated += chunk.toString();
|
|
62834
|
+
if (accumulated.includes("Recording started")) {
|
|
62835
|
+
resolve({ process: proc, startedAt: Date.now() });
|
|
62836
|
+
}
|
|
62837
|
+
});
|
|
62838
|
+
return import_neverthrow20.ResultAsync.fromPromise(
|
|
62839
|
+
promise2,
|
|
62840
|
+
(cause) => ({ type: "SPAWN_FAILED", cause })
|
|
62841
|
+
);
|
|
62842
|
+
}
|
|
62843
|
+
function startRecording(outputPath) {
|
|
62844
|
+
const procOrError = spawnRecorder(outputPath);
|
|
62845
|
+
if (procOrError instanceof Error) {
|
|
62846
|
+
return (0, import_neverthrow20.errAsync)({ type: "SPAWN_FAILED", cause: procOrError });
|
|
62847
|
+
}
|
|
62848
|
+
return waitForRecordingStart(procOrError);
|
|
62849
|
+
}
|
|
62850
|
+
function stopRecording(handle) {
|
|
62851
|
+
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
62852
|
+
handle.process.once("exit", () => {
|
|
62853
|
+
resolve(true);
|
|
62854
|
+
});
|
|
62855
|
+
handle.process.once("error", reject);
|
|
62856
|
+
if (handle.process.exitCode == void 0) {
|
|
62857
|
+
handle.process.kill("SIGINT");
|
|
62858
|
+
} else {
|
|
62859
|
+
resolve(true);
|
|
62860
|
+
}
|
|
62861
|
+
return import_neverthrow20.ResultAsync.fromPromise(
|
|
62862
|
+
promise2,
|
|
62863
|
+
(cause) => ({ type: "STOP_FAILED", cause })
|
|
62864
|
+
);
|
|
62865
|
+
}
|
|
62866
|
+
function speedUpVideo({
|
|
62867
|
+
inputPath,
|
|
62868
|
+
factor,
|
|
62869
|
+
outputPath
|
|
62870
|
+
}) {
|
|
62871
|
+
const pts = String(1 / factor);
|
|
62872
|
+
return (0, import_neverthrow20.fromAsyncThrowable)(
|
|
62873
|
+
runFfmpeg,
|
|
62874
|
+
(cause) => ({ type: "PROCESS_FAILED", cause })
|
|
62875
|
+
)(["-i", inputPath, "-filter:v", `setpts=${pts}*PTS`, "-an", "-y", outputPath]).map(
|
|
62876
|
+
() => outputPath
|
|
62877
|
+
);
|
|
62878
|
+
}
|
|
62891
62879
|
var SPEED_2X = 2;
|
|
62892
62880
|
var SPEED_4X = 4;
|
|
62893
62881
|
function applySpeedUpVariants({
|
|
@@ -62898,7 +62886,7 @@ function applySpeedUpVariants({
|
|
|
62898
62886
|
const { findings, snapshots } = result;
|
|
62899
62887
|
const { videoPath, videoPath2x, videoPath4x, signal } = params;
|
|
62900
62888
|
if (signal?.aborted) {
|
|
62901
|
-
return (0,
|
|
62889
|
+
return (0, import_neverthrow19.okAsync)({ findings, snapshots });
|
|
62902
62890
|
}
|
|
62903
62891
|
return speedUpVideo({ inputPath: videoPath, factor: SPEED_2X, outputPath: videoPath2x }).mapErr(toRecordingError).andThen(
|
|
62904
62892
|
() => speedUpVideo({ inputPath: videoPath, factor: SPEED_4X, outputPath: videoPath4x }).mapErr(toRecordingError).map(() => ({ findings, snapshots }))
|
|
@@ -62912,7 +62900,7 @@ function runWithRecording(handle, collectOutput) {
|
|
|
62912
62900
|
return collectOutput().andThen(
|
|
62913
62901
|
({ findings, snapshots }) => stopRecording(handle).mapErr(toRecordingError).map(() => ({ findings, snapshots }))
|
|
62914
62902
|
).orElse(
|
|
62915
|
-
(error48) => stopRecording(handle).mapErr(toRecordingError).andThen(() => (0,
|
|
62903
|
+
(error48) => stopRecording(handle).mapErr(toRecordingError).andThen(() => (0, import_neverthrow19.errAsync)(error48)).orElse(() => (0, import_neverthrow19.errAsync)(error48))
|
|
62916
62904
|
);
|
|
62917
62905
|
}
|
|
62918
62906
|
function startAndRun(params) {
|
|
@@ -62953,12 +62941,16 @@ At every reasoning step, maintain a mental ledger:
|
|
|
62953
62941
|
|
|
62954
62942
|
Consult the ledger before every action. Always prefer navigating to a QUEUE screen over a VISITED one.`;
|
|
62955
62943
|
var SESSION_START_RULE = `Before taking any other action \u2014 including initializing the Working State ledger or emitting findings \u2014 call \`view_ui\` once to observe the starting screen`;
|
|
62956
|
-
var BACK_NAV_RULE = `After navigating forward to any new screen:
|
|
62944
|
+
var BACK_NAV_RULE = `After navigating forward to any new screen: attempt to return to the expected parent in PATH \u2014 consult App Knowledge first for the correct exit gesture on this screen, then try in order: (1) any visible back/close button, (2) OS back gesture, (3) swipe up, (4) swipe down, (5) swipe left, (6) swipe right \u2014 confirm return via \`screenshot\` if the parent is visually unambiguous, \`view_ui\` otherwise \u2014 only after ALL attempts fail emit a \`back-nav-failure\` finding, then navigate forward again to continue`;
|
|
62957
62945
|
var STUCK_LOOP_RULE = `Stuck loop: emit a \`stuck-loop\` finding when any of these occur: (1) \`view_ui\` returns the same \`<screen_id>\` across 3 or more consecutive \`view_ui\` calls, (2) the same element has been tapped more than twice with no screen change confirmed by \`view_ui\`, (3) PATH shows the same screen at two non-adjacent positions \u2014 before emitting, try one alternative action (scroll, long-press, swipe) to rule out a gesture mismatch \u2014 note: \`screenshot\`-only calls do not update the stuck-loop counter; only \`view_ui\` calls count`;
|
|
62958
62946
|
var LOADING_STATE_RULE = `Transient loading state: when the screen shows spinners, skeleton screens, progress bars, "Loading..." text, or placeholder content NOT described in spec or app context \u2014 use \`screenshot\` to poll for resolution (up to 3 retries); switch to \`view_ui\` only on the final check or when you need element data to act \u2014 if loading persists after 3 retries, proceed with what is visible; if spec or app context explicitly describes a loading screen as a step, do not retry \u2014 call \`view_ui\` and assert normally`;
|
|
62959
62947
|
var EXPECTED_CONTENT_MISSING_RULE = `Expected content missing: when \`view_ui\` shows no loading indicator yet omits an element named or strongly implied by spec or app context \u2014 and its absence is not semantically consistent with the current screen \u2014 call \`wait_seconds\` with 2\u20135 seconds and retry \`view_ui\` up to 2 times; if element remains absent, emit a \`missing-content\` finding stating what was expected and what was observed`;
|
|
62960
62948
|
var CLIPPED_ELEMENT_RULE = `Never tap an element tagged \`[clipped-top]\`, \`[clipped-bottom]\`, \`[clipped-left]\`, or \`[clipped-right]\` \u2014 scroll to fully reveal it first, then re-call \`view_ui\` before tapping`;
|
|
62961
|
-
var
|
|
62949
|
+
var SCROLL_FOLD_RULE = `Scrollable lists: elements below the visible fold are absent from the a11y tree by design \u2014 scroll to reveal before asserting presence or absence, and never emit a finding solely because list items or rows are missing from the tree on a scrollable screen`;
|
|
62950
|
+
var FINDING_TAXONOMY_SECTION = `## Finding Types
|
|
62951
|
+
|
|
62952
|
+
You may emit only these trigger types: \`back-nav-failure\`, \`dead-end\`, \`stuck-modal\`, \`stuck-loop\`, \`missing-a11y-element\`, \`missing-content\`, \`spec-deviation\`, \`destructive-only-exit\`. Do NOT emit \`design-system-violation\`, \`motion-regression\`, \`continuity-regression\`, \`interaction-regression\`, or \`loading-regression\` \u2014 those belong to other agents.`;
|
|
62953
|
+
var A11Y_FALLBACK_RULE = `Missing a11y element: if you intend to tap or interact with a UI element and that element is absent from the most recent \`view_ui\` a11y tree \u2014 visible in the screenshot does NOT imply interactable; the a11y tree is authoritative \u2014 do NOT estimate its coordinates from the screenshot, do NOT attempt any pixel-based tap, do NOT retry at different coordinates, do NOT long-press or swipe in the element's visual region as a fallback; a failed pixel tap is never an \`interaction-regression\` \u2014 it is a \`missing-a11y-element\` \u2014 instead, emit a \`missing-a11y-element\` finding that states: (1) your intent (what you were trying to do), (2) the approximate visual region where the element appeared (coords/size from the screenshot), (3) nearby labeled elements from the a11y tree that serve as landmarks \u2014 then continue: in freestyle mode keep exploring other reachable screens; in spec mode advance to the next step`;
|
|
62962
62954
|
var WHAT_TO_TEST_SECTION = `## What to Test
|
|
62963
62955
|
|
|
62964
62956
|
Test navigation elements first, interactions second.
|
|
@@ -62989,9 +62981,9 @@ Rules:
|
|
|
62989
62981
|
- When in doubt about reversibility, treat the action as destructive and back out.`;
|
|
62990
62982
|
var DEAD_END_SECTION = `## Dead End and Modal Detection
|
|
62991
62983
|
|
|
62992
|
-
**Dead end** \u2014
|
|
62984
|
+
**Dead end** \u2014 emitted ONLY when every attempted exit fails to leave the screen. Consult App Knowledge first for the exit gesture on this screen, then attempt ALL before emitting: (1) any visible back/close button, (2) OS back gesture, (3) swipe up, (4) swipe down, (5) swipe left, (6) swipe right. If any succeeds, do NOT emit a finding \u2014 absence of an explicit "Cancel" or "Close" button is not a dead-end when a gesture dismisses the screen. If all fail, emit a \`dead-end\` finding describing what was visible and what was attempted.
|
|
62993
62985
|
|
|
62994
|
-
**Stuck modal** \u2014 when a modal or bottom sheet blocks the screen, attempt dismissal in order: (1) close/X button if present, (2) tap outside the modal, (3) swipe down, (4) swipe
|
|
62986
|
+
**Stuck modal** \u2014 when a modal or bottom sheet blocks the screen, attempt dismissal in order: (1) close/X button if present, (2) tap outside the modal, (3) swipe down, (4) swipe up, (5) swipe left, (6) swipe right. If any succeeds, do NOT emit a finding. If all fail, emit a \`stuck-modal\` finding listing the modal, the screen it appeared on, and the methods attempted.`;
|
|
62995
62987
|
var SPEC_WHAT_TO_TEST_SECTION = `## What to Test
|
|
62996
62988
|
|
|
62997
62989
|
Test only the elements and interactions described in the spec. Do not interact with elements outside the spec path.
|
|
@@ -62999,9 +62991,9 @@ Test only the elements and interactions described in the spec. Do not interact w
|
|
|
62999
62991
|
If you observe obvious breakage while navigating to a spec step \u2014 a broken control, unexpected error, missing screen, or crash \u2014 flag it as a passive observation without stopping to investigate it.`;
|
|
63000
62992
|
var SPEC_DEAD_END_SECTION = `## Dead End and Modal Detection
|
|
63001
62993
|
|
|
63002
|
-
**Dead end** \u2014 if a spec step leaves the agent on a screen with no path to the next spec step, attempt: (1) any visible back/close button, (2) swipe
|
|
62994
|
+
**Dead end** \u2014 if a spec step leaves the agent on a screen with no path to the next spec step, consult App Knowledge first for the exit gesture, then attempt ALL: (1) any visible back/close button, (2) OS back gesture, (3) swipe up, (4) swipe down, (5) swipe left, (6) swipe right. If all fail, emit a \`dead-end\` finding and halt \u2014 do not attempt further exploration to recover.
|
|
63003
62995
|
|
|
63004
|
-
**Stuck modal** \u2014 when a modal or bottom sheet blocks spec step execution, attempt dismissal in order: (1) close/X button if present, (2) tap outside the modal, (3) swipe down, (4) swipe
|
|
62996
|
+
**Stuck modal** \u2014 when a modal or bottom sheet blocks spec step execution, attempt dismissal in order: (1) close/X button if present, (2) tap outside the modal, (3) swipe down, (4) swipe up, (5) swipe left, (6) swipe right. If all fail, emit a \`stuck-modal\` finding listing the modal, the screen it appeared on, and the methods attempted.`;
|
|
63005
62997
|
var SPEC_STEP_READING_SECTION = `## Reading Spec Steps
|
|
63006
62998
|
|
|
63007
62999
|
Each step has this shape:
|
|
@@ -63049,6 +63041,7 @@ var SPEC_RULES_SECTION = `## Rules
|
|
|
63049
63041
|
- ${LOADING_STATE_RULE}
|
|
63050
63042
|
- ${EXPECTED_CONTENT_MISSING_RULE}
|
|
63051
63043
|
- ${CLIPPED_ELEMENT_RULE}
|
|
63044
|
+
- ${SCROLL_FOLD_RULE}
|
|
63052
63045
|
- ${A11Y_FALLBACK_RULE}
|
|
63053
63046
|
- Each item in \`**Assertions**\` is a mandatory pass/fail check \u2014 verify using \`view_ui\` when the assertion targets an element attribute, label, or presence in the tree; use \`screenshot\` when the assertion is purely visual; if neither can confirm, emit a \`spec-deviation\` finding based on what is observable
|
|
63054
63047
|
- Flag crash dialogs, unexpected system errors, or navigation failures that occur as a direct result of executing a spec step; if you observe a visibly broken element in passing while navigating, note it without interacting with it`;
|
|
@@ -63079,6 +63072,8 @@ ${SPEC_STEP_READING_SECTION}
|
|
|
63079
63072
|
|
|
63080
63073
|
${SPEC_OPTIONAL_STEPS_SECTION}
|
|
63081
63074
|
|
|
63075
|
+
${FINDING_TAXONOMY_SECTION}
|
|
63076
|
+
|
|
63082
63077
|
## Specs
|
|
63083
63078
|
|
|
63084
63079
|
${specContent}${environmentSection}
|
|
@@ -63112,6 +63107,7 @@ ${TOOL_SELECTION_SECTION}
|
|
|
63112
63107
|
- ${LOADING_STATE_RULE}
|
|
63113
63108
|
- ${EXPECTED_CONTENT_MISSING_RULE}
|
|
63114
63109
|
- ${CLIPPED_ELEMENT_RULE}
|
|
63110
|
+
- ${SCROLL_FOLD_RULE}
|
|
63115
63111
|
- ${A11Y_FALLBACK_RULE}
|
|
63116
63112
|
|
|
63117
63113
|
## Exploration Strategy
|
|
@@ -63122,7 +63118,9 @@ ${WHAT_TO_TEST_SECTION}
|
|
|
63122
63118
|
|
|
63123
63119
|
${DESTRUCTIVE_ACTIONS_SECTION}
|
|
63124
63120
|
|
|
63125
|
-
${DEAD_END_SECTION}
|
|
63121
|
+
${DEAD_END_SECTION}
|
|
63122
|
+
|
|
63123
|
+
${FINDING_TAXONOMY_SECTION}${environmentSection}
|
|
63126
63124
|
|
|
63127
63125
|
## Output
|
|
63128
63126
|
|
|
@@ -63380,7 +63378,7 @@ function buildPrompt(safeConfig, specs) {
|
|
|
63380
63378
|
});
|
|
63381
63379
|
}
|
|
63382
63380
|
function parseSpecs(resolvedSpecs) {
|
|
63383
|
-
return
|
|
63381
|
+
return import_neverthrow18.Result.combine(
|
|
63384
63382
|
resolvedSpecs.map(
|
|
63385
63383
|
(spec) => parseTestSpec(spec.name, spec.content).mapErr(
|
|
63386
63384
|
(cause) => ({ type: "SPEC_PARSE_FAILED", specName: spec.name, cause })
|
|
@@ -63439,7 +63437,7 @@ function collectAndFinalize({
|
|
|
63439
63437
|
}
|
|
63440
63438
|
function resolveAndParseSpecs(safeConfig) {
|
|
63441
63439
|
if (safeConfig.mode === "freestyle") {
|
|
63442
|
-
return (0,
|
|
63440
|
+
return (0, import_neverthrow18.okAsync)([]);
|
|
63443
63441
|
}
|
|
63444
63442
|
return resolveSpecs(safeConfig).mapErr((cause) => ({ type: "SPEC_RESOLVE_FAILED", cause })).andThen((specs) => parseSpecs(specs));
|
|
63445
63443
|
}
|
|
@@ -63462,11 +63460,11 @@ function runExplorer(config3) {
|
|
|
63462
63460
|
date: date5
|
|
63463
63461
|
});
|
|
63464
63462
|
if (runPathsResult.isErr()) {
|
|
63465
|
-
return (0,
|
|
63463
|
+
return (0, import_neverthrow18.errAsync)({ type: "RUN_PATHS_FAILED", cause: runPathsResult.error });
|
|
63466
63464
|
}
|
|
63467
63465
|
const runPaths = runPathsResult.value;
|
|
63468
63466
|
if (config3.signal?.aborted) {
|
|
63469
|
-
return (0,
|
|
63467
|
+
return (0, import_neverthrow18.okAsync)(toArtifacts({ findings: [], snapshots: [] }, runPaths));
|
|
63470
63468
|
}
|
|
63471
63469
|
const safeConfig = {
|
|
63472
63470
|
...config3,
|
|
@@ -63474,7 +63472,7 @@ function runExplorer(config3) {
|
|
|
63474
63472
|
onEvent: config3.onEvent === void 0 ? void 0 : safeHandler(config3.onEvent)
|
|
63475
63473
|
};
|
|
63476
63474
|
safeConfig.onEvent?.({ type: "STAGE_START", agent: "explorer" });
|
|
63477
|
-
return
|
|
63475
|
+
return import_neverthrow18.ResultAsync.fromSafePromise(
|
|
63478
63476
|
(0, import_promises11.mkdir)(runPaths.screenshotsDir, { recursive: true }).catch(() => null)
|
|
63479
63477
|
).andThen(() => runPipeline({ safeConfig, runPaths, start }));
|
|
63480
63478
|
}
|
|
@@ -66969,6 +66967,13 @@ var FsDesignStore = class {
|
|
|
66969
66967
|
// ../../packages/pipeline/dist/index.js
|
|
66970
66968
|
var RETRY_MAX_ATTEMPTS = 3;
|
|
66971
66969
|
var RETRY_BASE_DELAY_MS = 1e3;
|
|
66970
|
+
var CONFIDENCE_THRESHOLD = 0.7;
|
|
66971
|
+
function filterByConfidence(findings, threshold) {
|
|
66972
|
+
return {
|
|
66973
|
+
kept: findings.filter((finding) => finding.confidence >= threshold),
|
|
66974
|
+
dropped: findings.filter((finding) => finding.confidence < threshold).map((finding) => ({ finding, reason: "low-confidence" }))
|
|
66975
|
+
};
|
|
66976
|
+
}
|
|
66972
66977
|
function attemptRetry(options) {
|
|
66973
66978
|
const { factory, config: config3, delayFunction, onRetry, attempt } = options;
|
|
66974
66979
|
return factory().orElse((error48) => {
|
|
@@ -67284,11 +67289,12 @@ function buildExplorerConfig({
|
|
|
67284
67289
|
};
|
|
67285
67290
|
}
|
|
67286
67291
|
function buildOutput(consolidationResult, options) {
|
|
67287
|
-
const { runId, runPaths, signal } = options;
|
|
67292
|
+
const { runId, runPaths, signal, confidenceThreshold } = options;
|
|
67293
|
+
const filtered = filterByConfidence(consolidationResult.findings, confidenceThreshold);
|
|
67288
67294
|
const output = {
|
|
67289
67295
|
runId,
|
|
67290
|
-
findings:
|
|
67291
|
-
dismissed: consolidationResult.dismissed,
|
|
67296
|
+
findings: filtered.kept,
|
|
67297
|
+
dismissed: [...consolidationResult.dismissed, ...filtered.dropped],
|
|
67292
67298
|
findingsPath: runPaths.findingsPath,
|
|
67293
67299
|
aborted: signal?.aborted ? true : void 0
|
|
67294
67300
|
};
|
|
@@ -67342,7 +67348,8 @@ function executePipeline(setup, config3) {
|
|
|
67342
67348
|
(consolidationResult) => buildOutput(consolidationResult, {
|
|
67343
67349
|
runId: setup.runId,
|
|
67344
67350
|
runPaths: setup.runPaths,
|
|
67345
|
-
signal
|
|
67351
|
+
signal,
|
|
67352
|
+
confidenceThreshold: config3.confidenceThreshold ?? CONFIDENCE_THRESHOLD
|
|
67346
67353
|
})
|
|
67347
67354
|
);
|
|
67348
67355
|
}
|
|
@@ -76475,7 +76482,7 @@ function resolveXqaDirectory() {
|
|
|
76475
76482
|
return result.value;
|
|
76476
76483
|
}
|
|
76477
76484
|
var program2 = new Command();
|
|
76478
|
-
program2.name("xqa").description("AI-powered QA agent CLI").version(`${"1.
|
|
76485
|
+
program2.name("xqa").description("AI-powered QA agent CLI").version(`${"1.14.0"}${false ? ` (dev build +${"7c0bf90"})` : ""}`);
|
|
76479
76486
|
program2.command("init").description("Initialize a new xqa project in the current directory").action(() => {
|
|
76480
76487
|
runInitCommand();
|
|
76481
76488
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/xqa",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22"
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"typescript": "^5.8.3",
|
|
27
27
|
"vitest": "^3.2.1",
|
|
28
28
|
"zod": "^3.0.0",
|
|
29
|
-
"@qa-agents/display": "0.0.0",
|
|
30
29
|
"@qa-agents/explorer": "0.0.0",
|
|
31
30
|
"@qa-agents/eslint-config": "0.0.0",
|
|
31
|
+
"@qa-agents/display": "0.0.0",
|
|
32
32
|
"@qa-agents/mobile-ios": "0.0.0",
|
|
33
33
|
"@qa-agents/pipeline": "0.0.0",
|
|
34
34
|
"@qa-agents/shared": "0.0.0",
|