Skip to content

Conversation

@pirate
Copy link
Member

@pirate pirate commented Nov 22, 2025

"""
Stagehand Python SDK

A lightweight Python client for the Stagehand browser automation framework.
Connects to a remote Stagehand server (Node.js) and executes browser automation tasks.

Dependencies:
    pip install httpx

Usage:
    from stagehand import Stagehand

    async def main():
        stagehand = Stagehand(server_url="http://localhost:3000")
        await stagehand.init()

        await stagehand.goto("https://example.com")
        result = await stagehand.act("click the login button")
        data = await stagehand.extract("extract the page title")

        await stagehand.close()
"""

import json
from typing import Any, Dict, List, Optional, Union
import httpx


class StagehandError(Exception):
    """Base exception for Stagehand errors"""
    pass


class StagehandAPIError(StagehandError):
    """API-level errors from the Stagehand server"""
    pass


class StagehandConnectionError(StagehandError):
    """Connection errors when communicating with the server"""
    pass


class Action:
    """Represents a browser action returned by observe()"""

    def __init__(self, data: Dict[str, Any]):
        self.selector = data.get("selector")
        self.description = data.get("description")
        self.backend_node_id = data.get("backendNodeId")
        self.method = data.get("method")
        self.arguments = data.get("arguments", [])
        self._raw = data

    def __repr__(self):
        return f"Action(selector={self.selector!r}, description={self.description!r})"

    def to_dict(self) -> Dict[str, Any]:
        """Convert back to dict for sending to API"""
        return self._raw


class ActResult:
    """Result from act() method"""

    def __init__(self, data: Dict[str, Any]):
        self.success = data.get("success", False)
        self.message = data.get("message", "")
        self.actions = [Action(a) for a in data.get("actions", [])]
        self._raw = data

    def __repr__(self):
        return f"ActResult(success={self.success}, message={self.message!r})"


class Stagehand:
    """
    Main Stagehand client for browser automation.

    Connects to a remote Stagehand server and provides methods for browser automation:
    - act: Execute actions on the page
    - extract: Extract data from the page
    - observe: Observe possible actions on the page
    - goto: Navigate to a URL
    """

    def __init__(
        self,
        server_url: str = "http://localhost:3000",
        verbose: int = 0,
        timeout: float = 120.0,
    ):
        """
        Initialize the Stagehand client.

        Args:
            server_url: URL of the Stagehand server (default: http://localhost:3000)
            verbose: Verbosity level 0-2 (default: 0)
            timeout: Request timeout in seconds (default: 120)
        """
        self.server_url = server_url.rstrip("/")
        self.verbose = verbose
        self.timeout = timeout
        self.session_id: Optional[str] = None
        self._client = httpx.AsyncClient(timeout=timeout)

    async def init(self, **options) -> None:
        """
        Initialize a browser session on the remote server.

        Args:
            **options: Additional options to pass to the server (e.g., model, verbose, etc.)
        """
        if self.session_id:
            raise StagehandError("Already initialized. Call close() first.")

        try:
            response = await self._client.post(
                f"{self.server_url}/v1/sessions/start",
                json=options or {},
            )
            response.raise_for_status()
            data = response.json()

            self.session_id = data.get("sessionId")
            if not self.session_id:
                raise StagehandAPIError("Server did not return a sessionId")

            if self.verbose > 0:
                print(f"✓ Initialized session: {self.session_id}")

        except httpx.HTTPError as e:
            raise StagehandConnectionError(f"Failed to connect to server: {e}")

    async def goto(
        self,
        url: str,
        options: Optional[Dict[str, Any]] = None,
        frame_id: Optional[str] = None,
    ) -> Any:
        """
        Navigate to a URL.

        Args:
            url: The URL to navigate to
            options: Navigation options (waitUntil, timeout, etc.)
            frame_id: Optional frame ID to navigate

        Returns:
            Navigation response
        """
        return await self._execute(
            method="navigate",
            args={
                "url": url,
                "options": options,
                "frameId": frame_id,
            }
        )

    async def act(
        self,
        instruction: Union[str, Action],
        options: Optional[Dict[str, Any]] = None,
        frame_id: Optional[str] = None,
    ) -> ActResult:
        """
        Execute an action on the page.

        Args:
            instruction: Natural language instruction or Action object
            options: Additional options (model, variables, timeout, etc.)
            frame_id: Optional frame ID to act on

        Returns:
            ActResult with success status and executed actions
        """
        input_data = instruction.to_dict() if isinstance(instruction, Action) else instruction

        result = await self._execute(
            method="act",
            args={
                "input": input_data,
                "options": options,
                "frameId": frame_id,
            }
        )

        return ActResult(result)

    async def extract(
        self,
        instruction: Optional[str] = None,
        schema: Optional[Dict[str, Any]] = None,
        options: Optional[Dict[str, Any]] = None,
        frame_id: Optional[str] = None,
    ) -> Any:
        """
        Extract data from the page.

        Args:
            instruction: Natural language instruction for what to extract
            schema: JSON schema defining the expected output structure
            options: Additional options (model, selector, timeout, etc.)
            frame_id: Optional frame ID to extract from

        Returns:
            Extracted data matching the schema (if provided) or default extraction
        """
        return await self._execute(
            method="extract",
            args={
                "instruction": instruction,
                "schema": schema,
                "options": options,
                "frameId": frame_id,
            }
        )

    async def observe(
        self,
        instruction: Optional[str] = None,
        options: Optional[Dict[str, Any]] = None,
        frame_id: Optional[str] = None,
    ) -> List[Action]:
        """
        Observe possible actions on the page.

        Args:
            instruction: Natural language instruction for what to observe
            options: Additional options (model, selector, timeout, etc.)
            frame_id: Optional frame ID to observe

        Returns:
            List of Action objects representing possible actions
        """
        result = await self._execute(
            method="observe",
            args={
                "instruction": instruction,
                "options": options,
                "frameId": frame_id,
            }
        )

        return [Action(action) for action in result]

    async def agent_execute(
        self,
        instruction: str,
        agent_config: Optional[Dict[str, Any]] = None,
        execute_options: Optional[Dict[str, Any]] = None,
        frame_id: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Execute an agent task.

        Args:
            instruction: The task instruction for the agent
            agent_config: Agent configuration (model, systemPrompt, etc.)
            execute_options: Execution options (maxSteps, highlightCursor, etc.)
            frame_id: Optional frame ID to execute in

        Returns:
            Agent execution result
        """
        config = agent_config or {}
        exec_opts = execute_options or {}
        exec_opts["instruction"] = instruction

        return await self._execute(
            method="agentExecute",
            args={
                "agentConfig": config,
                "executeOptions": exec_opts,
                "frameId": frame_id,
            }
        )

    async def close(self) -> None:
        """Close the session and cleanup resources."""
        if self.session_id:
            try:
                await self._client.post(
                    f"{self.server_url}/v1/sessions/{self.session_id}/end"
                )
                if self.verbose > 0:
                    print(f"✓ Closed session: {self.session_id}")
            except Exception as e:
                if self.verbose > 0:
                    print(f"Warning: Failed to close session: {e}")
            finally:
                self.session_id = None

        await self._client.aclose()

    async def _execute(self, method: str, args: Dict[str, Any]) -> Any:
        """
        Execute a method on the remote server using SSE streaming.

        Args:
            method: The method name (act, extract, observe, navigate, agentExecute)
            args: Arguments to pass to the method

        Returns:
            The result from the server
        """
        if not self.session_id:
            raise StagehandError("Not initialized. Call init() first.")

        url = f"{self.server_url}/v1/sessions/{self.session_id}/{method}"

        try:
            async with self._client.stream(
                "POST",
                url,
                json=args,
                headers={"Accept": "text/event-stream"},
            ) as response:
                response.raise_for_status()

                result = None
                async for line in response.aiter_lines():
                    if not line.strip() or not line.startswith("data: "):
                        continue

                    # Parse SSE data
                    data_str = line[6:]  # Remove "data: " prefix
                    try:
                        event = json.loads(data_str)
                    except json.JSONDecodeError:
                        continue

                    event_type = event.get("type")
                    event_data = event.get("data", {})

                    if event_type == "log":
                        # Handle log events
                        if self.verbose > 0:
                            category = event_data.get("category", "")
                            message = event_data.get("message", "")
                            level = event_data.get("level", 0)
                            if level <= self.verbose:
                                print(f"[{category}] {message}")

                    elif event_type == "system":
                        # System events contain the result
                        if "result" in event_data:
                            result = event_data["result"]
                        elif "error" in event_data:
                            raise StagehandAPIError(event_data["error"])

                if result is None:
                    raise StagehandAPIError("No result received from server")

                return result

        except httpx.HTTPStatusError as e:
            error_text = await e.response.aread()
            raise StagehandAPIError(
                f"HTTP {e.response.status_code}: {error_text.decode()}"
            )
        except httpx.HTTPError as e:
            raise StagehandConnectionError(f"Connection error: {e}")

    async def __aenter__(self):
        """Context manager entry"""
        await self.init()
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """Context manager exit"""
        await self.close()


# Example usage
if __name__ == "__main__":
    import asyncio

    async def example():
        # Create and initialize Stagehand client
        stagehand = Stagehand(
            server_url="http://localhost:3000",
            verbose=1,
        )

        try:
            await stagehand.init()

            # Navigate to a page
            print("\n=== Navigating to example.com ===")
            await stagehand.goto("https://example.com")

            # Extract data
            print("\n=== Extracting page title ===")
            data = await stagehand.extract("extract the page title")
            print(f"Extracted: {data}")

            # Observe actions
            print("\n=== Observing actions ===")
            actions = await stagehand.observe("find all links on the page")
            print(f"Found {len(actions)} actions")
            if actions:
                print(f"First action: {actions[0]}")

            # Execute an action
            print("\n=== Executing action ===")
            result = await stagehand.act("scroll to the bottom")
            print(f"Result: {result}")

        finally:
            await stagehand.close()

    # Alternative: using context manager
    async def example_with_context_manager():
        async with Stagehand(server_url="http://localhost:3000") as stagehand:
            await stagehand.goto("https://example.com")
            data = await stagehand.extract("extract the page title")
            print(data)

    # Run the example
    asyncio.run(example())

After this ships we can also remove the duplicated fastify server in core/stagehand-api and use event hooks to do the cloud-only stuff. See my message in Slack https://browserbase.slack.com/archives/C08EZ5W9TB9/p1763775335075319

OpenAPI Spec:

openapi: 3.0.3
info:
  title: Stagehand P2P Server API
  description: |
    HTTP API for remote Stagehand browser automation. This API allows clients to
    connect to a Stagehand server and execute browser automation tasks remotely.

    All endpoints except /sessions/start require an active session ID.
    Responses are streamed using Server-Sent Events (SSE) when the
    `x-stream-response: true` header is provided.
  version: 3.0.0
  contact:
    name: Browserbase
    url: https://browserbase.com

servers:
  - url: http://localhost:3000/v1
    description: Local P2P server
  - url: https://api.stagehand.browserbase.com/v1
    description: Cloud API (for reference)

paths:
  /sessions/start:
    post:
      summary: Create a new browser session
      description: |
        Initializes a new Stagehand session with a browser instance.
        Returns a session ID that must be used for all subsequent requests.
      operationId: createSession
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SessionConfig'
            examples:
              local:
                summary: Local browser session
                value:
                  env: LOCAL
                  verbose: 1
              browserbase:
                summary: Browserbase session
                value:
                  env: BROWSERBASE
                  apiKey: bb_api_key_123
                  projectId: proj_123
                  verbose: 1
      responses:
        '200':
          description: Session created successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - sessionId
                  - available
                properties:
                  sessionId:
                    type: string
                    format: uuid
                    description: Unique identifier for the session
                  available:
                    type: boolean
                    description: Whether the session is ready to use
        '500':
          $ref: '#/components/responses/InternalError'

  /sessions/{sessionId}/act:
    post:
      summary: Execute an action on the page
      description: |
        Performs a browser action based on natural language instruction or
        a specific action object returned by observe().
      operationId: act
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - $ref: '#/components/parameters/StreamResponse'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActRequest'
            examples:
              stringInstruction:
                summary: Natural language instruction
                value:
                  input: "click the sign in button"
              actionObject:
                summary: Execute observed action
                value:
                  input:
                    selector: "#login-btn"
                    description: "Sign in button"
                    method: "click"
                    arguments: []
      responses:
        '200':
          description: Action executed successfully
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/SSEResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ActResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/SessionNotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /sessions/{sessionId}/extract:
    post:
      summary: Extract structured data from the page
      description: |
        Extracts data from the current page using natural language instructions
        and optional JSON schema for structured output.
      operationId: extract
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - $ref: '#/components/parameters/StreamResponse'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExtractRequest'
            examples:
              simple:
                summary: Simple extraction
                value:
                  instruction: "extract the page title"
              withSchema:
                summary: Structured extraction
                value:
                  instruction: "extract all product listings"
                  schema:
                    type: object
                    properties:
                      products:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                            price:
                              type: string
      responses:
        '200':
          description: Data extracted successfully
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/SSEResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ExtractResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/SessionNotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /sessions/{sessionId}/observe:
    post:
      summary: Observe possible actions on the page
      description: |
        Returns a list of candidate actions that can be performed on the page,
        optionally filtered by natural language instruction.
      operationId: observe
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - $ref: '#/components/parameters/StreamResponse'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ObserveRequest'
            examples:
              allActions:
                summary: Observe all actions
                value: {}
              filtered:
                summary: Observe specific actions
                value:
                  instruction: "find all buttons"
      responses:
        '200':
          description: Actions observed successfully
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/SSEResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ObserveResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/SessionNotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /sessions/{sessionId}/agentExecute:
    post:
      summary: Execute a multi-step agent task
      description: |
        Runs an autonomous agent that can perform multiple actions to
        complete a complex task.
      operationId: agentExecute
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - $ref: '#/components/parameters/StreamResponse'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentExecuteRequest'
            examples:
              basic:
                summary: Basic agent task
                value:
                  agentConfig:
                    model: "openai/gpt-4o"
                  executeOptions:
                    instruction: "Find and click the first product"
                    maxSteps: 10
      responses:
        '200':
          description: Agent task completed
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/SSEResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/AgentResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/SessionNotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /sessions/{sessionId}/navigate:
    post:
      summary: Navigate to a URL
      description: |
        Navigates the browser to the specified URL and waits for page load.
      operationId: navigate
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - $ref: '#/components/parameters/StreamResponse'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NavigateRequest'
            examples:
              simple:
                summary: Simple navigation
                value:
                  url: "https://example.com"
              withOptions:
                summary: Navigation with options
                value:
                  url: "https://example.com"
                  options:
                    waitUntil: "networkidle"
      responses:
        '200':
          description: Navigation completed
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/SSEResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/NavigateResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/SessionNotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /sessions/{sessionId}/end:
    post:
      summary: End the session and cleanup resources
      description: |
        Closes the browser and cleans up all resources associated with the session.
      operationId: endSession
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session ended successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '500':
          $ref: '#/components/responses/InternalError'

components:
  parameters:
    SessionId:
      name: sessionId
      in: path
      required: true
      description: The session ID returned by /sessions/start
      schema:
        type: string
        format: uuid

    StreamResponse:
      name: x-stream-response
      in: header
      description: Enable Server-Sent Events streaming for real-time logs
      schema:
        type: string
        enum: ["true", "false"]
        default: "true"

  schemas:
    SessionConfig:
      type: object
      required:
        - env
      properties:
        env:
          type: string
          enum: [LOCAL, BROWSERBASE]
          description: Environment to run the browser in
        verbose:
          type: integer
          minimum: 0
          maximum: 2
          default: 0
          description: Logging verbosity level
        model:
          type: string
          description: AI model to use for actions
          example: "openai/gpt-4o"
        apiKey:
          type: string
          description: API key for Browserbase (required when env=BROWSERBASE)
        projectId:
          type: string
          description: Project ID for Browserbase (required when env=BROWSERBASE)
        systemPrompt:
          type: string
          description: Custom system prompt for AI actions
        domSettleTimeout:
          type: integer
          description: Timeout in ms to wait for DOM to settle
        selfHeal:
          type: boolean
          description: Enable self-healing for failed actions
        localBrowserLaunchOptions:
          type: object
          description: Options for local browser launch
          properties:
            headless:
              type: boolean
              default: true

    ActRequest:
      type: object
      required:
        - input
      properties:
        input:
          oneOf:
            - type: string
              description: Natural language instruction
            - $ref: '#/components/schemas/Action'
        options:
          $ref: '#/components/schemas/ActionOptions'
        frameId:
          type: string
          description: Frame ID to act on (optional)

    Action:
      type: object
      required:
        - selector
        - description
        - method
        - arguments
      properties:
        selector:
          type: string
          description: CSS or XPath selector for the element
        description:
          type: string
          description: Human-readable description of the action
        backendNodeId:
          type: integer
          description: CDP backend node ID
        method:
          type: string
          description: Method to execute (e.g., "click", "fill")
        arguments:
          type: array
          items:
            type: string
          description: Arguments for the method

    ActionOptions:
      type: object
      properties:
        model:
          $ref: '#/components/schemas/ModelConfig'
        variables:
          type: object
          additionalProperties:
            type: string
          description: Template variables for instruction
        timeout:
          type: integer
          description: Timeout in milliseconds

    ModelConfig:
      type: object
      properties:
        provider:
          type: string
          enum: [openai, anthropic, google]
        model:
          type: string
          description: Model name
        apiKey:
          type: string
          description: API key for the model provider
        baseURL:
          type: string
          format: uri
          description: Custom base URL for API

    ExtractRequest:
      type: object
      properties:
        instruction:
          type: string
          description: Natural language instruction for extraction
        schema:
          type: object
          description: JSON Schema for structured output
          additionalProperties: true
        options:
          type: object
          properties:
            model:
              $ref: '#/components/schemas/ModelConfig'
            timeout:
              type: integer
            selector:
              type: string
              description: Extract only from elements matching this selector
        frameId:
          type: string
          description: Frame ID to extract from

    ObserveRequest:
      type: object
      properties:
        instruction:
          type: string
          description: Natural language instruction to filter actions
        options:
          type: object
          properties:
            model:
              $ref: '#/components/schemas/ModelConfig'
            timeout:
              type: integer
            selector:
              type: string
              description: Observe only elements matching this selector
        frameId:
          type: string
          description: Frame ID to observe

    AgentExecuteRequest:
      type: object
      required:
        - agentConfig
        - executeOptions
      properties:
        agentConfig:
          type: object
          properties:
            provider:
              type: string
              enum: [openai, anthropic, google]
            model:
              oneOf:
                - type: string
                - $ref: '#/components/schemas/ModelConfig'
            systemPrompt:
              type: string
            cua:
              type: boolean
              description: Enable Computer Use Agent mode
        executeOptions:
          type: object
          required:
            - instruction
          properties:
            instruction:
              type: string
              description: Task for the agent to complete
            maxSteps:
              type: integer
              default: 20
              description: Maximum number of steps the agent can take
            highlightCursor:
              type: boolean
              description: Visually highlight the cursor during actions
        frameId:
          type: string

    NavigateRequest:
      type: object
      required:
        - url
      properties:
        url:
          type: string
          format: uri
          description: URL to navigate to
        options:
          type: object
          properties:
            waitUntil:
              type: string
              enum: [load, domcontentloaded, networkidle]
              default: load
              description: When to consider navigation complete
        frameId:
          type: string

    ActResult:
      type: object
      required:
        - success
        - message
        - actions
      properties:
        success:
          type: boolean
          description: Whether the action succeeded
        message:
          type: string
          description: Result message
        actions:
          type: array
          items:
            $ref: '#/components/schemas/Action'
          description: Actions that were executed

    ExtractResult:
      oneOf:
        - type: object
          description: Default extraction result
          properties:
            extraction:
              type: string
        - type: object
          description: Structured data matching provided schema
          additionalProperties: true

    ObserveResult:
      type: array
      items:
        $ref: '#/components/schemas/Action'
      description: List of observed actions

    AgentResult:
      type: object
      properties:
        message:
          type: string
          description: Final message from the agent
        steps:
          type: array
          items:
            type: object
          description: Steps taken by the agent

    NavigateResult:
      type: object
      nullable: true
      description: Navigation response (may be null)
      properties:
        ok:
          type: boolean
        status:
          type: integer
        url:
          type: string

    SSEResponse:
      description: |
        Server-Sent Events stream. Each event is prefixed with "data: "
        and contains a JSON object with type and data fields.
      type: object
      required:
        - id
        - type
        - data
      properties:
        id:
          type: string
          format: uuid
          description: Unique event ID
        type:
          type: string
          enum: [system, log]
          description: Event type
        data:
          oneOf:
            - $ref: '#/components/schemas/SystemEvent'
            - $ref: '#/components/schemas/LogEvent'

    SystemEvent:
      type: object
      properties:
        status:
          type: string
          enum: [starting, connected, finished, error]
          description: System status
        result:
          description: Result data (present when status=finished)
        error:
          type: string
          description: Error message (present when status=error)

    LogEvent:
      type: object
      required:
        - status
        - message
      properties:
        status:
          type: string
          const: running
        message:
          type: object
          required:
            - category
            - message
            - level
          properties:
            category:
              type: string
              description: Log category
            message:
              type: string
              description: Log message
            level:
              type: integer
              minimum: 0
              maximum: 2
              description: Log level (0=error, 1=info, 2=debug)

    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Error message
        details:
          description: Additional error details

  responses:
    BadRequest:
      description: Invalid request parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    SessionNotFound:
      description: Session ID not found or expired
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

@changeset-bot
Copy link

changeset-bot bot commented Nov 22, 2025

⚠️ No Changeset found

Latest commit: 3eb42da

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

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