TabSailTabSail
Integrations

Browser Use

Attach a Python Browser Use browser to a TabSail session while keeping the agent loop in your application.

Validation scope

Browser Use 0.13.11 with gpt-6-luna passed the local Snipe-IT 8.8.3 workflow: the same Agent and run resume after two Live handoffs, preserve the operator's report selection, validate a CSV, and release resources. The operator is simulated through the real Live protocol. Validate your chosen model and task separately.

Responsibilities

Your Python application runs the Browser Use agent, selects and pays for its model, validates results, and owns task retries. TabSail provides the remote browser and human handoff; it does not run this agent. Browser Use Cloud credentials and TabSail project credentials belong to different services.

Install and configure

Create a Python project and install the SDK and Browser Use:

uv add "tabsail==0.1.0" "browser-use==0.13.11"
export TABSAIL_BASE_URL="https://tabsail.com"
export TABSAIL_API_KEY="YOUR_PROJECT_KEY"

Verify the browser attachment first

Save this as browser_use_connection.py and run uv run python browser_use_connection.py. It checks browser attachment without invoking a model.

import asyncio
import logging
import os

os.environ["ANONYMIZED_TELEMETRY"] = "false"
logging.disable(logging.CRITICAL)

from browser_use import Browser
from tabsail import AsyncTabSail

async def main():
    async with AsyncTabSail() as client:
        async with client.with_session(timeout_seconds=300) as session:
            grant = await client.sessions.connections.create(session.id)
            browser = Browser(
                cdp_url=grant.url, is_local=False, keep_alive=True,
                permissions=[], enable_default_extensions=False,
                captcha_solver=False, downloads_path=session.download_directory,
            )
            try:
                await browser.start()
                await browser.navigate_to("https://example.com", new_tab=False)
                page = await browser.get_current_page()
                if page is None:
                    raise RuntimeError("Existing page missing")
                print(await page.get_title())
            finally:
                await browser.stop()

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except Exception as error:
        print(type(error).__name__)
        raise SystemExit(1) from None

The expected title is Example Domain. The owned-session scope is responsible for closing the TabSail session even if the framework fails.

Add your agent loop

After validating attachment, pass the connected Browser object to your version's Browser Use Agent. Keep model credentials in the application environment. Configure a task deadline, inspect the result, and save it before leaving the session scope.

The qualified model configuration includes the action schema in the system prompt:

from browser_use.llm import ChatOpenAI

model = ChatOpenAI(
    model="gpt-6-luna",
    temperature=None,
    frequency_penalty=None,
    add_schema_to_system_prompt=True,
    max_completion_tokens=2048,
    max_retries=0,
    timeout=120,
)

Set OPENAI_API_KEY and, if needed, OPENAI_BASE_URL in your application environment. This option improved structured output in the tested model connection; response validation still applies.

Check the official Browser Use documentation against your installed version. Do not assume its latest Agent API matches the pinned connection adapter.

Continue the same task after handoff

The source example examples/sdk/python/browser_use_handoff.py provides handoff_bridge. Supply callbacks that save the checkpoint and notify your operator; model selection and task logic stay in your application.

With an initialized client, session, Browser and your own model:

from browser_use import Agent
from browser_use_handoff import handoff_bridge

tools, on_step_end = handoff_bridge(
    client, session.id, save_checkpoint, notify_operator,
)
agent = Agent(
    task=task, llm=model, browser_session=browser, tools=tools,
    use_vision=False, use_thinking=False,
)
history = await agent.run(max_steps=20, on_step_end=on_step_end)
if not history.is_successful():
    raise RuntimeError('Agent task failed')

save_checkpoint persists the supplied checkpoint in your application. notify_operator sends the reason to the person who takes control and later returns it through your app or console. Keep the Agent.run() alive. The bridge pauses it, disconnects before human control, waits for handoff completion, reconnects with a fresh grant, and observes the current tab before resuming. Timeout, cancellation, or a closed task tab stops the task.

Third-party libraries can log CDP URLs. Configure their logging and telemetry so credentials are not captured. See the handoff contract and business report fixture.

On this page