> ## Documentation Index
> Fetch the complete documentation index at: https://intunedhq.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Stagehand

export const CLICommandTabs = ({command, withOptions = true}) => {
  if (!Array.isArray(command)) {
    command = [command];
  }
  function mapAliases(aliases) {
    return aliases.map((alias, index) => <>{alias}{index === 0 ? '' : ' # Alias'}{'\n\n'}</>);
  }
  return <CodeGroup>
      <CodeBlock language="text" filename="General">
        {mapAliases(command.map(cmd => `${cmd}${withOptions ? " [options]" : ""}`))}
      </CodeBlock>

      <CodeBlock icon="python" language="uv" filename="uv">
        {mapAliases(command.map(cmd => `uv run ${cmd}${withOptions ? " [options]" : ""}`))}
      </CodeBlock>

      <CodeBlock icon="python" language="poetry" filename="poetry">
        {mapAliases(command.map(cmd => `poetry run ${cmd}${withOptions ? " [options]" : ""}`))}
      </CodeBlock>

      <CodeBlock icon="https://d3gk2c5xim1je2.cloudfront.net/devicon/typescript.svg" language="npm" filename="npm">
        {mapAliases(command.map(cmd => `npm run ${cmd}${withOptions ? " -- [options]" : ""}`))}
      </CodeBlock>

      <CodeBlock icon="https://d3gk2c5xim1je2.cloudfront.net/devicon/typescript.svg" language="yarn" filename="yarn">
        {mapAliases(command.map(cmd => `yarn ${cmd}${withOptions ? " [options]" : ""}`))}
      </CodeBlock>
    </CodeGroup>;
};

## Overview

[Stagehand](https://www.stagehand.dev/) is an AI-powered browser automation framework with methods like `observe()`, `extract()`, and `act()`.

This guide walks you through deploying and running a Stagehand project on Intuned. You'll build a sample scraper that extracts book data—without writing Playwright selectors or custom parsing logic. The same patterns apply to any automation you build with the framework.

<Note>This guide assumes you have a basic understanding of Intuned projects. If you're new to Intuned, start with the [getting started guide](/main/00-getting-started/introduction).</Note>

## When to use AI automation

Intuned supports AI-powered browser automation frameworks like Stagehand, Browser Use, and others. Use AI automation when:

* **Pages are dynamic** — Elements change position, structure, or content unpredictably
* **You don't know the exact page structure** — Scraping sites you haven't mapped in detail
* **You want natural language control** — Describe what to do instead of writing precise selectors
* **Traditional Playwright code is too brittle for your case** — AI agents adapt to minor UI changes automatically

Stagehand is one option for AI automation on Intuned. The setup patterns in this guide apply to other AI frameworks as well.

For a deeper dive into choosing between deterministic, AI-driven, and hybrid approaches, check out [Flexible Automations](/main/02-features/flexible-automation).

## Guide

Let's build a `getBooks` API with AI. The template handles all the Stagehand setup for you.

You can develop with Stagehand in two ways:

* **Online IDE** — Zero setup. Write, test, and deploy directly from your browser.
* **CLI** — Use your favorite IDE with full version control.

<Tabs>
  <Tab title="Online IDE">
    <Steps>
      <Step title="Create a project">
        1. Go to [Intuned dashboard](https://app.intuned.io)
        2. Select **+ New Project** > **Templates** > **Stagehand**
        3. Select your language (Python or TypeScript)
        4. Name it and select **Create Project**

        <Frame>
          <img src="https://mintcdn.com/intuned-dev/bhb38akfgMoZ2D8J/assets/integrations/stagehand-create-project.png?fit=max&auto=format&n=bhb38akfgMoZ2D8J&q=85&s=3db188c466122c603fd5347f224d612f" alt="Create Stagehand project from template" width="2880" height="2048" data-path="assets/integrations/stagehand-create-project.png" />
        </Frame>
      </Step>

      <Step title="Explore the project">
        The template includes a book scraper API that combines Playwright with AI agents.

        **Project structure:**

        <Tabs>
          <Tab title="TypeScript">
            ```
            my-stagehand-project/
            ├── api/
            │   └── get-books.ts             # Main automation API
            ├── hooks/
            │   └── setupContext.ts          # CDP URL setup
            ├── package.json                 # Dependencies
            └── intuned.json                 # Project configuration
            ```
          </Tab>

          <Tab title="Python">
            ```
            my-stagehand-project/
            ├── api/
            │   └── get-books.py             # Main automation API
            ├── hooks/
            │   └── setup_context.py         # CDP URL setup
            ├── pyproject.toml               # Dependencies
            └── intuned.json                 # Project configuration
            ```
          </Tab>
        </Tabs>

        **The automation code:**

        <CodeGroup dropdown>
          ```python Python theme={null}
          from typing import TypedDict
          from playwright.async_api import Page
          from pydantic import BaseModel
          from intuned_runtime import attempt_store, get_ai_gateway_config
          from stagehand import AsyncStagehand
          from stagehand.types.model_config_param import ModelConfigParam
          from stagehand.types.session_start_params import Browser, BrowserLaunchOptions


          class Params(TypedDict):
              category: str | None


          class BookDetails(BaseModel):
              title: str
              price: str
              rating: str | None = None
              availability: str | None = None


          class BooksResponse(BaseModel):
              books: list[BookDetails]


          MAX_PAGES = 10


          async def automation(page: Page, params: Params, **_kwargs):
              base_url, api_key = get_ai_gateway_config()
              cdp_url = attempt_store.get("cdp_url")

              model_name = "openai/gpt-5-mini"
              model_config: ModelConfigParam = {
                  "model_name": model_name,
                  "api_key": api_key,
                  "base_url": base_url,
                  "provider": "openai",
              }

              # Initialize Stagehand with act/extract/observe capabilities
              client = AsyncStagehand(
                  server="local",
                  model_api_key=api_key,
                  local_ready_timeout_s=30.0,
              )
              launch_options: BrowserLaunchOptions = {"headless": False}
              if cdp_url is not None:
                  launch_options["cdp_url"] = str(cdp_url)
              browser: Browser = {"type": "local", "launch_options": launch_options}
              session = await client.sessions.start(
                  model_name=model_name,
                  browser=browser,
              )
              session_id = session.data.session_id

              await page.set_viewport_size({"width": 1280, "height": 800})

              category = params.get("category")
              all_books: list[BookDetails] = []

              try:
                  await client.sessions.navigate(
                      id=session_id,
                      url="https://books.toscrape.com",
                  )

                  # Navigate to category if specified using observe and act
                  if category:
                      await client.sessions.observe(
                          id=session_id,
                          instruction=f'the "{category}" category link in the sidebar',
                          options={"model": model_config},
                      )
                      await client.sessions.act(
                          id=session_id,
                          input=f'Click on the "{category}" category link in the sidebar',
                          options={"model": model_config},
                      )

                  # Collect books from all pages
                  for page_num in range(1, MAX_PAGES + 1):
                      # Extract all book details from the current page
                      result = await client.sessions.extract(
                          id=session_id,
                          instruction="Extract all books on this page including title, price, rating and availability for each book",
                          options={"model": model_config},
                          schema={
                              "type": "object",
                              "properties": {
                                  "books": {
                                      "type": "array",
                                      "items": {
                                          "type": "object",
                                          "properties": {
                                              "title": {"type": "string"},
                                              "price": {"type": "string"},
                                              "rating": {"type": "string"},
                                              "availability": {"type": "string"},
                                          },
                                          "required": ["title", "price"],
                                      },
                                  }
                              },
                              "required": ["books"],
                          },
                      )

                      books_data = BooksResponse.model_validate(result.data.result)
                      all_books.extend(books_data.books)

                      # Check if there's a next page and navigate to it
                      if page_num < MAX_PAGES:
                          try:
                              next_button = await client.sessions.observe(
                                  id=session_id,
                                  instruction='the "next" button or link to go to the next page',
                                  options={"model": model_config},
                              )
                              if not next_button.data.result:
                                  break
                              await client.sessions.act(
                                  id=session_id,
                                  input='Click the "next" button to go to the next page',
                                  options={"model": model_config},
                              )
                          except Exception:
                              break

              finally:
                  await client.sessions.end(session_id)

              return BooksResponse(books=all_books)
          ```

          ```typescript TypeScript theme={null}
          import z from "zod";
          import { Stagehand } from "@browserbasehq/stagehand";
          import type { Page, BrowserContext } from "playwright";
          import { attemptStore, getAiGatewayConfig } from "@intuned/runtime";

          interface Params {
            category?: string;
          }

          const bookDetailsSchema = z.object({
            books: z.array(
              z.object({
                title: z.string(),
                price: z.string(),
                rating: z.string().optional(),
                availability: z.string().optional(),
              })
            ),
          });

          type BookDetails = z.infer<typeof bookDetailsSchema>["books"][number];
          type BooksResponse = z.infer<typeof bookDetailsSchema>;

          const MAX_PAGES = 10;

          async function getWebSocketUrl(cdpUrl: string): Promise<string> {
            if (cdpUrl.includes("ws://") || cdpUrl.includes("wss://")) {
              return cdpUrl;
            }
            const versionUrl = cdpUrl.endsWith("/")
              ? `${cdpUrl}json/version`
              : `${cdpUrl}/json/version`;
            const response = await fetch(versionUrl);
            const data = await response.json();
            return data.webSocketDebuggerUrl;
          }

          export default async function handler(
            { category }: Params,
            page: Page,
            _context: BrowserContext
          ) {
            const { baseUrl, apiKey } = await getAiGatewayConfig();
            const cdpUrl = attemptStore.get("cdpUrl") as string;
            const webSocketUrl = await getWebSocketUrl(cdpUrl);

            // Initialize Stagehand with observe/act/extract capabilities
            const stagehand = new Stagehand({
              env: "LOCAL",
              localBrowserLaunchOptions: {
                cdpUrl: webSocketUrl,
                viewport: { width: 1280, height: 800 },
              },
              model: {
                modelName: "openai/gpt-5-mini",
                apiKey,
                baseURL: baseUrl,
              },
            });
            await stagehand.init();

            await page.setViewportSize({ width: 1280, height: 800 });

            const allBooks: BookDetails[] = [];

            try {
              await page.goto("https://books.toscrape.com");

              // Navigate to category if specified using observe and act
              if (category) {
                await stagehand.observe(`the "${category}" category link in the sidebar`);
                await stagehand.act(`Click on the "${category}" category link in the sidebar`);
              }

              // Collect books from all pages
              for (let pageNum = 1; pageNum <= MAX_PAGES; pageNum++) {
                // Extract all book details from the current page
                const result = await stagehand.extract(
                  "Extract all books visible on the page with their complete details",
                  bookDetailsSchema
                );
                allBooks.push(...(result.books ?? []));

                // Check if there's a next page and navigate to it
                if (pageNum < MAX_PAGES) {
                  try {
                    const nextButton = await stagehand.observe(
                      'the "next" button or link to go to the next page'
                    );
                    if (!nextButton || nextButton.length === 0) {
                      break;
                    }
                    await stagehand.act('Click the "next" button to go to the next page');
                  } catch (e) {
                    break;
                  }
                }
              }
            } finally {
              await stagehand.close();
            }

            return { books: allBooks } satisfies BooksResponse;
          }
          ```
        </CodeGroup>

        <Info>
          **What this does:** Navigates to [https://books.toscrape.com](https://books.toscrape.com), uses Stagehand's `observe()` and `act()` methods to navigate to a specific category, then uses `extract()` to collect book details across multiple pages. TypeScript uses Zod schemas; Python uses a JSON schema dict validated into Pydantic models.
        </Info>
      </Step>

      <Step title="Run your automation">
        Run the book scraper to test your setup.

        1. In the Online IDE, select the API from the dropdown
        2. Select **Select Parameter** and enter `{"category": "Travel"}`
        3. Select **Start Run**

        <Frame>
          <img src="https://mintcdn.com/intuned-dev/bhb38akfgMoZ2D8J/assets/integrations/stagehand-run-ide.png?fit=max&auto=format&n=bhb38akfgMoZ2D8J&q=85&s=85f1f3abc8be852fde900b65b1315fa3" alt="Running Stagehand automation in IDE" width="2880" height="2048" data-path="assets/integrations/stagehand-run-ide.png" />
        </Frame>
      </Step>

      <Step title="Deploy and test">
        Deploy your automation to Intuned's infrastructure.

        1. In the Online IDE, select **Deploy** in the top-right corner
        2. After deployment completes, go to the project's **Runs** page
        3. Select **Start Run**, then choose your API
        4. Enter parameters: `{"category": "Travel"}` and select **Start Run**

        <Frame>
          <img src="https://mintcdn.com/intuned-dev/bhb38akfgMoZ2D8J/assets/integrations/stagehand-run-playground.png?fit=max&auto=format&n=bhb38akfgMoZ2D8J&q=85&s=9331a7125a032c9ca7a494e45a447196" alt="Running Stagehand automation in Dashboard" width="2880" height="2048" data-path="assets/integrations/stagehand-run-playground.png" />
        </Frame>

        5. After the run completes, view the extracted results

        <Frame>
          <img src="https://mintcdn.com/intuned-dev/bhb38akfgMoZ2D8J/assets/integrations/stagehand-run-result.png?fit=max&auto=format&n=bhb38akfgMoZ2D8J&q=85&s=21a077e2d97a73aae1fbf2a27c3c4211" alt="View Stagehand run results" width="2880" height="2048" data-path="assets/integrations/stagehand-run-result.png" />
        </Frame>

        <Tip>
          Learn more about [Runs and how to trigger them programmatically](/main/02-features/runs-single-executions).
        </Tip>
      </Step>
    </Steps>
  </Tab>

  <Tab title="CLI">
    <Steps>
      <Step title="Create a project">
        Run the following command and select your language (**Python** or **TypeScript**), then choose the **Stagehand** template:

        ```bash theme={null}
        npx create-intuned-project@latest
        ```

        Then navigate to your project:

        ```bash theme={null}
        cd my-stagehand-project
        ```
      </Step>

      <Step title="Explore the project">
        The template includes a book scraper API that combines Playwright with AI agents.

        **Project structure:**

        <Tabs>
          <Tab title="TypeScript">
            ```
            my-stagehand-project/
            ├── api/
            │   └── get-books.ts             # Main automation API
            ├── hooks/
            │   └── setupContext.ts          # CDP URL setup
            ├── package.json                 # Dependencies
            └── intuned.json                 # Project configuration
            ```
          </Tab>

          <Tab title="Python">
            ```
            my-stagehand-project/
            ├── api/
            │   └── get-books.py             # Main automation API
            ├── hooks/
            │   └── setup_context.py         # CDP URL setup
            ├── pyproject.toml               # Dependencies
            └── intuned.json                 # Project configuration
            ```
          </Tab>
        </Tabs>

        **The automation code:**

        <CodeGroup dropdown>
          ```python Python theme={null}
          from typing import TypedDict
          from playwright.async_api import Page
          from pydantic import BaseModel
          from intuned_runtime import attempt_store, get_ai_gateway_config
          from stagehand import AsyncStagehand
          from stagehand.types.model_config_param import ModelConfigParam
          from stagehand.types.session_start_params import Browser, BrowserLaunchOptions


          class Params(TypedDict):
              category: str | None


          class BookDetails(BaseModel):
              title: str
              price: str
              rating: str | None = None
              availability: str | None = None


          class BooksResponse(BaseModel):
              books: list[BookDetails]


          MAX_PAGES = 10


          async def automation(page: Page, params: Params, **_kwargs):
              base_url, api_key = get_ai_gateway_config()
              cdp_url = attempt_store.get("cdp_url")

              model_name = "openai/gpt-5-mini"
              model_config: ModelConfigParam = {
                  "model_name": model_name,
                  "api_key": api_key,
                  "base_url": base_url,
                  "provider": "openai",
              }

              # Initialize Stagehand with act/extract/observe capabilities
              client = AsyncStagehand(
                  server="local",
                  model_api_key=api_key,
                  local_ready_timeout_s=30.0,
              )
              launch_options: BrowserLaunchOptions = {"headless": False}
              if cdp_url is not None:
                  launch_options["cdp_url"] = str(cdp_url)
              browser: Browser = {"type": "local", "launch_options": launch_options}
              session = await client.sessions.start(
                  model_name=model_name,
                  browser=browser,
              )
              session_id = session.data.session_id

              await page.set_viewport_size({"width": 1280, "height": 800})

              category = params.get("category")
              all_books: list[BookDetails] = []

              try:
                  await client.sessions.navigate(
                      id=session_id,
                      url="https://books.toscrape.com",
                  )

                  # Navigate to category if specified using observe and act
                  if category:
                      await client.sessions.observe(
                          id=session_id,
                          instruction=f'the "{category}" category link in the sidebar',
                          options={"model": model_config},
                      )
                      await client.sessions.act(
                          id=session_id,
                          input=f'Click on the "{category}" category link in the sidebar',
                          options={"model": model_config},
                      )

                  # Collect books from all pages
                  for page_num in range(1, MAX_PAGES + 1):
                      # Extract all book details from the current page
                      result = await client.sessions.extract(
                          id=session_id,
                          instruction="Extract all books on this page including title, price, rating and availability for each book",
                          options={"model": model_config},
                          schema={
                              "type": "object",
                              "properties": {
                                  "books": {
                                      "type": "array",
                                      "items": {
                                          "type": "object",
                                          "properties": {
                                              "title": {"type": "string"},
                                              "price": {"type": "string"},
                                              "rating": {"type": "string"},
                                              "availability": {"type": "string"},
                                          },
                                          "required": ["title", "price"],
                                      },
                                  }
                              },
                              "required": ["books"],
                          },
                      )

                      books_data = BooksResponse.model_validate(result.data.result)
                      all_books.extend(books_data.books)

                      # Check if there's a next page and navigate to it
                      if page_num < MAX_PAGES:
                          try:
                              next_button = await client.sessions.observe(
                                  id=session_id,
                                  instruction='the "next" button or link to go to the next page',
                                  options={"model": model_config},
                              )
                              if not next_button.data.result:
                                  break
                              await client.sessions.act(
                                  id=session_id,
                                  input='Click the "next" button to go to the next page',
                                  options={"model": model_config},
                              )
                          except Exception:
                              break

              finally:
                  await client.sessions.end(session_id)

              return BooksResponse(books=all_books)
          ```

          ```typescript TypeScript theme={null}
          import z from "zod";
          import { Stagehand } from "@browserbasehq/stagehand";
          import type { Page, BrowserContext } from "playwright";
          import { attemptStore, getAiGatewayConfig } from "@intuned/runtime";

          interface Params {
            category?: string;
          }

          const bookDetailsSchema = z.object({
            books: z.array(
              z.object({
                title: z.string(),
                price: z.string(),
                rating: z.string().optional(),
                availability: z.string().optional(),
              })
            ),
          });

          type BookDetails = z.infer<typeof bookDetailsSchema>["books"][number];
          type BooksResponse = z.infer<typeof bookDetailsSchema>;

          const MAX_PAGES = 10;

          async function getWebSocketUrl(cdpUrl: string): Promise<string> {
            if (cdpUrl.includes("ws://") || cdpUrl.includes("wss://")) {
              return cdpUrl;
            }
            const versionUrl = cdpUrl.endsWith("/")
              ? `${cdpUrl}json/version`
              : `${cdpUrl}/json/version`;
            const response = await fetch(versionUrl);
            const data = await response.json();
            return data.webSocketDebuggerUrl;
          }

          export default async function handler(
            { category }: Params,
            page: Page,
            _context: BrowserContext
          ) {
            const { baseUrl, apiKey } = await getAiGatewayConfig();
            const cdpUrl = attemptStore.get("cdpUrl") as string;
            const webSocketUrl = await getWebSocketUrl(cdpUrl);

            // Initialize Stagehand with observe/act/extract capabilities
            const stagehand = new Stagehand({
              env: "LOCAL",
              localBrowserLaunchOptions: {
                cdpUrl: webSocketUrl,
                viewport: { width: 1280, height: 800 },
              },
              model: {
                modelName: "openai/gpt-4o",
                apiKey,
                baseURL: baseUrl,
              },
            });
            await stagehand.init();

            await page.setViewportSize({ width: 1280, height: 800 });

            const allBooks: BookDetails[] = [];

            try {
              await page.goto("https://books.toscrape.com");

              // Navigate to category if specified using observe and act
              if (category) {
                await stagehand.observe(`the "${category}" category link in the sidebar`);
                await stagehand.act(`Click on the "${category}" category link in the sidebar`);
              }

              // Collect books from all pages
              for (let pageNum = 1; pageNum <= MAX_PAGES; pageNum++) {
                // Extract all book details from the current page
                const result = await stagehand.extract(
                  "Extract all books visible on the page with their complete details",
                  bookDetailsSchema
                );
                allBooks.push(...(result.books ?? []));

                // Check if there's a next page and navigate to it
                if (pageNum < MAX_PAGES) {
                  try {
                    const nextButton = await stagehand.observe(
                      'the "next" button or link to go to the next page'
                    );
                    if (!nextButton || nextButton.length === 0) {
                      break;
                    }
                    await stagehand.act('Click the "next" button to go to the next page');
                  } catch (e) {
                    break;
                  }
                }
              }
            } finally {
              await stagehand.close();
            }

            return { books: allBooks } satisfies BooksResponse;
          }
          ```
        </CodeGroup>

        <Info>
          **What this does:** Navigates to [https://books.toscrape.com](https://books.toscrape.com), uses Stagehand's `observe()` and `act()` methods to navigate to a specific category, then uses `extract()` to collect book details across multiple pages. TypeScript uses Zod schemas; Python uses a JSON schema dict validated into Pydantic models.
        </Info>
      </Step>

      <Step title="Run locally">
        Test your automation locally:

        <CLICommandTabs command={`intuned dev run api get-books '{"category": "Travel"}'`} withOptions={false} />

        The automation navigates to the category and extracts book details.
      </Step>

      <Step title="Deploy and test">
        Deploy your automation using the CLI:

        <CLICommandTabs command="intuned dev deploy" withOptions={false} />

        After deployment, test in the Dashboard:

        1. Go to the project's **Runs** page
        2. Select **Start Run** and select your API
        3. Enter parameters: `{"category": "Travel"}` and select **Start Run**

        <Frame>
          <img src="https://mintcdn.com/intuned-dev/bhb38akfgMoZ2D8J/assets/integrations/stagehand-run-playground.png?fit=max&auto=format&n=bhb38akfgMoZ2D8J&q=85&s=9331a7125a032c9ca7a494e45a447196" alt="Running Stagehand automation in Dashboard" width="2880" height="2048" data-path="assets/integrations/stagehand-run-playground.png" />
        </Frame>

        4. After the run completes, view the extracted results

        <Frame>
          <img src="https://mintcdn.com/intuned-dev/bhb38akfgMoZ2D8J/assets/integrations/stagehand-run-result.png?fit=max&auto=format&n=bhb38akfgMoZ2D8J&q=85&s=21a077e2d97a73aae1fbf2a27c3c4211" alt="View Stagehand run results" width="2880" height="2048" data-path="assets/integrations/stagehand-run-result.png" />
        </Frame>

        <Tip>
          Learn more about [Runs and how to trigger them programmatically](/main/02-features/runs-single-executions).
        </Tip>
      </Step>
    </Steps>
  </Tab>
</Tabs>

## How it works

* The `setupContext` hook stores the CDP URL in [`attemptStore`](/main/05-references/runtime-sdk-python/attempt-store) for your API to access

<Tip>
  Check out our [setup hook recipe](/main/01-learn/recipes/setup-hooks).
</Tip>

* Your API initializes Stagehand with the CDP URL and AI gateway credentials from `getAiGatewayConfig()`
* Stagehand provides three core AI-powered methods:
  * `observe()` - Find elements on the page using natural language
  * `act()` - Perform actions like clicking using natural language
  * `extract()` - Extract structured data from the page with type-safe schemas

<Tabs>
  <Tab title="TypeScript">
    - Passes AI credentials via the `model` config (`modelName`, `apiKey`, `baseURL`)
    - Call methods directly on `stagehand` (e.g., `stagehand.act()`, `stagehand.extract()`)
    - Use the Playwright `page` for navigation (e.g., `page.goto()`)
  </Tab>

  <Tab title="Python">
    * Uses `AsyncStagehand` with a sessions-based API — start a session, then call methods via `client.sessions`
    * Navigate with `client.sessions.navigate()`, act/observe/extract with `client.sessions.act/observe/extract()`
    * Pass a JSON schema dict to `extract()` and validate the result with `BooksResponse.model_validate()`
    * End the session with `client.sessions.end(session_id)` in a `finally` block
    * Uses `get_ai_gateway_config()` to get AI credentials and passes them via `options={"model": model_config}` on each call
  </Tab>
</Tabs>

* Your API accepts parameters (like `category`) that can vary for each run
* The automation handles pagination automatically and cleans up Stagehand when done

## Related resources

<CardGroup cols={2}>
  <Card title="Stagehand Documentation" icon="book" href="https://docs.stagehand.dev">
    Official Stagehand documentation and API reference
  </Card>

  <Card title="Stagehand GitHub" icon="github" href="https://github.com/browserbase/stagehand">
    View the source code and contribute to Stagehand
  </Card>

  <Card title="Intuned Cookbook" icon="code" href="https://github.com/Intuned/cookbook">
    Browse examples and recipes for Intuned projects
  </Card>

  <Card title="attemptStore Reference" icon="database" href="/main/05-references/runtime-sdk-python/attempt-store">
    Learn more about sharing data between hooks and APIs
  </Card>
</CardGroup>
