@swmansion/argent 0.30.2-next.2 → 0.30.2-next.20
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/bin/darwin/simulator-server +0 -0
- package/bin/linux/simulator-server +0 -0
- package/bin/linux-arm64/simulator-server +0 -0
- package/bin/win32/simulator-server.exe +0 -0
- package/dist/cli-cmds.mjs +26 -9
- package/dist/client.js +23 -6
- package/dist/installer.mjs +2 -1
- package/dist/ios-device-runner/ArgentRunner/ArgentRunner.xcodeproj/project.pbxproj +80 -0
- package/dist/ios-device-runner/ArgentRunner/ArgentRunner.xcodeproj/xcshareddata/xcschemes/ArgentRunner.xcscheme +14 -0
- package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/Assets.xcassets/AppIcon.appiconset/AppIcon.png +0 -0
- package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/Assets.xcassets/AppIcon.appiconset/Contents.json +14 -0
- package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/Assets.xcassets/Contents.json +6 -0
- package/dist/ios-device-runner/ArgentRunner/brand-runner.sh +27 -0
- package/dist/mcp-server.mjs +149 -85
- package/dist/tool-args.generated.d.ts +2 -0
- package/dist/tool-server.cjs +358 -73
- package/package.json +1 -1
- package/rules/argent.md +9 -7
- package/skills/argent-device-interact/SKILL.md +3 -1
- package/skills/argent-device-interact/references/harmonyos.md +43 -0
- package/skills/argent-ios-device-setup/SKILL.md +1 -1
- package/skills/argent-native-profiler/SKILL.md +1 -0
- package/skills/argent-react-native-profiler/SKILL.md +2 -0
- package/skills/argent-test-ui-flow/SKILL.md +1 -2
package/dist/mcp-server.mjs
CHANGED
|
@@ -24566,6 +24566,7 @@ function spawnToolsServer(paths, port, options = {}) {
|
|
|
24566
24566
|
stdio: ["ignore", "pipe", logFd],
|
|
24567
24567
|
env: buildToolsServerEnv(paths, port, process.env, options)
|
|
24568
24568
|
});
|
|
24569
|
+
fs.closeSync(logFd);
|
|
24569
24570
|
child.unref();
|
|
24570
24571
|
const pid = child.pid;
|
|
24571
24572
|
if (!pid) {
|
|
@@ -25313,6 +25314,7 @@ var FAILURE_CODES = {
|
|
|
25313
25314
|
REACT_PROFILER_DEVTOOLS_RENDERER_MISSING: "REACT_PROFILER_DEVTOOLS_RENDERER_MISSING",
|
|
25314
25315
|
REACT_PROFILER_START_FAILED: "REACT_PROFILER_START_FAILED",
|
|
25315
25316
|
REACT_PROFILER_START_VERIFY_FAILED: "REACT_PROFILER_START_VERIFY_FAILED",
|
|
25317
|
+
REACT_PROFILER_SESSION_TORN_DOWN: "REACT_PROFILER_SESSION_TORN_DOWN",
|
|
25316
25318
|
REACT_PROFILER_HOOK_ERROR: "REACT_PROFILER_HOOK_ERROR",
|
|
25317
25319
|
REACT_PROFILER_ANALYZE_NO_DATA: "REACT_PROFILER_ANALYZE_NO_DATA",
|
|
25318
25320
|
NATIVE_PROFILER_FACTORY_OPTIONS_MISSING: "NATIVE_PROFILER_FACTORY_OPTIONS_MISSING",
|
|
@@ -25577,7 +25579,7 @@ async function getResolvedToolsUrl() {
|
|
|
25577
25579
|
const link2 = await readLinkConfig();
|
|
25578
25580
|
const envToken = process.env[AUTH_TOKEN_ENV];
|
|
25579
25581
|
return {
|
|
25580
|
-
url: envUrl,
|
|
25582
|
+
url: envUrl.replace(/\/+$/, ""),
|
|
25581
25583
|
source: "env",
|
|
25582
25584
|
...envToken ? { token: envToken } : {},
|
|
25583
25585
|
...link2 ? { shadowedLink: link2 } : {}
|
|
@@ -26485,8 +26487,13 @@ async function consumeToolStream(name, body, onProgress, signal) {
|
|
|
26485
26487
|
if (msg.event === "progress") {
|
|
26486
26488
|
progress++;
|
|
26487
26489
|
onProgress(msg.data);
|
|
26488
|
-
} else if (msg.event === "result")
|
|
26489
|
-
|
|
26490
|
+
} else if (msg.event === "result") {
|
|
26491
|
+
final = {
|
|
26492
|
+
data: msg.data,
|
|
26493
|
+
note: msg.note,
|
|
26494
|
+
...msg.profiling === true ? { profiling: true } : {}
|
|
26495
|
+
};
|
|
26496
|
+
} else if (msg.event === "error") {
|
|
26490
26497
|
throw new ToolInvocationError(msg.error ?? "tool invocation failed", {
|
|
26491
26498
|
errorCode: msg.error_code,
|
|
26492
26499
|
errorKind: msg.error_kind
|
|
@@ -26533,7 +26540,11 @@ async function consumeToolStream(name, body, onProgress, signal) {
|
|
|
26533
26540
|
if (!final) {
|
|
26534
26541
|
throw brokenStream(name, "the stream ended without a result", progress);
|
|
26535
26542
|
}
|
|
26536
|
-
return {
|
|
26543
|
+
return {
|
|
26544
|
+
data: final.data,
|
|
26545
|
+
note: final.note,
|
|
26546
|
+
...final.profiling ? { profiling: true } : {}
|
|
26547
|
+
};
|
|
26537
26548
|
}
|
|
26538
26549
|
function errorBodyMessage(body) {
|
|
26539
26550
|
if (Array.isArray(body.issues) && typeof body.message === "string") return body.message;
|
|
@@ -26706,7 +26717,8 @@ function createToolsClient(options = {}) {
|
|
|
26706
26717
|
return {
|
|
26707
26718
|
data: await settle2(streamed.data),
|
|
26708
26719
|
note: streamed.note,
|
|
26709
|
-
outputHint: meta2?.outputHint
|
|
26720
|
+
outputHint: meta2?.outputHint,
|
|
26721
|
+
...streamed.profiling ? { profiling: true } : {}
|
|
26710
26722
|
};
|
|
26711
26723
|
}
|
|
26712
26724
|
let json;
|
|
@@ -26731,7 +26743,12 @@ function createToolsClient(options = {}) {
|
|
|
26731
26743
|
});
|
|
26732
26744
|
}
|
|
26733
26745
|
opts?.signal?.throwIfAborted();
|
|
26734
|
-
return {
|
|
26746
|
+
return {
|
|
26747
|
+
data: await settle2(json.data),
|
|
26748
|
+
note: json.note,
|
|
26749
|
+
outputHint: meta2?.outputHint,
|
|
26750
|
+
...json.profiling === true ? { profiling: true } : {}
|
|
26751
|
+
};
|
|
26735
26752
|
}
|
|
26736
26753
|
return { fetchTools, fetchTool, callTool, baseUrl };
|
|
26737
26754
|
}
|
|
@@ -28485,7 +28502,9 @@ var AUTO_SCREENSHOT_TOOLS = /* @__PURE__ */ new Set([
|
|
|
28485
28502
|
"restart-app",
|
|
28486
28503
|
"open-url",
|
|
28487
28504
|
"describe",
|
|
28488
|
-
"run-sequence"
|
|
28505
|
+
"run-sequence",
|
|
28506
|
+
// A wait is almost always followed by a look at the screen it waited for.
|
|
28507
|
+
"await-ui-element"
|
|
28489
28508
|
]);
|
|
28490
28509
|
var AUTO_SCREENSHOT_DELAY_MS_BY_TOOL = {
|
|
28491
28510
|
"launch-app": 3e3,
|
|
@@ -28506,9 +28525,13 @@ var AUTO_SCREENSHOT_DELAY_MS_BY_TOOL = {
|
|
|
28506
28525
|
"fold": 1500,
|
|
28507
28526
|
"keyboard": 300,
|
|
28508
28527
|
"paste": 300,
|
|
28509
|
-
"describe": 100
|
|
28528
|
+
"describe": 100,
|
|
28529
|
+
// The wait returns on the first match, often mid-transition; the cap must
|
|
28530
|
+
// leave `await-screen-idle` room to see two identical reads.
|
|
28531
|
+
"await-ui-element": 1500
|
|
28510
28532
|
};
|
|
28511
28533
|
var DEFAULT_DELAY_MS = 1400;
|
|
28534
|
+
var CAPTURE_TIMEOUT_MS = 15e3;
|
|
28512
28535
|
function autoScreenshotEnabled(options) {
|
|
28513
28536
|
return !isFlagEnabled("disable-auto-screenshot", options);
|
|
28514
28537
|
}
|
|
@@ -28528,11 +28551,13 @@ var AUTO_DESCRIBE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
28528
28551
|
"launch-app",
|
|
28529
28552
|
"restart-app",
|
|
28530
28553
|
"open-url",
|
|
28531
|
-
"run-sequence"
|
|
28554
|
+
"run-sequence",
|
|
28555
|
+
"await-ui-element"
|
|
28532
28556
|
]);
|
|
28533
28557
|
function autoDescribeEnabled(options) {
|
|
28534
28558
|
return !isFlagEnabled("disable-auto-describe", options);
|
|
28535
28559
|
}
|
|
28560
|
+
var PROFILING_CAPTURE_SKIPPED_NOTE = "Auto-screenshot and element tree skipped while a profiler is recording on this device. Call screenshot or describe if you need to see the screen.";
|
|
28536
28561
|
var AUTO_DESCRIBE_HEADER = "--- Elements after action (describe) ---";
|
|
28537
28562
|
function shouldAutoDescribe(toolName) {
|
|
28538
28563
|
return AUTO_DESCRIBE_TOOLS.has(normalizeToolName(toolName));
|
|
@@ -28588,6 +28613,7 @@ function toMcpTool(t) {
|
|
|
28588
28613
|
name: t.name,
|
|
28589
28614
|
description: t.description,
|
|
28590
28615
|
inputSchema: { type: "object", ...withoutRetiredParams(t.inputSchema) },
|
|
28616
|
+
...t.readOnly ? { annotations: { readOnlyHint: true } } : {},
|
|
28591
28617
|
...Object.keys(meta2).length > 0 ? { _meta: meta2 } : {}
|
|
28592
28618
|
};
|
|
28593
28619
|
}
|
|
@@ -28623,22 +28649,50 @@ async function fetchWithReconnect(getUrl, reconnect, config2) {
|
|
|
28623
28649
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
28624
28650
|
const controller = new AbortController();
|
|
28625
28651
|
const timer = fetchTimeoutMs !== null ? setTimeout(() => controller.abort(), fetchTimeoutMs) : void 0;
|
|
28652
|
+
let callerSignal;
|
|
28626
28653
|
try {
|
|
28627
28654
|
const attemptInit = typeof init === "function" ? init() : init;
|
|
28628
|
-
|
|
28655
|
+
callerSignal = attemptInit?.signal;
|
|
28656
|
+
const signal = callerSignal ? AbortSignal.any([controller.signal, callerSignal]) : controller.signal;
|
|
28657
|
+
return await fetch(getUrl(), { ...attemptInit, signal });
|
|
28629
28658
|
} catch (err) {
|
|
28630
28659
|
lastError = err;
|
|
28631
|
-
if (attempt === maxRetries) break;
|
|
28632
|
-
|
|
28633
|
-
|
|
28660
|
+
if (callerSignal?.aborted || attempt === maxRetries) break;
|
|
28661
|
+
const pause = (async () => {
|
|
28662
|
+
if (attempt === 0) {
|
|
28663
|
+
await reconnect();
|
|
28664
|
+
}
|
|
28665
|
+
await new Promise((r) => setTimeout(r, expBackoffBase * Math.pow(2, attempt)));
|
|
28666
|
+
})();
|
|
28667
|
+
await untilAborted(pause, callerSignal);
|
|
28668
|
+
if (callerSignal?.aborted) {
|
|
28669
|
+
lastError = callerSignal.reason;
|
|
28670
|
+
break;
|
|
28634
28671
|
}
|
|
28635
|
-
await new Promise((r) => setTimeout(r, expBackoffBase * Math.pow(2, attempt)));
|
|
28636
28672
|
} finally {
|
|
28637
28673
|
clearTimeout(timer);
|
|
28638
28674
|
}
|
|
28639
28675
|
}
|
|
28640
28676
|
throw lastError;
|
|
28641
28677
|
}
|
|
28678
|
+
async function untilAborted(work, signal) {
|
|
28679
|
+
if (!signal) {
|
|
28680
|
+
await work;
|
|
28681
|
+
return;
|
|
28682
|
+
}
|
|
28683
|
+
let onAbort = () => {
|
|
28684
|
+
};
|
|
28685
|
+
const aborted2 = new Promise((resolve10) => {
|
|
28686
|
+
onAbort = resolve10;
|
|
28687
|
+
if (signal.aborted) resolve10();
|
|
28688
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
28689
|
+
});
|
|
28690
|
+
try {
|
|
28691
|
+
await Promise.race([work, aborted2]);
|
|
28692
|
+
} finally {
|
|
28693
|
+
signal.removeEventListener("abort", onAbort);
|
|
28694
|
+
}
|
|
28695
|
+
}
|
|
28642
28696
|
function parseUrl(url) {
|
|
28643
28697
|
let parsed;
|
|
28644
28698
|
try {
|
|
@@ -28683,9 +28737,9 @@ function createToolCaller(deps) {
|
|
|
28683
28737
|
});
|
|
28684
28738
|
return {
|
|
28685
28739
|
fetchTools: client.fetchTools,
|
|
28686
|
-
async callTool(name, args) {
|
|
28687
|
-
const { data, note, outputHint } = await client.callTool(name, args);
|
|
28688
|
-
return { result: data, outputHint, note };
|
|
28740
|
+
async callTool(name, args, opts) {
|
|
28741
|
+
const { data, note, outputHint, profiling } = await client.callTool(name, args, opts);
|
|
28742
|
+
return { result: data, outputHint, note, ...profiling ? { profiling } : {} };
|
|
28689
28743
|
}
|
|
28690
28744
|
};
|
|
28691
28745
|
}
|
|
@@ -28699,6 +28753,7 @@ async function startMcpServer(options) {
|
|
|
28699
28753
|
}
|
|
28700
28754
|
const autoScreenshotOn = autoScreenshotEnabled();
|
|
28701
28755
|
const autoDescribeOn = autoDescribeEnabled();
|
|
28756
|
+
const captureTimeoutMs = options.captureTimeoutMs ?? CAPTURE_TIMEOUT_MS;
|
|
28702
28757
|
let TOOLS_URL;
|
|
28703
28758
|
let AUTH_TOKEN;
|
|
28704
28759
|
const resolved = await getResolvedToolsUrl();
|
|
@@ -28758,11 +28813,75 @@ async function startMcpServer(options) {
|
|
|
28758
28813
|
reconnect,
|
|
28759
28814
|
extraHeaders: aiClientHeaders
|
|
28760
28815
|
});
|
|
28816
|
+
async function awaitScreenSettled(udid, toolName) {
|
|
28817
|
+
const maxWaitMs = getAutoScreenshotDelayMs(toolName);
|
|
28818
|
+
if (maxWaitMs === 0) return true;
|
|
28819
|
+
const started = Date.now();
|
|
28820
|
+
try {
|
|
28821
|
+
const idle = await callTool(
|
|
28822
|
+
"await-screen-idle",
|
|
28823
|
+
{ udid, timeoutMs: maxWaitMs },
|
|
28824
|
+
// The tool bounds its poll to maxWaitMs; the rest covers its setup.
|
|
28825
|
+
{ signal: AbortSignal.timeout(maxWaitMs + captureTimeoutMs) }
|
|
28826
|
+
);
|
|
28827
|
+
await spyLog({
|
|
28828
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28829
|
+
event: "auto_screenshot_readiness",
|
|
28830
|
+
name: toolName,
|
|
28831
|
+
maxWaitMs,
|
|
28832
|
+
...idle.result
|
|
28833
|
+
});
|
|
28834
|
+
return idle.result?.settled === true;
|
|
28835
|
+
} catch {
|
|
28836
|
+
await new Promise((r) => setTimeout(r, Math.max(0, maxWaitMs - (Date.now() - started))));
|
|
28837
|
+
return false;
|
|
28838
|
+
}
|
|
28839
|
+
}
|
|
28840
|
+
async function captureScreenshotBlocks(udid, signal) {
|
|
28841
|
+
try {
|
|
28842
|
+
const { result } = await callTool("screenshot", { udid }, { signal });
|
|
28843
|
+
const blocks = await toMcpContent(result, "image", {
|
|
28844
|
+
toolsUrl: TOOLS_URL,
|
|
28845
|
+
authToken: AUTH_TOKEN,
|
|
28846
|
+
deviceId: udid
|
|
28847
|
+
});
|
|
28848
|
+
if (!blocks.some((b) => b.type === "image")) return [];
|
|
28849
|
+
return [{ type: "text", text: "--- Screen after action ---" }, ...blocks];
|
|
28850
|
+
} catch {
|
|
28851
|
+
return [];
|
|
28852
|
+
}
|
|
28853
|
+
}
|
|
28854
|
+
async function captureTreeBlocks(udid, toolName, signal) {
|
|
28855
|
+
const t1 = Date.now();
|
|
28856
|
+
try {
|
|
28857
|
+
const d = await callTool("describe", { udid }, { signal });
|
|
28858
|
+
const desc = d.result?.description;
|
|
28859
|
+
await spyLog({
|
|
28860
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28861
|
+
event: "auto_describe",
|
|
28862
|
+
name: toolName,
|
|
28863
|
+
durationMs: Date.now() - t1,
|
|
28864
|
+
chars: typeof desc === "string" ? desc.length : 0
|
|
28865
|
+
});
|
|
28866
|
+
if (typeof desc !== "string" || desc.length === 0) return [];
|
|
28867
|
+
return [{ type: "text", text: `${AUTO_DESCRIBE_HEADER}
|
|
28868
|
+
${desc}` }];
|
|
28869
|
+
} catch (e) {
|
|
28870
|
+
await spyLog({
|
|
28871
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28872
|
+
event: "auto_describe",
|
|
28873
|
+
name: toolName,
|
|
28874
|
+
durationMs: Date.now() - t1,
|
|
28875
|
+
error: String(e instanceof Error ? e.message : e)
|
|
28876
|
+
});
|
|
28877
|
+
return [];
|
|
28878
|
+
}
|
|
28879
|
+
}
|
|
28761
28880
|
const server = new Server(
|
|
28762
28881
|
{ name: "argent", version: getInstalledVersion() },
|
|
28763
28882
|
{
|
|
28764
28883
|
capabilities: { tools: {} },
|
|
28765
|
-
instructions: "Argent \u2014 iOS Simulator, Android Emulator, and Chromium app control for interacting, testing, profiling and debugging mobile and Chromium applications. Interaction tools return the screen after the action: a screenshot plus the accessibility element tree with normalized tap frames. Take coordinates from that tree; call describe (or debugger-component-tree) only when no fresh tree is available \u2014 never guess coordinates from pixels.
|
|
28884
|
+
instructions: "Argent \u2014 iOS Simulator, Android Emulator, and Chromium app control for interacting, testing, profiling and debugging mobile and Chromium applications. Interaction tools return the screen after the action: a screenshot plus the accessibility element tree with normalized tap frames. Take coordinates from that tree; call describe (or debugger-component-tree) only when no fresh tree is available \u2014 never guess coordinates from pixels. Do not stop simulator servers when a task finishes: the next task reuses them. Call stop-all-simulator-servers when the user asks to clean up or ends the session, or when a skill tells you to recycle a device's services, always with devices: [...] naming the devices this session used. Never stop a Metro the user started unless they ask. One tool-server is shared by every agent using this argent install, so an unscoped call tears down their devices too \u2014 reserve it for a deliberate machine-wide cleanup. Full guidance is in the argent rule loaded from .claude/rules/argent.md."
|
|
28766
28885
|
}
|
|
28767
28886
|
);
|
|
28768
28887
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -28791,7 +28910,7 @@ async function startMcpServer(options) {
|
|
|
28791
28910
|
args: params.arguments
|
|
28792
28911
|
});
|
|
28793
28912
|
try {
|
|
28794
|
-
const { result, outputHint, note } = await callTool(params.name, params.arguments);
|
|
28913
|
+
const { result, outputHint, note, profiling } = await callTool(params.name, params.arguments);
|
|
28795
28914
|
await spyLog({
|
|
28796
28915
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28797
28916
|
event: "tool_result",
|
|
@@ -28824,73 +28943,18 @@ async function startMcpServer(options) {
|
|
|
28824
28943
|
text: "Auto-screenshot and element tree skipped: the input contains a {{secret:\u2026}} placeholder, and a capture of this screen could reveal the typed secret. The secret is already typed \u2014 do not send the typing step again, or the field will hold two copies of it. Submit or navigate away, then verify the resulting screen as usual. Only this call is covered: the next call is captured normally, and shows the secret if the field is still on screen. To cover the submit as well, put the typing and the submit in ONE `run-sequence` the next time you type a secret."
|
|
28825
28944
|
}
|
|
28826
28945
|
];
|
|
28827
|
-
} else if (udid && (wantScreenshot || wantTree)) {
|
|
28828
|
-
|
|
28829
|
-
|
|
28830
|
-
|
|
28831
|
-
const idle = await callTool("await-screen-idle", { udid, timeoutMs: maxWaitMs });
|
|
28832
|
-
await spyLog({
|
|
28833
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28834
|
-
event: "auto_screenshot_readiness",
|
|
28835
|
-
name: params.name,
|
|
28836
|
-
maxWaitMs,
|
|
28837
|
-
...idle.result
|
|
28838
|
-
});
|
|
28839
|
-
} catch {
|
|
28840
|
-
await new Promise((r) => setTimeout(r, maxWaitMs));
|
|
28841
|
-
}
|
|
28842
|
-
}
|
|
28843
|
-
if (wantScreenshot) {
|
|
28844
|
-
try {
|
|
28845
|
-
const screenshotResult = await callTool("screenshot", { udid });
|
|
28846
|
-
const screenshotContent = await toMcpContent(screenshotResult.result, "image", {
|
|
28847
|
-
toolsUrl: TOOLS_URL,
|
|
28848
|
-
authToken: AUTH_TOKEN,
|
|
28849
|
-
deviceId: udid
|
|
28850
|
-
});
|
|
28851
|
-
const hasImage = screenshotContent.some((b) => b.type === "image");
|
|
28852
|
-
if (hasImage) {
|
|
28853
|
-
content = [
|
|
28854
|
-
...content,
|
|
28855
|
-
{
|
|
28856
|
-
type: "text",
|
|
28857
|
-
text: "--- Screen after action ---"
|
|
28858
|
-
},
|
|
28859
|
-
...screenshotContent
|
|
28860
|
-
];
|
|
28861
|
-
}
|
|
28862
|
-
} catch {
|
|
28863
|
-
}
|
|
28864
|
-
}
|
|
28865
|
-
if (wantTree) {
|
|
28866
|
-
const t1 = Date.now();
|
|
28867
|
-
try {
|
|
28868
|
-
const d = await callTool("describe", { udid });
|
|
28869
|
-
const desc = d.result?.description;
|
|
28870
|
-
if (typeof desc === "string" && desc.length > 0) {
|
|
28871
|
-
content = [
|
|
28872
|
-
...content,
|
|
28873
|
-
{ type: "text", text: `${AUTO_DESCRIBE_HEADER}
|
|
28874
|
-
${desc}` }
|
|
28875
|
-
];
|
|
28876
|
-
}
|
|
28877
|
-
await spyLog({
|
|
28878
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28879
|
-
event: "auto_describe",
|
|
28880
|
-
name: params.name,
|
|
28881
|
-
durationMs: Date.now() - t1,
|
|
28882
|
-
chars: typeof desc === "string" ? desc.length : 0
|
|
28883
|
-
});
|
|
28884
|
-
} catch (e) {
|
|
28885
|
-
await spyLog({
|
|
28886
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28887
|
-
event: "auto_describe",
|
|
28888
|
-
name: params.name,
|
|
28889
|
-
durationMs: Date.now() - t1,
|
|
28890
|
-
error: String(e instanceof Error ? e.message : e)
|
|
28891
|
-
});
|
|
28892
|
-
}
|
|
28946
|
+
} else if (udid && (wantScreenshot || wantTree) && profiling) {
|
|
28947
|
+
if (params.name !== "describe") {
|
|
28948
|
+
await awaitScreenSettled(udid, params.name);
|
|
28949
|
+
content = [...content, { type: "text", text: PROFILING_CAPTURE_SKIPPED_NOTE }];
|
|
28893
28950
|
}
|
|
28951
|
+
} else if (udid && (wantScreenshot || wantTree)) {
|
|
28952
|
+
const screenSettled = await awaitScreenSettled(udid, params.name);
|
|
28953
|
+
const signal = AbortSignal.timeout(captureTimeoutMs);
|
|
28954
|
+
const screenshot = wantScreenshot ? captureScreenshotBlocks(udid, signal) : Promise.resolve([]);
|
|
28955
|
+
if (!screenSettled) await screenshot;
|
|
28956
|
+
const tree = wantTree ? captureTreeBlocks(udid, params.name, signal) : Promise.resolve([]);
|
|
28957
|
+
content = [...content, ...await screenshot, ...await tree];
|
|
28894
28958
|
}
|
|
28895
28959
|
if (note) {
|
|
28896
28960
|
content = [{ type: "text", text: note }, ...content];
|
|
@@ -53,6 +53,8 @@ export interface ArgentToolArgs {
|
|
|
53
53
|
force?: boolean;
|
|
54
54
|
/** Android only: boot the emulator with audio output enabled. Defaults to false: argent boots emulators MUTED so several agent-driven devices don't all play sound on the host machine; pass `true` when the task involves playing, hearing, or testing audio. Takes effect at boot: if the emulator is already running muted, add `force: true` to reboot it with sound. A boot snapshot saved in the other audio mode can't be reused, so the first boot after toggling is a slower cold boot. The `boot-sound` argent flag flips this default to true. Ignored on iOS/Vega/HarmonyOS/Electron, which argent never mutes. */
|
|
55
55
|
sound?: boolean;
|
|
56
|
+
/** Android on a macOS host only: boot the emulator with guest Vulkan. Defaults to false: guest Vulkan crashes Chrome's GPU process, which breaks Chrome and Custom Tab sign-in flows. Without it apps find no Vulkan device, although the guest still lists the Vulkan features, so a Vulkan-only renderer such as WebGPU (react-native-wgpu) draws nothing; pass `true` to test one. Takes effect at boot: an emulator already running in the other mode is reused with a `note`, and `force: true` reboots it. A boot snapshot saved in the other mode can't be reused, so the first boot after toggling is a slower cold boot. The `ARGENT_EMULATOR_GUEST_VULKAN` env var flips this default to true. Ignored on other hosts and platforms. */
|
|
57
|
+
guestVulkan?: boolean;
|
|
56
58
|
/** iOS only: boot the simulator core WITHOUT opening its GUI window (Simulator.app, or Device Hub on Xcode 27+). The device still streams via simulator-server; used by Argent Lens. Set the `ARGENT_SIMULATOR_NO_WINDOW` env var (1/true/yes) to force this host-wide without passing the flag per call (the iOS analog of `ARGENT_EMULATOR_NO_WINDOW`). Ignored on Android/Vega/HarmonyOS/Electron, which have no equivalent GUI step. */
|
|
57
59
|
headless?: boolean;
|
|
58
60
|
/** Electron: path to the Electron app to launch. Either a packaged .app bundle / executable, or a project directory whose package.json points the Electron binary at the entry script. Provide exactly one of `udid`, `avdName`, `vvdImage`, `harmonyInstance`, or `electronAppPath`. */
|