@swmansion/argent 0.22.1-next.5 → 0.22.1-next.6
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/cli-cmds.mjs +22 -10
- package/dist/installer.mjs +1 -1
- package/dist/mcp-server.mjs +7 -1
- package/dist/tool-server.cjs +238 -19
- package/package.json +1 -1
package/dist/cli-cmds.mjs
CHANGED
|
@@ -2675,11 +2675,13 @@ async function applyClientFileDirectives(result) {
|
|
|
2675
2675
|
var ToolInvocationError = class extends Error {
|
|
2676
2676
|
errorCode;
|
|
2677
2677
|
errorKind;
|
|
2678
|
+
issues;
|
|
2678
2679
|
constructor(message, signal) {
|
|
2679
2680
|
super(message);
|
|
2680
2681
|
this.name = "ToolInvocationError";
|
|
2681
2682
|
this.errorCode = signal?.errorCode;
|
|
2682
2683
|
this.errorKind = signal?.errorKind;
|
|
2684
|
+
this.issues = signal?.issues;
|
|
2683
2685
|
}
|
|
2684
2686
|
};
|
|
2685
2687
|
function authHeaders2(token) {
|
|
@@ -2727,6 +2729,10 @@ async function consumeToolStream(body, onProgress) {
|
|
|
2727
2729
|
const { result: data } = await applyClientFileDirectives(final.data);
|
|
2728
2730
|
return { data, note: final.note };
|
|
2729
2731
|
}
|
|
2732
|
+
function errorBodyMessage(body) {
|
|
2733
|
+
if (Array.isArray(body.issues) && typeof body.message === "string") return body.message;
|
|
2734
|
+
return body.error ?? body.message;
|
|
2735
|
+
}
|
|
2730
2736
|
function createToolsClient(options = {}) {
|
|
2731
2737
|
let cached2 = null;
|
|
2732
2738
|
async function baseUrl() {
|
|
@@ -2781,13 +2787,11 @@ function createToolsClient(options = {}) {
|
|
|
2781
2787
|
}
|
|
2782
2788
|
const json = await res.json().catch(() => ({}));
|
|
2783
2789
|
if (!res.ok) {
|
|
2784
|
-
throw new ToolInvocationError(
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
}
|
|
2790
|
-
);
|
|
2790
|
+
throw new ToolInvocationError(errorBodyMessage(json) ?? `${res.status} ${res.statusText}`, {
|
|
2791
|
+
errorCode: json.error_code,
|
|
2792
|
+
errorKind: json.error_kind,
|
|
2793
|
+
issues: Array.isArray(json.issues) ? json.issues : void 0
|
|
2794
|
+
});
|
|
2791
2795
|
}
|
|
2792
2796
|
const { result: data } = await applyClientFileDirectives(json.data);
|
|
2793
2797
|
return { data, note: json.note };
|
|
@@ -7206,7 +7210,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
7206
7210
|
var SESSION_ID2 = randomUUID5();
|
|
7207
7211
|
function readCliVersion() {
|
|
7208
7212
|
if (true) {
|
|
7209
|
-
return "0.22.1-next.
|
|
7213
|
+
return "0.22.1-next.6";
|
|
7210
7214
|
}
|
|
7211
7215
|
return "0.0.0";
|
|
7212
7216
|
}
|
|
@@ -8009,7 +8013,11 @@ function findMissingRequired(payload, schema) {
|
|
|
8009
8013
|
}
|
|
8010
8014
|
return names.filter((name) => !Object.hasOwn(payload, name));
|
|
8011
8015
|
}
|
|
8012
|
-
function
|
|
8016
|
+
function serverIssueList(err) {
|
|
8017
|
+
const carried = err?.issues;
|
|
8018
|
+
if (Array.isArray(carried)) {
|
|
8019
|
+
return carried.length > 0 && carried.every(isValidationIssue) ? carried : null;
|
|
8020
|
+
}
|
|
8013
8021
|
const message = err instanceof Error ? err.message : typeof err === "string" ? err : null;
|
|
8014
8022
|
if (message === null) return null;
|
|
8015
8023
|
let parsed;
|
|
@@ -8019,7 +8027,11 @@ function describeServerValidationFailure(err, payload, schema) {
|
|
|
8019
8027
|
return null;
|
|
8020
8028
|
}
|
|
8021
8029
|
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
8022
|
-
|
|
8030
|
+
return parsed.every(isValidationIssue) ? parsed : null;
|
|
8031
|
+
}
|
|
8032
|
+
function describeServerValidationFailure(err, payload, schema) {
|
|
8033
|
+
const parsed = serverIssueList(err);
|
|
8034
|
+
if (parsed === null) return null;
|
|
8023
8035
|
const properties = schema?.properties ?? {};
|
|
8024
8036
|
const addressesThisTool = (issue) => issue.path.length === 0 || typeof issue.path[0] === "string" && Object.hasOwn(properties, issue.path[0]);
|
|
8025
8037
|
if (!parsed.every(addressesThisTool)) return null;
|
package/dist/installer.mjs
CHANGED
|
@@ -16428,7 +16428,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
16428
16428
|
var SESSION_ID = randomUUID4();
|
|
16429
16429
|
function readCliVersion() {
|
|
16430
16430
|
if (true) {
|
|
16431
|
-
return "0.22.1-next.
|
|
16431
|
+
return "0.22.1-next.6";
|
|
16432
16432
|
}
|
|
16433
16433
|
return "0.0.0";
|
|
16434
16434
|
}
|
package/dist/mcp-server.mjs
CHANGED
|
@@ -16855,6 +16855,12 @@ async function applyClientFileDirectives(result) {
|
|
|
16855
16855
|
return { result: rewritten, written };
|
|
16856
16856
|
}
|
|
16857
16857
|
|
|
16858
|
+
// ../argent-tools-client/src/tools-client.ts
|
|
16859
|
+
function errorBodyMessage(body) {
|
|
16860
|
+
if (Array.isArray(body.issues) && typeof body.message === "string") return body.message;
|
|
16861
|
+
return body.error ?? body.message;
|
|
16862
|
+
}
|
|
16863
|
+
|
|
16858
16864
|
// ../argent-tools-client/src/artifacts.ts
|
|
16859
16865
|
import { copyFile, mkdir as mkdir4, readFile as readFile4, realpath, rm as rm3, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
|
|
16860
16866
|
import { constants as fsConstants } from "node:fs";
|
|
@@ -18685,7 +18691,7 @@ async function startMcpServer(options) {
|
|
|
18685
18691
|
fetchTimeoutMs: meta2?.longRunning ? null : FETCH_TIMEOUT_MS
|
|
18686
18692
|
});
|
|
18687
18693
|
const json = await res.json();
|
|
18688
|
-
if (!res.ok) throw new Error(json
|
|
18694
|
+
if (!res.ok) throw new Error(errorBodyMessage(json) ?? res.statusText);
|
|
18689
18695
|
const { result: data } = await applyClientFileDirectives(json.data);
|
|
18690
18696
|
return { result: data, outputHint: meta2?.outputHint, note: json.note };
|
|
18691
18697
|
}
|
package/dist/tool-server.cjs
CHANGED
|
@@ -16063,6 +16063,63 @@ function terminatingSignalCause(message) {
|
|
|
16063
16063
|
error_kind: "unknown"
|
|
16064
16064
|
});
|
|
16065
16065
|
}
|
|
16066
|
+
function valueAtPath(root, path41) {
|
|
16067
|
+
let current = root;
|
|
16068
|
+
for (const key2 of path41) {
|
|
16069
|
+
if (current === null || typeof current !== "object") return void 0;
|
|
16070
|
+
if (!Object.hasOwn(current, key2)) return void 0;
|
|
16071
|
+
current = current[key2];
|
|
16072
|
+
}
|
|
16073
|
+
return current;
|
|
16074
|
+
}
|
|
16075
|
+
function describeParamIssues(error52, params) {
|
|
16076
|
+
const allKeys = params !== null && typeof params === "object" && !Array.isArray(params) ? Object.keys(params) : [];
|
|
16077
|
+
const supplied = allKeys.slice(0, 24);
|
|
16078
|
+
const truncated = allKeys.length > supplied.length;
|
|
16079
|
+
const parts2 = error52.issues.map((issue2) => {
|
|
16080
|
+
const at = issue2.path.length > 0 ? issue2.path.join(".") : "(root)";
|
|
16081
|
+
if (issue2.code === "custom") {
|
|
16082
|
+
return issue2.path.length > 0 ? `\`${at}\`: ${issue2.message}` : issue2.message;
|
|
16083
|
+
}
|
|
16084
|
+
if (valueAtPath(params, issue2.path) === void 0) {
|
|
16085
|
+
const expected = issue2.expected;
|
|
16086
|
+
const kind = typeof expected === "string" ? ` (${expected})` : "";
|
|
16087
|
+
return `\`${at}\` is required${kind} and was not provided`;
|
|
16088
|
+
}
|
|
16089
|
+
if (issue2.code === "unrecognized_keys") {
|
|
16090
|
+
const keys = issue2.keys ?? [];
|
|
16091
|
+
const at2 = issue2.path.length > 0 ? `${issue2.path.join(".")}.` : "";
|
|
16092
|
+
return `unknown parameter${keys.length === 1 ? "" : "s"} ${keys.map((k) => `\`${at2}${k}\``).join(", ")}`;
|
|
16093
|
+
}
|
|
16094
|
+
if (issue2.code === "invalid_union") {
|
|
16095
|
+
const branches = issue2.errors ?? [];
|
|
16096
|
+
const alternatives = [];
|
|
16097
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16098
|
+
let moreAlternatives = false;
|
|
16099
|
+
for (const branch of branches) {
|
|
16100
|
+
for (const inner of branch) {
|
|
16101
|
+
const innerAt = inner.path.length > 0 ? `${at}.${inner.path.join(".")}: ` : "";
|
|
16102
|
+
const text = `${innerAt}${inner.message}`;
|
|
16103
|
+
if (seen.has(text)) continue;
|
|
16104
|
+
if (alternatives.length >= MAX_UNION_ALTERNATIVES) {
|
|
16105
|
+
moreAlternatives = true;
|
|
16106
|
+
break;
|
|
16107
|
+
}
|
|
16108
|
+
seen.add(text);
|
|
16109
|
+
alternatives.push(text);
|
|
16110
|
+
}
|
|
16111
|
+
if (moreAlternatives) break;
|
|
16112
|
+
}
|
|
16113
|
+
if (alternatives.length > 0) {
|
|
16114
|
+
return `\`${at}\`: ${alternatives.join("; or ")}${moreAlternatives ? "; or \u2026" : ""}`;
|
|
16115
|
+
}
|
|
16116
|
+
}
|
|
16117
|
+
return `\`${at}\`: ${issue2.message}`;
|
|
16118
|
+
});
|
|
16119
|
+
const sent = supplied.length > 0 ? ` You sent: ${supplied.map((k) => `\`${k}\``).join(", ")}${truncated ? ", \u2026" : ""}.` : "";
|
|
16120
|
+
const body = parts2.length > 0 ? `${parts2.map((p) => p.replace(/\.$/, "")).join("; ")}.` : "";
|
|
16121
|
+
return `${body}${sent}`.trim() || "invalid parameters";
|
|
16122
|
+
}
|
|
16066
16123
|
function formatInteractionMessage(format, fallback) {
|
|
16067
16124
|
try {
|
|
16068
16125
|
return format() ?? fallback;
|
|
@@ -16070,7 +16127,7 @@ function formatInteractionMessage(format, fallback) {
|
|
|
16070
16127
|
return fallback;
|
|
16071
16128
|
}
|
|
16072
16129
|
}
|
|
16073
|
-
var import_node_crypto2, Registry;
|
|
16130
|
+
var import_node_crypto2, Registry, MAX_UNION_ALTERNATIVES;
|
|
16074
16131
|
var init_registry = __esm({
|
|
16075
16132
|
"../registry/src/registry.ts"() {
|
|
16076
16133
|
"use strict";
|
|
@@ -16151,7 +16208,15 @@ var init_registry = __esm({
|
|
|
16151
16208
|
if (definition.zodSchema) {
|
|
16152
16209
|
const parsed = definition.zodSchema.safeParse(params ?? {});
|
|
16153
16210
|
if (!parsed.success) {
|
|
16154
|
-
throw new
|
|
16211
|
+
throw new FailureError(
|
|
16212
|
+
`Invalid params for tool "${id}": ${describeParamIssues(parsed.error, params)}`,
|
|
16213
|
+
{
|
|
16214
|
+
error_code: FAILURE_CODES.TOOL_INPUT_INVALID,
|
|
16215
|
+
failure_stage: "tool_params_parse",
|
|
16216
|
+
failure_area: "registry",
|
|
16217
|
+
error_kind: "validation"
|
|
16218
|
+
}
|
|
16219
|
+
);
|
|
16155
16220
|
}
|
|
16156
16221
|
effectiveParams = parsed.data;
|
|
16157
16222
|
}
|
|
@@ -16388,6 +16453,7 @@ var init_registry = __esm({
|
|
|
16388
16453
|
this._transition(node, cause ? "ERROR" /* ERROR */ : "IDLE" /* IDLE */, cause);
|
|
16389
16454
|
}
|
|
16390
16455
|
};
|
|
16456
|
+
MAX_UNION_ALTERNATIVES = 12;
|
|
16391
16457
|
}
|
|
16392
16458
|
});
|
|
16393
16459
|
|
|
@@ -95668,7 +95734,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
95668
95734
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
95669
95735
|
function readCliVersion() {
|
|
95670
95736
|
if (true) {
|
|
95671
|
-
return "0.22.1-next.
|
|
95737
|
+
return "0.22.1-next.6";
|
|
95672
95738
|
}
|
|
95673
95739
|
return "0.0.0";
|
|
95674
95740
|
}
|
|
@@ -113875,14 +113941,23 @@ async function resolveFileInputs(def, body, lookupUpload) {
|
|
|
113875
113941
|
};
|
|
113876
113942
|
const specs = def.fileInputs;
|
|
113877
113943
|
if (!specs || specs.length === 0 || typeof body !== "object" || body === null) {
|
|
113878
|
-
return {
|
|
113944
|
+
return {
|
|
113945
|
+
args: body ?? {},
|
|
113946
|
+
fileInputs: void 0,
|
|
113947
|
+
derivedTargets: [],
|
|
113948
|
+
cleanup
|
|
113949
|
+
};
|
|
113879
113950
|
}
|
|
113880
113951
|
const args = { ...body };
|
|
113881
113952
|
let resolved;
|
|
113953
|
+
const derivedTargets = [];
|
|
113882
113954
|
try {
|
|
113883
113955
|
for (const spec of specs) {
|
|
113884
113956
|
const value = args[spec.target];
|
|
113885
113957
|
if (!isFileInputWire(value)) continue;
|
|
113958
|
+
if (spec.path !== `\${${spec.target}}` && !derivedTargets.includes(spec.target)) {
|
|
113959
|
+
derivedTargets.push(spec.target);
|
|
113960
|
+
}
|
|
113886
113961
|
if (spec.unwrapWhenSet !== void 0 && isParamSet(args[spec.unwrapWhenSet])) {
|
|
113887
113962
|
args[spec.target] = value.path;
|
|
113888
113963
|
continue;
|
|
@@ -113899,7 +113974,7 @@ async function resolveFileInputs(def, body, lookupUpload) {
|
|
|
113899
113974
|
await cleanup();
|
|
113900
113975
|
throw err;
|
|
113901
113976
|
}
|
|
113902
|
-
return { args, fileInputs: resolved, cleanup };
|
|
113977
|
+
return { args, fileInputs: resolved, derivedTargets, cleanup };
|
|
113903
113978
|
}
|
|
113904
113979
|
|
|
113905
113980
|
// ../tool-server/src/utils/debugger/device-alias.ts
|
|
@@ -114190,6 +114265,14 @@ function isToolExposed(def) {
|
|
|
114190
114265
|
function findDependencyMissing(err) {
|
|
114191
114266
|
return findErrorInCauseChain(err, DependencyMissingError);
|
|
114192
114267
|
}
|
|
114268
|
+
function omitKeys(args, keys) {
|
|
114269
|
+
if (keys.length === 0 || args === null || typeof args !== "object" || Array.isArray(args)) {
|
|
114270
|
+
return args;
|
|
114271
|
+
}
|
|
114272
|
+
const copy = { ...args };
|
|
114273
|
+
for (const key2 of keys) delete copy[key2];
|
|
114274
|
+
return copy;
|
|
114275
|
+
}
|
|
114193
114276
|
function errorSignalFields(err) {
|
|
114194
114277
|
const signal = getFailureSignal(err);
|
|
114195
114278
|
return signal ? { error_code: signal.error_code, error_kind: signal.error_kind } : {};
|
|
@@ -114529,6 +114612,7 @@ function createHttpApp(registry2, options) {
|
|
|
114529
114612
|
}
|
|
114530
114613
|
let bodyArgs;
|
|
114531
114614
|
let resolvedFileInputs;
|
|
114615
|
+
let derivedTargets;
|
|
114532
114616
|
try {
|
|
114533
114617
|
const resolved = await resolveFileInputs(def, req.body, (id) => {
|
|
114534
114618
|
const entry = uploads.get(id);
|
|
@@ -114537,6 +114621,7 @@ function createHttpApp(registry2, options) {
|
|
|
114537
114621
|
});
|
|
114538
114622
|
bodyArgs = resolved.args;
|
|
114539
114623
|
resolvedFileInputs = resolved.fileInputs;
|
|
114624
|
+
derivedTargets = resolved.derivedTargets;
|
|
114540
114625
|
res.once("close", () => void resolved.cleanup());
|
|
114541
114626
|
} catch (err) {
|
|
114542
114627
|
if (err instanceof FileInputError) {
|
|
@@ -114560,7 +114645,11 @@ function createHttpApp(registry2, options) {
|
|
|
114560
114645
|
req.body,
|
|
114561
114646
|
{ invalid_params: deriveInvalidParams(parseResult.error, declared) }
|
|
114562
114647
|
);
|
|
114563
|
-
res.status(400).json({
|
|
114648
|
+
res.status(400).json({
|
|
114649
|
+
error: parseResult.error.message,
|
|
114650
|
+
message: describeParamIssues(parseResult.error, omitKeys(bodyArgs, derivedTargets)),
|
|
114651
|
+
issues: parseResult.error.issues
|
|
114652
|
+
});
|
|
114564
114653
|
return;
|
|
114565
114654
|
}
|
|
114566
114655
|
parsedData = parseResult.data;
|
|
@@ -114701,6 +114790,10 @@ function createHttpApp(registry2, options) {
|
|
|
114701
114790
|
res.status(400).json({ error: invalidInputErr.message, ...errorSignalFields(err) });
|
|
114702
114791
|
return;
|
|
114703
114792
|
}
|
|
114793
|
+
if (getFailureSignal(err)?.error_code === FAILURE_CODES.TOOL_INPUT_INVALID) {
|
|
114794
|
+
res.status(400).json({ error: formatErrorForAgent(err), ...errorSignalFields(err) });
|
|
114795
|
+
return;
|
|
114796
|
+
}
|
|
114704
114797
|
const notImplementedErr = findErrorInCauseChain(err, NotImplementedOnPlatformError);
|
|
114705
114798
|
if (notImplementedErr) {
|
|
114706
114799
|
res.status(501).json({
|
|
@@ -123254,6 +123347,7 @@ init_zod();
|
|
|
123254
123347
|
|
|
123255
123348
|
// ../tool-server/src/utils/sub-invoke.ts
|
|
123256
123349
|
var import_node_crypto7 = require("node:crypto");
|
|
123350
|
+
init_src();
|
|
123257
123351
|
async function invokeSubTool(registry2, ctx, toolId, args) {
|
|
123258
123352
|
const signal = ctx?.signal;
|
|
123259
123353
|
const recordChildInvocation = ctx?.recordChildInvocation;
|
|
@@ -123272,6 +123366,14 @@ async function invokeSubTool(registry2, ctx, toolId, args) {
|
|
|
123272
123366
|
release();
|
|
123273
123367
|
}
|
|
123274
123368
|
}
|
|
123369
|
+
function describeNestedParamError(registry2, err, toolId, dispatchedArgs, authoredArgs) {
|
|
123370
|
+
if (getFailureSignal(err)?.error_code !== FAILURE_CODES.TOOL_INPUT_INVALID) return void 0;
|
|
123371
|
+
const zodSchema76 = registry2.getTool(toolId)?.zodSchema;
|
|
123372
|
+
if (!zodSchema76) return void 0;
|
|
123373
|
+
const parsed = zodSchema76.safeParse(dispatchedArgs ?? {});
|
|
123374
|
+
if (parsed.success) return void 0;
|
|
123375
|
+
return `Invalid params for tool "${toolId}": ${describeParamIssues(parsed.error, authoredArgs)}`;
|
|
123376
|
+
}
|
|
123275
123377
|
|
|
123276
123378
|
// ../tool-server/src/tools/await-ui-element/index.ts
|
|
123277
123379
|
init_zod();
|
|
@@ -129750,8 +129852,8 @@ Stops on the first error (or unmet await-ui-element condition) and returns parti
|
|
|
129750
129852
|
throw err;
|
|
129751
129853
|
}
|
|
129752
129854
|
}
|
|
129855
|
+
const toolArgs = { ...step.args, udid };
|
|
129753
129856
|
try {
|
|
129754
|
-
const toolArgs = { ...step.args, udid };
|
|
129755
129857
|
const result = await invokeSubTool(registry2, ctx, step.tool, toolArgs);
|
|
129756
129858
|
if (isUnmetUiWaitResult(step.tool, result)) {
|
|
129757
129859
|
const note = result.note;
|
|
@@ -129763,9 +129865,16 @@ Stops on the first error (or unmet await-ui-element condition) and returns parti
|
|
|
129763
129865
|
}
|
|
129764
129866
|
results.push({ tool: step.tool, result });
|
|
129765
129867
|
} catch (err) {
|
|
129868
|
+
const reframed = describeNestedParamError(
|
|
129869
|
+
registry2,
|
|
129870
|
+
err,
|
|
129871
|
+
step.tool,
|
|
129872
|
+
toolArgs,
|
|
129873
|
+
step.args ?? {}
|
|
129874
|
+
);
|
|
129766
129875
|
results.push({
|
|
129767
129876
|
tool: step.tool,
|
|
129768
|
-
error: err instanceof Error ? err.message : String(err)
|
|
129877
|
+
error: reframed ?? (err instanceof Error ? err.message : String(err))
|
|
129769
129878
|
});
|
|
129770
129879
|
break;
|
|
129771
129880
|
}
|
|
@@ -145870,7 +145979,9 @@ var zodSchema63 = external_exports.object({
|
|
|
145870
145979
|
project_root: external_exports.string().describe(
|
|
145871
145980
|
"Absolute path to the project root of the flow being recorded \u2014 the same value passed to flow-start-recording. Together with `name` it identifies which recording this step belongs to."
|
|
145872
145981
|
),
|
|
145873
|
-
command: external_exports.string().describe(
|
|
145982
|
+
command: external_exports.string().describe(
|
|
145983
|
+
'MCP tool name (e.g. "gesture-tap", "screenshot", "launch-app") \u2014 a TOOL, not a flow directive. A flow-file directive name ("tap", "launch", "run", "type", "await", "assert", "pinch", "echo", "wait", "long-press", "scroll-to", "snapshot", "when") is answered with guidance, and nothing runs or is recorded: most name the tool that records the directive, while "wait", "long-press", "scroll-to", "snapshot" and "when" have no recording tool at all and are answered with what to do instead. A recording tool (flow-add-step, flow-add-echo, flow-start-recording, flow-finish-recording) is refused the same way, each for its own reason \u2014 nesting one would erase this flow at replay, end the take, or write the step twice.'
|
|
145984
|
+
),
|
|
145874
145985
|
args: external_exports.string().optional().describe(
|
|
145875
145986
|
`Tool arguments as a JSON string, e.g. '{"udid": "ABC", "x": 0.5, "y": 0.3}'. Omit for tools with no arguments.`
|
|
145876
145987
|
),
|
|
@@ -146070,6 +146181,77 @@ async function captureTapSelector(registry2, session, udid, point) {
|
|
|
146070
146181
|
};
|
|
146071
146182
|
}
|
|
146072
146183
|
}
|
|
146184
|
+
async function activeFlowState(session) {
|
|
146185
|
+
if (session.persist === "host") {
|
|
146186
|
+
try {
|
|
146187
|
+
session.flow = parseFlow(await fs48.readFile(session.filePath, "utf8"));
|
|
146188
|
+
} catch (err) {
|
|
146189
|
+
return {
|
|
146190
|
+
stepCount: session.flow.steps.length,
|
|
146191
|
+
note: `The persisted flow could not be read and parsed (${err instanceof Error ? err.message : String(err)}); the step count is from the last valid in-memory snapshot.`
|
|
146192
|
+
};
|
|
146193
|
+
}
|
|
146194
|
+
}
|
|
146195
|
+
return { stepCount: session.flow.steps.length };
|
|
146196
|
+
}
|
|
146197
|
+
async function recordNothing(session, guidance) {
|
|
146198
|
+
const { stepCount, note } = await activeFlowState(session);
|
|
146199
|
+
return {
|
|
146200
|
+
message: `${guidance} Nothing was executed and no step was recorded.${note ? ` ${note}` : ""}`,
|
|
146201
|
+
toolResult: void 0,
|
|
146202
|
+
stepCount,
|
|
146203
|
+
savedTo: session.filePath
|
|
146204
|
+
};
|
|
146205
|
+
}
|
|
146206
|
+
var DIRECTIVE_COMMAND_HINTS = {
|
|
146207
|
+
tap: { tool: "gesture-tap", rewritten: true },
|
|
146208
|
+
launch: {
|
|
146209
|
+
tool: "restart-app",
|
|
146210
|
+
rewritten: true,
|
|
146211
|
+
rewriteCondition: "when it carries only the bundle id (a call with an extra arg, e.g. an Android `activity`, is kept as a raw `tool: restart-app` step to convert during polish)"
|
|
146212
|
+
},
|
|
146213
|
+
run: {
|
|
146214
|
+
tool: "flow-execute",
|
|
146215
|
+
rewritten: true,
|
|
146216
|
+
rewriteCondition: "when the target resolves as a sibling flow in this recording's folder \u2014 a `name` that does not is kept as a raw `tool: flow-execute` step, and so is every target in a REMOTE recording (`run:` composition is host-resolved, so the host cannot validate the client's siblings); a `flow_path` that is not a sibling is refused outright and records nothing"
|
|
146217
|
+
},
|
|
146218
|
+
type: { tool: "keyboard", rewritten: false },
|
|
146219
|
+
await: { tool: AWAIT_UI_ELEMENT_TOOL_ID, rewritten: false },
|
|
146220
|
+
assert: { tool: AWAIT_UI_ELEMENT_TOOL_ID, rewritten: false },
|
|
146221
|
+
pinch: { tool: "gesture-pinch", rewritten: false }
|
|
146222
|
+
};
|
|
146223
|
+
var NESTED_RECORDER_TOOLS = {
|
|
146224
|
+
"flow-add-echo": "`flow-add-echo` records a step itself, so it must be called DIRECTLY, not through flow-add-step \u2014 nesting it would write the echo AND a `tool: flow-add-echo` step that fails on every replay.",
|
|
146225
|
+
"flow-add-step": "flow-add-step cannot record itself. Pass the MCP tool you want to execute as `command`.",
|
|
146226
|
+
"flow-start-recording": "`flow-start-recording` truncates the flow it names. Recording it as a step would erase this flow at replay; call it directly when you want to start a recording.",
|
|
146227
|
+
"flow-finish-recording": "`flow-finish-recording` ends the recording, so it cannot also be a step in it. Call it directly when the walkthrough is complete."
|
|
146228
|
+
};
|
|
146229
|
+
function isToolNotFound(err, command) {
|
|
146230
|
+
return err instanceof ToolNotFoundError && err.toolId === command;
|
|
146231
|
+
}
|
|
146232
|
+
function directiveCommandHint(command) {
|
|
146233
|
+
if (command === "echo") {
|
|
146234
|
+
return `"echo" is a flow directive, not a tool. Call \`flow-add-echo\` DIRECTLY \u2014 not through flow-add-step, which would run it as a nested tool AND record a \`tool: flow-add-echo\` step that fails on every replay.`;
|
|
146235
|
+
}
|
|
146236
|
+
if (command === "wait") {
|
|
146237
|
+
return `"wait" is a flow directive, not a tool, and there is no tool that records one \u2014 a fixed sleep is not a readiness signal. Record the thing you are actually waiting for with \`${AWAIT_UI_ELEMENT_TOOL_ID}\` instead.`;
|
|
146238
|
+
}
|
|
146239
|
+
if (command === "long-press") {
|
|
146240
|
+
return `"long-press" is a flow directive, not a tool, and no tool records one \u2014 there is no gesture-long-press. Record the rest of the path, then add the \`long-press:\` step by hand during polish and prove it with the replay.`;
|
|
146241
|
+
}
|
|
146242
|
+
if (command === "scroll-to") {
|
|
146243
|
+
return `"scroll-to" is a flow directive, not a tool, and no tool records one \u2014 it SEARCHES, scrolling until the target is visible, which no single recorded gesture reproduces. Record the movement with \`gesture-swipe\` (\`gesture-scroll\` on chromium) if the path needs it, then add the \`scroll-to:\` step by hand during polish and prove it with the replay.`;
|
|
146244
|
+
}
|
|
146245
|
+
if (command === "snapshot") {
|
|
146246
|
+
return `"snapshot" is a flow directive, not a tool, and no tool records one \u2014 it compares the screen against a stored baseline, which \`screenshot-diff\` does not manage. Add the \`snapshot:\` step by hand during polish, then adopt its baseline with a run that sets updateBaselines, and review the PNG before committing it.`;
|
|
146247
|
+
}
|
|
146248
|
+
if (command === "when") {
|
|
146249
|
+
return `"when" is a flow directive, not a tool, and no tool records one \u2014 it GUARDS the steps nested under it, so there is no action of its own to run. Record those steps, then wrap them in the \`when:\` block by hand during polish and prove both branches with the replay.`;
|
|
146250
|
+
}
|
|
146251
|
+
const hint = Object.hasOwn(DIRECTIVE_COMMAND_HINTS, command) ? DIRECTIVE_COMMAND_HINTS[command] : void 0;
|
|
146252
|
+
if (!hint) return void 0;
|
|
146253
|
+
return `"${command}" is a flow directive, not a tool. Record it by calling \`${hint.tool}\` through flow-add-step` + (hint.rewritten ? ` \u2014 the recorder rewrites it into the \`${command}:\` step ${hint.rewriteCondition ?? "for you"}. Where the call is recorded at all, a \`delayMs\` on it opts out of the rewrite: the step is then kept in its raw \`tool: ${hint.tool}\` form (a replay delay has no directive form), so leave \`delayMs\` off if you want the \`${command}:\` step.` : `. It is stored as a raw \`tool: ${hint.tool}\` step; converting it to \`${command}:\` is part of the polish pass.`);
|
|
146254
|
+
}
|
|
146073
146255
|
var RUN_TARGET_COMMAND = "flow-execute";
|
|
146074
146256
|
async function rewriteSiblingFlowPath(session, args) {
|
|
146075
146257
|
const flowPath = args.flow_path;
|
|
@@ -146187,12 +146369,12 @@ function createFlowAddStepTool(registry2) {
|
|
|
146187
146369
|
// Name the flow: recordings are concurrent, so several of these lines can
|
|
146188
146370
|
// interleave in one log and "the recorded flow" would not say which.
|
|
146189
146371
|
startedMsg: ({ params }) => `Adding ${params.command} step to flow ${params.name}`,
|
|
146190
|
-
completedMsg: ({ params }) => `Added ${params.command} step to flow ${params.name}`,
|
|
146372
|
+
completedMsg: ({ params, result }) => result.recorded === void 0 ? `Recorded no ${params.command} step in flow ${params.name}` : `Added ${params.command} step to flow ${params.name}`,
|
|
146191
146373
|
failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to add ${params.command} step to flow ${params.name}: ${failureSignal2.error_code}`
|
|
146192
146374
|
},
|
|
146193
146375
|
description: `Execute a tool call and record it as a step in the flow named by \`name\` + \`project_root\` (the recording must already be open \u2014 see flow-start-recording). Use when recording a flow and you want to run and capture each action. A coordinate \`gesture-tap\` is recorded as a portable \`tap: { selector }\` step when the tapped element has stable text/identifier (otherwise coordinates are kept with a warning); a \`restart-app\` is recorded as a \`launch\` step (record one FIRST to make the flow a self-contained e2e flow; restart-app has no chromium support, so a chromium flow records as a fragment \u2014 add the \`launch: { chromium: <app path> }\` line to the YAML afterward, deleting the executionPrerequisite line if one was recorded: a flow that starts with a launch must not declare it).
|
|
146194
146376
|
A recorded \`await-ui-element\` that PASSED is re-probed against the tree the RUNNER resolves \`await:\`/\`assert:\` directives against, which is NOT the tree the live call read; a wait that came back \`{ success: false }\` is not probed at all, and its warning says so; when the condition does not hold there the step is still recorded and \`message\` carries a warning to read before converting \u2014 whether the conversion actually breaks depends on WHY the two disagree, since a screen that moved on between the live wait and the re-probe reads the same way. If that tree could not be read at all, the warning says so instead: the conversion is UNKNOWN, not known-bad. The probe judges the selector exactly as recorded, so write the conversion in the strict map spelling (\`{ visible: { text: Continue } }\`, copying the step's \`selector:\`) \u2014 the bare-string spelling (\`{ visible: Continue }\`) re-parses as a loose selector that resolves identifier-first and falls back to text, which is a different check. \`message\` also warns when the live wait itself came back \`{ success: false }\` \u2014 that tool reports a failed wait by returning rather than throwing, so the step is recorded either way. That warning names the cause, because only one of them judges the condition: a genuine miss will stop the run at replay, while a wait whose tree source was unreadable, or one that was cancelled, observed nothing and leaves the condition UNKNOWN.
|
|
146195
|
-
Returns { message, toolResult, stepCount, recorded, savedTo } on success \u2014 \`message\` is \`Step added to "<name>" flow\` plus any warning about what was recorded (read it; a warning never means the step was skipped). If it fails an error is returned and nothing is recorded.
|
|
146377
|
+
Returns { message, toolResult, stepCount, recorded, savedTo } on success \u2014 \`message\` is \`Step added to "<name>" flow\` plus any warning about what was recorded (read it; a warning never means the step was skipped). If it fails an error is returned and nothing is recorded. Two calls SUCCEED while recording nothing, and omit \`recorded\` to say so: a \`command\` naming a recording tool, and one naming a flow-file directive rather than a tool. Both answer with what to do instead \u2014 usually the call to make (the tool that records that directive, or the recording tool called directly), but \`wait\`, \`long-press\`, \`scroll-to\`, \`snapshot\` and \`when\` have no recording tool, so those name no call and say what to record or add by hand in its place. Either way nothing runs at the device and the take is left untouched \u2014 read \`recorded\`, not the status, to know whether a step was appended.
|
|
146196
146378
|
If a step was recorded by mistake, remove it from the .yaml after \`flow-finish-recording\` rather than during the recording: against a remote client the in-memory copy is authoritative and every write serializes it over your edit, and in host mode a mid-recording edit renumbers the steps, which costs the finish the cross-tree verdicts anchored to them.`,
|
|
146197
146379
|
// The recorded tool RUNS here, so this call lasts as long as whatever it
|
|
146198
146380
|
// wraps, and the three it most often wraps declare this too. Without it the
|
|
@@ -146204,7 +146386,19 @@ If a step was recorded by mistake, remove it from the .yaml after \`flow-finish-
|
|
|
146204
146386
|
services: () => ({}),
|
|
146205
146387
|
async execute(_services, params, ctx) {
|
|
146206
146388
|
const session = await requireRecordingSession(params.project_root, params.name);
|
|
146207
|
-
const
|
|
146389
|
+
const nested = Object.hasOwn(NESTED_RECORDER_TOOLS, params.command) ? NESTED_RECORDER_TOOLS[params.command] : void 0;
|
|
146390
|
+
if (nested) return recordNothing(session, nested);
|
|
146391
|
+
let args;
|
|
146392
|
+
try {
|
|
146393
|
+
args = params.args ? JSON.parse(params.args) : {};
|
|
146394
|
+
} catch (err) {
|
|
146395
|
+
if (registry2.getTool(params.command) === void 0) {
|
|
146396
|
+
const hint = directiveCommandHint(params.command);
|
|
146397
|
+
if (hint) return recordNothing(session, hint);
|
|
146398
|
+
}
|
|
146399
|
+
throw err;
|
|
146400
|
+
}
|
|
146401
|
+
const authoredArgs = { ...args };
|
|
146208
146402
|
if (params.command === RUN_TARGET_COMMAND) await rewriteSiblingFlowPath(session, args);
|
|
146209
146403
|
const isTap = params.command === "gesture-tap" && params.delayMs === void 0 && typeof args.udid === "string" && typeof args.x === "number" && typeof args.y === "number";
|
|
146210
146404
|
let captured;
|
|
@@ -146214,7 +146408,27 @@ If a step was recorded by mistake, remove it from the .yaml after \`flow-finish-
|
|
|
146214
146408
|
y: args.y
|
|
146215
146409
|
});
|
|
146216
146410
|
}
|
|
146217
|
-
|
|
146411
|
+
let toolResult;
|
|
146412
|
+
try {
|
|
146413
|
+
toolResult = await invokeSubTool(registry2, ctx, params.command, args);
|
|
146414
|
+
} catch (err) {
|
|
146415
|
+
const hint = isToolNotFound(err, params.command) ? directiveCommandHint(params.command) : void 0;
|
|
146416
|
+
if (hint) return recordNothing(session, hint);
|
|
146417
|
+
const reframed = describeNestedParamError(
|
|
146418
|
+
registry2,
|
|
146419
|
+
err,
|
|
146420
|
+
params.command,
|
|
146421
|
+
args,
|
|
146422
|
+
authoredArgs
|
|
146423
|
+
);
|
|
146424
|
+
if (reframed === void 0) throw err;
|
|
146425
|
+
throw new FailureError(reframed, {
|
|
146426
|
+
error_code: FAILURE_CODES.TOOL_INPUT_INVALID,
|
|
146427
|
+
failure_stage: "flow_add_step_nested_params",
|
|
146428
|
+
failure_area: "tool_server",
|
|
146429
|
+
error_kind: "validation"
|
|
146430
|
+
});
|
|
146431
|
+
}
|
|
146218
146432
|
let waitWarning;
|
|
146219
146433
|
if (params.command === AWAIT_UI_ELEMENT_TOOL_ID) {
|
|
146220
146434
|
if (isUnmetUiWaitResult(params.command, toolResult)) {
|
|
@@ -148756,8 +148970,10 @@ var zodSchema65 = external_exports.object({
|
|
|
148756
148970
|
if (params.name === void 0 === (params.flow_path === void 0)) {
|
|
148757
148971
|
ctx.addIssue({
|
|
148758
148972
|
code: external_exports.ZodIssueCode.custom,
|
|
148759
|
-
message: "Pass exactly one flow source: name or flow_path.",
|
|
148760
|
-
|
|
148973
|
+
message: params.name !== void 0 ? "Pass exactly one flow source: name or flow_path." : "Pass exactly one flow source: name or flow_path. flow-execute needs the flow's name in `name` \u2014 it resolves <project_root>/.argent/flows/<name>.yaml.",
|
|
148974
|
+
// The ROOT, not `flow_path`: the rule spans both source fields, and a
|
|
148975
|
+
// path would prefix the message with "`flow_path`:".
|
|
148976
|
+
path: []
|
|
148761
148977
|
});
|
|
148762
148978
|
}
|
|
148763
148979
|
});
|
|
@@ -149847,7 +150063,8 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
149847
150063
|
}
|
|
149848
150064
|
return { ...base, status: "pass", tool: step.name, result, outputHint, args };
|
|
149849
150065
|
} catch (err) {
|
|
149850
|
-
|
|
150066
|
+
const reframed = describeNestedParamError(registry2, err, step.name, args, step.args ?? {});
|
|
150067
|
+
return { ...base, status: "error", tool: step.name, reason: reframed ?? errMsg3(err) };
|
|
149851
150068
|
}
|
|
149852
150069
|
}
|
|
149853
150070
|
default:
|
|
@@ -149995,8 +150212,10 @@ var zodSchema66 = external_exports.object({
|
|
|
149995
150212
|
if (params.name === void 0 === (params.flow_path === void 0)) {
|
|
149996
150213
|
ctx.addIssue({
|
|
149997
150214
|
code: external_exports.ZodIssueCode.custom,
|
|
149998
|
-
message: "Pass exactly one flow source: name or flow_path.",
|
|
149999
|
-
|
|
150215
|
+
message: params.name !== void 0 ? "Pass exactly one flow source: name or flow_path." : "Pass exactly one flow source: name or flow_path. flow-read-prerequisite needs the flow's name in `name` \u2014 it resolves <project_root>/.argent/flows/<name>.yaml.",
|
|
150216
|
+
// The ROOT, matching flow-execute: the rule spans both source fields,
|
|
150217
|
+
// so it must not be anchored on one of them.
|
|
150218
|
+
path: []
|
|
150000
150219
|
});
|
|
150001
150220
|
}
|
|
150002
150221
|
});
|
|
@@ -150028,7 +150247,7 @@ Use when you need to check what app/simulator state is required before executing
|
|
|
150028
150247
|
source (name or flow_path) you will pass to flow-execute, so the prerequisite you read is the contract of
|
|
150029
150248
|
the flow that will actually run.
|
|
150030
150249
|
Fails if the flow file does not exist.
|
|
150031
|
-
Address the flow exactly as you will address it in flow-execute: name or flow_path, one and only one; supplying both or neither is rejected.`,
|
|
150250
|
+
Address the flow exactly as you will address it in flow-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/flows/<name>.yaml.`,
|
|
150032
150251
|
zodSchema: zodSchema66,
|
|
150033
150252
|
fileInputs: fileInputs4,
|
|
150034
150253
|
services: () => ({}),
|
package/package.json
CHANGED