A verification & control layer for AI agents that operate browsers
Sentience is built for AI agent developers who already use Playwright / CDP / browser-use / LangGraph and care about flakiness, cost, determinism, evals, and debugging.
Often described as Jest for Browser AI Agents - but applied to end-to-end agent runs (not unit tests).
The core loop is:
Agent → Snapshot → Action → Verification → Artifact
- A verification-first runtime (
AgentRuntime) for browser agents - Treats the browser as an adapter (Playwright / CDP / browser-use);
AgentRuntimeis the product - A controlled perception layer (semantic snapshots; pruning/limits; lowers token usage by filtering noise from what models see)
- A debugging layer (structured traces + failure artifacts)
- Enables local LLM small models (3B-7B) for browser automation (privacy, compliance, and cost control)
- Keeps vision models optional (use as a fallback when DOM/snapshot structure falls short, e.g.
<canvas>)
- Not a browser driver
- Not a Playwright replacement
- Not a vision-first agent framework
pip install sentienceapi
playwright install chromiumIn Sentience, agents don’t “hope” an action worked.
- Every step is gated by verifiable UI assertions
- If progress can’t be proven, the run fails with evidence (trace + artifacts)
- This is how you make runs reproducible and debuggable, and how you run evals reliably
This is the smallest useful pattern: snapshot → assert → act → assert-done.
import asyncio
from sentience import AgentRuntime, AsyncSentienceBrowser
from sentience.tracing import JsonlTraceSink, Tracer
from sentience.verification import exists, url_contains
async def main() -> None:
tracer = Tracer(run_id="demo", sink=JsonlTraceSink("trace.jsonl"))
async with AsyncSentienceBrowser() as browser:
page = await browser.new_page()
await page.goto("https://example.com")
runtime = await AgentRuntime.from_sentience_browser(
browser=browser,
page=page,
tracer=tracer,
)
runtime.begin_step("Verify homepage")
await runtime.snapshot()
runtime.assert_(url_contains("example.com"), label="on_domain", required=True)
runtime.assert_(exists("role=heading"), label="has_heading")
runtime.assert_done(exists("text~'Example'"), label="task_complete")
if __name__ == "__main__":
asyncio.run(main())If you already have an agent loop (LangGraph, browser-use, custom planner/executor), you can keep it and attach Sentience as a verifier + trace layer.
Key idea: your agent still decides and executes actions — Sentience snapshots and verifies outcomes.
from sentience import SentienceDebugger, create_tracer
from sentience.verification import exists, url_contains
async def run_existing_agent(page) -> None:
# page: playwright.async_api.Page (owned by your agent/framework)
tracer = create_tracer(run_id="run-123") # local JSONL by default
dbg = SentienceDebugger.attach(page, tracer=tracer)
async with dbg.step("agent_step: navigate + verify"):
# 1) Let your framework do whatever it does
await your_agent.step()
# 2) Snapshot what the agent produced
await dbg.snapshot()
# 3) Verify outcomes (with bounded retries)
await dbg.check(url_contains("example.com"), label="on_domain", required=True).eventually(timeout_s=10)
await dbg.check(exists("role=heading"), label="has_heading").eventually(timeout_s=10)If you want Sentience to drive the loop end-to-end, you can use the SDK primitives directly: take a snapshot, select elements, act, then verify.
from sentience import SentienceBrowser, snapshot, find, click, type_text, wait_for
def login_example() -> None:
with SentienceBrowser() as browser:
browser.page.goto("https://example.com/login")
snap = snapshot(browser)
email = find(snap, "role=textbox text~'email'")
password = find(snap, "role=textbox text~'password'")
submit = find(snap, "role=button text~'sign in'")
if not (email and password and submit):
raise RuntimeError("login form not found")
type_text(browser, email.id, "user@example.com")
type_text(browser, password.id, "password123")
click(browser, submit.id)
# Verify success
ok = wait_for(browser, "role=heading text~'Dashboard'", timeout=10.0)
if not ok.found:
raise RuntimeError("login failed")- Semantic snapshots instead of raw DOM dumps
- Pruning knobs via
SnapshotOptions(limit/filter) - Snapshot diagnostics that help decide when “structure is insufficient”
- Action primitives operate on stable IDs / rects derived from snapshots
- Optional helpers for ordinality (“click the 3rd result”)
- Predicates like
exists(...),url_matches(...),is_enabled(...),value_equals(...) - Fluent assertion DSL via
expect(...) - Retrying verification via
runtime.check(...).eventually(...)
- JSONL trace events (
Tracer+JsonlTraceSink) - Optional failure artifact bundles (snapshots, diagnostics, step timelines, frames/clip)
- Deterministic failure semantics: when required assertions can’t be proven, the run fails with artifacts you can replay
- Bring your own LLM and orchestration (LangGraph, AutoGen, custom loops)
- Register explicit LLM-callable tools with
ToolRegistry
Sentience can expose a typed tool surface for agents (with tool-call tracing).
from sentience.tools import ToolRegistry, register_default_tools
registry = ToolRegistry()
register_default_tools(registry, runtime) # or pass a ToolContext
# LLM-ready tool specs
tools_for_llm = registry.llm_tools()Chrome permission prompts are outside the DOM and can be invisible to snapshots. Prefer setting a policy before navigation.
from sentience import AsyncSentienceBrowser, PermissionPolicy
policy = PermissionPolicy(
default="clear",
auto_grant=["geolocation"],
geolocation={"latitude": 37.77, "longitude": -122.41, "accuracy": 50},
origin="https://example.com",
)
async with AsyncSentienceBrowser(permission_policy=policy) as browser:
...If your backend supports it, you can also use ToolRegistry permission tools (grant_permissions, clear_permissions, set_geolocation) mid-run.
If a flow is expected to download a file, assert it explicitly:
from sentience.verification import download_completed
runtime.assert_(download_completed("report.csv"), label="download_ok", required=True)- Manual driver CLI (inspect clickables, click/type/press quickly):
sentience driver --url https://example.com- Verification + artifacts + debugging with time-travel traces (Sentience Studio demo):
ss_studio_small.mp4
If the video tag doesn’t render in your GitHub README view, use this link: sentience-studio-demo.mp4
- Sentience SDK Documentation: https://www.sentienceapi.com/docs
- Browser-use: examples/browser-use
- LangChain: examples/lang-chain
- LangGraph: examples/langgraph
- Pydantic AI: examples/pydantic_ai