Skip to content

Commit 9daa584

Browse files
authored
add playwright arguments to agent (#1066)
# why solves #1060 patch regression of playwright arguments being removed from agent execute response # what changed agent.execute now returns playwright arguments in its response # test plan tested locally
1 parent 6a002b2 commit 9daa584

File tree

4 files changed

+110
-18
lines changed

4 files changed

+110
-18
lines changed

.changeset/icy-toes-obey.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@browserbasehq/stagehand": patch
3+
---
4+
5+
Add playwright arguments to agent execute response

lib/agent/tools/act.ts

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { tool } from "ai";
22
import { z } from "zod/v3";
33
import { StagehandPage } from "../../StagehandPage";
4-
4+
import { buildActObservePrompt } from "../../prompt";
5+
import { SupportedPlaywrightAction } from "@/types/act";
56
export const createActTool = (
67
stagehandPage: StagehandPage,
78
executionModel?: string,
@@ -19,37 +20,91 @@ export const createActTool = (
1920
}),
2021
execute: async ({ action }) => {
2122
try {
22-
let result;
23-
if (executionModel) {
24-
result = await stagehandPage.page.act({
25-
action,
26-
modelName: executionModel,
27-
});
28-
} else {
29-
result = await stagehandPage.page.act(action);
23+
const builtPrompt = buildActObservePrompt(
24+
action,
25+
Object.values(SupportedPlaywrightAction),
26+
);
27+
28+
const observeOptions = executionModel
29+
? {
30+
instruction: builtPrompt,
31+
modelName: executionModel,
32+
}
33+
: {
34+
instruction: builtPrompt,
35+
};
36+
37+
const observeResults = await stagehandPage.page.observe(observeOptions);
38+
39+
if (!observeResults || observeResults.length === 0) {
40+
return {
41+
success: false,
42+
error: "No observable actions found for the given instruction",
43+
};
3044
}
31-
const isIframeAction = result.action === "an iframe";
45+
46+
const observeResult = observeResults[0];
47+
48+
const isIframeAction = observeResult.description === "an iframe";
3249

3350
if (isIframeAction) {
34-
const fallback = await stagehandPage.page.act(
35-
executionModel
36-
? { action, modelName: executionModel, iframes: true }
37-
: { action, iframes: true },
38-
);
51+
const iframeObserveOptions = executionModel
52+
? {
53+
instruction: builtPrompt,
54+
modelName: executionModel,
55+
iframes: true,
56+
}
57+
: {
58+
instruction: builtPrompt,
59+
iframes: true,
60+
};
61+
62+
const iframeObserveResults =
63+
await stagehandPage.page.observe(iframeObserveOptions);
64+
65+
if (!iframeObserveResults || iframeObserveResults.length === 0) {
66+
return {
67+
success: false,
68+
error: "No observable actions found within iframe context",
69+
isIframe: true,
70+
};
71+
}
72+
73+
const iframeObserveResult = iframeObserveResults[0];
74+
const fallback = await stagehandPage.page.act(iframeObserveResult);
75+
3976
return {
4077
success: fallback.success,
4178
action: fallback.action,
4279
isIframe: true,
80+
playwrightArguments: {
81+
description: iframeObserveResult.description,
82+
method: iframeObserveResult.method,
83+
arguments: iframeObserveResult.arguments,
84+
selector: iframeObserveResult.selector,
85+
},
4386
};
4487
}
4588

89+
const result = await stagehandPage.page.act(observeResult);
90+
const playwrightArguments = {
91+
description: observeResult.description,
92+
method: observeResult.method,
93+
arguments: observeResult.arguments,
94+
selector: observeResult.selector,
95+
};
96+
4697
return {
4798
success: result.success,
4899
action: result.action,
49100
isIframe: false,
101+
playwrightArguments,
50102
};
51103
} catch (error) {
52-
return { success: false, error: error.message };
104+
return {
105+
success: false,
106+
error: error.message,
107+
};
53108
}
54109
},
55110
});

lib/handlers/stagehandAgentHandler.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { AgentAction, AgentExecuteOptions, AgentResult } from "@/types/agent";
1+
import {
2+
AgentAction,
3+
AgentExecuteOptions,
4+
AgentResult,
5+
ActToolResult,
6+
} from "@/types/agent";
27
import { LogLine } from "@/types/log";
38
import { StagehandPage } from "../StagehandPage";
49
import { LLMClient } from "../llm/LLMClient";
@@ -99,7 +104,8 @@ export class StagehandAgentHandler {
99104
});
100105

101106
if (event.toolCalls && event.toolCalls.length > 0) {
102-
for (const toolCall of event.toolCalls) {
107+
for (let i = 0; i < event.toolCalls.length; i++) {
108+
const toolCall = event.toolCalls[i];
103109
const args = toolCall.args as Record<string, unknown>;
104110

105111
if (event.text.length > 0) {
@@ -122,6 +128,21 @@ export class StagehandAgentHandler {
122128
}
123129
}
124130

131+
// Get the tool result if available
132+
const toolResult = event.toolResults?.[i];
133+
134+
const getPlaywrightArguments = () => {
135+
if (toolCall.toolName !== "act" || !toolResult) {
136+
return {};
137+
}
138+
const result = toolResult.result as ActToolResult;
139+
if (result && result.playwrightArguments) {
140+
return { playwrightArguments: result.playwrightArguments };
141+
}
142+
143+
return {};
144+
};
145+
125146
const action: AgentAction = {
126147
type: toolCall.toolName,
127148
reasoning: event.text || undefined,
@@ -130,6 +151,7 @@ export class StagehandAgentHandler {
130151
? (args?.taskComplete as boolean)
131152
: false,
132153
...args,
154+
...getPlaywrightArguments(),
133155
};
134156

135157
actions.push(action);

types/agent.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
import { LogLine } from "./log";
2+
import { ObserveResult } from "./stagehand";
3+
4+
export interface ActToolResult {
5+
success: boolean;
6+
action?: string;
7+
error?: string;
8+
isIframe?: boolean;
9+
playwrightArguments?: ObserveResult | null;
10+
}
211

312
export interface AgentAction {
413
type: string;
@@ -10,6 +19,7 @@ export interface AgentAction {
1019
pageText?: string; // ariaTree tool
1120
pageUrl?: string; // ariaTree tool
1221
instruction?: string; // various tools
22+
playwrightArguments?: ObserveResult | null; // act tool
1323
[key: string]: unknown;
1424
}
1525

0 commit comments

Comments
 (0)