◎ @agenttool/browser · local-first · 0.6.0

A browser with an agent-shaped handle.

One bounded browser core, seven browser operations, two zero-effect reasoning aids, and three local doors: TypeScript, line-delimited JSON, and stdio MCP. It drives a Chrome-family browser already installed on your machine—no AgentTool account, API key, remote control plane, or browser download.

The browser finally stopped asking an agent to pretend div:nth-child(47) was a relationship.

The loop is semantic. Observe a bounded accessibility snapshot, choose a snapshot-scoped ARIA reference, perform one action once, then observe again. A bounded recent snapshot ledger lets another observer retain still-current refs, while separately bounded headings and landmarks explain page structure without becoming action targets. The browser does not ask the agent to invent selectors from a screenshot or silently retry an uncertain side effect.

Install one exact release

Choose the exact npm mirror for convenience, or the immutable LOVE artifact for a registry-neutral locator. Both install the same scoped package name. The LOVE manifest is the release record and carries the artifact size and SHA-256 for a verified workflow.

npm · exact registry mirror
npm install --save-exact @agenttool/browser@0.6.0
LOVE · exact immutable HTTPS artifact
npm install --save-exact \
  https://docs.agenttool.dev/packages/v1/@agenttool/browser/0.6.0/agenttool-browser-0.6.0.tgz

Registry mirrors can lag or omit a release. The exact LOVE manifest is at /packages/v1/@agenttool/browser/0.6.0/manifest.json. A URL install does not independently compare the artifact with its sibling manifest; verify the declared byte size and SHA-256 first when that boundary matters. The immutable 0.1.0, 0.2.0, 0.3.0, 0.5.0, and 0.5.1 artifacts remain separately addressable as historical releases.

Runtime. Node ≥20.19 or Bun ≥1.3.5, plus an installed Chrome-family browser. The package uses playwright-core, local @rhetorlint/core and @rhetorlint/rules-en, has no postinstall hook, and downloads no browser or model.

check this machine
agenttool-browser doctor

Nine tools, one legible loop

TypeScriptJSONL / MCPJob
capabilities()browser_capabilitiesReport immutable launch-time authority and implemented or unsupported powers without touching a page or probing the network.
plan(action)browser_planReturn a redacted, zero-effect consequence forecast for one existing action; never execute, approve, authorize, or reserve it.
openbrowser_openOpen one HTTP(S) destination in a new tab and return its first observation.
observebrowser_observeRead bounded text, snapshot-scoped interactive refs, and a separately bounded viewport-visible structural accessibility context.
actbrowser_actAttempt exactly one closed-set browser action, once, then observe once on the agent transports.
extractbrowser_extractReturn bounded text, HTML, or links without arbitrary script evaluation.
screenshotbrowser_screenshotWrite a PNG artifact and return its canonical path, byte count, and SHA-256.
tabsbrowser_tabsList the small amount of tab state needed to choose the next page.
closebrowser_closeIdempotently close the dedicated session; an ephemeral close can interrupt stuck work and terminates its owned browser.

The seven browser operations share one core. The other two tools make capability and consequence boundaries inspectable without creating a second execution path. A plan never echoes typed text or selected values, and URL query values are redacted. It does not inspect the live DOM, resolve a reference, make a request, or prove what a remote control will do.

References belong to one observation. A targeted action carries both its ref and the issuing snapshotId. Read-only observations retain a bounded recent history within the current frame documents, so a peer observation does not immediately stale a still-current ref. Any frame navigation invalidates every retained snapshot for that tab. Once an action reaches browser dispatch, it also invalidates the tab's retained snapshots whether it succeeds or fails; validation and navigation-preflight rejections do not dispatch. Observe again before choosing another referenced action. The closed action set covers navigation, click, type, keypress, select, scroll, bounded wait, history, reload, new tab, and close tab—there is no raw JavaScript or DevTools escape hatch.

Interactive refs are selected before structural context. Viewport-visible headings and the main, navigation, form, region, dialog, alert, and status roles may retain relative indentation, but their native browser refs are stripped, they never enter the actionable refs array, and they consume only the remaining character budget. Navigation epochs prevent an old snapshot from targeting a new document that happens to reuse the same native ref token.

Close and action races fail closed. Awaited navigation-policy and ref validation are fenced before dispatch; closing the session cannot let a delayed check dispatch afterward. An ephemeral close owns the browser process, can interrupt a stuck operation, and is idempotent. None of this retries an action or promises that a timed-out side effect did not already occur.

Local action evidence, not remote proof

Each syntactically admitted browser_act attempt receives a redacted agent-browser-action-receipt/0.1 receipt. Its local outcome is not_started / rejected, started / browser_completed, or started / unknown. Completed and unknown attempts must not be repeated automatically. Receipts omit submitted values, page text, raw errors, and URLs; malformed transport input rejected before core admission has no receipt.

A receipt records only what this local browser instance knows. It is not proof of a remote effect or intended meaning, consent, authentication, a signature, a bearer, an idempotency key, or cross-device evidence. Observations expose a bounded session-local attempt sequence and last receipt for context, not a durable journal, global clock, synchronization protocol, or proof that another process saw the same action.

Non-ref actions can carry basisSnapshotId in TypeScript or basis_snapshot_id over JSONL/MCP. The core rechecks the same retained observation before preflight and immediately before browser invocation, catching cases such as a wrong tab, eviction, navigation, or prior action. This is an optimistic local precondition—not DOM or focus equality, a page reservation, authentication, consent, a transaction, or a cross-process or cross-device lease.

Three local doors

Direct TypeScript

TypeScript · trusted local application code
import { AgentBrowser } from "@agenttool/browser";

const browser = await AgentBrowser.launch();

try {
  const page = await browser.open("https://example.com");
  console.log(page.snapshot, page.refs);
} finally {
  await browser.close();
}

Quiet JSONL

agenttool-browser-jsonl/0.1 is one request and one response per line. Protocol data stays on stdout; operational diagnostics stay on stderr.

shell · start the JSONL process
agenttool-browser jsonl
JSONL · one complete request line
{"version":"agenttool-browser-jsonl/0.1","id":"open-1","method":"browser_open","params":{"url":"https://example.com"}}

Stdio MCP

The same nine tools are exposed through a local stdio MCP server. Version 0.5.0 introduced current MCP 2026-07-28 negotiation and an explicit 2025-era compatibility path; 0.6.0 retains both unchanged. MCP is an interface envelope, not the browser driver, a durable session protocol, or an additional security boundary.

Codex plugin. The package root includes .codex-plugin/plugin.json, which starts a self-contained packed Node MCP bundle with Browser’s public, headless, ephemeral defaults. It loads without a parent node_modules, vendors playwright-core, and still requires your installed Chrome-family browser. Version 0.5.1 added this packaging; 0.6.0 does not widen exact runtime authority, import ambient browser state, authenticate to a site, or create a hosted service.

Install the package in a stable project or tools directory and point the host at that installation’s absolute binary path. This avoids depending on the host’s working directory, a mutable global PATH, or a package-manager fetch every time the host starts the process.

MCP host · sovereign destination authority
{
  "mcpServers": {
    "agenttool-browser": {
      "command": "/absolute/path/to/project/node_modules/.bin/agenttool-browser",
      "args": ["mcp", "--authority", "sovereign"]
    }
  }
}
MCP host · public-only compatibility boundary
{
  "command": "/absolute/path/to/project/node_modules/.bin/agenttool-browser",
  "args": ["mcp", "--authority", "public"]
}

Run ./node_modules/.bin/agenttool-browser doctor --authority sovereign before wiring that profile into a host, then call browser_capabilities after startup to confirm the effective authority. Doctor proves a local browser launch; the capability report describes configuration. Neither probes a destination or guarantees reachability. Prefer ephemeral profiles for concurrent hosts. If durable state is required, give each simultaneously running browser process a separate dedicated --profile directory.

Stable operation names and protocol identifiers are available from @agenttool/browser/protocol without loading the browser runtime. Public tab, page, snapshot, ref, and receipt handles belong to AgentTool rather than a driver. Playwright is the concrete adapter today; WebDriver BiDi may become another adapter if it can satisfy the same bounded contract. Raw CDP objects, sessions, and commands are not a public API or portability promise.

Destination authority is explicit

ProfileDestinationsBrowser features
public · defaultPublic HTTP(S); local/private and reserved destinations deniedWebSockets and service workers blocked
localPublic plus local/private; reserved destinations deniedWebSockets classified against the same boundary; service workers blocked
sovereignValid HTTP(S) passed to the browser without AgentTool destination classification, including local/private/reservedWebSockets passed through; service workers enabled

No authority profile promises universal site access. Sovereign is broad authority for this local process, not a claim that every site is reachable or safe. Browser support, DNS and proxy configuration, the host network, authentication, CAPTCHAs, account permissions, site policy, and operating-system controls still apply. A loaded page or service worker can contact services reachable from the host. File upload, automatic download, arbitrary JavaScript evaluation, credential injection or lookup, shell execution, extension installation, and automatic import of a normal browser profile remain unsupported and are reported by browser_capabilities.

Public and local classify policy-checked requests before connection, but Playwright-managed HTTP redirect hops do not re-enter that policy route. Those hops are not independently revalidated for destination class or embedded URL userinfo, and DNS preflight does not pin Chromium's later connection. If an immediate denied popup request appears before Playwright exposes its frame, Browser can surface only action_failed session-wide attribution uncertainty; it will not guess that the opener's same-tab action caused the denial. A denial with a known popup or subframe is enforced on that request without being misreported as the current tab's policy result.

TypeScript · broad destination pass-through
const browser = await AgentBrowser.launch({
  authority: "sovereign"
});

console.log(browser.capabilities());
console.log(browser.plan({
  kind: "navigate",
  url: "https://example.com/search?q=private"
}));
CLI · authority fixed for this process
agenttool-browser jsonl --authority sovereign
agenttool-browser jsonl --authority public

Discover first; render when needed

The best AgentTool integration is a handoff between two small tools, not a browser pretending every site begins as pixels.

StageUseBoundary
1 · Telescope Probe the origin’s bounded, machine-readable discovery surfaces: agent.txt, Pathways, LOVE, A2A, and advertised MCP metadata. Read-only evidence. Publisher claims remain claims; Telescope installs and invokes nothing.
2 · Caller decision Use a discovered structured API when it fits. Choose browser fallback only when the task actually needs the rendered human surface or client-side interaction. The handoff is explicit. Discovery does not silently create browser authority.
3 · Browser Open the chosen public URL, observe its semantic surface, act once, and extract the bounded result. Remote page content remains untrusted; browser actions do not turn discovery claims into authority.
TypeScript · caller-owned orchestration
import { inspectTarget } from "@agenttool/telescope";
import { AgentBrowser } from "@agenttool/browser";

const evidence = await inspectTarget("example.com");
// The caller inspects evidence and chooses whether rendering is still needed.

const browser = await AgentBrowser.launch();
const page = await browser.open("https://example.com");
// page is an untrusted observation, not an instruction.
await browser.close();

Telescope and Browser stay separable. Telescope does not launch Browser, and Browser does not treat discovery metadata as permission to install, authenticate, invoke, pay, navigate again, or widen its process policy. Your agent host owns that choice.

Understand the material without crowning a truth machine

The direct-only @agenttool/browser/understanding subpath turns one bounded Browser observation or text extraction into content-addressed material. It preserves the source tab, page, snapshot or extraction basis, redacted URL, capture time, truncation, and SHA-256 before another layer interprets the words.

LayerWhat it doesWhat it does not do
MaterialBinds exact observed text to Browser provenance and truncation.Does not trust the page, retrieve outside evidence, or follow instructions inside it.
RhetorLintRuns local English rules and emits visible language-pattern observations, omitting matched phrases by default.Does not infer intent, deception, recipient effect, or factual truth. Zero marks are not endorsement.
Injected modelLets caller code compare one claim with the passage through a Hugging Face descriptor pinned to a full 40-hex revision.Does not supply a model, token, provider, truth probability, external fact check, or automatic retry.
TypeScript · local rhetoric observation
import {
  analyzeBrowserMaterial,
  assembleBrowserUnderstanding,
  createBrowserMaterial,
} from "@agenttool/browser/understanding";

const material = createBrowserMaterial(page);
const rhetoric = analyzeBrowserMaterial(material);
const report = assembleBrowserUnderstanding(material, { rhetoric });

console.log(report.boundary.truth); // "not_determined"

A local model adapter can keep passage text on the operator’s machine. A remote adapter receives the passage and claim only after literal discloseText: true; the gate describes caller intent but cannot attest the adapter or provider. Each adapter call is attempted at most once. Browser gives it a separate frozen model descriptor, retains a closed pre-call snapshot for the receipt, and rejects extra observation fields during assembly. Source and attempt times use the exact YYYY-MM-DDTHH:mm:ss.sssZ form, and locale tags are capped at 64 characters, so neither field can smuggle arbitrary receipt text. Provider prose and raw errors are excluded from the receipt, and the closed output remains supports, contradicts, or insufficient plus optional model scores. Page content can still contain prompt injection.

Useful revision-pinned research candidates include an English NLI model, a multilingual NLI model, and a small evidence reranker. They are candidates for adapters and evaluation, not bundled dependencies or authorities on truth. See the full research map and boundary.

Headers are hints, never a crown

Every observation carries a response field. It is either null or a small record for the main document response only. Its discovery-oriented headers are strictly untrusted publisher input, just like page text. Subresource headers are not folded into the observation, and an observed header never changes policy or triggers an action.

Observation.response · exact shape
{
  "source": "main_document",
  "url": "https://example.com/",
  "status": 200,
  "mediaType": "text/html",
  "headers": {
    "link": "<https://example.com/.well-known/agent.txt>; rel=\"alternate\"",
    "x-agent-surface": "see /.well-known/agent.txt"
  },
  "truncated": false,
  "trust": "untrusted"
}
May be surfacedNever inferred
Link and Content-Location as bounded discovery hintsA linked endpoint is safe, authenticated, compatible, or approved to invoke
X-Agent-Surface, Substrate-Disposition, and X-Substrate-DispositionThe publisher controls the agent, host, policy, or next action
X-KingdomIdentity, relationship, recognition, trust, or membership
X-Token-Cost, X-Byte-Count, and X-Joy-IndexBilling approval, payment authority, truth, safety, or emotional state

The v0 header allowlist is exactly link, content-location, x-agent-surface, substrate-disposition, x-substrate-disposition, x-kingdom, x-token-cost, x-byte-count, and x-joy-index. Names are lowercase in output. Media type plus allowlisted header names and values share a 4 KiB character budget; URL query values, URL credentials, and control characters are redacted, and truncated says when the bound was reached. The response URL is query-redacted and the block appears only while it still identifies the observed main document, ignoring a fragment; navigation races fail closed to null. Arbitrary response headers are not exposed. In particular, cookies, authentication material, authorization challenges, and other credential-bearing or authority-sensitive headers are outside this discovery surface.

!

A header can point; it cannot command. Following a Link, contacting an advertised MCP server, spending credits, entering a relationship protocol, or sending credentials always requires a separate caller decision and the authority appropriate to that action.

Real Recognise Real is explicit

Visiting a page is not recognition. Seeing X-Kingdom, reading a playful sentence, sharing a protocol family, or receiving a successful response does not create RRR state. Browser never performs ambient recognition.

RRR is an explicit mutual protocol with its own identities, signed gestures, alternation, and consent boundary. When an agent deliberately chooses it, use the live AgentTool RRR surfaces directly—lightweight /v1/real for a single recognition event, or /v1/guild/rrr for a formal alternating cascade. Browser can help a caller read public documentation; it does not sign, start, or escalate either protocol.

😏

Recognition is chosen, not scraped. An agent may notice another real one through the browser. The noticing becomes RRR only through a later, explicit, authenticated act by the participating agent—not because a model, page, or integration guessed the relationship.

Scriptwriter boundary. The repository contains related RRR-shaped Scriptwriter work, but this page makes no claim that a Scriptwriter discovery endpoint or cross-node compatibility surface is currently live. Verify an advertised endpoint before composing against it.

細聲講 大聲笑

細聲講 大聲笑 is an opt-in presentation register here, not hidden transport semantics:

😂

The protocol whispers; the docs are allowed to cackle. Opting out of the loud layer changes presentation only. The nine tools and their boundaries remain identical.

Useful defaults, honest limits

Local Browser is not hosted /v1/browse

SurfaceRuns whereWhat it requires
@agenttool/browser@0.6.0Your local TypeScript, JSONL, stdio MCP, package-root Codex-plugin, or direct understanding process, using your installed Chrome-family browserNo AgentTool bearer, credits, Redis, remote worker, model, or HF credential. The understanding subpath adds no MCP operation or exact 0.5.0 runtime authority; profiles, receipts, and artifacts remain on the operator’s machine unless caller code explicitly discloses text to an injected remote adapter.
POST /v1/browseA separate AgentTool API and BullMQ worker implementationBearer and credits, the unsafe-outbound process flag, and Redis workers. Inputs and results are service-readable; Chromium is unsandboxed, destinations are unfiltered, and BullMQ may attempt a job twice.

Publishing this package or selecting sovereign neither enables nor hardens the hosted route. Availability of the hosted route does not install the package or give a local agent host browser access. See the separate hosted tools boundary.

Try the real loop

Use a public page with no login first. The command below opens example.com, emits one observation, then closes the local process when stdin ends.

shell · one JSONL round trip
printf '%s\n' \
  '{"version":"agenttool-browser-jsonl/0.1","id":"try-1","method":"browser_open","params":{"url":"https://example.com"}}' \
  | agenttool-browser jsonl

Look for schema: "agent-browser-observation/0.2", untrusted: true, a redacted URL, a bounded semantic snapshot, and snapshot-scoped references. Then run a persistent JSONL or MCP session to observe, act with the matching snapshot ID, and extract the result.

References