all repos — sz-chrome-remote-cdp @ 7d70b2ba0cb98d0c100d3506b91518289be575e7

pi-browser-harness, patched to drive a Chrome on another host over CDP, with castle_cdp_search web search

docs: add speed-optimizations research; chore: improve browser detection in setup
amankumarsingh77 amankumarsingh7702@gmal.com
Mon, 04 May 2026 21:14:30 +0530
commit

7d70b2ba0cb98d0c100d3506b91518289be575e7

parent

cb9a51eb557ae67f9d7c65f20e600c278cf7f6ab

3 files changed, 204 insertions(+), 5 deletions(-)

jump to
M .gitignore → .gitignore

@@ -4,3 +4,4 @@ benchmark/

*.tsbuildinfo .DS_Store *.log +docs/
A docs/speed-optimizations.md

@@ -0,0 +1,180 @@

+# Pi Agent Features for Blazingly Fast Browser Automation + +> Deep research on every pi agent capability that can be leveraged to make `pi-browser-harness` as fast as possible. + +--- + +## 1. Parallel Execution (The Biggest Speed Multiplier) + +| Feature | How It Makes Browser Harness Fast | +|---------|-----------------------------------| +| **Parallel Tool Execution (Default)** | Pi executes sibling tool calls from the same assistant message **concurrently** by default. Multiple `browser_click`, `browser_type`, `browser_scroll`, or `browser_http_get` calls on independent targets run simultaneously instead of sequentially. | +| **`browser_open_urls`** | Opens multiple URLs in **parallel new tabs** in one shot. For research/scraping workflows, this eliminates sequential navigation delays entirely. | +| **Parallel `browser_execute_js` across tabs** | After listing tabs, you can switch to each tab and extract data in parallel batches rather than one-by-one. | +| **Parallel `browser_http_get`** | Direct HTTP GETs outside the browser are independent of browser state—fire dozens of API calls simultaneously. | +| **Read + Browser tools in same turn** | `read` (file system) and `browser_*` tools can run in parallel in the same turn because they hit different resources. | +| **`withFileMutationQueue()`** | For custom tools that write files, this queues mutations per-file so parallel tool execution doesn't cause race conditions/overwrite bugs. | + +### Patterns + +```typescript +// Open all result pages at once +browser_open_urls({ urls: ["url1", "url2", "url3"] }) + +// Then extract from each tab in parallel batches +browser_switch_tab(targetId1) → browser_execute_js("...") +browser_switch_tab(targetId2) → browser_execute_js("...") +``` + +--- + +## 2. Batching & Scripting (Eliminate Round-Trips) + +| Feature | How It Makes Browser Harness Fast | +|---------|-----------------------------------| +| **`browser_run_script`** | Write a temporary `.js` script and execute it with **full daemon + Node.js access**. This is the single biggest speedup for multi-step browser workflows—scrape 47 paginated pages, bulk-export CSV, or run complex CDP sequences in **one round-trip** instead of 47 screenshot→click loops. | +| **Custom Tools via Extensions (`pi.registerTool`)** | Register high-level browser tools like `batch_extract`, `bulk_screenshot`, or `auto_login` that internally run multiple CDP commands. The LLM calls one tool; the extension does the orchestration. | +| **Dynamic Tool Registration** | Tools can be registered at runtime (e.g., after detecting a page type). This lets you inject specialized, fast-path tools only when needed. | +| **Early Termination (`terminate: true`)** | Custom tools can return `terminate: true` to hint that no follow-up LLM turn is needed. For structured extraction tasks, this saves an entire model call. | +| **`onUpdate` streaming in custom tools** | Stream partial results back while long browser scripts run, so the user sees progress without waiting for full completion. | + +### When to Use Scripts vs. Built-in Tools + +| Use Scripts | Use Built-in Tools | +|-------------|-------------------| +| Same 3+ step sequence repeated | One-off actions | +| Bulk CSV export, structured extraction, pagination | Simple click or type | +| Domain-specific reusable pattern | Quick screenshot verification | + +```typescript +// Example: Batch extraction script +write("/tmp/extract-products.js", ` + const data = await daemon.evaluateJs(\` + JSON.stringify(Array.from(document.querySelectorAll('${params.rowSelector}')).map(el => ({ + title: el.querySelector('${params.titleSelector}')?.textContent?.trim(), + price: el.querySelector('${params.priceSelector}')?.textContent?.trim(), + }))) + \`); + return { content: [{ type: 'text', text: data }], details: { raw: JSON.parse(data) } }; +`) +browser_run_script("/tmp/extract-products.js", { rowSelector: "...", titleSelector: "...", priceSelector: "..." }) +``` + +--- + +## 3. Subagents & Delegation (Scale Across Cores/Sessions) + +| Feature | How It Makes Browser Harness Fast | +|---------|-----------------------------------| +| **Parallel subagent tasks** | Launch multiple `scout`, `researcher`, or `worker` agents simultaneously with `tasks: [...]`. Each can control its own browser tab or perform independent research. | +| **Async/background runs (`async: true`)** | Launch a long-running browser automation (e.g., full test suite, deep crawl) in the background while the parent agent continues working. | +| **Chain execution** | Sequence dependent steps (e.g., `scout` → `planner` → `worker`) with `{previous}` and `{chain_dir}` templating so each step doesn't rediscover context. | +| **Worktree isolation (`worktree: true`)** | Run parallel agents on isolated git worktrees so concurrent file writes don't conflict—essential for parallel browser-based testing or scraping pipelines. | +| **File-only output mode (`outputMode: "file-only"`)** | For subagents, return compact file references instead of inline text. This prevents massive context bloat on the parent session, keeping subsequent turns fast. | +| **Forked context (`context: "fork"`)** | Spawn advisory `oracle` or `reviewer` agents that inherit parent history but run on a branched session—no context copying overhead. | + +### Example: Parallel Research with Browser + +```typescript +subagent({ + tasks: [ + { agent: "scout", task: "Audit frontend auth flow" }, + { agent: "researcher", task: "Research current retry/backoff best practices" } + ], + concurrency: 2 +}) +``` + +--- + +## 4. Event Interception & Mutation (Optimize at the Harness Level) + +| Feature | How It Makes Browser Harness Fast | +|---------|-----------------------------------| +| **`tool_call` event interception** | Block, modify, or batch tool arguments before execution. Example: detect three sequential `browser_click` calls and rewrite them into a single `browser_run_script` batch. | +| **`tool_result` event modification** | Post-process raw browser output (e.g., truncate huge HTML, extract only diff, cache repeated queries) before it hits the LLM context. | +| **`before_agent_start` / `context` events** | Inject pre-computed page state or strip unnecessary history before the LLM call, reducing token count and latency. | +| **`input` event transformation** | Transform user prompts before they reach the agent. Example: expand `!{selector}` inline bash or auto-convert "scrape all links" into a batched script invocation. | +| **AbortSignal (`ctx.signal`)** | Cancel long-running browser operations (navigation, waits, downloads) immediately when the user hits Escape or a turn ends. | + +### Example: Extension-Level Batching + +```typescript +pi.on("tool_call", async (event, ctx) => { + if (event.toolName === "browser_click" && shouldBatch(event.input)) { + // Rewrite into a script call + event.input = { scriptPath: "/tmp/batch-clicks.js", params: { clicks: [...] } }; + } +}); +``` + +--- + +## 5. Context Efficiency (Keep Token Latency Low) + +| Feature | How It Makes Browser Harness Fast | +|---------|-----------------------------------| +| **Automatic Compaction** | Proactively summarizes old messages when approaching the context window. For long browser sessions (multi-page crawls), this prevents slowdowns from oversized contexts. | +| **Custom Compaction via Extensions** | Override compaction logic to preserve critical browser state (current URL, cookies, form data) while summarizing away redundant screenshots or HTML dumps. | +| **Context Files (`AGENTS.md`)** | Load browser-specific conventions once at startup. The model doesn't need to relearn "always screenshot before clicking" every turn. | +| **Skills (Progressive Disclosure)** | Browser-specific skills (like `pi-browser-harness`) only load their full instructions when needed. The system prompt stays lean until a browser task is detected. | +| **`PI_CACHE_RETENTION=long`** | Extended prompt cache retention (Anthropic: 1h, OpenAI: 24h) means repeated browser-tool-heavy prompts reuse cached prefixes, cutting latency and cost. | + +### Settings for Long Browser Sessions + +```json +{ + "compaction": { + "enabled": true, + "reserveTokens": 16384, + "keepRecentTokens": 20000 + } +} +``` + +--- + +## 6. Provider & Model Control (Speed at the LLM Layer) + +| Feature | How It Makes Browser Harness Fast | +|---------|-----------------------------------| +| **Model Cycling (`Ctrl+P`)** | Switch to faster/cheaper models for simple browser tasks (e.g., `gpt-4o-mini` for extraction) and reserve heavy models for complex reasoning. | +| **Thinking Level Control (`Shift+Tab`)** | Set thinking to `off` or `minimal` for deterministic browser automation, eliminating reasoning latency when it's not needed. | +| **Custom Providers (`pi.registerProvider`)** | Route browser-heavy sessions through faster proxies, local endpoints, or team-wide caches. | +| **Transport Selection (`sse` vs `websocket`)** | Choose the lowest-latency transport for your provider. | +| **`before_provider_request` hook** | Inspect or rewrite the provider payload to inject cache-control markers, strip unnecessary tool definitions, or enable prompt caching for repeated browser schemas. | + +--- + +## 7. Session Architecture (Never Lose Speed to State Management) + +| Feature | How It Makes Browser Harness Fast | +|---------|-----------------------------------| +| **Session Branching (`/tree`, `/fork`)** | Experiment with different browser strategies without losing state. If one automation path is slow, fork and try another without rebuilding context from scratch. | +| **Session Compaction (`/compact`)** | Manually trigger compaction mid-session after a heavy crawl to instantly restore context window headroom. | +| **`pi.appendEntry()`** | Persist browser state (cookies, auth tokens, current page index) as custom session entries. Restore on resume without re-authenticating or re-navigating. | +| **Custom Commands (`pi.registerCommand`)** | Register `/browser-batch` or `/crawl` commands that kick off optimized script-based workflows without LLM involvement. | + +--- + +## 8. UI & Control Flow (Reduce Human-in-the-Loop Delay) + +| Feature | How It Makes Browser Harness Fast | +|---------|-----------------------------------| +| **Message Queue (`steer` / `followUp`)** | Queue corrective messages while the agent is mid-crawl. The browser harness receives steering immediately after the current tool batch instead of waiting for full completion. | +| **Custom UI / Overlays** | Build a live browser dashboard (status line showing current URL, progress bar for crawls) so the user doesn't interrupt prematurely. | +| **`browser_page_info` dialog detection** | Instantly detect blocking JS dialogs instead of timing out. Extensions can auto-dismiss known popups via `tool_call` interception. | + +--- + +## Summary: The "Blazingly Fast" Formula + +To maximize browser harness speed with pi: + +1. **Parallelize everything** — Use `browser_open_urls`, parallel tool execution, and parallel subagents. +2. **Script heavy sequences** — Use `browser_run_script` for any 3+ step repetitive flow. +3. **Build custom tools** — Register batch tools via extensions to collapse multi-turn loops into single tool calls. +4. **Intercept and optimize** — Use `tool_call`/`tool_result` events to batch, cache, and truncate before the LLM sees it. +5. **Keep context lean** — Use compaction, file-only subagent output, and skills to prevent token bloat. +6. **Delegate asynchronously** — Launch long crawls as `async` subagents while the parent continues. +7. **Choose the right model** — Cycle to fast models with minimal thinking for deterministic automation.
M src/setup.ts → src/setup.ts

@@ -23,7 +23,7 @@ // Step 1: Check Chrome is running

const chromeRunning = checkChromeRunning(); if (!chromeRunning) { ctx.ui.notify( - "Chrome/Chromium/Edge not detected. Please start your browser and retry /browser-setup.", + "No browser instance running. Please open your browser and then run /browser-setup.", "error", ); return;

@@ -80,11 +80,29 @@ }

function checkChromeRunning(): boolean { try { - if (process.platform === "darwin" || process.platform === "linux") { + if (process.platform === "darwin") { + // Exact line matching — excludes "Google Chrome Helper" etc. + // that linger after the user quits the browser. const out = execSync("ps -A -o comm=", { timeout: 5000 }).toString().toLowerCase(); - return ["google chrome", "chrome", "chromium", "microsoft edge", "msedge"].some( - (n) => out.includes(n), - ); + const lines = out.split("\n").map((l) => l.trim()).filter(Boolean); + const browserProcesses = ["google chrome", "chromium", "microsoft edge"]; + return lines.some((l) => { + if (l.includes("helper") || l.includes("renderer")) return false; + return browserProcesses.includes(l); + }); + } else if (process.platform === "linux") { + // Use args to distinguish the main browser process from sub-processes + // (GPU, renderer, utility, etc.) which carry --type= flags. + const out = execSync("ps -A -o comm=,args=", { timeout: 5000 }).toString().toLowerCase(); + const lines = out.split("\n").map((l) => l.trim()).filter(Boolean); + const browserComms = ["chrome", "chromium", "chromium-browser", "msedge", "microsoft-edge", "google-chrome"]; + return lines.some((line) => { + const parts = line.split(/\s+/); + const comm = parts[0] ?? ""; + if (!comm || !browserComms.includes(comm)) return false; + const isSubprocess = line.includes("--type=") && !line.includes("--type=browser"); + return !isSubprocess; + }); } else if (process.platform === "win32") { const out = execSync("tasklist", { timeout: 5000 }).toString().toLowerCase(); return ["chrome.exe", "msedge.exe"].some((n) => out.includes(n));