[feat]: auto-pause agent during Browserbase captcha solving#1752
[feat]: auto-pause agent during Browserbase captcha solving#1752
Conversation
🦋 Changeset detectedLatest commit: 54d9bf4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Greptile SummaryThis PR adds Browserbase captcha solver awareness to all Stagehand agent execution paths (DOM/hybrid, Anthropic CUA, Google CUA, Microsoft CUA, OpenAI CUA). A new shared Key changes:
Issues found:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Agent as Agent Loop
participant PSH as prepareStepHandler
participant CS as CaptchaSolver
participant BB as Browserbase (console)
participant AH as actionHandler
participant Guard as CaptchaClickGuard
Agent->>PSH: call prepareStepHandler()
PSH->>CS: waitIfSolving()
BB-->>CS: browserbase-solving-started
Note over CS: solving = true
CS-->>PSH: (blocks — waitPromise pending)
BB-->>CS: browserbase-solving-finished
Note over CS: solving = false, settled
CS-->>PSH: (resolves)
PSH->>CS: consumeSolveResult()
CS-->>PSH: {solved: true}
PSH->>Agent: addContextNote("captcha solved…")
Agent->>Agent: executeStep (model call)
Agent->>AH: action {type:"click", x, y}
AH->>CS: waitIfSolving() — returns immediately
AH->>Guard: shouldSkipSolvedCaptchaInteraction?
Guard->>Guard: page.evaluate(captcha bounding boxes)
alt click inside captcha widget
Guard-->>AH: true — skip click, inject note
else click outside captcha
Guard-->>AH: false — execute action
end
Note over Agent: next loop iteration includes captcha note in inputItems
Last reviewed commit: 8b70cd3 |
There was a problem hiding this comment.
2 issues found across 5 files
Confidence score: 3/5
- Concurrent
waitIfSolving()calls inpackages/core/lib/v3/agent/utils/captchaSolver.tscan orphan earlier waiters due to a singleresolveWaitslot, causing hangs until timeout for some callers. packages/core/lib/v3/handlers/v3AgentHandler.tsmay leakcaptchaSolverifstreamText()throws before callbacks run, since disposal only happens inside stream handlers instream().- These are concrete runtime risks (waiting hangs and resource leaks), so there is some user-impacting risk despite the rest of the change looking contained.
- Pay close attention to
packages/core/lib/v3/agent/utils/captchaSolver.tsandpackages/core/lib/v3/handlers/v3AgentHandler.ts- concurrency waiting and disposal paths.
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/agent/utils/captchaSolver.ts">
<violation number="1" location="packages/core/lib/v3/agent/utils/captchaSolver.ts:72">
P1: Bug: `resolveWait` is a single slot, so concurrent `waitIfSolving()` calls orphan earlier waiters. The second call overwrites the resolver, leaving the first caller's promise unresolved until its 90s timeout. Consider sharing a single deferred promise across all waiters, e.g.:
```ts
private waitPromise: Promise<void> | null = null;
waitIfSolving(): Promise<void> {
if (!this.solving) return Promise.resolve();
if (!this.waitPromise) {
this.waitPromise = new Promise<void>((resolve) => {
const timer = setTimeout(() => { /* ... */ }, SOLVE_TIMEOUT_MS);
this.resolveWait = () => { clearTimeout(timer); resolve(); this.waitPromise = null; };
});
}
return this.waitPromise;
}
```</violation>
</file>
<file name="packages/core/lib/v3/handlers/v3AgentHandler.ts">
<violation number="1" location="packages/core/lib/v3/handlers/v3AgentHandler.ts:293">
P2: Resource leak: if `streamText()` throws synchronously, `captchaSolver` is never disposed. The `execute()` method properly wraps this in a `try/finally`, but `stream()` only disposes inside stream callbacks (`onError`/`onFinish`/`onAbort`), which won't fire if `streamText` itself throws. Wrap the `streamText` call and subsequent setup in a try/catch that calls `captchaSolver?.dispose()` on failure.</violation>
</file>
Architecture diagram
sequenceDiagram
participant User as Client/V3
participant Handler as Agent Handler (DOM/CUA)
participant Solver as NEW: CaptchaSolver
participant Page as Browser Page (Browserbase)
participant LLM as LLM Provider
User->>Handler: execute(instruction)
opt NEW: isBrowserbase && captchaSolverEnabled
Handler->>Solver: attach(page)
Solver->>Page: on("console", listener)
end
Note over Handler, LLM: CHANGED: System prompt tells LLM to ignore captchas
loop Agent Step Loop
Page-->>Solver: NEW: console("browserbase-solving-started")
Note right of Solver: solving = true
Handler->>Handler: prepareStep() / actionHandler()
Handler->>Solver: NEW: waitIfSolving()
alt Solver is active
Note over Solver: NEW: Block execution (max 90s timeout)
alt Solve Success
Page-->>Solver: NEW: console("browserbase-solving-finished")
Solver-->>Handler: Resolve (Resume)
else Solve Error
Page-->>Solver: NEW: console("browserbase-solving-errored")
Solver-->>Handler: Resolve (Resume with error flag)
Handler->>Handler: Log solver error & resetError()
else Timeout (90s)
Solver->>Solver: Internal timeout
Solver-->>Handler: Resolve (Resume)
end
else Solver is idle
Solver-->>Handler: Resolve immediately
end
Handler->>LLM: Request next action
LLM-->>Handler: Action response
Handler->>Page: Execute action
end
Handler->>Solver: NEW: dispose()
Solver->>Page: off("console", listener)
Handler-->>User: Result
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dc13b5a25a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/agent/utils/captchaSolver.ts">
<violation number="1" location="packages/core/lib/v3/agent/utils/captchaSolver.ts:54">
P1: Stale `solving` state after page change: when `ensureAttached()` detects a page switch and detaches the old listener, `this.solving` is not reset. If a solve was in progress on the old page, the agent will block for up to 90 seconds waiting for a finish/error event that can never arrive (since the old page's listener was removed). Reset `solving` and settle pending waiters when detaching from a changed page.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/agent/utils/captchaSolver.ts">
<violation number="1" location="packages/core/lib/v3/agent/utils/captchaSolver.ts:148">
P2: When `detachListener()` resolves waiting callers via `settle()`, it should also set `_erroredSinceLastConsume = true` to signal the solve was interrupted. Without this, consumers of `consumeSolveResult()` get `{solved: false, errored: false}` and silently skip the error-handling/notification path. The timeout codepath correctly sets `_erroredSinceLastConsume = true` before `settle()` — this should be consistent.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/agent/prompts/agentSystemPrompt.ts">
<violation number="1" location="packages/core/lib/v3/agent/prompts/agentSystemPrompt.ts:200">
P1: Existing tests in `agent-hybrid-mode.spec.ts` pass `isBrowserbase: true` (without `solveCaptchas`) and expect captcha content in the prompt. Since the condition now depends on `solveCaptchas`, these tests will break. The tests need to be updated to pass `solveCaptchas: true` and the expected substring should match the new prompt text (e.g., `"automatically detected and solved"` instead of `"automatically be solved"`).</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Automatically pause agent execution when Browserbase's captcha solver is active. Listens for browserbase-solving-started/finished/errored console messages and blocks the agent's prepareStep (DOM/hybrid) or action handler (CUA) until solving completes, errors, or hits a 90s timeout. Also updates the system prompt to tell the agent not to interact with captchas since they are handled transparently. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix race condition: concurrent waitIfSolving() callers now share a single deferred promise so no waiter is orphaned. - Extract settle() helper for consistent timeout/resolve/cleanup paths. - Fix stream() disposal: wrap streamText() in try/catch so captchaSolver is disposed if streamText throws synchronously. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aptchas - CaptchaSolver now accepts a page-provider callback and re-attaches the console listener whenever the active page changes (popups, new tabs). This ensures captcha events are observed on whichever page is currently active. - System prompt captcha messaging is now gated on captchaSolverEnabled (solveCaptchas !== false) rather than just isBrowserbase, so sessions with solveCaptchas: false don't incorrectly tell the agent that captchas are auto-solved. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test was passing isBrowserbase: true but not captchaSolverEnabled, so the auto-solve captcha messaging wasn't included in the prompt. Updated test to pass captchaSolverEnabled: true and match new wording. Added a new test case for isBrowserbase: true with captchaSolverEnabled: false to verify the solver-off path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevents the agent from blocking up to 90s waiting for solve events that can never arrive from a detached page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… enabled Only includes captcha roadblocks section when isCaptchaSolverEnabled is true (Browserbase + solveCaptchas !== false). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace isBrowserbase with solveCaptchas in system prompt interface and update existing tests to match. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add prepareStepHandler hook to AgentClient base class, called at the top of every CUA step loop iteration. The CUA handler uses this to block before each LLM call when a captcha solve is in progress, rather than only blocking at action execution time. Also appends captcha instructions to the CUA system prompt when solveCaptchas is enabled. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
329f8ff to
90e0ed9
Compare
| private settle(): void { | ||
| if (this.waitTimer) { | ||
| clearTimeout(this.waitTimer); | ||
| this.waitTimer = null; | ||
| } | ||
| if (this.resolveWait) { | ||
| const resolve = this.resolveWait; | ||
| this.resolveWait = null; | ||
| this.waitPromise = null; | ||
| resolve(); | ||
| } | ||
| } |
There was a problem hiding this comment.
waitPromise cleared before resolve() — add a comment explaining the ordering
this.waitPromise = null is set before resolve() is called. This is intentional (allows new callers to create a fresh waitPromise once resolution is signalled) but can confuse readers into thinking it might orphan existing awaiters. A brief comment would clarify the intent:
| private settle(): void { | |
| if (this.waitTimer) { | |
| clearTimeout(this.waitTimer); | |
| this.waitTimer = null; | |
| } | |
| if (this.resolveWait) { | |
| const resolve = this.resolveWait; | |
| this.resolveWait = null; | |
| this.waitPromise = null; | |
| resolve(); | |
| } | |
| } | |
| /** Resolve the shared wait promise and clear the timeout. */ | |
| private settle(): void { | |
| if (this.waitTimer) { | |
| clearTimeout(this.waitTimer); | |
| this.waitTimer = null; | |
| } | |
| if (this.resolveWait) { | |
| const resolve = this.resolveWait; | |
| this.resolveWait = null; | |
| // Clear waitPromise first so new callers can create a fresh promise | |
| // once this one is resolved; existing awaiters still hold a reference | |
| // to the old promise and will be unblocked by resolve(). | |
| this.waitPromise = null; | |
| resolve(); | |
| } | |
| } |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
…olves as errored - CaptchaSolver.detachListener() now sets _erroredSinceLastConsume when a solve was in progress, consistent with the timeout path - OpenAICUAClient shouldContinueWithoutConfirmation is now gated behind a captcha context note check, so legitimate model confirmations are no longer silently bypassed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pirate
left a comment
There was a problem hiding this comment.
Good feature, agents definitely waste a lot of tokens and race with auto-handling logic implemented by extensions / browser providers currently.
Just a few comments:
-
naming of the fields and helpers like
solveCaptchasandsetPrepareStepHandler, what aboutcaptchasWillAutoSolveandattachPreStagehandStepHookor something? -
duplication of logic / system prompts / LLM steering code. would it be possible to centralize the system prompt text / hook logic for captchas in one place and then import it in
agentSystemPrompt.ts,OpenAICUAClient.ts,v3AgentHandler.ts,v3CuaAgentHandler.ts, andv3.ts -
can you add a live integration test that uses a real url with a captcha like
https://2captcha.com/demo/recaptcha-v2(or ask the stealth team for some good test urls) and assert it gets auto-solved by bb prod browser and that the agent doesn't race/conflict with it
…n test Address PR feedback from pirate: - Rename solveCaptchas → captchasAutoSolve, isCaptchaSolverEnabled → isCaptchaAutoSolveEnabled, setPrepareStepHandler → setPreStepHook to better reflect intent - Centralize captcha notification strings (CAPTCHA_SOLVED_MSG, CAPTCHA_ERRORED_MSG, CAPTCHA_SYSTEM_PROMPT_NOTE, CAPTCHA_CUA_SYSTEM_PROMPT_NOTE) in captchaSolver.ts - Add live integration test against 2captcha.com reCAPTCHA v2 demo (Browserbase-only) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verify the auto-pause mechanism fires (BB emits solving-started events) and the agent doesn't click the reCAPTCHA checkbox itself. Don't assert on BB successfully solving the captcha since that depends on BB infra. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cloudflare Turnstile: asserts "Captcha is passed successfully!" on 2captcha.com demo - reCAPTCHA v2: asserts "Verification Success... Hooray!" on Google's official demo (same URL the stealth team uses in their test suite) Both tests verify the full e2e flow: BB detects captcha → auto-solves it → agent waits via CaptchaSolver mechanism → proceeds without racing the solver. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion Instead of parsing English phrases to detect when the OpenAI CUA model asks for confirmation after a captcha solve, expose a captchaSolvedProceed tool that the model calls to confirm it should continue. This is more reliable (structured tool call vs free-text matching), works in any language, and has no false-positive risk. Deletes isFollowUpQuestion() and shouldContinueWithoutConfirmation logic. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep captcha solver integration (try/catch, dispose in callbacks, captchaSolver param in createPrepareStep) while adopting main's updated providerOptions format and other changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @browserbasehq/browse-cli@0.2.0 ### Minor Changes - [#1816](#1816) [`687d54a`](687d54a) Thanks [@shrey150](https://github.com/shrey150)! - Add `--context-id` and `--persist` flags to `browse open` for loading and persisting Browserbase Contexts across sessions - [#1793](#1793) [`e38c13b`](e38c13b) Thanks [@shrey150](https://github.com/shrey150)! - Initial release of browse CLI - browser automation for AI agents ### Patch Changes - [#1806](#1806) [`f8c7738`](f8c7738) Thanks [@shrey150](https://github.com/shrey150)! - Fix `browse env` showing stale mode after `browse env remote` - Updated dependencies \[[`505e8c6`](505e8c6), [`2f43ffa`](2f43ffa), [`63ee247`](63ee247), [`7dc35f5`](7dc35f5), [`335cf47`](335cf47), [`6ba0a1d`](6ba0a1d), [`4ff3bb8`](4ff3bb8), [`c27054b`](c27054b), [`2abf5b9`](2abf5b9), [`7817fcc`](7817fcc), [`7390508`](7390508), [`611f43a`](611f43a), [`521a10e`](521a10e), [`2402a3c`](2402a3c)]: - @browserbasehq/stagehand@3.2.0 ## @browserbasehq/stagehand@3.2.0 ### Minor Changes - [#1779](#1779) [`2f43ffa`](2f43ffa) Thanks [@shrey150](https://github.com/shrey150)! - feat: add `cdpHeaders` option to `localBrowserLaunchOptions` for passing custom HTTP headers when connecting to an existing browser via CDP URL - [#1834](#1834) [`63ee247`](63ee247) Thanks [@tkattkat](https://github.com/tkattkat)! - Update stagehand agents search tool - [#1774](#1774) [`521a10e`](521a10e) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add new page.setExtraHTTPHeaders() method ### Patch Changes - [#1759](#1759) [`505e8c6`](505e8c6) Thanks [@shrey150](https://github.com/shrey150)! - Add bedrock to the provider enum in model configuration schemas and regenerate OpenAPI spec. - [#1814](#1814) [`7dc35f5`](7dc35f5) Thanks [@tkattkat](https://github.com/tkattkat)! - Change usage of openai provider in agent to default to store:false - [#1846](#1846) [`335cf47`](335cf47) Thanks [@aq17](https://github.com/aq17)! - Fix streaming finished event being silently dropped. The final SSE event containing the result payload (success status, message, actions, usage, and messages) was previously discarded instead of being yielded to the caller. - [#1764](#1764) [`6ba0a1d`](6ba0a1d) Thanks [@shrey150](https://github.com/shrey150)! - Expose `headers` in `GoogleVertexProviderSettings` so model configs can pass custom provider headers (for example `X-Goog-Priority`) without TypeScript errors. - [#1847](#1847) [`4ff3bb8`](4ff3bb8) Thanks [@miguelg719](https://github.com/miguelg719)! - Enable FlowLogger on BROWSERBASE_FLOW_LOGS=1 - [#1752](#1752) [`c27054b`](c27054b) Thanks [@derekmeegan](https://github.com/derekmeegan)! - fix: pause Browserbase agents while captcha solving is active and improve CUA recovery after the solve completes - [#1800](#1800) [`2abf5b9`](2abf5b9) Thanks [@shrey150](https://github.com/shrey150)! - Make projectId optional for Browserbase sessions — only BROWSERBASE_API_KEY is required - [#1766](#1766) [`7817fcc`](7817fcc) Thanks [@tkattkat](https://github.com/tkattkat)! - Add configurable timeout to tools in agent - [#1749](#1749) [`7390508`](7390508) Thanks [@pirate](https://github.com/pirate)! - When connecting to a browser session that has zero open tabs, Stagehand now automatically creates an initial `about:blank` tab so the connection can continue. - [#1761](#1761) [`611f43a`](611f43a) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix issue where handlePossibleNavigation was producing unnecessary error logs on clicks that trigger page close - [#1817](#1817) [`2402a3c`](2402a3c) Thanks [@tkattkat](https://github.com/tkattkat)! - Add support for passing custom headers in clientOptions ## @browserbasehq/stagehand-evals@1.1.9 ### Patch Changes - Updated dependencies \[[`505e8c6`](505e8c6), [`2f43ffa`](2f43ffa), [`63ee247`](63ee247), [`7dc35f5`](7dc35f5), [`335cf47`](335cf47), [`6ba0a1d`](6ba0a1d), [`4ff3bb8`](4ff3bb8), [`c27054b`](c27054b), [`2abf5b9`](2abf5b9), [`7817fcc`](7817fcc), [`7390508`](7390508), [`611f43a`](611f43a), [`521a10e`](521a10e), [`2402a3c`](2402a3c)]: - @browserbasehq/stagehand@3.2.0 ## @browserbasehq/stagehand-server-v3@3.6.1 ### Patch Changes - [#1759](#1759) [`505e8c6`](505e8c6) Thanks [@shrey150](https://github.com/shrey150)! - Add bedrock to the provider enum in model configuration schemas and regenerate OpenAPI spec. - Updated dependencies \[[`505e8c6`](505e8c6), [`2f43ffa`](2f43ffa), [`63ee247`](63ee247), [`7dc35f5`](7dc35f5), [`335cf47`](335cf47), [`6ba0a1d`](6ba0a1d), [`4ff3bb8`](4ff3bb8), [`c27054b`](c27054b), [`2abf5b9`](2abf5b9), [`7817fcc`](7817fcc), [`7390508`](7390508), [`611f43a`](611f43a), [`521a10e`](521a10e), [`2402a3c`](2402a3c)]: - @browserbasehq/stagehand@3.2.0 ## @browserbasehq/stagehand-server-v4@3.6.1 ### Patch Changes - Updated dependencies \[[`505e8c6`](505e8c6), [`2f43ffa`](2f43ffa), [`63ee247`](63ee247), [`7dc35f5`](7dc35f5), [`335cf47`](335cf47), [`6ba0a1d`](6ba0a1d), [`4ff3bb8`](4ff3bb8), [`c27054b`](c27054b), [`2abf5b9`](2abf5b9), [`7817fcc`](7817fcc), [`7390508`](7390508), [`611f43a`](611f43a), [`521a10e`](521a10e), [`2402a3c`](2402a3c)]: - @browserbasehq/stagehand@3.2.0 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
why
Browserbase can solve captchas asynchronously, but agents were still trying to interact with the page while the solver was active. That led to CUA and DOM/hybrid flows clicking solved captcha widgets again, pausing on confirmation questions, or resuming with stale assumptions instead of continuing the original task cleanly.
This PR pauses agent execution while Browserbase's captcha solver is active and hardens the post-solve resume path so the agent keeps working on the original task after Browserbase finishes.
what changed
CaptchaSolverutility that listens for Browserbasebrowserbase-solving-started/finished/erroredconsole events, supports concurrent waiters, and disposes listeners cleanlyprepareStepexecution and CUAprepareStep/ action execution while Browserbase is solving a captchabrowserSettings.solveCaptchas: falsetest plan
pnpm --filter @browserbasehq/stagehand lintpnpm --filter @browserbasehq/stagehand build:esmcd packages/core && pnpm exec vitest run --config vitest.esm.config.mjs dist/esm/tests/unit/openai-cua-client.test.js dist/esm/tests/unit/captcha-solver.test.js dist/esm/tests/unit/agent-captcha-hooks.test.js dist/esm/tests/unit/browserbase-session-accessors.test.jsBrowserbase smoke:
Verification SuccessVerification SuccessVerification SuccesssolveCaptchas: false: solver stays disabled, no wait/resume path is triggered, and the agent stops at the captcha instead of bypassing it