Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ native/speech/tandem-speech

# Cowork workspace (local only, not for the repo)
.cowork/

# Implementation plans (private, local only)
docs/superpowers/
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ how it works, and why it exists.

- **Repo:** `hydro13/tandem-browser` (GitHub: hydro13)
- **Stack:** Electron 40 + TypeScript + Express.js API (`localhost:8765`) +
MCP server (236 tools)
MCP server (239 tools)
- **Goal:** An agent-first browser where any AI (via MCP, HTTP API, or
WebSocket) and a human browse together
- **Philosophy:** Local-first, privacy-first, no cloud dependencies in the
Expand All @@ -39,7 +39,7 @@ tandem-browser/
│ ├── snapshot/ # Accessibility tree with @refs
│ ├── network/ # Inspector + mocking
│ ├── sessions/ # Multi-session isolation
│ ├── mcp/ # MCP server (236 tools, full API parity)
│ ├── mcp/ # MCP server (239 tools, full API parity)
│ │ ├── server.ts # MCP server entry point
│ │ └── tools/ # Tool definitions (one file per domain)
│ ├── agents/ # TaskManager, X-Scout, TabLockManager
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

All notable changes to Tandem Browser will be documented in this file.

## [v0.71.0] - 2026-04-11

- feat: tab emoji badges — assign emoji to tabs for quick visual identification

New endpoints:
- POST /tabs/:id/emoji — set or flash an emoji badge on a tab
- DELETE /tabs/:id/emoji — remove emoji badge from a tab

New MCP tools:
- tandem_tab_emoji_set — set emoji badge on a tab
- tandem_tab_emoji_remove — remove emoji badge from a tab
- tandem_tab_emoji_flash — flash a pulsing emoji to attract user attention

Emoji badges are per tab session. The AI can flash emojis to signal the user
(e.g. "this tab is ready for review"). Users assign emojis via right-click
context menu. Everything runs in the shell — no injection into webviews.

## [v0.70.0] - 2026-04-10

- feat: add awareness tools — digest and focus for shared human-AI context
Expand Down
14 changes: 8 additions & 6 deletions PROJECT.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ bicycle: two riders, one machine, each contributing what the other can't do
alone.

The browser runs two things in parallel. The human uses it like any other browser
while AI agents operate through a built-in **MCP server** (236 tools) or a full
while AI agents operate through a built-in **MCP server** (239 tools) or a full
local **HTTP API** on `127.0.0.1:8765` with 300+ endpoints for navigation,
interaction, data extraction, automation, sessions, sync, extensions, and
developer tooling. Websites see a normal Chrome browser on macOS. They don't see
Expand Down Expand Up @@ -192,15 +192,17 @@ New Tab
─────────────────
Reload
Duplicate Tab
Copy Page Address
─────────────────
Move to Workspace ▶ [workspace icon + name per workspace]
─────────────────
Add to / Remove from Quick Links
Pin Tab / Unpin Tab
Mute Tab / Unmute Tab
Let Wingman handle this tab / Take back from Wingman
Set Emoji... ▶ [50 popular emojis grid, + Remove Emoji if set]
─────────────────
Close Tab
Close Other Tabs
Close Tabs to the Right
─────────────────
Reopen Closed Tab
```

---
Expand All @@ -211,7 +213,7 @@ Most endpoints require the `Authorization: Bearer <token>` header. The token is

Current route modules:
- `browser.ts` — navigation, screenshots, page actions
- `tabs.ts` — tab management, groups, focus
- `tabs.ts` — tab management, groups, focus, emoji badges
- `snapshots.ts` — accessibility tree and `@ref` interaction surfaces
- `devtools.ts` — CDP bridge (console, network, DOM, storage)
- `extensions.ts` — extension management and helper routes
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![Coverage](https://codecov.io/gh/hydro13/tandem-browser/branch/main/graph/badge.svg)](https://codecov.io/gh/hydro13/tandem-browser)
[![Ask a question](https://img.shields.io/badge/discussions-Q%26A-blue)](https://github.com/hydro13/tandem-browser/discussions/categories/q-a)

**236 MCP tools. Plug in any AI. No scraping. No API wrangling.**
**239 MCP tools. Plug in any AI. No scraping. No API wrangling.**

Tandem is a local-first Electron browser where a human and an AI agent browse
together. The agent sees what you see, navigates your tabs, reads your pages,
Expand All @@ -25,7 +25,7 @@ either protocol.
| Category | Tools | Examples |
|----------|-------|---------|
| **Navigation & Input** | 10 | Navigate, click, type, scroll, press keys, wait for load |
| **Tabs & Workspaces** | 10 | Open/close/focus tabs, create workspaces, move tabs between them |
| **Tabs & Workspaces** | 13 | Open/close/focus tabs, emoji badges, create workspaces, move tabs between them |
| **Page Content** | 8 | Read page, get HTML, extract content, get links, forms, screenshots |
| **Accessibility Snapshots** | 7 | Accessibility tree with `@ref` IDs, click/fill by ref, semantic find |
| **DevTools** | 12 | Console logs, network requests, DOM queries, XPath, performance, storage |
Expand All @@ -42,7 +42,7 @@ either protocol.
| **System** | 6 | Browser status, headless mode, Google Photos, security overrides |
| **Awareness** | 2 | Activity digest, real-time focus detection — the AI knows what you're doing |

**236 tools total** — full parity with the HTTP API.
**239 tools total** — full parity with the HTTP API.

## Why Not Just Use Playwright?

Expand Down Expand Up @@ -102,7 +102,7 @@ Add to your MCP configuration:
}
```

Start Tandem, and 236 tools are available immediately.
Start Tandem, and 239 tools are available immediately.

### Cursor / Windsurf / Other MCP Clients

Expand Down
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Last updated: April 9, 2026
## Current Snapshot

- Current app version: `0.70.0`
- MCP server: 236 tools (full API parity + awareness)
- MCP server: 239 tools (full API parity + awareness)
- The codebase scope is larger than this backlog summary and includes major subsystems such as `sidebar`, `workspaces`, `pinboards`, `sync`, `headless`, and `sessions`.
- Scheduled browsing already exists in baseline form via `WatchManager` and the `/watch/*` API routes.
- Session isolation already exists in baseline form via `SessionManager` and the `/sessions/*` API routes.
Expand Down
6 changes: 4 additions & 2 deletions docs/api.html
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ <h1>Full programmatic control over a real browser</h1>
<div class="hero-stats">
<div class="stat"><span class="stat-num">280+</span><span class="stat-label">Endpoints</span></div>
<div class="stat"><span class="stat-num">19</span><span class="stat-label">Domains</span></div>
<div class="stat"><span class="stat-num">236</span><span class="stat-label">MCP tools</span></div>
<div class="stat"><span class="stat-num">239</span><span class="stat-label">MCP tools</span></div>
</div>
</div>

Expand All @@ -126,7 +126,7 @@ <h1>Full programmatic control over a real browser</h1>
<section>
<div class="toc">
<a href="#browser">Browser <span class="count">16</span></a>
<a href="#tabs">Tabs <span class="count">8</span></a>
<a href="#tabs">Tabs <span class="count">10</span></a>
<a href="#snapshots">Snapshots <span class="count">8</span></a>
<a href="#content">Content <span class="count">17</span></a>
<a href="#devtools">DevTools <span class="count">16</span></a>
Expand Down Expand Up @@ -188,6 +188,8 @@ <h1>Full programmatic control over a real browser</h1>
<div class="endpoint"><span class="method post">POST</span><span class="path">/tabs/source <span class="desc">Set tab source (agent owner)</span></span></div>
<div class="endpoint"><span class="method post">POST</span><span class="path">/tabs/reconcile <span class="desc">Reconcile tabs with renderer</span></span></div>
<div class="endpoint"><span class="method post">POST</span><span class="path">/tabs/cleanup <span class="desc">Cleanup zombie tabs</span></span></div>
<div class="endpoint"><span class="method post">POST</span><span class="path">/tabs/:id/emoji <span class="desc">Set or flash emoji badge on tab</span></span></div>
<div class="endpoint"><span class="method delete">DELETE</span><span class="path">/tabs/:id/emoji <span class="desc">Remove emoji badge from tab</span></span></div>
</div>
</section>

Expand Down
6 changes: 3 additions & 3 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tandem Browser — The AI browser where human and AI work as one</title>
<meta name="description" content="Tandem Browser gives AI agents secure access to a real human browser. 236 MCP tools, 8-layer security, accessibility tree as universal API. Open source, MIT license.">
<meta name="description" content="Tandem Browser gives AI agents secure access to a real human browser. 239 MCP tools, 8-layer security, accessibility tree as universal API. Open source, MIT license.">
<meta property="og:title" content="Tandem Browser">
<meta property="og:description" content="The AI browser where human and AI work as one. 236 MCP tools. 8-layer security. MIT license.">
<meta property="og:description" content="The AI browser where human and AI work as one. 239 MCP tools. 8-layer security. MIT license.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://tandembrowser.org">
<link rel="canonical" href="https://tandembrowser.org">
Expand Down Expand Up @@ -133,7 +133,7 @@
<h1>The browser where human and AI work <em>as one</em></h1>
<p>Tandem Browser gives AI agents secure access to a real human browser. No API wrappers, no scraping, no bot detection. The accessibility tree of every website is the universal API. The AI rides alongside you &mdash; same tabs, same session, same&nbsp;screen.</p>
<div class="hero-stats">
<div class="stat"><span class="stat-num">236</span><span class="stat-label">MCP tools</span></div>
<div class="stat"><span class="stat-num">239</span><span class="stat-label">MCP tools</span></div>
<div class="stat"><span class="stat-num">300+</span><span class="stat-label">HTTP endpoints</span></div>
<div class="stat"><span class="stat-num">8</span><span class="stat-label">Security layers</span></div>
<div class="stat"><span class="stat-num">0</span><span class="stat-label">Telemetry</span></div>
Expand Down
6 changes: 3 additions & 3 deletions docs/public-launch.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ repository to the public.

## GitHub Repository Description

Agent-first browser for human-AI collaboration — 236 MCP tools, 300+ HTTP endpoints, built-in security.
Agent-first browser for human-AI collaboration — 239 MCP tools, 300+ HTTP endpoints, built-in security.

## Short Tagline

An agent-first browser for human-AI collaboration.

## Social / Announcement One-Liner

Tandem Browser is now public: an agent-first browser for human-AI collaboration with 236 MCP tools and 300+ HTTP endpoints, released as a developer preview.
Tandem Browser is now public: an agent-first browser for human-AI collaboration with 239 MCP tools and 300+ HTTP endpoints, released as a developer preview.

## Launch Post

Tandem Browser is now public.

Tandem is an agent-first browser built for human-AI collaboration on the local
machine. The human browses normally. Any AI agent that speaks MCP (236 tools) or
machine. The human browses normally. Any AI agent that speaks MCP (239 tools) or
HTTP (300+ endpoints) gets full browser control for navigation, extraction,
automation, screenshots, session work, and observability, while websites
continue to see a normal Chromium browser instead of an "AI browser" fingerprint.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "tandem-browser",
"version": "0.70.0",
"description": "Local-first Electron browser for human-AI collaboration with 236-tool MCP server, 300+ endpoint HTTP API, and built-in security controls",
"description": "Local-first Electron browser for human-AI collaboration with 239-tool MCP server, 300+ endpoint HTTP API, and built-in security controls",
"main": "dist/main.js",
"author": "Tandem Browser contributors",
"license": "MIT",
Expand Down
15 changes: 15 additions & 0 deletions shell/css/browser-shell.css
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,21 @@
flex-shrink: 0;
}

.tab .tab-emoji {
font-size: 12px;
flex-shrink: 0;
line-height: 1;
}

.tab .tab-emoji.flash {
animation: emojiFlash 1s ease-in-out infinite;
}

@keyframes emojiFlash {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.4; transform: scale(1.3); }
}

.tab .tab-favicon {
width: 14px;
height: 14px;
Expand Down
26 changes: 26 additions & 0 deletions shell/css/sidebar-panels.css
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,32 @@
right: 100%;
}

/* Emoji picker grid inside context menu submenu */
.tandem-emoji-grid {
min-width: 240px;
max-width: 260px;
}
.tandem-emoji-picker {
display: grid;
grid-template-columns: repeat(10, 1fr);
gap: 2px;
padding: 6px 8px;
}
.tandem-emoji-btn {
font-size: 16px;
width: 22px;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 4px;
transition: background 0.1s;
}
.tandem-emoji-btn:hover {
background: rgba(255,255,255,0.15);
}

/* ═══════════════════════════════════════════════ */
/* PINBOARDS — Sidebar Panel + Card Grid */
/* ═══════════════════════════════════════════════ */
Expand Down
64 changes: 64 additions & 0 deletions shell/js/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2124,6 +2124,70 @@
if (wv) wv.audioMuted = !isMuted;
});

// — Set Emoji (submenu)
{
const emojiItem = document.createElement('div');
emojiItem.className = 'tandem-ctx-menu-item';
const tabEl = document.querySelector('.tab[data-tab-id="' + domTabId + '"]');
const tabEmojiSpan = tabEl ? tabEl.querySelector('.tab-emoji') : null;
const currentEmoji = (tabEmojiSpan && tabEmojiSpan.style.display !== 'none') ? tabEmojiSpan.textContent : '';
const emojiLabel = document.createElement('span');
emojiLabel.textContent = currentEmoji ? ('Emoji: ' + currentEmoji) : 'Set Emoji...';
const emojiArrow = document.createElement('span');
emojiArrow.className = 'ctx-arrow';
emojiArrow.textContent = '▶';
emojiItem.appendChild(emojiLabel);
emojiItem.appendChild(emojiArrow);

const emojiSub = document.createElement('div');
emojiSub.className = 'tandem-ctx-submenu tandem-emoji-grid';

if (currentEmoji) {
const removeItem = document.createElement('div');
removeItem.className = 'tandem-ctx-submenu-item';
removeItem.textContent = 'Remove Emoji';
removeItem.addEventListener('click', async () => {
closeCtxMenu();
await fetch('http://localhost:8765/tabs/' + encodeURIComponent(domTabId) + '/emoji', {
method: 'DELETE',
headers: { Authorization: 'Bearer ' + TOKEN }
});
});
emojiSub.appendChild(removeItem);
const sep = document.createElement('div');
sep.className = 'tandem-ctx-separator';
emojiSub.appendChild(sep);
}

const emojis = [
'🔥','⭐','💡','🚀','✅','❌','⚠️','🎯','💬','📌',
'📚','🧪','🔧','🎨','📊','🔒','👀','💰','🎵','❤️',
'🏠','📧','🛒','📝','🗂️','🌍','☁️','📸','🎮','🤖',
'🧠','🔍','📅','🎁','🏷️','⏰','🔔','💻','📱','🎬',
'🍕','☕','🌟','💎','🦊','🐛','🏗️','📦','🔗','🏆',
];
const grid = document.createElement('div');
grid.className = 'tandem-emoji-picker';
emojis.forEach(emoji => {
const btn = document.createElement('span');
btn.className = 'tandem-emoji-btn';
btn.textContent = emoji;
btn.addEventListener('click', async () => {
closeCtxMenu();
await fetch('http://localhost:8765/tabs/' + encodeURIComponent(domTabId) + '/emoji', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + TOKEN },
body: JSON.stringify({ emoji: emoji })
});
});
grid.appendChild(btn);
});
emojiSub.appendChild(grid);

emojiItem.appendChild(emojiSub);
menu.appendChild(emojiItem);
}

addSep();

// — Close Tab
Expand Down
25 changes: 24 additions & 1 deletion shell/js/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@
tabEl.dataset.tabId = tabId;
tabEl.draggable = true;
tabEl.innerHTML = `
<span class="tab-source" title="You controlled">👤</span>
<span class="tab-source" style="display:none"></span>
<span class="group-dot" style="display:none"></span>
<span class="tab-emoji" style="display:none"></span>
<img class="tab-favicon" src="" style="display:none">
<span class="tab-title">New Tab</span>
<button class="tab-close" title="Close tab">✕</button>
Expand Down Expand Up @@ -345,6 +346,22 @@
focusTab(tabId) {
focusRendererTab(tabId);
},

setEmoji(tabId, emoji, flash) {
const entry = tabs.get(tabId);
if (!entry) return;
const emojiEl = entry.tabEl.querySelector('.tab-emoji');
if (!emojiEl) return;
if (emoji) {
emojiEl.textContent = emoji;
emojiEl.style.display = '';
emojiEl.classList.toggle('flash', !!flash);
} else {
emojiEl.textContent = '';
emojiEl.style.display = 'none';
emojiEl.classList.remove('flash');
}
},
};

window.__tandemRenderer = {
Expand Down Expand Up @@ -445,5 +462,11 @@
}
});
}

if (window.tandem && window.tandem.onTabEmojiChanged) {
window.tandem.onTabEmojiChanged((data) => {
window.__tandemTabs.setEmoji(data.tabId, data.emoji, data.flash);
});
}
})();
})();
Loading
Loading