The material below is a bundle of source-file excerpts, with each original file and line range labeled.
SKILL.md is the entry point; the labeled ranges identify which excerpts are supplied.

--- BEGIN FILE "pair-agent/SKILL.md" (lines 418-663; entrypoint) ---
## Step 1: Check prerequisites

```bash
$B status 2>/dev/null
```

If the browse server is not running, start it:

```bash
$B goto about:blank
```

This ensures the server is up and healthy before pairing.

## Step 2: Ask what they want

Use AskUserQuestion:

> Which agent do you want to pair with your browser? This determines the
> instructions format and where credentials get written.

Options:
- A) OpenClaw (local or remote)
- B) Codex / OpenAI Agents (local)
- C) Cursor (local)
- D) Another Claude Code session (local or remote)
- E) Something else (generic HTTP instructions — use this for Hermes)

Based on the answer, set `TARGET_HOST`:
- A → `openclaw`
- B → `codex`
- C → `cursor`
- D → `claude`
- E → generic (no host-specific config)

## Step 4: Execute pairing

**Live-daemon consent (one-way door).** Pairing can relaunch the browser
daemon; a relaunch KILLS the running headless daemon — open tabs, cookies,
and logged-in sessions die with it. The CLI honors the iron rule (only an
explicit `--force-restart` may kill a live daemon), so check first:

```bash
$B status 2>/dev/null | head -5
```

If a daemon is running, ask via AskUserQuestion (one-way door — lost
tabs/cookies/logins cannot be recovered):

> "A headless browser daemon is live (tabs and logins may be active). Pairing
> headed requires relaunching it — everything in the current daemon is lost.
>
> RECOMMENDATION: Choose B unless the remote agent specifically needs a
> visible browser window; pairing works against the existing daemon."

Options:
- A) Relaunch (pass `--force-restart`; current tabs/cookies/logins are lost)
- B) Keep the live daemon (recommended — pair against it as-is)

Only pass `--force-restart` to the commands below after an explicit A. Never
default to A on a vague reply — this is a destructive confirmation.

### If same machine (option A):

Run pair-agent with --local flag:

```bash
$B pair-agent --local TARGET_HOST
```

Replace `TARGET_HOST` with the value from Step 2 (openclaw, codex, cursor, etc.).

If it succeeds, tell the user:
"Done. TARGET_HOST can now use your browser. It will read credentials from the
config file that was written. Try asking it to navigate to a URL."

If it fails (host not found, write permission error), show the error and suggest
using the generic remote flow instead.

### If different machine (option B):

**Consent gate (once per machine).** The tunnel exposes this browser beyond
the machine, so it is OFF until the user opts in — the daemon refuses
`/tunnel/start` and `BROWSE_TUNNEL=1` otherwise. Check the standing consent:

```bash
~/.claude/skills/gstack/bin/gstack-config get pair_agent 2>/dev/null || echo "unset"
```

If the value is not `on`, ask via AskUserQuestion (one-way-door posture —
this opens a path from the internet to the local browser):

> "Remote pairing runs an ngrok tunnel from the internet to this machine's
> browser (locked to a 26-command allowlist + scoped token, but still an
> exposure). Enable pair-agent on this machine?"

Options: A) Enable — run `~/.claude/skills/gstack/bin/gstack-config set pair_agent on`, confirm it reads back `on`, and continue. B) No — stop here; local pairing (option A above) still works.

If the value is already `on`, say nothing and continue — consent stands until
`gstack-config set pair_agent off`.

Then detect ngrok status:

```bash
which ngrok 2>/dev/null && echo "NGROK_INSTALLED" || echo "NGROK_NOT_INSTALLED"
ngrok config check 2>/dev/null && echo "NGROK_AUTHED" || echo "NGROK_NOT_AUTHED"
```

**If ngrok is installed and authed:** Just run the command. The CLI will auto-detect
ngrok, start the tunnel, and print the instruction block with the tunnel URL:

```bash
$B pair-agent --client TARGET_HOST
```

Default access already includes JS execution. To also grant browser-wide
control (stop, restart, disconnect):

```bash
$B pair-agent --control --client TARGET_HOST
```

For a less-trusted agent, narrow the scopes instead:

```bash
$B pair-agent --restrict read --client TARGET_HOST            # read-only
$B pair-agent --restrict "read,write" --client TARGET_HOST    # no JS, no cookies
```

**CRITICAL: You MUST output the full instruction block to the user.** The command
prints everything between ═══ lines. Copy the ENTIRE block verbatim into your
response so the user can copy-paste it into their other agent. Do NOT summarize it,
do NOT skip it, do NOT just say "here's the output." The user needs to SEE the block
to copy it. Output it inside a markdown code block so it's easy to select and copy.

Then tell the user:
"Copy the block above and paste it into your other agent's chat. The setup key
expires in 5 minutes."

**If ngrok is installed but NOT authed:** Walk the user through authentication.

SECURITY: the ngrok authtoken must NEVER pass through this chat, a Bash tool
call, or shell history — a token pasted here lands in the transcript (and
anything the transcript syncs to). The user runs the auth command in their
OWN terminal; you only verify the result.

Tell the user:
"ngrok is installed but not logged in. Let's fix that — in your own terminal
(not here; the token should never enter this chat):

1. Go to https://dashboard.ngrok.com/get-started/your-authtoken
2. Copy your auth token
3. In YOUR terminal, run: ngrok config add-authtoken <paste your token>
4. Tell me 'done' when finished."

STOP here and wait for the user to say they've run it. Do NOT accept a pasted
token; if the user pastes one anyway, tell them to rotate it at
https://dashboard.ngrok.com (it's now in the transcript) and re-auth in their
terminal with the new one.

When they say done, verify without touching the token:
```bash
ngrok config check 2>/dev/null && echo "NGROK_AUTHED" || echo "NGROK_NOT_AUTHED"
```

If `NGROK_AUTHED`: retry `$B pair-agent --client TARGET_HOST`.
If still `NGROK_NOT_AUTHED`: ask them to re-run the command in their terminal.

**If ngrok is NOT installed:** Walk the user through installation:

Tell the user:
"To connect a remote agent, we need ngrok (a tunnel that exposes your local
browser to the internet securely).

1. Go to https://ngrok.com and sign up (free tier works)
2. Install ngrok:
   - macOS: `brew install ngrok`
   - Linux: `snap install ngrok` or download from ngrok.com/download
3. Auth it: `ngrok config add-authtoken YOUR_TOKEN`
   (get your token from https://dashboard.ngrok.com/get-started/your-authtoken)
4. Come back here and run `/pair-agent` again."

STOP here. Wait for the user to install ngrok and re-invoke.

## What the remote agent can do

Default access is read+write+admin+meta. The trust boundary is the pairing
ceremony, not the scope:
- Navigate to URLs, click elements, fill forms, take screenshots
- Read page content (text, HTML, snapshot)
- Create new tabs (each agent gets its own)
- Execute JavaScript via `eval`
- Cannot stop or restart the browser, or disconnect headed mode (needs --control)

Remote agents go through the tunnel command allowlist: `eval` works, but the
`js`, `cookies`, and `storage` commands are not dispatchable over the tunnel
even with admin scope. Agents paired with `--local` get all four.

With --restrict (`--restrict read`, `--restrict "read,write"`):
- Sandboxed sessions: read-only, or read+write with no JS, cookie, or storage
  access. Pair this way when the remote agent will read untrusted web content:
  a trusted agent can be prompt-injected by pages it reads, and scope caps the
  blast radius (eval works over the tunnel).
- `--restrict` never grants `control`; that scope stays behind --control.
- To tighten an agent that is ALREADY paired, re-pair it with the **same
  `--client` name** and the narrower `--restrict`/`--domain`. A reducing re-pair
  revokes the previous session immediately and releases its tabs — the agent
  must reconnect with the new key, so the old wide access does not linger.
  Re-pairing without `--client` mints a brand-new agent and leaves the old one
  untouched. Broadening or refreshing keeps the working session (no outage).
- `root` is a reserved `--client` name (it would bypass all scope enforcement).

With --control (--admin is the legacy alias):
- Everything, plus browser-wide destructive ops (stop, restart, disconnect)
- Only for agents you fully trust.


--- END FILE "pair-agent/SKILL.md" ---