// memory layer for browser agents

Stop repeating solved
browser workflows.

TERX records successful CDP workflows once. Matching repeat runs replay in milliseconds with no LLM call, no tokens, and a structured audit report.

TERX local replay demo
$ pip install terx
10/10 cache hits 173.9x speedup (measured) MIT Python 3.11+ Raw CDP — no Playwright
// real benchmark — Groq API, openai/gpt-oss-120b, measured
173.9x
average speedup on warm runs
100%
token savings on cache hits
10/10
cache hit rate across task suite
task llm steps agent (cold) terx (warm) speedup tokens cost saved hit
User Login Flow43.05s · $0.00650.090s · $0.00034.0x1,985100%
Search and Filter417.82s · $0.01080.099s · $0.000179.3x2,634100%
Multi-step Signup641.05s · $0.01420.103s · $0.000399.4x4,339100%
E-commerce Product49.84s · $0.01230.100s · $0.00098.1x2,798100%
Settings Toggles415.42s · $0.00820.100s · $0.000154.5x2,131100%
Data Table Pagination311.27s · $0.00930.088s · $0.000128.7x1,756100%
Support Ticket418.35s · $0.00780.100s · $0.000184.2x2,130100%
Fuzzy Search310.41s · $0.00550.083s · $0.000124.8x1,393100%
Profile Update417.20s · $0.01040.077s · $0.000223.7x2,544100%
Complex Form416.51s · $0.00740.086s · $0.000192.2x2,072100%
total / average160.93s · $0.09250.926s · $0.000173.9x23,782100%10/10

Reproduce: GROQ_API_KEY=... python -m terx.benchmarks.real_agent

No-key proof: terx demo and terx eval-local

// quickstart

MCP server
Python library
Run benchmark

Works with Claude Desktop, Cursor, Windsurf

Start Chrome with debugging enabled:

# close all Chrome windows first
google-chrome --remote-debugging-port=9222 --no-first-run

Start TERX:

pip install terx
terx-server

Add to your MCP config:

// claude_desktop_config.json
{
  "mcpServers": {
    "terx": { "command": "terx-server" }
  }
}

Wrap repeatable workflows with browser_task_start and browser_task_finish. Pass variables and postconditions for safe replay. First run records. Matching repeats replay without another LLM call.

Wrap your existing agent — 3 lines

from terx.cdp.session import BrowserSession
from terx.cache.cache import MemoryCache, session_for

cache = MemoryCache()

	async with BrowserSession() as session:
	    bridge = session.bridge()

	    async with session_for(
	        cache, bridge, "login to salesforce",
	        variables={"email": "user@example.com", "password": "..."},
	        postcondition={"text_contains": "Welcome"},
	    ) as ctx:
	        if ctx.hit:
	            await ctx.replay()          # 0 tokens, ~80ms
	        else:
            await your_agent.run(...)   # agent runs, TERX records

Run the real LLM benchmark yourself

# free key at console.groq.com
	pip install "terx[benchmark]"
	cp .env.example .env   # fill in GROQ_API_KEY
	python -m terx.benchmarks.real_agent

Runs 10 real tasks. Prints the table above with your measured numbers.

# local Chrome demo + deterministic eval suite
	terx demo
	terx eval-local

// how it works

  ┌─────────────────────────────────────────────────┐
  │                  your agent                      │
  │          (browser-use / Claude / anything)        │
  └───────────────────────┬─────────────────────────┘
                          │
  ┌───────────────────────▼─────────────────────────┐
  │              TERX session_for()                │
  │  on miss → records CDP commands                  │
  │  on hit  → replays them directly                 │
  └───────────────────────┬─────────────────────────┘
                          │ raw WebSocket
  ┌───────────────────────▼─────────────────────────┐
  │                  Chrome                          │
  │           (remote debugging port 9222)            │
  └─────────────────────────────────────────────────┘
01

CDP Bridge

Raw asyncio WebSocket to Chrome. No Playwright subprocess. <50ms startup, ~2MB RAM.

02

DOM Extractor

Reads Chrome's Accessibility Tree (not raw HTML). Computes a fuzzy structural hash — survives CSS changes and A/B tests without breaking cache hits.

03

Muscle Memory Cache

SQLite. On success: stores CDP command sequence keyed by (domain, dom_hash, task). On future runs: replays directly. INSERT OR IGNORE — first successful recording is canonical.

04

Dynamic node ID translation

Chrome assigns new backendNodeIds each session. TERX re-snapshots on replay and maps old IDs to current equivalents by role + label matching.

// mcp tools

toolwhat it does
browser_task_startStart a cacheable workflow; replays immediately on cache hit
browser_task_finishStore a successful workflow or discard a failed one; postconditions prevent bad hits
browser_get_stateAX tree snapshot — stable element IDs, no hallucination-prone HTML
browser_navigateNavigate to URL (scheme-validated, blocks javascript: data: file:)
browser_clickClick element by stable ID using a remappable node handle
browser_click_atFallback physical click at an element center point
browser_typeFocus an input by stable ID and insert text
browser_screenshotReturns hash ref, not base64 — no context window poisoning
browser_screenshot_getRetrieve a screenshot by hash ref when image bytes are needed
browser_scrollScroll up/down
browser_new_tabOpen new tab
cache_statsHit rate, total savings, unique domains cached
cache_invalidateClear cache for a domain when the UI ships a redesign