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
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 (253 tools)
MCP server (257 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 (253 tools, full API parity)
│ ├── mcp/ # MCP server (257 tools, full API parity)
│ │ ├── server.ts # MCP server entry point
│ │ └── tools/ # Tool definitions (one file per domain)
│ ├── agents/ # TaskManager, X-Scout, TabLockManager
Expand Down
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ All notable changes to Tandem Browser will be documented in this file.
messages and reply into the panel without requiring OpenClaw on the host.
OpenClaw remains available as a separate backend and the shell falls back to
Tandem local chat when the gateway is not reachable.
- **Agent bootstrap contract** (`src/api/routes/bootstrap.ts`,
`src/api/agent-bootstrap.ts`) - adds authenticated `GET /agent/bootstrap`
with runtime workspace/tab context, a required startup sequence, operating
rules, toolbox guidance, and tool-selection hints so newly connected agents
understand how to use Tandem as a full browser layer.

### Changed

Expand All @@ -20,6 +25,64 @@ All notable changes to Tandem Browser will be documented in this file.
agents configured in Connected Agents, so a lone Codex binding no longer
exposes stale OpenClaw/Claude tabs or lets the legacy Claude polling backend
mirror the Codex conversation.
- **Pairing response** (`src/api/routes/pairing.ts`) - now returns bootstrap
next-read links, docs URLs, and snapshot-first workflow guidance alongside
the durable binding token without changing the existing token contract.
- **Paired-agent startup enforcement** (`src/api/server.ts`,
`src/pairing/manager.ts`) - new binding tokens must read `/skill`,
`/agent/manifest`, and `/agent/bootstrap` with the token before normal
API/MCP routes are unlocked; skipped startup now returns
`428 agent_startup_required` instead of silently letting the agent continue
uninformed. Legacy local `api-token` clients remain ungated.
- **Agent discovery docs** (`/agent`, `/skill`, `/agent/manifest`,
`skill/SKILL.md`) - now explicitly tell agents to read `/skill`,
`/agent/manifest`, `/agent/bootstrap`, `/status`, and `/workspaces` after
connecting instead of stopping at successful authentication.

## [v1.11.0] - 2026-05-05

Configurable Agent API port release. Tandem now lets users change the Agent API
port from the default `8765` while preserving local MCP/HTTP clients and remote
Tailscale agent connectivity.

### Added

- **Agent API port setting** (`src/config/manager.ts`, `shell/settings.html`) -
stores `general.apiPort`, validates TCP port values, and shows local plus
remote endpoint previews in Settings > Connected Agents.
- **Endpoint bootstrap artifacts** (`~/.tandem/api-port`,
`~/.tandem/api-endpoints.json`) - publish the configured local loopback URL
and Tailscale/private-network metadata without replacing the readable
`api-token` compatibility contract.
- **Port helper tests** (`src/config/tests/api-endpoints.test.ts`,
`src/config/tests/config.test.ts`) - cover strict port validation, local
loopback URL construction, custom ports, and local-only remote warnings.

### Changed

- **API startup** (`src/main.ts`, `src/api/server.ts`, `scripts/start.js`) -
starts TandemAPI on the configured port, writes endpoint discovery metadata,
and reports configured-port conflicts without blindly terminating unrelated
processes.
- **Local clients** (`src/mcp/api-client.ts`, `src/mcp/server.ts`,
`src/agents/x-scout.ts`, `cli/client.ts`) - discover the configured port and
connect through `http://127.0.0.1:<configured-port>`.
- **Shell API helpers** (`src/preload/index.ts`, `shell/js/api-auth.js`,
shell settings and chat modules) - route internal calls through the configured
loopback endpoint while preserving compatibility with legacy default-port
fetches.
- **Remote agent instructions** (`src/api/routes/pairing.ts`, docs) - advertise
`http://<tailscale-or-private-ip>:<configured-port>` only when remote listen
mode is enabled, and warn clearly for loopback-only mode.

### Technical Details

- `apiListenHost` remains separate from `apiPort`; supported remote mode still
binds to `0.0.0.0` for private overlay networks and is not documented as
public-WAN support.
- Invalid ports such as empty strings, text, decimals, negatives, `0`, and
values above `65535` are rejected with a clear API/UI error.
- No new dependency was added.

## [v1.10.0] - 2026-05-05

Expand Down
9 changes: 5 additions & 4 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** (253 tools) or a
while AI agents operate through a built-in **MCP server** (257 tools) or a
**300+ endpoint HTTP API** for navigation, interaction, data extraction,
automation, sessions, sync, extensions, and developer tooling. Local agents can
use MCP or HTTP. Remote agents on the same Tailscale network connect via HTTP
Expand All @@ -31,7 +31,7 @@ The security layer exists because when an AI has access to your browser, your th
Data stays local. Sessions are isolated. Nothing leaves the machine through Tandem Browser without going through a filter first.

**GitHub:** `hydro13/tandem-browser`
**Current version:** `1.10.0`
**Current version:** `1.11.0`
**Repository status:** Public developer preview
**Started:** February 11, 2026

Expand Down Expand Up @@ -107,7 +107,7 @@ Within the product UI, the right-side assistant surface is called the Wingman pa
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Tandem HTTP API — localhost:8765 (Express) │ │
│ │ Tandem HTTP API — configured port, default 8765 (Express) │ │
│ │ 300+ route handlers across 16 route modules │ │
│ │ │ │
│ │ Navigation, Content, Interaction, Tabs, Screenshots │ │
Expand Down Expand Up @@ -291,7 +291,8 @@ npm run compile
npm start

# API
curl http://127.0.0.1:8765/status
API_PORT="$(cat ~/.tandem/api-port 2>/dev/null || printf 8765)"
curl "http://127.0.0.1:${API_PORT}/status"
```

**macOS note:** `npm start` already clears Electron quarantine flags before launch. If Electron is re-downloaded or started outside the provided scripts, run `xattr -cr node_modules/electron/dist/Electron.app` first or macOS may terminate the process silently.
Expand Down
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Want the fastest path in?
| **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 |

**253 tools total** — full parity with the HTTP API.
**257 tools total** — full parity with the HTTP API.

## Why Not Just Use Playwright?

Expand Down Expand Up @@ -158,7 +158,7 @@ That's it. Tandem publishes its own bootstrap surface — the agent reads `/agen

**Windows 11 x64** — download the installer or portable build:

**[Download Tandem Browser v1.10.0 →](https://github.com/hydro13/tandem-browser/releases/tag/v1.10.0)**
**[Download Tandem Browser v1.11.0 →](https://github.com/hydro13/tandem-browser/releases/tag/v1.11.0)**

Windows builds are official Tandem Browser downloads, but they are currently
unsigned. Windows may show an unknown publisher or SmartScreen warning during
Expand All @@ -181,7 +181,7 @@ macOS and Windows are supported platforms. Linux is best-effort.

Depending on what you want to do:

- **Install Tandem** -> download [macOS v1.0.0](https://github.com/hydro13/tandem-browser/releases/tag/v1.0.0) or [Windows v1.10.0](https://github.com/hydro13/tandem-browser/releases/tag/v1.10.0), or follow Quick Start above
- **Install Tandem** -> download [macOS v1.0.0](https://github.com/hydro13/tandem-browser/releases/tag/v1.0.0) or [Windows v1.11.0](https://github.com/hydro13/tandem-browser/releases/tag/v1.11.0), or follow Quick Start above
- **Connect an agent** -> see [Connect Your AI Agent](#connect-your-ai-agent)
- **Explore the API and docs** -> browse [docs/](docs/) and [docs/INDEX.md](docs/INDEX.md)
- **See the product story and website** -> visit [tandembrowser.org](https://tandembrowser.org)
Expand All @@ -208,6 +208,14 @@ The primary onboarding flow is now inside Tandem itself:
Tandem handles the setup-code flow and publishes its own bootstrap/discovery
surface for the agent at `/agent`, `/agent/manifest`, `/agent/version`, and
`/skill`.
New paired agents must read `/skill`, `/agent/manifest`, and
`/agent/bootstrap` with their binding token before normal API/MCP use; Tandem
returns `428 agent_startup_required` when that startup sequence is skipped.

The Agent API port defaults to `8765` and can be changed in **Settings ->
Connected Agents -> Agent API settings**. Local clients should discover the
current port from `~/.tandem/api-port` or `~/.tandem/api-endpoints.json`.
`~/.tandem/api-token` remains the readable local token compatibility contract.

### On the same machine (MCP or HTTP)

Expand All @@ -231,15 +239,17 @@ Cursor, Windsurf, or any MCP client):
}
```

Start Tandem, and 253 tools are available immediately.
Start Tandem, and 257 tools are available immediately.

**HTTP API** — Use the local API token directly:

```bash
API_PORT="$(cat ~/.tandem/api-port 2>/dev/null || printf 8765)"
API="http://127.0.0.1:${API_PORT}"
TOKEN="$(cat ~/.tandem/api-token)"

curl -sS http://127.0.0.1:8765/status
curl -sS http://127.0.0.1:8765/tabs/list \
curl -sS "$API/status"
curl -sS "$API/tabs/list" \
-H "Authorization: Bearer $TOKEN"
```

Expand All @@ -266,7 +276,7 @@ Connected Agents UI.
"mcpServers": {
"tandem": {
"type": "streamable-http",
"url": "http://<tandem-tailscale-ip>:8765/mcp",
"url": "http://<tandem-tailscale-ip>:<configured-port>/mcp",
"headers": {
"Authorization": "Bearer <your-binding-token>"
}
Expand All @@ -277,19 +287,19 @@ Connected Agents UI.

**HTTP API** works the same way as local, using the binding token as Bearer auth.

Both transports give remote agents the same 253 tools and 300+ endpoints as local agents.
Both transports give remote agents the same 257 tools and 300+ endpoints as local agents.

<details>
<summary>Manual pairing (for scripts or custom tooling)</summary>

```bash
# Exchange setup code for token
curl -X POST http://<tandem-tailscale-ip>:8765/pairing/exchange \
curl -X POST http://<tandem-tailscale-ip>:<configured-port>/pairing/exchange \
-H "Content-Type: application/json" \
-d '{"code":"TDM-XXXX-XXXX","machineId":"...","machineName":"...","agentLabel":"...","agentType":"..."}'

# Use the returned token
curl -sS http://<tandem-tailscale-ip>:8765/status \
curl -sS http://<tandem-tailscale-ip>:<configured-port>/status \
-H "Authorization: Bearer <token>"
```

Expand All @@ -304,7 +314,8 @@ A running Tandem instance publishes its own version-matched discovery surface:
- `GET /skill` — version-matched usage guide

These are public (no auth required) and use the request `Host` header, so they
return correct URLs whether accessed locally or over Tailscale.
return correct URLs whether accessed locally or over Tailscale on the
configured port.

## Security Model

Expand Down Expand Up @@ -367,7 +378,7 @@ contributors, not yet a polished mass-user release.
- Supported platform: Windows 11 x64
- Secondary platform: Linux
- Binaries: signed and notarized macOS Apple Silicon builds plus unsigned Windows x64 installer/portable builds on [GitHub Releases](https://github.com/hydro13/tandem-browser/releases), starting with Windows in v1.10.0
- Current version: `1.10.0`
- Current version: `1.11.0`
- Package metadata: [package.json](package.json)

## Community
Expand Down
11 changes: 9 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ Last updated: May 5, 2026

## Current Snapshot

- Current app version: `1.10.0`
- MCP server: 253 tools (full API parity + awareness)
- Current app version: `1.11.0`
- MCP server: 257 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 Expand Up @@ -98,6 +98,13 @@ Last updated: May 5, 2026

## Recently Completed

- [x] Agent bootstrap contract: pairing now returns explicit next-read links,
`/agent/bootstrap` exposes runtime context plus the agent toolbox, and
`/agent`, `/skill`, and `/agent/manifest` now make the required startup
sequence clear for newly connected agents.
- [x] Configurable Agent API port settings: users can change the default
`8765` port, local clients discover the configured loopback endpoint through
bootstrap files, and remote Tailscale instructions use the configured port.
- [x] Windows support Phase 12: centralized shortcut accelerator and label
handling, switched Electron menus to `CommandOrControl` accelerator strings,
preserved macOS shortcut labels, and rendered Windows shortcut labels as
Expand Down
3 changes: 2 additions & 1 deletion cli/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import fs from 'fs';
import { buildLocalApiBaseUrl, readApiPortFromBootstrap } from '../src/config/api-endpoints';
import { tandemDir } from '../src/utils/paths';

const API_BASE = process.env.TANDEM_API || 'http://localhost:8765';
const API_BASE = process.env.TANDEM_API || buildLocalApiBaseUrl(readApiPortFromBootstrap());
const TOKEN_PATH = tandemDir('api-token');

function getToken(): string {
Expand Down
6 changes: 3 additions & 3 deletions docs/api.html
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,11 @@
<div class="hero">
<div class="hero-label">HTTP API Reference</div>
<h1>Full programmatic control over a real browser</h1>
<p>Default port <code style="background:var(--bg3);padding:.15rem .4rem;border-radius:3px;font-size:.8rem;color:var(--green)">8765</code>. Accessible locally and remotely over Tailscale. Authenticate with <code style="background:var(--bg3);padding:.15rem .4rem;border-radius:3px;font-size:.8rem;color:var(--green)">Authorization: Bearer &lt;token&gt;</code>.</p>
<p>Default port <code style="background:var(--bg3);padding:.15rem .4rem;border-radius:3px;font-size:.8rem;color:var(--green)">8765</code>, configurable in Tandem Settings. Accessible locally and remotely over Tailscale/private overlay networks. Authenticate with <code style="background:var(--bg3);padding:.15rem .4rem;border-radius:3px;font-size:.8rem;color:var(--green)">Authorization: Bearer &lt;token&gt;</code>.</p>
<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">253</span><span class="stat-label">MCP tools</span></div>
<div class="stat"><span class="stat-num">257</span><span class="stat-label">MCP tools</span></div>
</div>
</div>

Expand Down Expand Up @@ -590,7 +590,7 @@ <h1>Full programmatic control over a real browser</h1>
<!-- Info boxes -->
<section>
<div class="info-box">
<p><strong>Base URL:</strong> <code>http://localhost:8765</code> (local) or <code>http://&lt;tailscale-ip&gt;:8765</code> (remote)</p>
<p><strong>Base URL:</strong> <code>http://127.0.0.1:&lt;configured-port&gt;</code> (local) or <code>http://&lt;tailscale-ip&gt;:&lt;configured-port&gt;</code> (remote over Tailscale/private networks)</p>
<p style="margin-top:.5rem"><strong>Auth:</strong> <code>Authorization: Bearer &lt;token&gt;</code> &mdash; local token from <code>~/.tandem/api-token</code> or binding token from pairing.</p>
<p style="margin-top:.5rem"><strong>Discovery:</strong> <code>GET /agent/manifest</code> returns the full endpoint list as structured JSON. <code>GET /agent</code> returns a human-readable bootstrap page.</p>
<p style="margin-top:.5rem"><strong>Tab targeting:</strong> Use the <code>X-Tab-Id</code> header to target a specific background tab without focusing it.</p>
Expand Down
Loading
Loading