Skip to main content
A terminal browser powered by Carbonyl that gives agents direct DOM access while rendering visually for humans. No screenshots needed.

Why this exists

ToolAgent seesHuman seesBidirectional?
CursorScreenshotsNothing
Claude CodeScreenshotsNothing
TENET BrowserLive DOMTerminal rendering

Start

In a separate terminal:
./tools/browser/tenet-browser.sh https://10et.ai
The human sees the page rendered in their terminal. The agent gets structured DOM via CDP.

Agent commands (via Subway)

# Navigate
subway_call("browser.relay", "navigate", '{"url":"https://example.com"}')

# Read page structure (headings, forms, links, tables, errors)
subway_call("browser.relay", "read", '{}')

# Click an element
subway_call("browser.relay", "click", '{"selector":"#deploy-btn"}')

# Type into a field
subway_call("browser.relay", "type", '{"selector":"#search", "text":"error logs"}')

# Scroll
subway_call("browser.relay", "scroll", '{"direction":"down"}')

# Wait for element to appear
subway_call("browser.relay", "wait", '{"selector":".results", "timeout":5000}')

What agents get back

Not screenshots. Structured DOM:
{
  "url": "https://app.example.com/dashboard",
  "title": "Dashboard",
  "dom_summary": {
    "headings": ["Dashboard", "System Health"],
    "forms": [{"id": "search", "fields": ["query"]}],
    "links": [{"text": "Settings", "href": "/settings"}],
    "tables": [{"id": "health-table", "rows": 5}],
    "interactive": [{"type": "button", "text": "Deploy"}],
    "errors": [],
    "text_content": "3 services healthy, 1 degraded."
  }
}

Human → Agent flow

When you click or type in the browser, events broadcast on the Subway mesh:
{
  "type": "human_click",
  "element": {
    "tag": "div",
    "class": "error-banner",
    "text": "API timeout on /api/v2/health",
    "selector": "#dashboard > .error-banner"
  }
}
Your agent receives this and can respond: “I see you’re looking at the monitoring page — the 502s started 20 minutes ago…”

Authentication

No special auth system. The human is literally using the browser:
  1. Agent navigates to login page
  2. Human sees the form, types credentials in terminal
  3. Agent detects auth state change
  4. Agent proceeds with authenticated session

HTTP fallback

If Subway isn’t running, use the local API:
curl -X POST http://127.0.0.1:9223/navigate -d '{"url":"https://example.com"}'
curl -X POST http://127.0.0.1:9223/read
curl -X POST http://127.0.0.1:9223/click -d '{"selector":"button.submit"}'