Skip to content

refactor(examples): use top-level await instead of async IIFE#1727

Open
miguelg719 wants to merge 1 commit intomainfrom
contrib/1725
Open

refactor(examples): use top-level await instead of async IIFE#1727
miguelg719 wants to merge 1 commit intomainfrom
contrib/1725

Conversation

@miguelg719
Copy link
Collaborator

@miguelg719 miguelg719 commented Feb 21, 2026

why

Now that ESM is supported, examples can use top-level await directly instead of wrapping code in (async () => { ... })() patterns.

what changed

test plan


Summary by cubic

Converted example scripts to ESM with top-level await to simplify flow and reduce boilerplate. This makes the examples easier to read and follow.

  • Refactors
    • Removed async IIFE/main wrappers; use top-level await across all examples.
    • Moved Stagehand/V3 setup to module scope and added try/finally for cleanup where needed.
    • Preserved explicit error logging in cuaReplay.ts and persist_logs_example.ts.
    • Added a changeset for a patch release.

Written for commit 60f1527. Summary will update on new commits. Review in cubic

Now that ESM is supported, examples can use top-level await directly
instead of wrapping code in `(async () => { ... })()` patterns.

This simplifies the examples and makes them more readable.

# why

# what changed

# test plan

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Converted all examples to ESM with top-level await and removed
main/example function wrappers for simpler, linear code. Restored .catch
handlers in cuaReplay.ts and persist_logs_example.ts to ensure errors
are logged, and added a changeset for a patch release.

- **Refactors**
- Moved Stagehand/V3 construction to module scope with try/finally for
cleanup.

<sup>Written for commit 6349d2f.
Summary will update on new commits. <a
href="https://cubic.dev/pr/browserbase/stagehand/pull/1725">Review in
cubic</a></sup>

<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Chromie Bot <chromie@browserbase.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Feb 21, 2026

🦋 Changeset detected

Latest commit: 60f1527

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@browserbasehq/stagehand Patch
@browserbasehq/stagehand-evals Patch
@browserbasehq/stagehand-server Patch

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

@miguelg719 miguelg719 marked this pull request as ready for review February 21, 2026 01:39
@miguelg719 miguelg719 changed the title refactor(examples): use top-level await instead of async IIFE (#1725) refactor(examples): use top-level await instead of async IIFE Feb 21, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 33 files

Confidence score: 4/5

  • Both issues are in example scripts; impact is limited, so this looks safe to merge with minimal risk overall.
  • packages/core/examples/v3/cuaReplay.ts drops the top-level .catch(console.error) during refactor, so errors may go unlogged unless wrapped in a try/catch.
  • packages/core/examples/wordle.ts lacks a try/finally to ensure stagehand.close() on failures, risking leaked Browserbase sessions if an await throws.
  • Pay close attention to packages/core/examples/v3/cuaReplay.ts and packages/core/examples/wordle.ts - restore explicit error logging and guaranteed cleanup.
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/examples/v3/cuaReplay.ts">

<violation number="1" location="packages/core/examples/v3/cuaReplay.ts:53">
P2: The `.catch(console.error)` error handling from the original `main()` call was dropped during the refactor. To preserve the explicit error logging as intended, wrap the top-level logic in a try/catch. This also ensures `stagehand.close()` is called if `runDemo` throws after `stagehand.init()`.</violation>
</file>

<file name="packages/core/examples/wordle.ts">

<violation number="1" location="packages/core/examples/wordle.ts:18">
P2: Missing `try/finally` to guarantee `stagehand.close()` runs on error. If any `await` throws (e.g., `page.goto` or any `act()` call), the Browserbase session will leak. Other refactored examples in this PR consistently use `try/finally` for cleanup—this one should follow the same pattern.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Script as ESM Script (Top-level)
    participant SH as Stagehand / V3 Core
    participant Driver as Browser Driver (Playwright/Puppeteer)
    participant BB as Browserbase (Remote Browser)
    participant LLM as LLM Provider (OpenAI/Anthropic/Gemini)

    Note over Script, LLM: NEW: Runtime Flow using ESM Top-level Await

    Script->>SH: Instantiate with Config (env, model, verbose)
    
    rect rgb(20, 30, 50)
        Note right of Script: CHANGED: Immediate execution (no IIFE wrapper)
        Script->>SH: await init()
        
        alt env: "BROWSERBASE"
            SH->>BB: Connect via CDP/WebSocket
            BB-->>SH: Remote Session
        else env: "LOCAL"
            SH->>Driver: Launch Local Browser
            Driver-->>SH: Local Instance
        end
    end

    Script->>SH: await page.goto(url)
    SH->>Driver: Navigate Tab

    loop Interaction Loop (act/extract/observe)
        Script->>SH: await act(instruction) OR extract(schema)
        
        SH->>Driver: Capture Accessibility Tree / DOM / Screenshot
        Driver-->>SH: Page State
        
        SH->>LLM: Send Instruction + Context
        LLM-->>SH: Return Action / Structured Data
        
        opt is Action
            SH->>Driver: Perform Playwright/Puppeteer Command
        end
        
        SH-->>Script: Return Result
    end

    rect rgb(40, 10, 10)
        Note right of Script: NEW: Lifecycle handled in global try/finally
        Script->>SH: await close()
        SH->>Driver: Cleanup Process / Close Session
    end

    opt Error Handling
        Script->>Script: CHANGED: Errors propagate to top-level catch/process
    end
Loading

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

level: 1,
category: "demo",
message: `
const metrics1 = await runDemo(1);
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The .catch(console.error) error handling from the original main() call was dropped during the refactor. To preserve the explicit error logging as intended, wrap the top-level logic in a try/catch. This also ensures stagehand.close() is called if runDemo throws after stagehand.init().

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/examples/v3/cuaReplay.ts, line 53:

<comment>The `.catch(console.error)` error handling from the original `main()` call was dropped during the refactor. To preserve the explicit error logging as intended, wrap the top-level logic in a try/catch. This also ensures `stagehand.close()` is called if `runDemo` throws after `stagehand.init()`.</comment>

<file context>
@@ -50,30 +50,29 @@ async function runDemo(runNumber: number) {
-    level: 1,
-    category: "demo",
-    message: `
+const metrics1 = await runDemo(1);
+
+v3Logger({
</file context>
Fix with Cubic

await example();
})();
await stagehand.act("press enter");
await stagehand.close();
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Missing try/finally to guarantee stagehand.close() runs on error. If any await throws (e.g., page.goto or any act() call), the Browserbase session will leak. Other refactored examples in this PR consistently use try/finally for cleanup—this one should follow the same pattern.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/examples/wordle.ts, line 18:

<comment>Missing `try/finally` to guarantee `stagehand.close()` runs on error. If any `await` throws (e.g., `page.goto` or any `act()` call), the Browserbase session will leak. Other refactored examples in this PR consistently use `try/finally` for cleanup—this one should follow the same pattern.</comment>

<file context>
@@ -1,24 +1,18 @@
-  await example();
-})();
+await stagehand.act("press enter");
+await stagehand.close();
</file context>
Fix with Cubic

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 21, 2026

Greptile Summary

Converted 33 example files from async IIFE pattern to top-level await, removing wrapper boilerplate and simplifying code structure.

Major changes:

  • Removed (async () => { ... })() wrappers across all examples
  • Moved Stagehand/V3 initialization to module scope
  • Preserved try/finally cleanup in files that already had it
  • Maintained explicit error logging in cuaReplay.ts and persist_logs_example.ts

Issues found:

  • 14 examples missing stagehand.close() or v3.close() calls, causing resource leaks
  • 2 examples have try/catch but missing finally blocks for cleanup (2048.ts, agent_stream_example.ts)

The refactoring correctly leverages ESM's top-level await support, but inconsistent cleanup patterns were introduced or left unaddressed.

Confidence Score: 2/5

  • This PR introduces resource leaks in multiple example files
  • While the refactoring pattern is correct and simplifies the code, 14 out of 33 files are missing cleanup calls to close browser instances. This creates resource leaks that will leave browser processes running after examples complete or error out. Files with try/catch blocks also need finally blocks to ensure cleanup happens.
  • Pay close attention to all files with missing stagehand.close() or v3.close() calls: 2048.ts, agent_stream_example.ts, form_filling_sensible.ts, parameterizeApiKey.ts, v3_example.ts, and 9 files in v3/ directory

Important Files Changed

Filename Overview
packages/core/examples/2048.ts Missing finally block to close stagehand instance on error/exit
packages/core/examples/agent_stream_example.ts Missing finally block to ensure stagehand cleanup after errors
packages/core/examples/form_filling_sensible.ts Missing try/finally cleanup, stagehand instance never closed
packages/core/examples/v3/deepLocator.ts Missing cleanup, stagehand instance never closed
packages/core/examples/v3/dropdown.ts Missing cleanup, stagehand instance never closed
packages/core/examples/v3/patchright.ts Missing cleanup, stagehand instance never closed
packages/core/examples/v3_example.ts Missing cleanup, v3 instance never closed
packages/core/examples/parameterizeApiKey.ts Missing cleanup, stagehand instance never closed
packages/core/examples/v3/playwright.ts Missing cleanup, stagehand instance never closed
packages/core/examples/v3/puppeteer.ts Missing cleanup, stagehand instance never closed
packages/core/examples/v3/shadowRoot.ts Missing cleanup, stagehand instance never closed
packages/core/examples/v3/targetedExtract.ts Missing cleanup, stagehand instance never closed

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Async IIFE Pattern] -->|Refactor| B[Top-level Await Pattern]
    
    A -->|Before| A1["(async () => {<br/>  const stagehand = new Stagehand()<br/>  await stagehand.init()<br/>  // code<br/>})()"]
    
    B -->|After| B1["const stagehand = new Stagehand()<br/>await stagehand.init()<br/>// code"]
    
    B1 --> C{Has Error Handling?}
    
    C -->|Yes - Complete| D["try {<br/>  // code<br/>} finally {<br/>  await stagehand.close()<br/>}"]
    
    C -->|Partial - Missing finally| E["try {<br/>  // code<br/>} catch (error) {<br/>  // handle<br/>}<br/>⚠️ No cleanup"]
    
    C -->|No| F["// code runs<br/>⚠️ No cleanup"]
    
    D -->|✅| G[Resources Cleaned Up]
    E -->|❌| H[Resource Leak]
    F -->|❌| H
    
    style D fill:#90EE90
    style E fill:#FFB6C1
    style F fill:#FFB6C1
    style G fill:#90EE90
    style H fill:#FF6B6B
Loading

Last reviewed commit: 60f1527

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

33 files reviewed, 14 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +13 to 89
try {
console.log("🌐 Navigating to 2048...");
await page.goto("https://ovolve.github.io/2048-AI/");
// Main game loop
while (true) {
console.log("🔄 Game loop iteration...");
// Add a small delay for UI updates
await new Promise((resolve) => setTimeout(resolve, 300));
// Get current game state
const gameState = await stagehand.extract(
`Extract the current game state:
1. Score from the score counter
2. All tile values in the 4x4 grid (empty spaces as 0)
3. Highest tile value present`,
z.object({
score: z.number(),
highestTile: z.number(),
grid: z.array(z.array(z.number())),
}),
);
const transposedGrid = gameState.grid[0].map((_, colIndex) =>
gameState.grid.map((row) => row[colIndex]),
);
const grid = transposedGrid.map((row, rowIndex) => ({
[`row${rowIndex + 1}`]: row,
}));
console.log("Game State:", {
score: gameState.score,
highestTile: gameState.highestTile,
grid: grid,
});
if (isGameOver) {
console.log("🏁 Game Over!");
return;
}
// Analyze board and decide next move
const analysis = await stagehand.extract(
`Based on the current game state:
- Score: ${gameState.score}
- Highest tile: ${gameState.highestTile}
- Grid: This is a 4x4 matrix ordered by row (top to bottom) and column (left to right). The rows are stacked vertically, and tiles can move vertically between rows or horizontally between columns:\n${grid
.map((row) => {
const rowName = Object.keys(row)[0];
return ` ${rowName}: ${row[rowName].join(", ")}`;
})
.join("\n")}
What is the best move (up/down/left/right)? Consider:
1. Keeping high value tiles in corners (bottom left, bottom right, top left, top right)
2. Maintaining a clear path to merge tiles
3. Avoiding moves that could block merges
4. Only adjacent tiles of the same value can merge
5. Making a move will move all tiles in that direction until they hit a tile of a different value or the edge of the board
6. Tiles cannot move past the edge of the board
7. Each move must move at least one tile`,
z.object({
move: z.enum(["up", "down", "left", "right"]),
confidence: z.number(),
reasoning: z.string(),
}),
);
console.log("Move Analysis:", analysis);
const moveKey = {
up: "ArrowUp",
down: "ArrowDown",
left: "ArrowLeft",
right: "ArrowRight",
}[analysis.move];
await page.keyPress(moveKey);
console.log("🎯 Executed move:", analysis.move);
}
} catch (error) {
console.error("❌ Error in game loop:", error);
const isGameOver = await page.evaluate(() => {
return document.querySelector(".game-over") !== null;
});
if (isGameOver) {
console.log("🏁 Game Over!");
} else {
throw error; // Re-throw non-game-over errors
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing finally block - stagehand.close() never called if error occurs or game ends

Suggested change
try {
console.log("🌐 Navigating to 2048...");
await page.goto("https://ovolve.github.io/2048-AI/");
// Main game loop
while (true) {
console.log("🔄 Game loop iteration...");
// Add a small delay for UI updates
await new Promise((resolve) => setTimeout(resolve, 300));
// Get current game state
const gameState = await stagehand.extract(
`Extract the current game state:
1. Score from the score counter
2. All tile values in the 4x4 grid (empty spaces as 0)
3. Highest tile value present`,
z.object({
score: z.number(),
highestTile: z.number(),
grid: z.array(z.array(z.number())),
}),
);
const transposedGrid = gameState.grid[0].map((_, colIndex) =>
gameState.grid.map((row) => row[colIndex]),
);
const grid = transposedGrid.map((row, rowIndex) => ({
[`row${rowIndex + 1}`]: row,
}));
console.log("Game State:", {
score: gameState.score,
highestTile: gameState.highestTile,
grid: grid,
});
if (isGameOver) {
console.log("🏁 Game Over!");
return;
}
// Analyze board and decide next move
const analysis = await stagehand.extract(
`Based on the current game state:
- Score: ${gameState.score}
- Highest tile: ${gameState.highestTile}
- Grid: This is a 4x4 matrix ordered by row (top to bottom) and column (left to right). The rows are stacked vertically, and tiles can move vertically between rows or horizontally between columns:\n${grid
.map((row) => {
const rowName = Object.keys(row)[0];
return ` ${rowName}: ${row[rowName].join(", ")}`;
})
.join("\n")}
What is the best move (up/down/left/right)? Consider:
1. Keeping high value tiles in corners (bottom left, bottom right, top left, top right)
2. Maintaining a clear path to merge tiles
3. Avoiding moves that could block merges
4. Only adjacent tiles of the same value can merge
5. Making a move will move all tiles in that direction until they hit a tile of a different value or the edge of the board
6. Tiles cannot move past the edge of the board
7. Each move must move at least one tile`,
z.object({
move: z.enum(["up", "down", "left", "right"]),
confidence: z.number(),
reasoning: z.string(),
}),
);
console.log("Move Analysis:", analysis);
const moveKey = {
up: "ArrowUp",
down: "ArrowDown",
left: "ArrowLeft",
right: "ArrowRight",
}[analysis.move];
await page.keyPress(moveKey);
console.log("🎯 Executed move:", analysis.move);
}
} catch (error) {
console.error("❌ Error in game loop:", error);
const isGameOver = await page.evaluate(() => {
return document.querySelector(".game-over") !== null;
});
if (isGameOver) {
console.log("🏁 Game Over!");
} else {
throw error; // Re-throw non-game-over errors
}
}
try {
console.log("🌐 Navigating to 2048...");
await page.goto("https://ovolve.github.io/2048-AI/");
// Main game loop
while (true) {
console.log("🔄 Game loop iteration...");
// Add a small delay for UI updates
await new Promise((resolve) => setTimeout(resolve, 300));
// Get current game state
const gameState = await stagehand.extract(
`Extract the current game state:
1. Score from the score counter
2. All tile values in the 4x4 grid (empty spaces as 0)
3. Highest tile value present`,
z.object({
score: z.number(),
highestTile: z.number(),
grid: z.array(z.array(z.number())),
}),
);
const transposedGrid = gameState.grid[0].map((_, colIndex) =>
gameState.grid.map((row) => row[colIndex]),
);
const grid = transposedGrid.map((row, rowIndex) => ({
[`row${rowIndex + 1}`]: row,
}));
console.log("Game State:", {
score: gameState.score,
highestTile: gameState.highestTile,
grid: grid,
});
// Analyze board and decide next move
const analysis = await stagehand.extract(
`Based on the current game state:
- Score: ${gameState.score}
- Highest tile: ${gameState.highestTile}
- Grid: This is a 4x4 matrix ordered by row (top to bottom) and column (left to right). The rows are stacked vertically, and tiles can move vertically between rows or horizontally between columns:\n${grid
.map((row) => {
const rowName = Object.keys(row)[0];
return ` ${rowName}: ${row[rowName].join(", ")}`;
})
.join("\n")}
What is the best move (up/down/left/right)? Consider:
1. Keeping high value tiles in corners (bottom left, bottom right, top left, top right)
2. Maintaining a clear path to merge tiles
3. Avoiding moves that could block merges
4. Only adjacent tiles of the same value can merge
5. Making a move will move all tiles in that direction until they hit a tile of a different value or the edge of the board
6. Tiles cannot move past the edge of the board
7. Each move must move at least one tile`,
z.object({
move: z.enum(["up", "down", "left", "right"]),
confidence: z.number(),
reasoning: z.string(),
}),
);
console.log("Move Analysis:", analysis);
const moveKey = {
up: "ArrowUp",
down: "ArrowDown",
left: "ArrowLeft",
right: "ArrowRight",
}[analysis.move];
await page.keyPress(moveKey);
console.log("🎯 Executed move:", analysis.move);
}
} catch (error) {
console.error("❌ Error in game loop:", error);
const isGameOver = await page.evaluate(() => {
return document.querySelector(".game-over") !== null;
});
if (isGameOver) {
console.log("🏁 Game Over!");
} else {
throw error; // Re-throw non-game-over errors
}
} finally {
await stagehand.close();
}

Comment on lines +17 to 44
try {
const page = stagehand.context.pages()[0];
await page.goto("https://amazon.com");

const agentRun = await agent.execute({
instruction: "go to amazon, and search for shampoo, stop after searching",
maxSteps: 20,
});
// stream the text
for await (const delta of agentRun.textStream) {
process.stdout.write(delta);
}
// stream everything ( toolcalls, messages, etc.)
// for await (const delta of result.fullStream) {
// console.log(delta);
// }
// Create a streaming agent with stream: true in the config
const agent = stagehand.agent({
model: "anthropic/claude-sonnet-4-5-20250929",
stream: true, // This makes execute() return AgentStreamResult
});

const finalResult = await agentRun.result;
console.log("Final Result:", finalResult);
} catch (error) {
console.log(`${chalk.red("✗")} Error: ${error}`);
const agentRun = await agent.execute({
instruction: "go to amazon, and search for shampoo, stop after searching",
maxSteps: 20,
});
// stream the text
for await (const delta of agentRun.textStream) {
process.stdout.write(delta);
}
// stream everything ( toolcalls, messages, etc.)
// for await (const delta of result.fullStream) {
// console.log(delta);
// }

const finalResult = await agentRun.result;
console.log("Final Result:", finalResult);
} catch (error) {
console.log(`${chalk.red("✗")} Error: ${error}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing finally block - stagehand.close() never called

Suggested change
try {
const page = stagehand.context.pages()[0];
await page.goto("https://amazon.com");
const agentRun = await agent.execute({
instruction: "go to amazon, and search for shampoo, stop after searching",
maxSteps: 20,
});
// stream the text
for await (const delta of agentRun.textStream) {
process.stdout.write(delta);
}
// stream everything ( toolcalls, messages, etc.)
// for await (const delta of result.fullStream) {
// console.log(delta);
// }
// Create a streaming agent with stream: true in the config
const agent = stagehand.agent({
model: "anthropic/claude-sonnet-4-5-20250929",
stream: true, // This makes execute() return AgentStreamResult
});
const finalResult = await agentRun.result;
console.log("Final Result:", finalResult);
} catch (error) {
console.log(`${chalk.red("✗")} Error: ${error}`);
const agentRun = await agent.execute({
instruction: "go to amazon, and search for shampoo, stop after searching",
maxSteps: 20,
});
// stream the text
for await (const delta of agentRun.textStream) {
process.stdout.write(delta);
}
// stream everything ( toolcalls, messages, etc.)
// for await (const delta of result.fullStream) {
// console.log(delta);
// }
const finalResult = await agentRun.result;
console.log("Final Result:", finalResult);
} catch (error) {
console.log(`${chalk.red("✗")} Error: ${error}`);
}
try {
const page = stagehand.context.pages()[0];
await page.goto("https://amazon.com");
// Create a streaming agent with stream: true in the config
const agent = stagehand.agent({
model: "anthropic/claude-sonnet-4-5-20250929",
stream: true, // This makes execute() return AgentStreamResult
});
const agentRun = await agent.execute({
instruction: "go to amazon, and search for shampoo, stop after searching",
maxSteps: 20,
});
// stream the text
for await (const delta of agentRun.textStream) {
process.stdout.write(delta);
}
// stream everything ( toolcalls, messages, etc.)
// for await (const delta of result.fullStream) {
// console.log(delta);
// }
const finalResult = await agentRun.result;
console.log("Final Result:", finalResult);
} catch (error) {
console.log(`${chalk.red("✗")} Error: ${error}`);
} finally {
await stagehand.close();
}

Comment on lines +3 to +26
const stagehand = new Stagehand({
env: "LOCAL",
verbose: 0,
model: "openai/gpt-4.1",
});

// crossing OOPIF & shadow root boundaries with deep locator
await page
.deepLocator(
"/html/body/shadow-host//section/iframe/html/body/main/section[1]/form/div/div[1]/input",
)
.fill("nunya");
await page
.deepLocator(
"/html/body/shadow-host//section/iframe/html/body/main/section[1]/form/div/div[2]/input",
)
.fill("business");
}
await stagehand.init();

(async () => {
const stagehand = new Stagehand({
env: "LOCAL",
verbose: 0,
model: "openai/gpt-4.1",
});
await stagehand.init();
await example(stagehand);
})();
const page = stagehand.context.pages()[0];
await page.goto(
"https://browserbase.github.io/stagehand-eval-sites/sites/oopif-in-closed-shadow-dom/",
);

// crossing OOPIF & shadow root boundaries with deep locator
await page
.deepLocator(
"/html/body/shadow-host//section/iframe/html/body/main/section[1]/form/div/div[1]/input",
)
.fill("nunya");
await page
.deepLocator(
"/html/body/shadow-host//section/iframe/html/body/main/section[1]/form/div/div[2]/input",
)
.fill("business");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing cleanup - stagehand.close() never called

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 21, 2026

Additional Comments (11)

packages/core/examples/form_filling_sensible.ts
missing cleanup - stagehand.close() never called


packages/core/examples/v3/dropdown.ts
missing cleanup - stagehand.close() never called


packages/core/examples/v3/highlight.ts
missing cleanup - stagehand.close() never called


packages/core/examples/v3/patchright.ts
missing cleanup - stagehand.close() never called


packages/core/examples/v3/playwright.ts
missing cleanup - stagehand.close() never called


packages/core/examples/v3/puppeteer.ts
missing cleanup - stagehand.close() never called


packages/core/examples/v3/returnXpath.ts
missing cleanup - stagehand.close() never called


packages/core/examples/v3/shadowRoot.ts
missing cleanup - stagehand.close() never called


packages/core/examples/v3/targetedExtract.ts
missing cleanup - stagehand.close() never called


packages/core/examples/v3_example.ts
missing cleanup - v3.close() never called


packages/core/examples/parameterizeApiKey.ts
missing cleanup - stagehand.close() never called

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants