A Chrome agent that lives in your browser, learns who you are, and gets things done for you.
This started as a text-fill tool. It is now a full agentic browser companion. It watches what you do, builds a durable model of who you are, writes in your voice across every site, and can operate the browser autonomously to execute tasks on your behalf — outreach campaigns, profile enrichment, form automation, and more.
The longer you use it, the more capable it becomes.
Most AI tools are stateless. You give context, get output, start over. Every session is a blank slate.
This extension is the opposite. It is a persistent intelligence layer running inside your browser that:
- Observes every writing session — what you asked for, what the AI produced, what you actually sent
- Learns behavioral patterns from those sessions and encodes them as procedural rules
- Remembers durable facts about you across three memory tiers
- Proactively scans pages for actionable signals and surfaces them as suggestions
- Executes multi-step browser tasks autonomously with your approval
- Writes in your voice, shaped by full context retrieval, on any text field across 15+ platforms
It is a Chrome companion, not a writing plugin.
A floating action button appears next to every supported text field. From it you can:
- Generate a fresh response using your full context
- Rewrite what is already typed
- Shorten or Expand text
- Adjust tone — casual to formal
- Apply a domain lens — general, sales, legal, technical, academic
Single-click opens the action menu. Double-click generates instantly. Alt+Shift+G quick-generates on the active focused field.
The extension includes a full agent runtime for autonomous multi-step browser tasks.
How it works:
- You trigger a task — e.g., "connect with all recruiters on this page"
- The extension scans the page, identifies candidates, drafts personalized messages using your memory
- An approval panel shows you the full queue with generated messages before anything is sent
- You approve, and the agent executes: navigates tabs, fills forms, clicks send, respects daily limits
What it can do today:
- Scan LinkedIn search results for candidate profiles (detects 20+ per page)
- Draft personalized connect messages using your memory and the recipient's visible context
- Queue and execute batched outreach with configurable daily limits
- Navigate tabs, extract structured data, click elements, fill fields
- Run multi-step workflows with state machine orchestration
Human-in-the-loop by design. The agent always queues tasks for your review before executing. You see the full batch, edit any message, and confirm before the agent touches anything.
Long-running agentic tasks run through a local Node.js companion (companion/), not through a cloud service. The companion runs a WebSocket server on localhost:4315 that the extension connects to automatically.
The companion uses two MCP layers stacked together to give the agent full browser control:
Chrome Extension
│ WebSocket (localhost:4315)
▼
companion/server.ts (Node.js)
│
▼
LocalAgentCompanionService
│
▼
ChromeDevtoolsMcpRuntime
├─── chrome-devtools-mcp (npm) ← MCP server: exposes Chrome DevTools as tools
│ list_pages, select_page, new_page,
│ navigate_page, close_page, evaluate_script
│
└─── mcp-agent (Python via uv) ← AI agent: reasons over MCP tools to execute goals
python_browser_runtime.py
+ mcp_agent_bridge.py
chrome-devtools-mcp is an npm package (chrome-devtools-mcp@latest) that connects to Chrome's remote debugging port and exposes browser control as MCP tools. The companion launches it via npx and communicates through the MCP protocol.
mcp-agent is a Python AI agent framework (installed via uv) that acts as the reasoning layer. It receives a goal in natural language, uses chrome-devtools-mcp tools to observe and interact with the browser, and iterates until the task is complete. This is what handles open-ended instructions like "fill in this job application" or "connect with all recruiters on this page."
The two layers are bridged through mcp-agent-bridge.ts (Node.js → Python JSON-over-stdin/stdout) and python_browser_runtime.py (Python agent entry point).
Chrome must have remote debugging enabled. The companion connects via --remote-debugging-port=9222 or --autoConnect. If Chrome is running without remote debugging, the companion will surface a clear error with instructions.
To start the companion:
npm run companion:devWithout the companion, all writing features still work. The companion is only required for autonomous multi-step browser tasks.
Prerequisites for the companion:
- Python 3 with
uvinstalled (pip install uvorbrew install uv) - Chrome started with
--remote-debugging-port=9222, or remote debugging enabled inchrome://inspect
Environment variables (optional):
| Variable | Default | Purpose |
|---|---|---|
CHROME_DEVTOOLS_MCP_BROWSER_URL |
auto-connect | Override the Chrome debug endpoint |
CHROME_DEVTOOLS_MCP_AUTO_CONNECT |
1 |
Set to 0 to disable auto-connect |
CHROME_DEVTOOLS_MCP_HEADLESS |
0 |
Run Chrome headlessly |
CHROME_DEVTOOLS_MCP_USER_DATA_DIR |
default profile | Chrome user data dir |
MCP_AGENT_BRIDGE_SCRIPT |
companion/mcp_agent_bridge.py |
Path to the Python bridge script |
MCP_AGENT_PYTHON_RUNTIME_SCRIPT |
companion/python_browser_runtime.py |
Path to the Python agent runtime |
Semantic memory — durable facts, stored indefinitely, retrieved by vector similarity:
- Work: roles, skills, projects, career direction
- Social: communities, interests, relationships
- Personal: background, values, long-term goals
- Persona: writing identity, only when the signal is strong
Procedural memory — behavioral rules learned from repeated session patterns. When you consistently write a certain way in a certain context, the system encodes a rule. Rules gain confidence over time and decay if the pattern disappears.
Episodic memory — recent session summaries injected as few-shot examples. The most recent relevant generations, anonymized and condensed, give the model concrete examples of your voice before it writes.
All three tiers are injected as labeled sections into every generation prompt. You get durable facts, behavioral rules, and concrete examples working together.
- Weekly cron job handles confidence decay, archival, and cleanup
- Caps: 500 active memories, 200 archived
- Memory extraction is conservative — one-off events and stale context stay out
- Bi-temporal validity tracks when facts were true, not just when they were saved
The memory page is a full React + Convex interface where you can:
- Browse active and archived memories, filter by category
- Sort by recency, importance, confidence, mentions, last used, forget risk, or semantic similarity
- Run semantic search across saved memories
- Edit, archive, restore, or delete individual entries
The extension watches every compose session and learns from it.
After you close a field, it computes:
- How much you edited the generated text (Levenshtein + trigram diff)
- Whether you actually sent it (three-signal send detection: form submit, Enter/Ctrl+Enter, and mousedown+XHR interception)
- The outcome: accepted, lightly edited, heavily edited, rewritten, abandoned, or sent
This data feeds pattern promotion. When the same platform + context type combination produces a consistent editing pattern across multiple sessions, the system extracts a procedural rule.
The extension builds and maintains a graph of entities — people, companies, platforms — extracted from your sessions.
- Entities are resolved by lexical match + vector similarity + LLM disambiguation
- Relationships (works_at, knows, reports_to) are stored with temporal validity
- The graph is used to surface recipient context during generation (e.g., "you've messaged this person 3 times, you both worked at the same company")
The extension scans pages in the background and surfaces actionable signals.
On LinkedIn search results, it detects candidate profiles, counts connectable targets, and shows a suggestion chip: "20 profiles found — start outreach batch." One click opens the approval queue.
The scanner is extensible. The same architecture supports detecting job listings, recruiter profiles, and other opportunity signals across any page.
An offscreen document runs continuous Web Speech API recognition. Say the wake word and issue a command:
- "Write a follow-up to Sarah about the contract"
- "Search my memory for Python experience"
- "Connect with all engineers on this page"
The extension parses intent (compose, search, connect) and routes to the right action.
Sometimes the context you need is on another page.
The floating Context Library button lets you capture pages and keep them active while writing elsewhere. Toggle multiple contexts on or off per session — job descriptions, company about pages, LinkedIn profiles, reference material.
The extension detects where you are and adapts.
First-class platform handling for:
- Email: Gmail, Outlook
- Social / Messaging: LinkedIn, Messenger, Facebook, Twitter/X, Threads, Instagram, YouTube, Reddit, Discord
- Work: Slack, Google Docs
- Job boards: Greenhouse, Ashby, Workday, Lever, Canvas
- Generic: any text field on the rest of the web
Each platform has custom field detection, page-context extraction, and generation behavior. On job boards and LinkedIn, the system is especially careful not to leak irrelevant memory into high-stakes messages.
Backed by Convex with Convex Auth.
Sign in with:
- Email + password
- Magic code over email
Bring your own API keys. Keys are stored on the Convex backend — not in chrome.storage.
| Provider | Writing | Memory extraction | Embeddings |
|---|---|---|---|
| OpenAI | gpt-5-nano, gpt-5-mini |
gpt-5-nano |
text-embedding-3-small, text-embedding-3-large |
| Anthropic | claude-sonnet-4-5, claude-haiku-3-5 |
claude-haiku-3-5 |
Pair with OpenAI or Gemini for embeddings |
gemini-3-pro-preview, gemini-3-flash-preview |
gemini-2.5-flash-lite |
gemini-embedding-001 |
- Provider, model, embedding provider/model
- Custom system prompt
- Career & Work, Social & Personal, and Always Active context blocks (paste or upload
.txt/.md) - Memory caps and pattern confidence (advanced)
| Layer | Tech |
|---|---|
| Extension build | WXT (Chrome MV3) |
| UI | React 18, Tailwind CSS v4, Radix UI, Lucide |
| Backend | Convex (auth, database, vector search, actions, crons) |
| Agent orchestration | Convex Workflows + local companion (Node.js) |
| Browser automation — tools | chrome-devtools-mcp (npm) — exposes Chrome DevTools as MCP tools |
| Browser automation — reasoning | mcp-agent (Python, via uv) — AI agent that drives those tools |
| Browser protocol | Chrome DevTools Protocol (--remote-debugging-port=9222) |
| Voice | Web Speech API (offscreen document), Porcupine wake word |
| LLM | Vercel AI SDK (@ai-sdk/openai), Anthropic SDK, Google GenAI |
| Local companion | Node.js, WebSocket (ws), tsx |
text-fill-v2/
├── entrypoints/
│ ├── background.ts # Service worker: auth, message routing, task queue
│ ├── content/
│ │ ├── App.tsx # Main content script: field detection, session lifecycle
│ │ ├── FieldButton.tsx # Floating button on text fields
│ │ ├── GenerateModal.tsx # Generation UI (tone, domain, preview, insert)
│ │ ├── ContextFAB.tsx # Context Library floating button
│ │ ├── AgentFAB.tsx # Agent task trigger button
│ │ ├── SuggestionChip.tsx # Proactive scan result chip
│ │ ├── QueuePreviewPanel.tsx # Batch task approval panel
│ ├── popup/ # Extension popup (memory stats, recent writings)
│ ├── options/ # Settings page
│ ├── memory/ # Memory Bank page
│ └── offscreen/main.ts # Voice runtime (Web Speech API, wake word)
├── src/
│ ├── lib/
│ │ ├── platform.ts # Platform detection, field finding, DOM queries
│ │ ├── platforms/ # Per-platform field detection (LinkedIn, Gmail, etc.)
│ │ ├── session-observer.ts # Compose session lifecycle, diff, send detection
│ │ ├── candidate-scan.ts # LinkedIn candidate scanning
│ │ ├── scanner.ts # Opportunity signal detection
│ │ ├── browser-control.ts # LinkedIn browser automation (state machine)
│ │ ├── browser-executor.ts # BrowserExecutor interface + Chrome implementation
│ │ ├── browser-command-spec.ts# Serializable browser command types
│ │ ├── browser-observation.ts # Page snapshot types
│ │ ├── local-agent-bridge.ts # WebSocket bridge to local companion
│ │ ├── local-agent-protocol.ts# Companion protocol types
│ │ ├── agent-planner.ts # Planner state and decision types
│ │ ├── agent-run-context.ts # Agent run start context
│ │ ├── agent-panel-runtime.ts # Agent panel state types
│ │ ├── task-queue-storage.ts # chrome.storage task queue persistence
│ │ ├── task-batch-handoff.ts # Task batch + Convex sync types
│ │ ├── voice-runtime.ts # Voice runtime state enum
│ │ ├── captured-contexts.ts # Context library storage
│ │ ├── context.ts # Page context extraction orchestrator
│ │ └── dom/ # ARIA DOM walker, theme detection
│ ├── components/ # Auth screen, shared UI wiring
│ ├── hooks/ # Convex-backed hooks
│ └── styles/
├── convex/
│ ├── schema.ts # All tables: memories, sessions, entities, agent runs
│ ├── auth.ts / auth.config.ts # Password + magic code auth
│ ├── generate.ts # Generation action (3-tier retrieval, prompt assembly)
│ ├── memoryExtract.ts # Memory extraction pipeline
│ ├── memories.ts # Memory lifecycle, search, maintenance
│ ├── embeddings.ts # Embedding generation + vector search
│ ├── retrieval.ts # Episodic + procedural retrieval
│ ├── interactions.ts # Session recording + pattern promotion trigger
│ ├── patterns.ts # Procedural pattern promotion and decay
│ ├── entities.ts # Entity graph lifecycle and resolution
│ ├── taskBatches.ts # Task batch management
│ ├── voice.ts # Voice intent parsing
│ ├── traces.ts # Generation trace recording
│ ├── crons.ts # Scheduled: pattern decay, memory archival
│ └── llmProvider.ts # Provider resolution by user config
├── companion/
│ ├── server.ts # WebSocket server (localhost:4315)
│ ├── service.ts # LocalAgentCompanionService: run + approval lifecycle
│ ├── chrome-devtools-mcp-runtime.ts # Drives chrome-devtools-mcp and python agent bridge
│ ├── mcp-agent-bridge.ts # Node.js → Python mcp-agent bridge (JSON over stdin/stdout)
│ ├── python-browser-runtime-bridge.ts # Node.js → Python browser runtime bridge
│ ├── python_browser_runtime.py # Python agent entry point (uses mcp-agent + openai/anthropic)
│ ├── mcp_agent_bridge.py # Python bridge: receives tool calls, forwards to chrome-devtools-mcp
│ ├── state-store.ts # Local approval + run state persistence
│ └── live-logger.ts # Companion logging to .data/live.log
├── test/ # Vitest unit + integration tests
├── public/
├── wxt.config.ts
└── package.json
npm installnpx convex devVITE_CONVEX_URL=your_convex_deployment_urlSet AUTH_RESEND_KEY in your Convex environment variables if you want magic-code sign-in.
npm run devGo to chrome://extensions, enable Developer mode, click Load unpacked, and select:
.output/chrome-mv3-dev
For agentic task execution, start the companion in a separate terminal:
npm run companion:devThe companion runs on ws://localhost:4315 and the extension connects automatically.
npm run build
# outputs to .output/chrome-mv3
npm run zip
# creates distributable zipnpm testTests cover: session observation, platform detection, candidate scanning, agent run lifecycle, memory retrieval, pattern promotion, entity graph, task batching, companion service, and browser command execution.
- Website: https://www.cheatresume.com/
- Repository: https://github.com/sajanpoudel/text-fill
MIT
