@browserbasehq/browse-cli 0.2.0-alpha-4ff3bb831a6ef6e2d57148e7afb68ea8d23e395d → 0.2.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/index.js +451 -53
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -15214,7 +15214,7 @@ var init_version = __esm({
|
|
|
15214
15214
|
"../core/dist/esm/lib/version.js"() {
|
|
15215
15215
|
"use strict";
|
|
15216
15216
|
init_cjs_shims();
|
|
15217
|
-
STAGEHAND_VERSION = "3.
|
|
15217
|
+
STAGEHAND_VERSION = "3.2.0";
|
|
15218
15218
|
}
|
|
15219
15219
|
});
|
|
15220
15220
|
|
|
@@ -103029,6 +103029,155 @@ function createAgentTools(v32, options) {
|
|
|
103029
103029
|
|
|
103030
103030
|
// ../core/dist/esm/lib/v3/agent/prompts/agentSystemPrompt.js
|
|
103031
103031
|
init_cjs_shims();
|
|
103032
|
+
|
|
103033
|
+
// ../core/dist/esm/lib/v3/agent/utils/captchaSolver.js
|
|
103034
|
+
init_cjs_shims();
|
|
103035
|
+
var SOLVING_STARTED = "browserbase-solving-started";
|
|
103036
|
+
var SOLVING_FINISHED = "browserbase-solving-finished";
|
|
103037
|
+
var SOLVING_ERRORED = "browserbase-solving-errored";
|
|
103038
|
+
var SOLVE_TIMEOUT_MS = 9e4;
|
|
103039
|
+
var CAPTCHA_SOLVED_MSG = "A captcha was automatically detected and solved \u2014 no further interaction with the captcha is needed, even if it does not visually appear solved. Do not click the captcha checkbox, widget, or challenge again. Continue with your task.";
|
|
103040
|
+
var CAPTCHA_ERRORED_MSG = "A captcha was detected but the automatic captcha solver failed to solve it. You may need to try a different approach or navigate around the captcha.";
|
|
103041
|
+
var CAPTCHA_SYSTEM_PROMPT_NOTE = "Captchas on this page are automatically detected and solved by the browser environment. Do not interact with or attempt to solve any captchas yourself \u2014 they will be handled for you. Do not click the captcha checkbox, widget, or challenge again after it has been solved, even if it still looks unresolved. Continue with your task as if the captcha does not exist.";
|
|
103042
|
+
var CAPTCHA_CUA_SYSTEM_PROMPT_NOTE = "\n\nCaptchas on this page are automatically detected and solved by the browser environment. Do not interact with or attempt to solve any captchas yourself \u2014 they will be handled for you. Continue with your task as if the captcha does not exist.";
|
|
103043
|
+
var CaptchaSolver = class {
|
|
103044
|
+
solving = false;
|
|
103045
|
+
_solvedSinceLastConsume = false;
|
|
103046
|
+
_erroredSinceLastConsume = false;
|
|
103047
|
+
listener = null;
|
|
103048
|
+
attachedPage = null;
|
|
103049
|
+
pageProvider = null;
|
|
103050
|
+
/** Shared promise that all concurrent waitIfSolving() callers await. */
|
|
103051
|
+
waitPromise = null;
|
|
103052
|
+
/** Resolves the shared waitPromise. */
|
|
103053
|
+
resolveWait = null;
|
|
103054
|
+
/** Timeout handle for the 90s deadline. */
|
|
103055
|
+
waitTimer = null;
|
|
103056
|
+
/**
|
|
103057
|
+
* Initialise with a callback that returns the current active page.
|
|
103058
|
+
* The listener is lazily (re-)attached whenever the active page changes.
|
|
103059
|
+
*/
|
|
103060
|
+
init(pageProvider) {
|
|
103061
|
+
this.pageProvider = pageProvider;
|
|
103062
|
+
}
|
|
103063
|
+
/** Whether a captcha solve is currently in progress. */
|
|
103064
|
+
isSolving() {
|
|
103065
|
+
return this.solving;
|
|
103066
|
+
}
|
|
103067
|
+
/**
|
|
103068
|
+
* Ensure the console listener is attached to the current active page.
|
|
103069
|
+
* If the active page has changed since the last call, the old listener
|
|
103070
|
+
* is removed and a new one is installed.
|
|
103071
|
+
*/
|
|
103072
|
+
async ensureAttached() {
|
|
103073
|
+
if (!this.pageProvider)
|
|
103074
|
+
return;
|
|
103075
|
+
const page = await this.pageProvider();
|
|
103076
|
+
if (page === this.attachedPage)
|
|
103077
|
+
return;
|
|
103078
|
+
this.detachListener();
|
|
103079
|
+
this.attachedPage = page;
|
|
103080
|
+
this.listener = (msg) => {
|
|
103081
|
+
const text2 = msg.text();
|
|
103082
|
+
if (text2 === SOLVING_STARTED) {
|
|
103083
|
+
this.solving = true;
|
|
103084
|
+
} else if (text2 === SOLVING_FINISHED) {
|
|
103085
|
+
this.solving = false;
|
|
103086
|
+
this._solvedSinceLastConsume = true;
|
|
103087
|
+
this.settle();
|
|
103088
|
+
} else if (text2 === SOLVING_ERRORED) {
|
|
103089
|
+
this.solving = false;
|
|
103090
|
+
this._erroredSinceLastConsume = true;
|
|
103091
|
+
this.settle();
|
|
103092
|
+
}
|
|
103093
|
+
};
|
|
103094
|
+
page.on("console", this.listener);
|
|
103095
|
+
}
|
|
103096
|
+
/**
|
|
103097
|
+
* Returns a promise that resolves immediately if no captcha is being
|
|
103098
|
+
* solved, or blocks until the solver finishes, errors, or the 90s
|
|
103099
|
+
* timeout is reached.
|
|
103100
|
+
*
|
|
103101
|
+
* Also re-attaches the listener to the current active page if it has
|
|
103102
|
+
* changed since the last call.
|
|
103103
|
+
*
|
|
103104
|
+
* All concurrent callers share the same promise, so no waiter is
|
|
103105
|
+
* orphaned.
|
|
103106
|
+
*/
|
|
103107
|
+
async waitIfSolving() {
|
|
103108
|
+
await this.ensureAttached();
|
|
103109
|
+
if (!this.solving)
|
|
103110
|
+
return;
|
|
103111
|
+
if (this.waitPromise)
|
|
103112
|
+
return this.waitPromise;
|
|
103113
|
+
this.waitPromise = new Promise((resolve4) => {
|
|
103114
|
+
this.resolveWait = resolve4;
|
|
103115
|
+
this.waitTimer = setTimeout(() => {
|
|
103116
|
+
this.solving = false;
|
|
103117
|
+
this._erroredSinceLastConsume = true;
|
|
103118
|
+
this.settle();
|
|
103119
|
+
}, SOLVE_TIMEOUT_MS);
|
|
103120
|
+
});
|
|
103121
|
+
return this.waitPromise;
|
|
103122
|
+
}
|
|
103123
|
+
/**
|
|
103124
|
+
* Returns and resets the solve event flags.
|
|
103125
|
+
* Call after `waitIfSolving()` to check whether a captcha was solved
|
|
103126
|
+
* (or errored) since the last consume. This captures events even if
|
|
103127
|
+
* the solve completed between two `waitIfSolving()` calls.
|
|
103128
|
+
*/
|
|
103129
|
+
consumeSolveResult() {
|
|
103130
|
+
const result = {
|
|
103131
|
+
solved: this._solvedSinceLastConsume,
|
|
103132
|
+
errored: this._erroredSinceLastConsume
|
|
103133
|
+
};
|
|
103134
|
+
this._solvedSinceLastConsume = false;
|
|
103135
|
+
this._erroredSinceLastConsume = false;
|
|
103136
|
+
return result;
|
|
103137
|
+
}
|
|
103138
|
+
/**
|
|
103139
|
+
* Remove the console listener and reset all state.
|
|
103140
|
+
*/
|
|
103141
|
+
dispose() {
|
|
103142
|
+
this.detachListener();
|
|
103143
|
+
this.attachedPage = null;
|
|
103144
|
+
this.pageProvider = null;
|
|
103145
|
+
this.solving = false;
|
|
103146
|
+
this._solvedSinceLastConsume = false;
|
|
103147
|
+
this._erroredSinceLastConsume = false;
|
|
103148
|
+
this.settle();
|
|
103149
|
+
}
|
|
103150
|
+
// ------------------------------------------------------------------
|
|
103151
|
+
// Internal helpers
|
|
103152
|
+
// ------------------------------------------------------------------
|
|
103153
|
+
/** Remove the console listener from the currently attached page. */
|
|
103154
|
+
detachListener() {
|
|
103155
|
+
if (this.attachedPage && this.listener) {
|
|
103156
|
+
this.attachedPage.off("console", this.listener);
|
|
103157
|
+
}
|
|
103158
|
+
this.listener = null;
|
|
103159
|
+
if (this.solving) {
|
|
103160
|
+
this._erroredSinceLastConsume = true;
|
|
103161
|
+
}
|
|
103162
|
+
this.solving = false;
|
|
103163
|
+
this.settle();
|
|
103164
|
+
}
|
|
103165
|
+
/** Resolve the shared wait promise and clear the timeout. */
|
|
103166
|
+
settle() {
|
|
103167
|
+
if (this.waitTimer) {
|
|
103168
|
+
clearTimeout(this.waitTimer);
|
|
103169
|
+
this.waitTimer = null;
|
|
103170
|
+
}
|
|
103171
|
+
if (this.resolveWait) {
|
|
103172
|
+
const resolve4 = this.resolveWait;
|
|
103173
|
+
this.resolveWait = null;
|
|
103174
|
+
this.waitPromise = null;
|
|
103175
|
+
resolve4();
|
|
103176
|
+
}
|
|
103177
|
+
}
|
|
103178
|
+
};
|
|
103179
|
+
|
|
103180
|
+
// ../core/dist/esm/lib/v3/agent/prompts/agentSystemPrompt.js
|
|
103032
103181
|
function buildToolsSection(isHybridMode, hasSearch, excludeTools) {
|
|
103033
103182
|
const excludeSet = new Set(excludeTools ?? []);
|
|
103034
103183
|
const hybridTools = [
|
|
@@ -103102,7 +103251,7 @@ ${toolLines}
|
|
|
103102
103251
|
</tools>`;
|
|
103103
103252
|
}
|
|
103104
103253
|
function buildAgentSystemPrompt(options) {
|
|
103105
|
-
const { url: url2, executionInstruction, mode, systemInstructions,
|
|
103254
|
+
const { url: url2, executionInstruction, mode, systemInstructions, captchasAutoSolve = false, excludeTools, variables, useSearch = false } = options;
|
|
103106
103255
|
const localeDate = (/* @__PURE__ */ new Date()).toLocaleDateString();
|
|
103107
103256
|
const isoDate = (/* @__PURE__ */ new Date()).toISOString();
|
|
103108
103257
|
const cdata = (text2) => `<![CDATA[${text2}]]>`;
|
|
@@ -103156,9 +103305,8 @@ function buildAgentSystemPrompt(options) {
|
|
|
103156
103305
|
</secondary_tool>
|
|
103157
103306
|
</step_1>
|
|
103158
103307
|
</page_understanding_protocol>`;
|
|
103159
|
-
const roadblocksSection =
|
|
103160
|
-
<note
|
|
103161
|
-
<captcha>If you see a captcha, use the wait tool. It will automatically be solved by our internal solver.</captcha>
|
|
103308
|
+
const roadblocksSection = captchasAutoSolve ? `<roadblocks>
|
|
103309
|
+
<note>${CAPTCHA_SYSTEM_PROMPT_NOTE}</note>
|
|
103162
103310
|
</roadblocks>` : "";
|
|
103163
103311
|
const customInstructionsBlock = systemInstructions ? `<customInstructions>${cdata(systemInstructions)}</customInstructions>
|
|
103164
103312
|
` : "";
|
|
@@ -103541,7 +103689,8 @@ var V3AgentHandler = class {
|
|
|
103541
103689
|
systemInstructions;
|
|
103542
103690
|
mcpTools;
|
|
103543
103691
|
mode;
|
|
103544
|
-
|
|
103692
|
+
captchaAutoSolveEnabled;
|
|
103693
|
+
constructor(v32, logger, llmClient, executionModel, systemInstructions, mcpTools, mode, captchaAutoSolveEnabled) {
|
|
103545
103694
|
this.v3 = v32;
|
|
103546
103695
|
this.logger = logger;
|
|
103547
103696
|
this.llmClient = llmClient;
|
|
@@ -103549,6 +103698,7 @@ var V3AgentHandler = class {
|
|
|
103549
103698
|
this.systemInstructions = systemInstructions;
|
|
103550
103699
|
this.mcpTools = mcpTools;
|
|
103551
103700
|
this.mode = mode ?? "dom";
|
|
103701
|
+
this.captchaAutoSolveEnabled = captchaAutoSolveEnabled ?? false;
|
|
103552
103702
|
}
|
|
103553
103703
|
async prepareAgent(instructionOrOptions) {
|
|
103554
103704
|
try {
|
|
@@ -103560,7 +103710,7 @@ var V3AgentHandler = class {
|
|
|
103560
103710
|
executionInstruction: options.instruction,
|
|
103561
103711
|
mode: this.mode,
|
|
103562
103712
|
systemInstructions: this.systemInstructions,
|
|
103563
|
-
|
|
103713
|
+
captchasAutoSolve: this.v3.isCaptchaAutoSolveEnabled,
|
|
103564
103714
|
excludeTools: options.excludeTools,
|
|
103565
103715
|
variables: options.variables,
|
|
103566
103716
|
useSearch: options.useSearch
|
|
@@ -103609,9 +103759,42 @@ var V3AgentHandler = class {
|
|
|
103609
103759
|
throw error46;
|
|
103610
103760
|
}
|
|
103611
103761
|
}
|
|
103612
|
-
createPrepareStep(userCallback) {
|
|
103762
|
+
createPrepareStep(userCallback, captchaSolver) {
|
|
103613
103763
|
return async (options) => {
|
|
103614
103764
|
processMessages(options.messages);
|
|
103765
|
+
if (captchaSolver) {
|
|
103766
|
+
if (captchaSolver.isSolving()) {
|
|
103767
|
+
this.logger({
|
|
103768
|
+
category: "agent",
|
|
103769
|
+
message: "Captcha detected \u2014 waiting for Browserbase to solve it before continuing",
|
|
103770
|
+
level: 1
|
|
103771
|
+
});
|
|
103772
|
+
}
|
|
103773
|
+
await captchaSolver.waitIfSolving();
|
|
103774
|
+
const { solved, errored } = captchaSolver.consumeSolveResult();
|
|
103775
|
+
if (solved) {
|
|
103776
|
+
options.messages.push({
|
|
103777
|
+
role: "user",
|
|
103778
|
+
content: CAPTCHA_SOLVED_MSG
|
|
103779
|
+
});
|
|
103780
|
+
this.logger({
|
|
103781
|
+
category: "agent",
|
|
103782
|
+
message: "Captcha solved \u2014 injected notification into agent message stream",
|
|
103783
|
+
level: 1
|
|
103784
|
+
});
|
|
103785
|
+
}
|
|
103786
|
+
if (errored) {
|
|
103787
|
+
options.messages.push({
|
|
103788
|
+
role: "user",
|
|
103789
|
+
content: CAPTCHA_ERRORED_MSG
|
|
103790
|
+
});
|
|
103791
|
+
this.logger({
|
|
103792
|
+
category: "agent",
|
|
103793
|
+
message: "Captcha solver failed \u2014 injected error notification into agent message stream",
|
|
103794
|
+
level: 1
|
|
103795
|
+
});
|
|
103796
|
+
}
|
|
103797
|
+
}
|
|
103615
103798
|
if (userCallback) {
|
|
103616
103799
|
return userCallback(options);
|
|
103617
103800
|
}
|
|
@@ -103678,6 +103861,7 @@ var V3AgentHandler = class {
|
|
|
103678
103861
|
currentPageUrl: ""
|
|
103679
103862
|
};
|
|
103680
103863
|
let messages = [];
|
|
103864
|
+
let captchaSolver;
|
|
103681
103865
|
try {
|
|
103682
103866
|
const { options: preparedOptions, maxSteps, systemPrompt, allTools, messages: preparedMessages, wrappedModel, initialPageUrl } = await this.prepareAgent(instructionOrOptions);
|
|
103683
103867
|
if (shouldHighlightCursor && this.mode === "hybrid") {
|
|
@@ -103685,6 +103869,10 @@ var V3AgentHandler = class {
|
|
|
103685
103869
|
await page.enableCursorOverlay().catch(() => {
|
|
103686
103870
|
});
|
|
103687
103871
|
}
|
|
103872
|
+
if (this.captchaAutoSolveEnabled) {
|
|
103873
|
+
captchaSolver = new CaptchaSolver();
|
|
103874
|
+
captchaSolver.init(() => this.v3.context.awaitActivePage());
|
|
103875
|
+
}
|
|
103688
103876
|
messages = preparedMessages;
|
|
103689
103877
|
state.currentPageUrl = initialPageUrl;
|
|
103690
103878
|
const callbacks = instructionOrOptions.callbacks;
|
|
@@ -103707,7 +103895,7 @@ var V3AgentHandler = class {
|
|
|
103707
103895
|
stopWhen: (result2) => this.handleStop(result2, maxSteps),
|
|
103708
103896
|
temperature: 1,
|
|
103709
103897
|
toolChoice: "auto",
|
|
103710
|
-
prepareStep: this.createPrepareStep(callbacks?.prepareStep),
|
|
103898
|
+
prepareStep: this.createPrepareStep(callbacks?.prepareStep, captchaSolver),
|
|
103711
103899
|
onStepFinish: this.createStepHandler(state, callbacks?.onStepFinish),
|
|
103712
103900
|
abortSignal: preparedOptions.signal,
|
|
103713
103901
|
providerOptions: {
|
|
@@ -103739,6 +103927,8 @@ var V3AgentHandler = class {
|
|
|
103739
103927
|
completed: false,
|
|
103740
103928
|
messages
|
|
103741
103929
|
};
|
|
103930
|
+
} finally {
|
|
103931
|
+
captchaSolver?.dispose();
|
|
103742
103932
|
}
|
|
103743
103933
|
}
|
|
103744
103934
|
async stream(instructionOrOptions) {
|
|
@@ -103750,6 +103940,11 @@ var V3AgentHandler = class {
|
|
|
103750
103940
|
await page.enableCursorOverlay().catch(() => {
|
|
103751
103941
|
});
|
|
103752
103942
|
}
|
|
103943
|
+
let captchaSolver;
|
|
103944
|
+
if (this.captchaAutoSolveEnabled) {
|
|
103945
|
+
captchaSolver = new CaptchaSolver();
|
|
103946
|
+
captchaSolver.init(() => this.v3.context.awaitActivePage());
|
|
103947
|
+
}
|
|
103753
103948
|
const callbacks = instructionOrOptions.callbacks;
|
|
103754
103949
|
const state = {
|
|
103755
103950
|
collectedReasoning: [],
|
|
@@ -103774,45 +103969,57 @@ var V3AgentHandler = class {
|
|
|
103774
103969
|
});
|
|
103775
103970
|
rejectResult(error46);
|
|
103776
103971
|
};
|
|
103777
|
-
|
|
103778
|
-
|
|
103779
|
-
|
|
103780
|
-
|
|
103781
|
-
|
|
103782
|
-
|
|
103783
|
-
|
|
103784
|
-
|
|
103785
|
-
|
|
103786
|
-
|
|
103787
|
-
|
|
103788
|
-
|
|
103789
|
-
|
|
103790
|
-
|
|
103791
|
-
|
|
103792
|
-
|
|
103793
|
-
|
|
103794
|
-
|
|
103795
|
-
|
|
103796
|
-
|
|
103797
|
-
|
|
103798
|
-
|
|
103799
|
-
|
|
103800
|
-
|
|
103801
|
-
|
|
103802
|
-
|
|
103803
|
-
|
|
103804
|
-
|
|
103805
|
-
|
|
103972
|
+
let streamResult;
|
|
103973
|
+
try {
|
|
103974
|
+
streamResult = this.llmClient.streamText({
|
|
103975
|
+
model: wrappedModel,
|
|
103976
|
+
messages: prependSystemMessage(systemPrompt, messages),
|
|
103977
|
+
tools: allTools,
|
|
103978
|
+
stopWhen: (result) => this.handleStop(result, maxSteps),
|
|
103979
|
+
temperature: 1,
|
|
103980
|
+
toolChoice: "auto",
|
|
103981
|
+
prepareStep: this.createPrepareStep(callbacks?.prepareStep, captchaSolver),
|
|
103982
|
+
onStepFinish: this.createStepHandler(state, callbacks?.onStepFinish),
|
|
103983
|
+
onError: (event) => {
|
|
103984
|
+
captchaSolver?.dispose();
|
|
103985
|
+
if (callbacks?.onError) {
|
|
103986
|
+
callbacks.onError(event);
|
|
103987
|
+
}
|
|
103988
|
+
handleError(event.error);
|
|
103989
|
+
},
|
|
103990
|
+
onChunk: callbacks?.onChunk,
|
|
103991
|
+
onFinish: (event) => {
|
|
103992
|
+
captchaSolver?.dispose();
|
|
103993
|
+
if (callbacks?.onFinish) {
|
|
103994
|
+
callbacks.onFinish(event);
|
|
103995
|
+
}
|
|
103996
|
+
const allMessages = [
|
|
103997
|
+
...messages,
|
|
103998
|
+
...event.response?.messages || []
|
|
103999
|
+
];
|
|
104000
|
+
this.ensureDone(state, wrappedModel, allMessages, options.instruction, options.output, this.logger).then((doneResult) => {
|
|
104001
|
+
const result = this.consolidateMetricsAndResult(startTime, state, doneResult.messages, event, maxSteps, doneResult.output);
|
|
104002
|
+
resolveResult(result);
|
|
104003
|
+
});
|
|
104004
|
+
},
|
|
104005
|
+
onAbort: (event) => {
|
|
104006
|
+
captchaSolver?.dispose();
|
|
104007
|
+
if (callbacks?.onAbort) {
|
|
104008
|
+
callbacks.onAbort(event);
|
|
104009
|
+
}
|
|
104010
|
+
const reason = options.signal?.reason ? String(options.signal.reason) : "Stream was aborted";
|
|
104011
|
+
rejectResult(new AgentAbortError(reason));
|
|
104012
|
+
},
|
|
104013
|
+
abortSignal: options.signal,
|
|
104014
|
+
providerOptions: {
|
|
104015
|
+
google: { mediaResolution: "MEDIA_RESOLUTION_HIGH" },
|
|
104016
|
+
openai: { store: false }
|
|
103806
104017
|
}
|
|
103807
|
-
|
|
103808
|
-
|
|
103809
|
-
|
|
103810
|
-
|
|
103811
|
-
|
|
103812
|
-
google: { mediaResolution: "MEDIA_RESOLUTION_HIGH" },
|
|
103813
|
-
openai: { store: false }
|
|
103814
|
-
}
|
|
103815
|
-
});
|
|
104018
|
+
});
|
|
104019
|
+
} catch (error46) {
|
|
104020
|
+
captchaSolver?.dispose();
|
|
104021
|
+
throw error46;
|
|
104022
|
+
}
|
|
103816
104023
|
const agentStreamResult = streamResult;
|
|
103817
104024
|
agentStreamResult.result = resultPromise;
|
|
103818
104025
|
return agentStreamResult;
|
|
@@ -107613,6 +107820,18 @@ var AgentClient = class {
|
|
|
107613
107820
|
this.userProvidedInstructions = userProvidedInstructions;
|
|
107614
107821
|
this.clientOptions = {};
|
|
107615
107822
|
}
|
|
107823
|
+
/** Optional hook called at the top of every step in the agent loop. */
|
|
107824
|
+
preStepHook;
|
|
107825
|
+
setPreStepHook(handler) {
|
|
107826
|
+
this.preStepHook = handler;
|
|
107827
|
+
}
|
|
107828
|
+
/**
|
|
107829
|
+
* Optional ephemeral context note that should be sent to the next model turn.
|
|
107830
|
+
* Clients that do not support this can ignore it.
|
|
107831
|
+
*/
|
|
107832
|
+
addContextNote(note) {
|
|
107833
|
+
void note;
|
|
107834
|
+
}
|
|
107616
107835
|
};
|
|
107617
107836
|
|
|
107618
107837
|
// ../core/dist/esm/lib/v3/agent/utils/imageCompression.js
|
|
@@ -107800,6 +108019,7 @@ var AnthropicCUAClient = class extends AgentClient {
|
|
|
107800
108019
|
let totalInferenceTime = 0;
|
|
107801
108020
|
try {
|
|
107802
108021
|
while (!completed && currentStep < maxSteps) {
|
|
108022
|
+
await this.preStepHook?.();
|
|
107803
108023
|
logger({
|
|
107804
108024
|
category: "agent",
|
|
107805
108025
|
message: `Executing step ${currentStep + 1}/${maxSteps}`,
|
|
@@ -114205,7 +114425,10 @@ var openai_default = OpenAI;
|
|
|
114205
114425
|
init_sdkErrors();
|
|
114206
114426
|
init_FlowLogger();
|
|
114207
114427
|
init_esm3();
|
|
114428
|
+
var CAPTCHA_PROCEED_TOOL = "captchaSolvedProceed";
|
|
114208
114429
|
var OpenAICUAClient = class extends AgentClient {
|
|
114430
|
+
pendingContextNotes = [];
|
|
114431
|
+
captchaSolvedToolActive = false;
|
|
114209
114432
|
apiKey;
|
|
114210
114433
|
organization;
|
|
114211
114434
|
baseURL;
|
|
@@ -114255,6 +114478,12 @@ var OpenAICUAClient = class extends AgentClient {
|
|
|
114255
114478
|
setSafetyConfirmationHandler(handler) {
|
|
114256
114479
|
this.safetyConfirmationHandler = handler;
|
|
114257
114480
|
}
|
|
114481
|
+
addContextNote(note) {
|
|
114482
|
+
this.pendingContextNotes.push(note);
|
|
114483
|
+
if (note.toLowerCase().includes("captcha")) {
|
|
114484
|
+
this.captchaSolvedToolActive = true;
|
|
114485
|
+
}
|
|
114486
|
+
}
|
|
114258
114487
|
/**
|
|
114259
114488
|
* Execute a task with the OpenAI CUA
|
|
114260
114489
|
* This is the main entry point for the agent
|
|
@@ -114277,6 +114506,7 @@ var OpenAICUAClient = class extends AgentClient {
|
|
|
114277
114506
|
let totalInferenceTime = 0;
|
|
114278
114507
|
try {
|
|
114279
114508
|
while (!completed && currentStep < maxSteps) {
|
|
114509
|
+
await this.preStepHook?.();
|
|
114280
114510
|
logger({
|
|
114281
114511
|
category: "agent",
|
|
114282
114512
|
message: `Executing step ${currentStep + 1}/${maxSteps}`,
|
|
@@ -114291,6 +114521,16 @@ var OpenAICUAClient = class extends AgentClient {
|
|
|
114291
114521
|
previousResponseId = result.responseId;
|
|
114292
114522
|
if (!completed) {
|
|
114293
114523
|
inputItems = result.nextInputItems;
|
|
114524
|
+
const contextNotes = this.drainContextNotes();
|
|
114525
|
+
if (contextNotes.length > 0) {
|
|
114526
|
+
inputItems = [
|
|
114527
|
+
...inputItems,
|
|
114528
|
+
...contextNotes.map((note) => ({
|
|
114529
|
+
role: "user",
|
|
114530
|
+
content: note
|
|
114531
|
+
}))
|
|
114532
|
+
];
|
|
114533
|
+
}
|
|
114294
114534
|
}
|
|
114295
114535
|
if (result.message) {
|
|
114296
114536
|
messageList.push(result.message);
|
|
@@ -114358,7 +114598,7 @@ var OpenAICUAClient = class extends AgentClient {
|
|
|
114358
114598
|
if (item.type === "computer_call" && this.isComputerCallItem(item)) {
|
|
114359
114599
|
logger({
|
|
114360
114600
|
category: "agent",
|
|
114361
|
-
message: `Found computer_call: ${item.action.type}, call_id: ${item.call_id}`,
|
|
114601
|
+
message: `Found computer_call: ${item.action.type}, payload: ${JSON.stringify(item.action)}, call_id: ${item.call_id}`,
|
|
114362
114602
|
level: 2
|
|
114363
114603
|
});
|
|
114364
114604
|
const action = this.convertComputerCallToAction(item);
|
|
@@ -114508,6 +114748,20 @@ var OpenAICUAClient = class extends AgentClient {
|
|
|
114508
114748
|
...customTools
|
|
114509
114749
|
];
|
|
114510
114750
|
}
|
|
114751
|
+
if (this.captchaSolvedToolActive) {
|
|
114752
|
+
requestParams.tools = [
|
|
114753
|
+
...requestParams.tools,
|
|
114754
|
+
{
|
|
114755
|
+
type: "function",
|
|
114756
|
+
name: CAPTCHA_PROCEED_TOOL,
|
|
114757
|
+
function: {
|
|
114758
|
+
name: CAPTCHA_PROCEED_TOOL,
|
|
114759
|
+
description: "The captcha on this page was solved automatically. Call this tool to confirm and continue with your task instead of asking the user for permission.",
|
|
114760
|
+
parameters: { type: "object", properties: {}, required: [] }
|
|
114761
|
+
}
|
|
114762
|
+
}
|
|
114763
|
+
];
|
|
114764
|
+
}
|
|
114511
114765
|
if (previousResponseId) {
|
|
114512
114766
|
requestParams.previous_response_id = previousResponseId;
|
|
114513
114767
|
}
|
|
@@ -114634,6 +114888,15 @@ var OpenAICUAClient = class extends AgentClient {
|
|
|
114634
114888
|
}
|
|
114635
114889
|
}
|
|
114636
114890
|
} else if (item.type === "function_call" && this.isFunctionCallItem(item)) {
|
|
114891
|
+
if (item.name === CAPTCHA_PROCEED_TOOL) {
|
|
114892
|
+
this.captchaSolvedToolActive = false;
|
|
114893
|
+
nextInputItems.push({
|
|
114894
|
+
type: "function_call_output",
|
|
114895
|
+
call_id: item.call_id,
|
|
114896
|
+
output: "Confirmed. The captcha is solved. Continue completing the original task autonomously without asking for further confirmation."
|
|
114897
|
+
});
|
|
114898
|
+
continue;
|
|
114899
|
+
}
|
|
114637
114900
|
try {
|
|
114638
114901
|
const action = this.convertFunctionCallToAction(item);
|
|
114639
114902
|
if (action && this.actionHandler) {
|
|
@@ -114704,6 +114967,14 @@ var OpenAICUAClient = class extends AgentClient {
|
|
|
114704
114967
|
// Spread all properties from the action
|
|
114705
114968
|
};
|
|
114706
114969
|
}
|
|
114970
|
+
drainContextNotes() {
|
|
114971
|
+
if (this.pendingContextNotes.length === 0) {
|
|
114972
|
+
return [];
|
|
114973
|
+
}
|
|
114974
|
+
const notes = [...this.pendingContextNotes];
|
|
114975
|
+
this.pendingContextNotes = [];
|
|
114976
|
+
return notes;
|
|
114977
|
+
}
|
|
114707
114978
|
convertFunctionCallToAction(call) {
|
|
114708
114979
|
try {
|
|
114709
114980
|
const args = JSON.parse(call.arguments);
|
|
@@ -115061,6 +115332,7 @@ var GoogleCUAClient = class extends AgentClient {
|
|
|
115061
115332
|
let totalInferenceTime = 0;
|
|
115062
115333
|
try {
|
|
115063
115334
|
while (!completed && currentStep < maxSteps) {
|
|
115335
|
+
await this.preStepHook?.();
|
|
115064
115336
|
logger({
|
|
115065
115337
|
category: "agent",
|
|
115066
115338
|
message: `Executing step ${currentStep + 1}/${maxSteps}`,
|
|
@@ -116228,6 +116500,7 @@ ${textPrompt}`;
|
|
|
116228
116500
|
this.actionHistory = [];
|
|
116229
116501
|
try {
|
|
116230
116502
|
while (!completed && currentStep < maxSteps) {
|
|
116503
|
+
await this.preStepHook?.();
|
|
116231
116504
|
logger({
|
|
116232
116505
|
category: "agent",
|
|
116233
116506
|
message: `Executing step ${currentStep + 1}/${maxSteps}`,
|
|
@@ -116355,6 +116628,9 @@ var V3CuaAgentHandler = class {
|
|
|
116355
116628
|
agentClient;
|
|
116356
116629
|
options;
|
|
116357
116630
|
highlightCursor;
|
|
116631
|
+
captchaSolver = null;
|
|
116632
|
+
captchaClickGuardRemaining = 0;
|
|
116633
|
+
currentInstruction = "";
|
|
116358
116634
|
constructor(v32, logger, options, tools) {
|
|
116359
116635
|
this.v3 = v32;
|
|
116360
116636
|
this.logger = logger;
|
|
@@ -116383,7 +116659,28 @@ var V3CuaAgentHandler = class {
|
|
|
116383
116659
|
});
|
|
116384
116660
|
this.agentClient.setActionHandler(async (action) => {
|
|
116385
116661
|
this.ensureNotClosed();
|
|
116662
|
+
if (this.captchaSolver) {
|
|
116663
|
+
if (this.captchaSolver.isSolving()) {
|
|
116664
|
+
this.logger({
|
|
116665
|
+
category: "agent",
|
|
116666
|
+
message: "Captcha detected \u2014 waiting for Browserbase to solve it before continuing",
|
|
116667
|
+
level: 1
|
|
116668
|
+
});
|
|
116669
|
+
}
|
|
116670
|
+
await this.captchaSolver.waitIfSolving();
|
|
116671
|
+
this.handleCaptchaSolveResult(this.captchaSolver.consumeSolveResult());
|
|
116672
|
+
}
|
|
116386
116673
|
action.pageUrl = (await this.v3.context.awaitActivePage()).url();
|
|
116674
|
+
if (await this.shouldSkipSolvedCaptchaInteraction(action)) {
|
|
116675
|
+
this.captchaClickGuardRemaining = Math.max(0, this.captchaClickGuardRemaining - 1);
|
|
116676
|
+
this.agentClient.addContextNote(`The captcha has already been solved automatically. Do not click the captcha checkbox, widget, or challenge again. Continue with the original task outside the captcha area. Original task: ${this.currentInstruction}`);
|
|
116677
|
+
this.logger({
|
|
116678
|
+
category: "agent",
|
|
116679
|
+
message: "Skipped click on solved captcha widget \u2014 injected follow-up guidance",
|
|
116680
|
+
level: 1
|
|
116681
|
+
});
|
|
116682
|
+
return;
|
|
116683
|
+
}
|
|
116387
116684
|
const defaultDelay = 500;
|
|
116388
116685
|
const waitBetween = this.options.clientOptions?.waitBetweenActions || defaultDelay;
|
|
116389
116686
|
try {
|
|
@@ -116439,6 +116736,7 @@ var V3CuaAgentHandler = class {
|
|
|
116439
116736
|
const options = typeof optionsOrInstruction === "string" ? { instruction: optionsOrInstruction } : optionsOrInstruction;
|
|
116440
116737
|
this.setSafetyConfirmationHandler(options.callbacks?.onSafetyConfirmation);
|
|
116441
116738
|
this.highlightCursor = options.highlightCursor !== false;
|
|
116739
|
+
this.currentInstruction = options.instruction;
|
|
116442
116740
|
const page = await this.v3.context.awaitActivePage();
|
|
116443
116741
|
const currentUrl = page.url();
|
|
116444
116742
|
if (!currentUrl || currentUrl === "about:blank") {
|
|
@@ -116449,6 +116747,21 @@ var V3CuaAgentHandler = class {
|
|
|
116449
116747
|
});
|
|
116450
116748
|
await page.goto("https://www.google.com", { waitUntil: "load" });
|
|
116451
116749
|
}
|
|
116750
|
+
if (this.v3.isCaptchaAutoSolveEnabled) {
|
|
116751
|
+
this.captchaSolver = new CaptchaSolver();
|
|
116752
|
+
this.captchaSolver.init(() => this.v3.context.awaitActivePage());
|
|
116753
|
+
this.agentClient.setPreStepHook(async () => {
|
|
116754
|
+
if (this.captchaSolver?.isSolving()) {
|
|
116755
|
+
this.logger({
|
|
116756
|
+
category: "agent",
|
|
116757
|
+
message: "Captcha detected \u2014 waiting for Browserbase to solve it before continuing",
|
|
116758
|
+
level: 1
|
|
116759
|
+
});
|
|
116760
|
+
}
|
|
116761
|
+
await this.captchaSolver?.waitIfSolving();
|
|
116762
|
+
this.handleCaptchaSolveResult(this.captchaSolver?.consumeSolveResult());
|
|
116763
|
+
});
|
|
116764
|
+
}
|
|
116452
116765
|
if (this.highlightCursor) {
|
|
116453
116766
|
try {
|
|
116454
116767
|
await this.injectCursor();
|
|
@@ -116462,7 +116775,13 @@ var V3CuaAgentHandler = class {
|
|
|
116462
116775
|
}
|
|
116463
116776
|
}
|
|
116464
116777
|
const start = Date.now();
|
|
116465
|
-
|
|
116778
|
+
let result;
|
|
116779
|
+
try {
|
|
116780
|
+
result = await this.agent.execute({ options, logger: this.logger });
|
|
116781
|
+
} finally {
|
|
116782
|
+
this.captchaSolver?.dispose();
|
|
116783
|
+
this.captchaSolver = null;
|
|
116784
|
+
}
|
|
116466
116785
|
const inferenceTimeMs = Date.now() - start;
|
|
116467
116786
|
if (result.usage) {
|
|
116468
116787
|
this.v3.updateMetrics(V3FunctionName.AGENT, result.usage.input_tokens, result.usage.output_tokens, result.usage.reasoning_tokens ?? 0, result.usage.cached_input_tokens ?? 0, inferenceTimeMs);
|
|
@@ -116797,6 +117116,78 @@ var V3CuaAgentHandler = class {
|
|
|
116797
117116
|
return null;
|
|
116798
117117
|
}
|
|
116799
117118
|
}
|
|
117119
|
+
handleCaptchaSolveResult(result) {
|
|
117120
|
+
if (!result)
|
|
117121
|
+
return;
|
|
117122
|
+
if (result.solved) {
|
|
117123
|
+
this.captchaClickGuardRemaining = 3;
|
|
117124
|
+
this.agentClient.addContextNote(CAPTCHA_SOLVED_MSG);
|
|
117125
|
+
this.logger({
|
|
117126
|
+
category: "agent",
|
|
117127
|
+
message: "Captcha solved \u2014 continuing with task",
|
|
117128
|
+
level: 1
|
|
117129
|
+
});
|
|
117130
|
+
}
|
|
117131
|
+
if (result.errored) {
|
|
117132
|
+
this.captchaClickGuardRemaining = 0;
|
|
117133
|
+
this.agentClient.addContextNote(CAPTCHA_ERRORED_MSG);
|
|
117134
|
+
this.logger({
|
|
117135
|
+
category: "agent",
|
|
117136
|
+
message: "Captcha solver failed or errored",
|
|
117137
|
+
level: 1
|
|
117138
|
+
});
|
|
117139
|
+
}
|
|
117140
|
+
}
|
|
117141
|
+
async shouldSkipSolvedCaptchaInteraction(action) {
|
|
117142
|
+
if (this.captchaClickGuardRemaining <= 0) {
|
|
117143
|
+
return false;
|
|
117144
|
+
}
|
|
117145
|
+
if (action.type !== "click") {
|
|
117146
|
+
return false;
|
|
117147
|
+
}
|
|
117148
|
+
const x3 = action.x;
|
|
117149
|
+
const y2 = action.y;
|
|
117150
|
+
if (typeof x3 !== "number" || typeof y2 !== "number") {
|
|
117151
|
+
return false;
|
|
117152
|
+
}
|
|
117153
|
+
try {
|
|
117154
|
+
const page = await this.v3.context.awaitActivePage();
|
|
117155
|
+
const boxes = await page.evaluate(() => {
|
|
117156
|
+
const selectors = [
|
|
117157
|
+
'iframe[title*="reCAPTCHA"]',
|
|
117158
|
+
'iframe[src*="recaptcha"]',
|
|
117159
|
+
'iframe[src*="hcaptcha"]',
|
|
117160
|
+
'iframe[src*="turnstile"]',
|
|
117161
|
+
".g-recaptcha",
|
|
117162
|
+
"[data-sitekey]",
|
|
117163
|
+
'[class*="captcha"]',
|
|
117164
|
+
'[id*="captcha"]'
|
|
117165
|
+
];
|
|
117166
|
+
const seen = /* @__PURE__ */ new Set();
|
|
117167
|
+
const bounds = [];
|
|
117168
|
+
for (const selector of selectors) {
|
|
117169
|
+
for (const element of document.querySelectorAll(selector)) {
|
|
117170
|
+
if (seen.has(element))
|
|
117171
|
+
continue;
|
|
117172
|
+
seen.add(element);
|
|
117173
|
+
const rect = element.getBoundingClientRect();
|
|
117174
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
117175
|
+
continue;
|
|
117176
|
+
bounds.push({
|
|
117177
|
+
left: rect.left,
|
|
117178
|
+
top: rect.top,
|
|
117179
|
+
right: rect.right,
|
|
117180
|
+
bottom: rect.bottom
|
|
117181
|
+
});
|
|
117182
|
+
}
|
|
117183
|
+
}
|
|
117184
|
+
return bounds;
|
|
117185
|
+
});
|
|
117186
|
+
return boxes.some((box) => x3 >= box.left && x3 <= box.right && y2 >= box.top && y2 <= box.bottom);
|
|
117187
|
+
} catch {
|
|
117188
|
+
return false;
|
|
117189
|
+
}
|
|
117190
|
+
}
|
|
116800
117191
|
async injectCursor() {
|
|
116801
117192
|
try {
|
|
116802
117193
|
const page = await this.v3.context.awaitActivePage();
|
|
@@ -162401,6 +162792,13 @@ var V3 = (() => {
|
|
|
162401
162792
|
get isBrowserbase() {
|
|
162402
162793
|
return this.state.kind === "BROWSERBASE";
|
|
162403
162794
|
}
|
|
162795
|
+
/**
|
|
162796
|
+
* Returns true if captcha auto-solving is enabled on Browserbase.
|
|
162797
|
+
* Defaults to true when not explicitly set to false.
|
|
162798
|
+
*/
|
|
162799
|
+
get isCaptchaAutoSolveEnabled() {
|
|
162800
|
+
return this.isBrowserbase && this.opts.browserbaseSessionCreateParams?.browserSettings?.solveCaptchas !== false;
|
|
162801
|
+
}
|
|
162404
162802
|
/**
|
|
162405
162803
|
* Returns true if advancedStealth is enabled in Browserbase settings.
|
|
162406
162804
|
*/
|
|
@@ -163421,7 +163819,7 @@ var V3 = (() => {
|
|
|
163421
163819
|
const tools = options?.integrations ? await resolveTools(options.integrations, options.tools) : options?.tools ?? {};
|
|
163422
163820
|
const agentLlmClient = options?.model ? this.resolveLlmClient(options.model) : this.llmClient;
|
|
163423
163821
|
const resolvedExecutionModel = options?.executionModel ?? options?.model;
|
|
163424
|
-
const handler = new V3AgentHandler(this, this.logger, agentLlmClient, resolvedExecutionModel, options?.systemPrompt, tools, options?.mode);
|
|
163822
|
+
const handler = new V3AgentHandler(this, this.logger, agentLlmClient, resolvedExecutionModel, options?.systemPrompt, tools, options?.mode, this.isCaptchaAutoSolveEnabled);
|
|
163425
163823
|
const resolvedOptions = typeof instructionOrOptions === "string" ? {
|
|
163426
163824
|
instruction: instructionOrOptions,
|
|
163427
163825
|
toolTimeout: DEFAULT_AGENT_TOOL_TIMEOUT_MS
|
|
@@ -163510,8 +163908,8 @@ var V3 = (() => {
|
|
|
163510
163908
|
const handler = new V3CuaAgentHandler(this, this.logger, {
|
|
163511
163909
|
modelName,
|
|
163512
163910
|
clientOptions,
|
|
163513
|
-
userProvidedInstructions: options.systemPrompt ?? `You are a helpful assistant that can use a web browser.
|
|
163514
|
-
Do not ask follow up questions, the user will trust your judgement.`
|
|
163911
|
+
userProvidedInstructions: (options.systemPrompt ?? `You are a helpful assistant that can use a web browser.
|
|
163912
|
+
Do not ask follow up questions, the user will trust your judgement.`) + (this.isCaptchaAutoSolveEnabled ? CAPTCHA_CUA_SYSTEM_PROMPT_NOTE : "")
|
|
163515
163913
|
}, tools);
|
|
163516
163914
|
const resolvedOptions = typeof instructionOrOptions === "string" ? {
|
|
163517
163915
|
instruction: instructionOrOptions,
|
|
@@ -164115,7 +164513,7 @@ var import_child_process4 = require("child_process");
|
|
|
164115
164513
|
var readline = __toESM(require("readline"));
|
|
164116
164514
|
|
|
164117
164515
|
// package.json
|
|
164118
|
-
var version3 = "0.2.0
|
|
164516
|
+
var version3 = "0.2.0";
|
|
164119
164517
|
|
|
164120
164518
|
// src/index.ts
|
|
164121
164519
|
var program = new import_commander.Command();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserbasehq/browse-cli",
|
|
3
|
-
"version": "0.2.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Browser automation CLI for AI agents, built on Stagehand",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"pino": "^9.6.0",
|
|
48
48
|
"pino-pretty": "^13.0.0",
|
|
49
49
|
"ws": "^8.18.0",
|
|
50
|
-
"@browserbasehq/stagehand": "3.2.0
|
|
50
|
+
"@browserbasehq/stagehand": "3.2.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "^20.11.30",
|