Skip to content

Commit 369b3aa

Browse files
Reject whitespace XPath and methods in cached replay
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 8380d1e commit 369b3aa

2 files changed

Lines changed: 53 additions & 9 deletions

File tree

‎src/agent/shared/run-cached-action.test.ts‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,43 @@ describe("runCachedStep", () => {
151151
expect(result.output).toBe("Unsupported cached action");
152152
});
153153

154+
it("treats whitespace xpath or method as unsupported cached action", async () => {
155+
executeReplaySpecialAction.mockResolvedValue(null);
156+
157+
const whitespaceXPathResult = await runCachedStep({
158+
page: createMockPage(),
159+
instruction: "click login",
160+
cachedAction: {
161+
actionType: "actElement",
162+
xpath: " ",
163+
method: "click",
164+
},
165+
tokenLimit: 8000,
166+
llm: createMockLLM(),
167+
mcpClient: undefined,
168+
variables: [],
169+
});
170+
171+
const whitespaceMethodResult = await runCachedStep({
172+
page: createMockPage(),
173+
instruction: "click login",
174+
cachedAction: {
175+
actionType: "actElement",
176+
xpath: "//button[1]",
177+
method: " ",
178+
},
179+
tokenLimit: 8000,
180+
llm: createMockLLM(),
181+
mcpClient: undefined,
182+
variables: [],
183+
});
184+
185+
expect(whitespaceXPathResult.status).toBe(TaskStatus.FAILED);
186+
expect(whitespaceXPathResult.output).toBe("Unsupported cached action");
187+
expect(whitespaceMethodResult.status).toBe(TaskStatus.FAILED);
188+
expect(whitespaceMethodResult.output).toBe("Unsupported cached action");
189+
});
190+
154191
it("falls back to perform when cached attempts fail", async () => {
155192
executeReplaySpecialAction.mockResolvedValue(null);
156193
resolveXPathWithCDP.mockRejectedValue(new Error("xpath resolution failed"));

‎src/agent/shared/run-cached-action.ts‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ export async function runCachedStep(
7676

7777
const taskId = uuidv4();
7878
const attempts = normalizeMaxSteps(maxSteps);
79+
const normalizedXPath = cachedAction.xpath?.trim();
80+
const normalizedMethod = cachedAction.method?.trim();
81+
const normalizedCachedAction: CachedActionInput = {
82+
...cachedAction,
83+
xpath: normalizedXPath,
84+
method: normalizedMethod,
85+
};
7986

8087
const specialActionResult = await executeReplaySpecialAction({
8188
taskId,
@@ -98,7 +105,7 @@ export async function runCachedStep(
98105
usedCachedAction: true,
99106
fallbackUsed: false,
100107
retries: 1,
101-
cachedXPath: cachedAction.xpath ?? null,
108+
cachedXPath: normalizedXPath ?? null,
102109
fallbackXPath: null,
103110
fallbackElementId: null,
104111
},
@@ -110,8 +117,8 @@ export async function runCachedStep(
110117

111118
if (
112119
cachedAction.actionType !== "actElement" ||
113-
!cachedAction.xpath ||
114-
!cachedAction.method
120+
!normalizedXPath ||
121+
!normalizedMethod
115122
) {
116123
return {
117124
taskId,
@@ -128,7 +135,7 @@ export async function runCachedStep(
128135
const attemptResult = await runCachedAttempt({
129136
page,
130137
instruction,
131-
cachedAction,
138+
cachedAction: normalizedCachedAction,
132139
debug,
133140
tokenLimit,
134141
llm,
@@ -165,7 +172,7 @@ export async function runCachedStep(
165172
usedCachedAction: true,
166173
fallbackUsed: false,
167174
retries: attemptIndex,
168-
cachedXPath: cachedAction.xpath ?? null,
175+
cachedXPath: normalizedXPath ?? null,
169176
fallbackXPath: null,
170177
fallbackElementId: null,
171178
},
@@ -186,14 +193,14 @@ export async function runCachedStep(
186193
usedCachedAction: true,
187194
fallbackUsed: true,
188195
retries: attempts,
189-
cachedXPath: cachedAction.xpath ?? null,
196+
cachedXPath: normalizedXPath ?? null,
190197
fallbackXPath: null,
191198
fallbackElementId: null,
192199
},
193200
} satisfies TaskOutput;
194201
});
195202
if (debug) {
196-
const cachedXPath = cachedAction.xpath || "N/A";
203+
const cachedXPath = normalizedXPath || "N/A";
197204
const resolvedXPath = fb.replayStepMeta?.fallbackXPath || "N/A";
198205
// eslint-disable-next-line no-console
199206
console.log(
@@ -211,7 +218,7 @@ export async function runCachedStep(
211218
usedCachedAction: true,
212219
fallbackUsed: true,
213220
retries: attempts,
214-
cachedXPath: cachedAction.xpath ?? null,
221+
cachedXPath: normalizedXPath ?? null,
215222
fallbackXPath: fb.replayStepMeta?.fallbackXPath ?? null,
216223
fallbackElementId: fb.replayStepMeta?.fallbackElementId ?? null,
217224
},
@@ -230,7 +237,7 @@ export async function runCachedStep(
230237
usedCachedAction: true,
231238
fallbackUsed: false,
232239
retries: attempts,
233-
cachedXPath: cachedAction.xpath ?? null,
240+
cachedXPath: normalizedXPath ?? null,
234241
fallbackXPath: null,
235242
fallbackElementId: null,
236243
},

0 commit comments

Comments
 (0)