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

# run

> Execute automation tasks using the Narada Python SDK

`Agent.run()` executes a natural-language task in a Narada environment and returns an `AgentResponse`.

Create an environment first, then bind an agent to it:

```python theme={null}
from narada import Agent, BrowserEnvironment

env = BrowserEnvironment()
agent = Agent(environment=env)

response = await agent.run(prompt="Search for Narada AI and summarize the first result.")
print(response.text)
```

<Note>
  `Agent.run()` is the current SDK interface. The older `Narada().open_and_initialize_browser_window()` and `window.agent(...)` pattern is no longer the recommended API.
</Note>

## Constructor

```python theme={null}
class Agent:
    def __init__(
        self,
        *,
        environment: Environment,
        kind: AgentKind | str = AgentKind.OPERATOR,
    ) -> None: ...
```

## Run Signature

```python theme={null}
async def run(
    self,
    prompt: str,
    *,
    reasoning: ReasoningEffort | None = None,
    clear_chat: bool | None = None,
    generate_gif: bool | None = None,
    output_schema: type[BaseModel] | None = None,
    previous_request_id: str | None = None,
    chat_history: list[RemoteDispatchChatHistoryItem] | None = None,
    additional_context: dict[str, str] | None = None,
    attachment: File | IO[Any] | None = None,
    time_zone: str = "America/Los_Angeles",
    user_resource_credentials: UserResourceCredentials | None = None,
    mcp_servers: list[McpServer] | None = None,
    secret_variables: dict[str, str] | None = None,
    input_variables: Mapping[str, Any] | None = None,
    callback_url: str | None = None,
    callback_secret: str | None = None,
    callback_headers: Mapping[str, Any] | None = None,
    on_input_required: InputRequiredCallback | None = None,
    critic: CriticConfig | None = None,
    timeout: int = 1000,
) -> AgentResponse
```

## Agent Kinds

Choose the agent kind when constructing `Agent`.

<ParamField query="kind" type="AgentKind | str" default="AgentKind.OPERATOR">
  The agent to use for task execution.

  <Tabs>
    <Tab title="OPERATOR">
      Best for browser automation tasks like clicking, navigating, filling forms, and extracting data from pages.

      ```python theme={null}
      from narada import Agent, AgentKind

      agent = Agent(environment=env, kind=AgentKind.OPERATOR)
      ```
    </Tab>

    <Tab title="CORE_AGENT">
      Best for read-only reasoning, answering questions about page content, and conversation-style tasks.

      ```python theme={null}
      from narada import Agent, AgentKind

      agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
      ```
    </Tab>

    <Tab title="PRODUCTIVITY">
      Best for general productivity and non-browser-specific tasks.

      ```python theme={null}
      from narada import Agent, AgentKind

      agent = Agent(environment=env, kind=AgentKind.PRODUCTIVITY)
      ```
    </Tab>

    <Tab title="Custom Agent">
      Invoke custom agents created in [Agent Studio](https://app.narada.ai/agent-studio) using the namespaced format.

      ```python theme={null}
      # Use $USER shorthand for your own account.
      agent = Agent(environment=env, kind="/$USER/my-data-analyst")

      # Or reference a shared agent explicitly.
      agent = Agent(environment=env, kind="/jane@company.com/sales-report-generator")
      ```

      The same syntax works in the Narada chat side panel:

      ```text theme={null}
      /$USER/my-agent Search for recent tech news
      /jane@company.com/research-bot Find papers on transformers
      ```

      <Note>
        Agents must be shared or published in [Agent Studio](/documentation/agent-studio) before other users can invoke them.
      </Note>
    </Tab>
  </Tabs>
</ParamField>

## Parameters

<ParamField query="prompt" type="str" required>
  The natural-language instruction for the task. When using `AgentKind.OPERATOR`, Narada applies the correct Operator prefix automatically.

  ```python theme={null}
  prompt="Search for machine learning jobs and extract the first 5 results"
  ```
</ParamField>

<ParamField query="reasoning" type="ReasoningEffort | None" default="None">
  Controls how much reasoning the Core Agent uses before responding. This option is only valid with `AgentKind.CORE_AGENT`.

  ```python theme={null}
  from narada import AgentKind, ReasoningEffort

  agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
  response = await agent.run(
      prompt="Analyze the page and explain the pricing model.",
      reasoning=ReasoningEffort.MEDIUM,
  )
  ```
</ParamField>

<ParamField query="clear_chat" type="bool | None" default="None">
  Whether to clear chat context before executing the command.

  ```python theme={null}
  clear_chat=True
  ```
</ParamField>

<ParamField query="generate_gif" type="bool | None" default="None">
  Whether to capture an animated GIF of the automation trajectory.

  ```python theme={null}
  generate_gif=True
  ```
</ParamField>

<ParamField query="output_schema" type="type[BaseModel] | None" default="None">
  A Pydantic model class defining the expected structured response.

  ```python theme={null}
  from pydantic import BaseModel, Field

  class JobListing(BaseModel):
      title: str = Field(description="Job title")
      company: str = Field(description="Company name")
      location: str = Field(description="Job location")

  response = await agent.run(
      prompt="Extract the first job listing.",
      output_schema=JobListing,
  )
  ```
</ParamField>

<ParamField query="previous_request_id" type="str | None" default="None">
  The prior request ID to continue a conversation.

  ```python theme={null}
  first = await agent.run(prompt="Pick a lucky number.")
  second = await agent.run(
      prompt="What number did you pick?",
      previous_request_id=first.request_id,
  )
  ```
</ParamField>

<ParamField query="attachment" type="File | IO[Any] | None" default="None">
  A file-like object to attach to the request. The SDK uploads the file automatically before dispatching the task.

  ```python theme={null}
  with open("document.pdf", "rb") as f:
      response = await agent.run(
          prompt="Summarize the attached document.",
          attachment=f,
      )
  ```
</ParamField>

<ParamField query="time_zone" type="str" default="America/Los_Angeles">
  The time zone for time-related operations.

  ```python theme={null}
  time_zone="America/New_York"
  ```
</ParamField>

<ParamField query="mcp_servers" type="list[McpServer] | None" default="None">
  MCP servers to connect during task execution.

  ```python theme={null}
  from narada_core.models import AuthenticationNone, McpServer

  mcp_server = McpServer(
      url="https://your-mcp-server.example.com",
      label="My MCP Server",
      description="Custom tools",
      authentication=AuthenticationNone(),
  )

  response = await agent.run(
      prompt="Use the MCP tools to process this data.",
      mcp_servers=[mcp_server],
  )
  ```
</ParamField>

<ParamField query="secret_variables" type="dict[str, str] | None" default="None">
  Sensitive values substituted at action time. The LLM sees placeholders such as `${password}`, not the actual values.

  ```python theme={null}
  response = await agent.run(
      prompt="Log in with username ${username} and password ${password}.",
      secret_variables={
          "username": "john.doe@company.com",
          "password": "super_secret_password_123",
      },
  )
  ```
</ParamField>

<ParamField query="input_variables" type="Mapping[str, Any] | None" default="None">
  Values passed into prompts using `{{$variable_name}}` syntax. File-like values are uploaded automatically.

  ```python theme={null}
  response = await agent.run(
      prompt="Summarize {{$doc}}.",
      input_variables={"doc": file_obj},
  )
  ```
</ParamField>

<ParamField query="callback_url" type="str | None" default="None">
  URL to call when the task status changes or completes. Use this with `callback_secret` or `callback_headers` to authenticate callbacks.
</ParamField>

<ParamField query="on_input_required" type="InputRequiredCallback | None" default="None">
  Callback invoked when a task enters an input-required state.
</ParamField>

<ParamField query="critic" type="CriticConfig | None" default="None">
  Optional critic configuration that validates the run after the main agent finishes.
</ParamField>

<ParamField query="timeout" type="int" default="1000">
  Maximum time in seconds to wait for task completion.

  ```python theme={null}
  timeout=180
  ```
</ParamField>

## Response

`Agent.run()` returns an `AgentResponse` object:

<ResponseField name="request_id" type="str">
  Unique identifier for this request. Use it for debugging, callbacks, or conversation continuation.
</ResponseField>

<ResponseField name="status" type="str">
  Execution status: `"success"`, `"error"`, or `"input-required"`.
</ResponseField>

<ResponseField name="text" type="str">
  The text response from the agent.
</ResponseField>

<ResponseField name="structured_output" type="BaseModel | None">
  Parsed structured data when `output_schema` is provided. Otherwise `None`.
</ResponseField>

<ResponseField name="output" type="TextOutput | StructuredOutput">
  Discriminated union containing either text or structured data:

  ```typescript theme={null}
  { type: "text", content: string }
  | { type: "structured", content: object }
  ```
</ResponseField>

<ResponseField name="usage" type="AgentUsage">
  Usage metrics:

  * `actions`: Number of actions performed
  * `credits`: Credits consumed for the task
</ResponseField>

<ResponseField name="action_trace" type="ActionTrace | None">
  Detailed log of agent actions. See [Action Trace](/documentation/action-trace) for the trace format.
</ResponseField>

<ResponseField name="workflow_trace" type="dict | None">
  Workflow-level trace data when available.
</ResponseField>

<ResponseField name="critic_result" type="CriticResult | None">
  Result from the optional critic step.
</ResponseField>

## Examples

<Tabs>
  <Tab title="Basic Web Automation">
    ```python theme={null}
    import asyncio

    from narada import Agent, BrowserEnvironment


    async def main() -> None:
        env = BrowserEnvironment()
        agent = Agent(environment=env)

        try:
            response = await agent.run(
                prompt="Search for Python tutorials on Google and get the title of the first result",
            )

            print(f"Status: {response.status}")
            print(f"Result: {response.text}")
            print(f"Actions used: {response.usage.actions}")
        finally:
            await env.close()


    if __name__ == "__main__":
        asyncio.run(main())
    ```
  </Tab>

  <Tab title="Structured Data Extraction">
    ```python theme={null}
    import asyncio

    from narada import Agent, BrowserEnvironment
    from pydantic import BaseModel, Field


    class SearchResult(BaseModel):
        query: str = Field(description="The search query used")
        result_count: str = Field(description="Number of results found")
        top_result_title: str = Field(description="Title of the first result")
        top_result_url: str = Field(description="URL of the first result")


    async def main() -> None:
        env = BrowserEnvironment()
        agent = Agent(environment=env)

        try:
            response = await agent.run(
                prompt="Search for AI research papers and extract search details",
                output_schema=SearchResult,
            )

            data = response.structured_output
            assert data is not None

            print(f"Query: {data.query}")
            print(f"Results: {data.result_count}")
            print(f"Top result: {data.top_result_title}")
            print(f"URL: {data.top_result_url}")
        finally:
            await env.close()


    if __name__ == "__main__":
        asyncio.run(main())
    ```
  </Tab>

  <Tab title="Conversation">
    ```python theme={null}
    import asyncio

    from narada import Agent, AgentKind, BrowserEnvironment


    async def main() -> None:
        env = BrowserEnvironment()
        agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)

        try:
            response = await agent.run(
                prompt="Pick a lucky number between 1 and 100 for me.",
            )
            print("Agent:", response.text)

            response = await agent.run(
                prompt="What did you pick again?",
                previous_request_id=response.request_id,
            )
            print("Agent:", response.text)

            response = await agent.run(
                prompt="What's double that number?",
                previous_request_id=response.request_id,
            )
            print("Agent:", response.text)
        finally:
            await env.close()


    if __name__ == "__main__":
        asyncio.run(main())
    ```
  </Tab>

  <Tab title="Error Handling">
    ```python theme={null}
    import asyncio

    from narada import Agent, BrowserEnvironment, NaradaTimeoutError


    async def main() -> None:
        env = BrowserEnvironment()
        agent = Agent(environment=env)

        try:
            max_attempts = 3
            for attempt in range(max_attempts):
                try:
                    response = await agent.run(
                        prompt="Search for complex data analysis tutorials and summarize the top 3 results",
                        timeout=60,
                        generate_gif=True,
                    )

                    print("Task completed successfully!")
                    print(response.text)
                    break
                except NaradaTimeoutError:
                    if attempt == max_attempts - 1:
                        raise

                    print(f"Attempt {attempt + 1} timed out, retrying...")
                    await agent.reset_agent_state()
        finally:
            await env.close()


    if __name__ == "__main__":
        asyncio.run(main())
    ```
  </Tab>

  <Tab title="File Attachments">
    ```python theme={null}
    import asyncio

    from narada import Agent, AgentKind, BrowserEnvironment
    from pydantic import BaseModel, Field


    class InvoiceData(BaseModel):
        invoice_number: str = Field(description="Invoice number")
        date: str = Field(description="Invoice date")
        vendor_name: str = Field(description="Vendor or supplier name")
        total_amount: str = Field(description="Total amount with currency")
        line_items: list[str] = Field(description="List of items or services")


    async def main() -> None:
        env = BrowserEnvironment()
        agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)

        try:
            with open("/path/to/invoice.pdf", "rb") as f:
                response = await agent.run(
                    prompt="Extract the invoice details from the attached document.",
                    attachment=f,
                    output_schema=InvoiceData,
                )

            invoice = response.structured_output
            assert invoice is not None

            print(f"Invoice: {invoice.invoice_number}")
            print(f"Date: {invoice.date}")
            print(f"Vendor: {invoice.vendor_name}")
            print(f"Total: {invoice.total_amount}")
            print(f"Items: {', '.join(invoice.line_items)}")
        finally:
            await env.close()


    if __name__ == "__main__":
        asyncio.run(main())
    ```
  </Tab>

  <Tab title="Custom Agent">
    ```python theme={null}
    import asyncio

    from narada import Agent, BrowserEnvironment


    async def main() -> None:
        env = BrowserEnvironment()
        agent = Agent(environment=env, kind="/$USER/my-research-agent")

        try:
            response = await agent.run(
                prompt="Search for recent machine learning papers.",
            )
            print(response.text)
        finally:
            await env.close()


    if __name__ == "__main__":
        asyncio.run(main())
    ```
  </Tab>
</Tabs>

## Best Practices

<CardGroup cols={2}>
  <Card title="Reuse Environments" icon="browser">
    Create one environment and reuse it across multiple `Agent` instances when tasks should share a browser session.
  </Card>

  <Card title="Choose the Agent Kind" icon="check">
    Use `AgentKind.OPERATOR` for browser automation and `AgentKind.CORE_AGENT` for read-only reasoning or conversation.
  </Card>

  <Card title="Use Structured Output" icon="database">
    Define Pydantic schemas for consistent, typed responses from data extraction tasks.
  </Card>

  <Card title="Protect Sensitive Data" icon="shield">
    Use `secret_variables` for passwords, API keys, or personal information that should not be exposed to the LLM.
  </Card>

  <Card title="Pass Files Directly" icon="file-pdf">
    Pass file-like objects with `attachment=` instead of calling a separate upload method.
  </Card>

  <Card title="Handle Timeouts" icon="clock">
    Set appropriate timeouts and call `agent.reset_agent_state()` before retrying after a timeout.
  </Card>
</CardGroup>

## Migration from Older SDKs

Older SDK examples used a Narada client and window object:

```python theme={null}
async with Narada() as narada:
    window = await narada.open_and_initialize_browser_window()
    response = await window.agent(prompt='search for "jobs" on Google')
```

Use an environment and agent instead:

```python theme={null}
env = BrowserEnvironment()
agent = Agent(environment=env)

try:
    response = await agent.run(prompt='search for "jobs" on Google')
    print(response.text)
finally:
    await env.close()
```

For browser actions, call methods on `agent`:

```python theme={null}
await agent.go_to_url(url="https://example.com")
await agent.agentic_selector(
    action={"type": "click"},
    selectors={"aria_label": "Submit"},
    fallback_operator_query="click the Submit button",
)
```

For lifecycle and IDs, use the environment:

```python theme={null}
await env.start()
print(env.browser_window_id)
await env.close()
```
