Packages
Browser automation actions for Jido AI agents
Current section
Files
Jump to
Current section
Files
jido_browser
README.md
README.md
# Jido Browser
[](https://hex.pm/packages/jido_browser)
[](https://hexdocs.pm/jido_browser/)
[](https://github.com/agentjido/jido_browser/actions/workflows/ci.yml)
[](https://github.com/agentjido/jido_browser/blob/main/LICENSE)
[](https://jido.run)
[](https://jido.run/ecosystem)
[](https://jido.run/discord)
Browser automation for Jido AI agents.
## Overview
`Jido.Browser` is organized around three simple lanes:
- `web_fetch/2` for stateless HTTP-first retrieval
- `fetch_rich/2` for agent-friendly retrieval with optional browser fallback
- `start_session/1` and `end_session/1` for browser-backed workflows
- `Jido.Browser.Pool` plus `start_session(pool: ...)` as an optional acceleration layer
`agent-browser` remains the default adapter. `Web` also supports warm pools when
you want browser-backed sessions with lower cold-start overhead. `Vibium`
remains available without warm-pool support. `Lightpanda` is available as an
optional limited adapter for lightweight DOM and JavaScript automation, with
warm-pool support for prestarted CDP sessions.
The Hex package and OTP app remain `jido_browser`, while the public Elixir namespace is `Jido.Browser.*`.
## Installation
Add the dependency:
```elixir
def deps do
[
{:jido_browser, "~> 2.0"}
]
end
```
Install the default browser backend:
```bash
mix jido_browser.install
```
That installs the pinned `agent-browser` binary for the current platform and runs `agent-browser install` to provision the browser runtime.
### Recommended Alias Setup
```elixir
defp aliases do
[
setup: ["deps.get", "jido_browser.install --if-missing"],
test: ["jido_browser.install --if-missing", "test"]
]
end
```
### Installing Specific Backends
```bash
mix jido_browser.install agent_browser
mix jido_browser.install vibium
mix jido_browser.install web
mix jido_browser.install lightpanda
```
Lightpanda support uses optional dependencies. Add them to applications that
select `Jido.Browser.Adapters.Lightpanda`:
```elixir
def deps do
[
{:jido_browser, "~> 2.0"},
{:light_cdp, "~> 0.2.1"},
{:lightpanda_ex, "~> 0.1.0"}
]
end
```
## Quick Start
```elixir
{:ok, session} = Jido.Browser.start_session()
{:ok, session, _} = Jido.Browser.navigate(session, "https://example.com")
{:ok, session, snapshot} = Jido.Browser.snapshot(session)
snapshot["snapshot"] || snapshot[:snapshot]
{:ok, session, _} = Jido.Browser.click(session, "@e1")
{:ok, _session, %{content: markdown}} = Jido.Browser.extract_content(session, format: :markdown)
:ok = Jido.Browser.end_session(session)
```
Selectors remain supported, but ref-based interaction is the preferred 2.0 flow:
1. `snapshot`
2. act on `@eN` refs
3. re-snapshot
### Stateless Web Fetch
```elixir
{:ok, result} =
Jido.Browser.web_fetch(
"https://example.com/docs",
format: :markdown,
allowed_domains: ["example.com"],
focus_terms: ["API", "authentication"],
citations: true
)
result.content
result.passages
result.metadata # present when extraction returns document metadata
```
`web_fetch/2` keeps HTML handling native for selector extraction and markdown conversion, and uses `extractous_ex` for fetched binary documents such as PDFs, Word, Excel, PowerPoint, OpenDocument, EPUB, and common email formats. Binary document responses may also include `result.metadata` when extraction returns document metadata.
`Req` is the default HTTP backend. `jido_browser` also includes a vendored
BrowseyHttp-backed backend when you want a browser-imitating HTTP path for pages
that do not require JavaScript execution. Select it globally or per request:
```elixir
config :jido_browser, :web_fetch,
backend: Jido.Browser.WebFetch.Backends.Browsey,
browsey: [
browser: :chrome,
timeout: 30_000
]
{:ok, result} =
Jido.Browser.web_fetch(
"https://example.com/docs",
format: :markdown,
backend: :browsey,
browsey: [browser: :safari]
)
```
BrowseyHttp still does not execute JavaScript. Sites that require a rendered browser should use a browser session instead. Egress also matters: datacenter IP ranges, CI traffic, or too many requests from one IP can still trigger challenges even with browser-like HTTP fingerprints. `web_fetch/2` passes backend-specific `:req` and `:browsey` keyword options from config and runtime opts so applications can supply transport settings without coupling `jido_browser` to a proxy provider.
BrowseyHttp is vendored from [s3cur3/browsey_http](https://github.com/s3cur3/browsey_http)
under its MIT license because it is not currently published on Hex. The vendored
copy keeps `jido_browser` Hex-publishable; if BrowseyHttp is released on Hex,
this project should replace the vendored copy with the upstream Hex dependency.
### Agent-Friendly Rich Fetch
Use `fetch_rich/2` when an agent needs one retrieval tool that starts with cheap
HTTP/document extraction and can fall back to a browser only when explicitly
allowed:
```elixir
{:ok, result} =
Jido.Browser.fetch_rich(
"https://example.com/protected-docs",
http_backends: [:req, :browsey],
browser_fallback: true,
pool: :default,
citations: true
)
result.retrieval_path # :web_fetch, :browsey, or :browser
result.blocked?
result.content
```
`fetch_rich/2` returns the same core result shape as `web_fetch/2` and adds
`retrieval_path`, `fallback_reason`, and `blocked?`. `web_fetch/2` remains
stateless and never uses pools.
### State Persistence
```elixir
state_path = Path.expand("tmp/browser-state.json")
File.mkdir_p!(Path.dirname(state_path))
{:ok, session} = Jido.Browser.start_session()
{:ok, session, _} = Jido.Browser.navigate(session, "https://example.com")
{:ok, session, _} = Jido.Browser.save_state(session, state_path)
:ok = Jido.Browser.end_session(session)
{:ok, restored} = Jido.Browser.start_session()
{:ok, restored, _} = Jido.Browser.load_state(restored, state_path)
```
### Tab Workflow
```elixir
{:ok, session} = Jido.Browser.start_session()
{:ok, session, _} = Jido.Browser.navigate(session, "https://example.com")
{:ok, session, _} = Jido.Browser.new_tab(session, "https://example.org")
{:ok, session, tabs} = Jido.Browser.list_tabs(session)
{:ok, session, _} = Jido.Browser.switch_tab(session, 1)
{:ok, session, _} = Jido.Browser.close_tab(session, 1)
```
### Warm Session Pools
Warm pools are explicit and optional. They speed up browser-backed workflows,
while `web_fetch/2` stays stateless and never uses pools.
For OTP applications, prefer adding a named pool to your supervision tree:
```elixir
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
{Jido.Browser.Pool,
name: :default,
size: 2,
headless: true,
startup_timeout: 60_000}
]
Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
end
end
```
Then check out pooled sessions by name:
```elixir
{:ok, session} =
Jido.Browser.start_session(
pool: :default,
checkout_timeout: 5_000
)
{:ok, session, _} = Jido.Browser.navigate(session, "https://example.com")
:ok = Jido.Browser.end_session(session)
```
Use `start_pool/1` for scripts, tests, or ad hoc startup:
```elixir
{:ok, _pool} =
Jido.Browser.start_pool(
name: :default,
size: 2,
headless: true
)
{:ok, session} =
Jido.Browser.start_session(
pool: :default,
checkout_timeout: 5_000
)
{:ok, session, _} = Jido.Browser.navigate(session, "https://example.com")
:ok = Jido.Browser.end_session(session)
```
Warm pools are currently supported by `Jido.Browser.Adapters.AgentBrowser`,
`Jido.Browser.Adapters.Lightpanda`, and `Jido.Browser.Adapters.Web`.
- AgentBrowser pools keep full warm daemon-backed sessions ready for checkout.
- Lightpanda pools keep prestarted Lightpanda/CDP sessions ready for checkout.
- Web pools keep reserved warmed profiles ready for checkout.
- `lifecycle: :ephemeral` is the default: `end_session/1` recycles the checked-out
worker and warms a replacement in the background.
- `lifecycle: :persistent` returns healthy workers to the pool after normal
`end_session/1`; owner crashes, failed health checks, `max_uses`, and
`max_age_ms` still recycle workers.
Inspect a pool with:
```elixir
{:ok, status} = Jido.Browser.pool_status(:default)
status.ready
status.leased
status.lifecycle
```
For the `Web` adapter, pooled sessions are still browser sessions, not HTTP
fetches. Use `web_fetch/2` when you want the simplest request/response API
without browser state.
Persistent pools can preserve browser profile continuity, cookies, storage, and
session history for application-managed workflows. They do not guarantee access
through bot filters; egress, traffic rate, target-site policy, and user-provided
state remain application concerns.
### Plugin Setup
```elixir
defmodule MyBrowsingAgent do
use Jido.Agent,
name: "browser_agent",
plugins: [
{Jido.Browser.Plugin,
[
adapter: Jido.Browser.Adapters.AgentBrowser,
pool: :default,
checkout_timeout: 5_000,
headless: true,
timeout: 30_000
]}
]
end
```
## Configuration
```elixir
config :jido_browser,
adapter: Jido.Browser.Adapters.AgentBrowser
config :jido_browser, :agent_browser,
binary_path: "/usr/local/bin/agent-browser",
headed: false
```
Other adapters can still be configured explicitly:
```elixir
config :jido_browser, :vibium,
binary_path: "/path/to/vibium"
config :jido_browser, :web,
binary_path: "/usr/local/bin/web",
profile: "default"
config :jido_browser, :lightpanda,
binary_path: "/usr/local/bin/lightpanda",
disable_telemetry: true
```
Optional web fetch settings:
```elixir
config :jido_browser, :web_fetch,
backend: Jido.Browser.WebFetch.Backends.Req,
cache_ttl_ms: 300_000,
req: [
connect_options: [
timeout: 10_000
]
],
extractous: [
pdf: [extract_annotation_text: true],
office: [include_headers_and_footers: true]
]
```
Configured `req`, `browsey`, and `extractous` options are merged with any per-call options passed to `Jido.Browser.web_fetch/2`.
## Backends
### AgentBrowser (Default)
- native snapshot support with refs
- supervised daemon per session
- optional warm session pools with explicit checkout
- direct JSON IPC from Elixir
- built-in state save/load and tab management support
### Lightpanda (Limited)
- optional adapter backed by `light_cdp`
- supports session lifecycle, navigation, click, type, PNG screenshots, content extraction, and JavaScript evaluation
- supports warm pools for prestarted Lightpanda/CDP sessions
- uses `lightpanda_ex` for pinned Lightpanda binary installation
- disables Lightpanda telemetry by default with `LIGHTPANDA_DISABLE_TELEMETRY=true`
- does not provide AgentBrowser-native refs, state persistence, tab management, or console capture
### Vibium (Legacy)
- retained for transitional compatibility
- feature-frozen in 2.0
### Web (Legacy)
- retained for transitional compatibility
- feature-frozen in 2.0
## Public API
Core operations:
- `start_pool/1`
- `stop_pool/1`
- `start_session/1`
- `end_session/1`
- `navigate/3`
- `click/3`
- `type/4`
- `screenshot/2`
- `extract_content/2`
- `web_fetch/2`
- `evaluate/3`
Agent-browser-native operations:
- `snapshot/2`
- `wait_for_selector/3`
- `wait_for_navigation/2`
- `query/3`
- `get_text/3`
- `get_attribute/4`
- `is_visible/3`
- `save_state/3`
- `load_state/3`
- `list_tabs/2`
- `new_tab/3`
- `switch_tab/3`
- `close_tab/3`
- `console/2`
- `errors/2`
## Available Actions
### Session
- `StartSession`
- `EndSession`
- `GetStatus`
- `SaveState`
- `LoadState`
### Navigation
- `Navigate`
- `Back`
- `Forward`
- `Reload`
- `GetUrl`
- `GetTitle`
### Interaction
- `Click`
- `Type`
- `Hover`
- `Focus`
- `Scroll`
- `SelectOption`
### Waiting and Queries
- `Wait`
- `WaitForSelector`
- `WaitForNavigation`
- `Query`
- `GetText`
- `GetAttribute`
- `IsVisible`
### Content and Diagnostics
- `Snapshot`
- `Screenshot`
- `ExtractContent`
- `Console`
- `Errors`
### Tabs
- `ListTabs`
- `NewTab`
- `SwitchTab`
- `CloseTab`
### Advanced and Composite
- `Evaluate`
- `ReadPage`
- `SnapshotUrl`
- `SearchWeb`
- `WebFetch`
## Using With Jido Agents
```elixir
defmodule MyBrowsingAgent do
use Jido.Agent,
name: "web_browser",
description: "An agent that can browse the web",
plugins: [{Jido.Browser.Plugin, [headless: true]}]
end
```
`Jido.Browser.Plugin` now exposes 38 browser actions, including snapshot/refs workflows, browser state actions, diagnostics, tab management, and stateless web fetch.
## License
Apache-2.0 - See [LICENSE](LICENSE) for details.