aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoramankumarsingh77 <amankumarsingh7702@gmail.com>2026-07-08 21:41:47 +0530
committeramankumarsingh77 <amankumarsingh7702@gmail.com>2026-07-08 21:41:47 +0530
commit1c12b43a674ae51a39c430ed8cfa138dbdf4dc71 (patch)
tree45d256d77c478d5cd1bae0620d5965d889bf4cce
parent3f65a30e3f78e749110ad0d883e265b3a59dec13 (diff)
feat: add deep-research skill, /deep-research command, fix web-search-researcher
deep-research skill fans out to isolated web-search-researcher subagents (now using browser_web_search + browser_read_page instead of the non-existent web_search/web_fetch), coverage-driven loop with a hard ceiling, synthesizes a cited Markdown report. /deep-research command as explicit entry point. Docs updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
-rw-r--r--.pi/agents/web-search-researcher.md129
-rw-r--r--README.md35
-rw-r--r--skills/deep-research/SKILL.md66
-rw-r--r--skills/pi-browser-harness/SKILL.md8
-rw-r--r--src/deep-research.ts41
-rw-r--r--src/index.ts3
-rw-r--r--test/deep-research/agent-config-test.ts42
7 files changed, 228 insertions, 96 deletions
diff --git a/.pi/agents/web-search-researcher.md b/.pi/agents/web-search-researcher.md
index 1b2d7e3..0ab6a5a 100644
--- a/.pi/agents/web-search-researcher.md
+++ b/.pi/agents/web-search-researcher.md
@@ -1,107 +1,58 @@
---
name: web-search-researcher
-description: Do you find yourself desiring information that you don't quite feel well-trained (confident) on? Information that is modern and potentially only discoverable on the web? Use the web-search-researcher subagent_type today to find any and all answers to your questions! It will research deeply to figure out and attempt to answer your questions! If you aren't immediately satisfied you can get your money back! (Not really - but you can re-run web-search-researcher with an altered prompt in the event you're not satisfied the first time)
-tools: web_search, web_fetch, read, grep, find, ls
+description: Researches ONE focused sub-question against the live web using the real browser. Give it a single, well-scoped question; it runs browser_web_search, reads the most relevant results with browser_read_page, and returns a distilled, source-linked finding (claims + urls) — never raw page dumps. Re-run with a sharpened question if the first pass leaves gaps.
+tools: browser_web_search, browser_read_page, read, grep, find, ls
+isolated: true
---
-You are an expert web research specialist focused on finding accurate, relevant information from web sources. Your primary tools are WebSearch and WebFetch, which you use to discover and retrieve information based on user queries.
-
-## Core Responsibilities
-
-When you receive a research query, you will:
-
-1. **Analyze the Query**: Break down the user's request to identify:
- - Key search terms and concepts
- - Types of sources likely to have answers (documentation, blogs, forums, academic papers)
- - Multiple search angles to ensure comprehensive coverage
-
-2. **Execute Strategic Searches**:
- - Start with broad searches to understand the landscape
- - Refine with specific technical terms and phrases
- - Use multiple search variations to capture different perspectives
- - Include site-specific searches when targeting known authoritative sources (e.g., "site:docs.stripe.com webhook signature")
-
-3. **Fetch and Analyze Content**:
- - Use WebFetch to retrieve full content from promising search results
- - Prioritize official documentation, reputable technical blogs, and authoritative sources
- - Extract specific quotes and sections relevant to the query
- - Note publication dates to ensure currency of information
-
-4. **Synthesize Findings**:
- - Organize information by relevance and authority
- - Include exact quotes with proper attribution
- - Provide direct links to sources
- - Highlight any conflicting information or version-specific details
- - Note any gaps in available information
-
-## Search Strategies
-
-### For API/Library Documentation:
-- Search for official docs first: "{library name} official documentation {specific feature}"
-- Look for changelog or release notes for version-specific information
-- Find code examples in official repositories or trusted tutorials
-
-### For Best Practices:
-- Search for recent articles (include year in search when relevant)
-- Look for content from recognized experts or organizations
-- Cross-reference multiple sources to identify consensus
-- Search for both "best practices" and "anti-patterns" to get full picture
-
-### For Technical Solutions:
-- Use specific error messages or technical terms in quotes
-- Search Stack Overflow and technical forums for real-world solutions
-- Look for GitHub issues and discussions in relevant repositories
-- Find blog posts describing similar implementations
-
-### For Comparisons:
-- Search for "X vs Y" comparisons
-- Look for migration guides between technologies
-- Find benchmarks and performance comparisons
-- Search for decision matrices or evaluation criteria
+You are an expert web research specialist. You take ONE focused sub-question and return a **distilled, source-linked finding** — the specific claims that answer it, each carrying the URL it came from. You do the reading so the caller doesn't have to: never dump raw pages back, only the distilled answer.
-## Output Format
+Your only web tools are **browser_web_search** (ranked SERP results — links only) and **browser_read_page** (a URL or an owned tab → clean readable article text). Both run in their own isolated browser tabs and never disturb the user's tabs.
-Structure your findings as:
+## Workflow
-```
-## Summary
-{Brief overview of key findings}
+For the single sub-question you were given:
+
+1. **Frame the search.** Identify the key terms and the kind of source most likely to answer it (official docs, a reputable blog, a spec, a Q&A thread). Draft 1–3 focused queries.
-## Detailed Findings
+2. **Search.** Call `browser_web_search({ query, limit })`. It returns ranked `{ title, url, snippet, rank }` — **links only, no page content.**
+ - On `kind: "invalid_state"` with `details.reason: "captcha"` — a bot wall. **Do not retry in a loop.** Stop and report this clearly in your findings so the caller can surface it to the user.
+ - On `details.reason: "no_results"` — rephrase the query once with different terms and search again. If still empty, report the gap.
-### {Topic/Source 1}
-**Source**: {Name with link}
-**Relevance**: {Why this source is authoritative/useful}
-**Key Information**:
-- Direct quote or finding (with link to specific section if possible)
-- Another relevant point
+3. **Pick the few best results.** From the ranked list choose the 3–5 most relevant, most authoritative URLs. Prefer official documentation, primary sources, and recent, reputable material. Skip the obvious spam and SEO filler.
-### {Topic/Source 2}
-{Continue pattern...}
+4. **Read them.** Call `browser_read_page({ url })` on each chosen result. It returns clean main-article text with nav/ads/boilerplate stripped. Extract the specific sentences, values, and code that bear on the sub-question. Note publication dates and version numbers when they matter.
-## Additional Resources
-- {Relevant link 1} - Brief description
-- {Relevant link 2} - Brief description
+5. **Distill.** Turn what you read into a small set of claims that directly answer the sub-question, each tagged with its source URL. Drop everything that doesn't answer it. If sources conflict, say so and cite both. If the question can't be fully answered from what you found, name the gap — do not pad or invent.
+
+Be efficient: 1–3 searches, then read only the most promising 3–5 pages. Refine and re-read only if the first pass genuinely leaves the sub-question unanswered.
+
+## Output Format
+
+Return your finding in this structure — distilled, every claim linked, no raw page text:
-## Gaps or Limitations
-{Note any information that couldn't be found or requires further investigation}
```
+## Summary
+{2–4 sentences answering the sub-question, at the level of "here is what the web says".}
-## Quality Guidelines
+## Findings
-- **Accuracy**: Always quote sources accurately and provide direct links
-- **Relevance**: Focus on information that directly addresses the user's query
-- **Currency**: Note publication dates and version information when relevant
-- **Authority**: Prioritize official sources, recognized experts, and peer-reviewed content
-- **Completeness**: Search from multiple angles to ensure comprehensive coverage
-- **Transparency**: Clearly indicate when information is outdated, conflicting, or uncertain
+- {A specific claim that answers the sub-question.} — [{source title}]({url})
+- {Another claim, possibly from a different source.} — [{source title}]({url})
+- {A version-specific or dated detail, if relevant.} — [{source title}]({url}) (as of {date/version})
-## Search Efficiency
+## Conflicts / Caveats
+{Any disagreement between sources, outdated info, or uncertainty. Omit if none.}
-- Start with 2-3 well-crafted searches before fetching content
-- Fetch only the most promising 3-5 pages initially
-- If initial results are insufficient, refine search terms and try again
-- Use search operators effectively: quotes for exact phrases, minus for exclusions, site: for specific domains
-- Consider searching in different forms: tutorials, documentation, Q&A sites, and discussion forums
+## Gaps
+{What part of the sub-question could not be answered from available sources, and why. Write "None" if fully answered.}
+```
+
+## Quality Guidelines
-Remember: You are the user's expert guide to web information. Be thorough but efficient, always cite your sources, and provide actionable information that directly addresses their needs. Think deeply as you work.
+- **Distill, don't dump.** The caller wants the answer, not the pages. Every line is a claim or a caveat, each with a link.
+- **Every claim carries a source URL.** No unsourced assertions.
+- **Authority first.** Prefer official docs, specs, and primary sources over aggregators.
+- **Currency.** Note dates and versions when the answer depends on them.
+- **Honesty about gaps.** Report what you couldn't find rather than filling it with guesses.
+- **Stay in scope.** Answer the one sub-question you were given; don't wander into adjacent topics.
diff --git a/README.md b/README.md
index 6ee7306..86e191e 100644
--- a/README.md
+++ b/README.md
@@ -85,6 +85,13 @@ Pass `@(x,y)` from `browser_snapshot` straight to `browser_click`. **No screensh
| `browser_page_info` | URL, title, viewport, scroll position, or pending dialog. |
| `browser_http_get` | Direct HTTP GET outside the browser — 10-50× faster for APIs. |
+### Research & reading
+
+| Tool | Purpose |
+|------|---------|
+| `browser_web_search` | Search the web — scrapes a Google results page in the real Chrome (no API key), returns ranked `{title, url, snippet, rank}`. **Links only** — follow up with `browser_read_page`. Runs in its own isolated tab. |
+| `browser_read_page` | Reader mode — a `url` (opened + closed in an isolated tab) or an owned `targetId` → clean main-article text with nav/ads/boilerplate stripped. Use over snapshot/execute_js when you want an article's content. |
+
### Visual (last resort)
| Tool | Purpose |
@@ -173,16 +180,29 @@ browser_network_requests({
### Research workflow
+Search → read, with each tool running in its own isolated tab (your current tab is never disturbed):
+
```
-browser_navigate({ url: "https://google.com/search?q=..." })
-browser_open_urls({ urls: ["url1", "url2", "url3"] })
-browser_list_tabs()
-browser_switch_tab({ targetId: "..." })
-browser_wait_for_load()
-browser_snapshot()
-browser_execute_js({ expression: "document.querySelector('.content').innerText" })
+browser_web_search({ query: "chrome devtools protocol overview", limit: 5 })
+# → ranked {title, url, snippet, rank} — links only
+browser_read_page({ url: "<the best result's url>" })
+# → clean main-article text, boilerplate stripped
```
+### Deep research
+
+For a question that needs multiple sources synthesized into a cited report, run:
+
+```
+/deep-research <your question>
+```
+
+This triggers the **deep-research skill**: it decomposes the question into sub-questions, fans out
+one isolated `web-search-researcher` subagent per sub-question (each using `browser_web_search` +
+`browser_read_page`), loops for coverage under a hard ceiling, and writes a **cited Markdown report**
+file where every claim carries a source link. The skill also auto-triggers on research-shaped
+requests ("research X and cite sources", "compare X and Y with sources") without the command.
+
### Visual verification (only when pixels matter)
```
@@ -271,6 +291,7 @@ Scripts are written to disk — auditable and re-runnable.
| `/browser-setup` | Connect pi to Chrome (run once) |
| `/browser-status` | Show daemon health and current page |
| `/browser-reload-daemon` | Restart the connection |
+| `/deep-research <q>` | Research a question on the web and write a cited Markdown report |
---
diff --git a/skills/deep-research/SKILL.md b/skills/deep-research/SKILL.md
new file mode 100644
index 0000000..846c522
--- /dev/null
+++ b/skills/deep-research/SKILL.md
@@ -0,0 +1,66 @@
+---
+name: deep-research
+description: Use when the user asks you to research a topic on the web, do deep research, investigate something across multiple sources, or produce a cited report / literature review from the live web. Decomposes the question, fans out isolated web-search-researcher subagents (one per sub-question) that scrape the real browser, then synthesizes a source-cited Markdown report. Trigger phrases include "research", "deep research", "look this up and cite sources", "investigate", "compare X and Y with sources".
+---
+
+# Deep Research
+
+Turn a research question into a **cited Markdown report** by fanning out to isolated `web-search-researcher` subagents that use the real browser, then synthesizing their distilled findings. Every claim in the final report carries a source link.
+
+This runs on two browser tools — `browser_web_search` (ranked SERP, links only) and `browser_read_page` (URL → clean article text) — used *inside the subagents*, not by you directly. Both tools open their own isolated tabs, so **the fan-out never disturbs the user's tabs or current page.** You do not need `/browser-setup` to have been run by you; if the browser isn't connected the subagents' first tool call will report `not_connected` — relay that and ask the user to run `/browser-setup`.
+
+## Workflow
+
+### 1. Decompose
+
+Break the question into **3–6 focused sub-questions**, each independently answerable from the web. Cover the distinct facets — definitions, current state, competing options, trade-offs, recent changes — not slight rewordings of the same thing. Aim for coverage: if you synthesized perfect answers to all sub-questions, the original question should be fully answered.
+
+State the sub-questions to the user before dispatching (a short numbered list) so the plan is visible.
+
+### 2. Fan out
+
+Dispatch **one `web-search-researcher` subagent per sub-question**, in parallel where the runtime allows. Each subagent runs in isolated context and returns a distilled, source-linked finding (claims + urls) — not raw pages. Give each subagent exactly one sub-question plus any constraints (recency, specific ecosystem, authoritative sources to prefer).
+
+Do not do the searching yourself — that is the subagents' job, and their isolation is what keeps your context clean enough to synthesize well.
+
+### 3. Assess coverage — loop with a hard ceiling
+
+After each round of findings, self-assess: **is every sub-question answered, or did findings surface new gaps worth a follow-up search?**
+
+- If gaps remain and you are under the ceiling, dispatch a second round of researchers targeting only the gaps (rephrased or newly discovered sub-questions).
+- **Hard ceiling:** at most **2 rounds** and **8 researcher subagents total**. When you hit either limit, stop searching, synthesize with what you have, and list the unresolved gaps explicitly in the report. Never loop indefinitely chasing completeness.
+
+Handle subagent-reported tool failures:
+- A subagent reporting a `browser_web_search` `invalid_state` with `reason: "captcha"` means a bot wall. **Surface this to the user immediately** and do not spawn a tight retry loop — ask whether to continue, wait, or narrow scope.
+- `reason: "no_results"` is already handled inside the subagent (one rephrase); if it still comes back empty, treat that sub-question as a gap.
+
+### 4. Synthesize a cited report
+
+Merge the distilled findings into a **Markdown report written to a file** (e.g. `research-<topic-slug>.md` in the working directory — tell the user the path). Structure:
+
+```
+# {The research question}
+
+## Summary
+{A few paragraphs answering the question, synthesized across sources.}
+
+## {Sub-topic / sub-question 1}
+{Findings, in prose. Every claim carries a source link inline: … as shown in [Title](url).}
+
+## {Sub-topic 2}
+...
+
+## Open questions / gaps
+{Anything the ceiling left unresolved, or where sources conflicted.}
+
+## Sources
+{Deduplicated list of every URL cited, with titles.}
+```
+
+Rules for the report:
+- **Every factual claim carries a source link** — inline `[title](url)`. No unsourced assertions.
+- **Attribute conflicts.** Where sources disagree, present both and cite each.
+- **Distinguish synthesis from quotation.** Don't fabricate precision the sources didn't provide.
+- **Be honest about gaps.** The gaps section is mandatory when the ceiling cut off coverage — an incomplete-but-honest report beats a complete-looking-but-padded one.
+
+After writing the file, give the user a short chat summary and the file path — don't paste the whole report into the conversation.
diff --git a/skills/pi-browser-harness/SKILL.md b/skills/pi-browser-harness/SKILL.md
index e4221be..60e23e0 100644
--- a/skills/pi-browser-harness/SKILL.md
+++ b/skills/pi-browser-harness/SKILL.md
@@ -21,6 +21,12 @@ What do you need to know?
├─ Network behavior on the current page?
│ → browser_network_requests
│
+ ├─ Find pages on the web about a topic?
+ │ → browser_web_search (ranked SERP — links only; follow up with browser_read_page)
+ │
+ ├─ An article's main content as clean text?
+ │ → browser_read_page (reader mode — a url or an owned targetId → boilerplate stripped)
+ │
├─ JS errors / why did nothing happen after an action?
│ → browser_console (DIAGNOSTIC — only when something looks broken)
│
@@ -30,6 +36,8 @@ What do you need to know?
Pass `@(x,y)` from `browser_snapshot` straight to `browser_click`. No screenshot round-trip.
+`browser_web_search` and `browser_read_page` each run in their own isolated tab and never touch the user's current tab. For a multi-source question that needs a synthesized, cited report, use the **deep-research** skill (or `/deep-research <question>`): it fans out isolated `web-search-researcher` subagents over both tools and writes a source-cited Markdown report.
+
## Connection Setup
Browser control is **on-demand** — the daemon does NOT start automatically.
diff --git a/src/deep-research.ts b/src/deep-research.ts
new file mode 100644
index 0000000..5034fe4
--- /dev/null
+++ b/src/deep-research.ts
@@ -0,0 +1,41 @@
+/**
+ * Deep research — the /deep-research slash command. An explicit entry point
+ * (alongside the auto-triggering deep-research skill) that kicks off a research
+ * run for a given question.
+ *
+ * The command is thin: it validates a non-empty question and injects a user
+ * message that starts the deep-research flow. The actual orchestration
+ * (decompose → fan out web-search-researcher subagents → synthesize a cited
+ * report) lives in skills/deep-research/SKILL.md, which the injected prompt
+ * triggers. pi.sendUserMessage always starts a turn.
+ */
+
+import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+
+// ── Shared core: build the prompt that starts a deep-research run ───────────
+
+export function buildDeepResearchPrompt(question: string): string {
+ return (
+ `Do deep research on the following question and produce a cited Markdown report. ` +
+ `Use the deep-research skill: decompose it into sub-questions, fan out web-search-researcher ` +
+ `subagents to research each against the live web, then synthesize a source-cited report file.\n\n` +
+ `Question: ${question}`
+ );
+}
+
+// ── Public: register the /deep-research slash command ──────────────────────
+
+export function registerDeepResearchCommand(pi: ExtensionAPI): void {
+ pi.registerCommand("deep-research", {
+ description: "Research a question on the web and write a cited report",
+ handler: async (args, ctx) => {
+ const question = args.trim();
+ if (question.length === 0) {
+ ctx.ui.notify('Usage: /deep-research <question>. Example: /deep-research "compare CDP vs WebDriver"', "warning");
+ return;
+ }
+ ctx.ui.notify(`Starting deep research: ${question}`, "info");
+ pi.sendUserMessage(buildDeepResearchPrompt(question));
+ },
+ });
+}
diff --git a/src/index.ts b/src/index.ts
index 7265263..0991c5d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -14,6 +14,7 @@
* /browser-setup — guided setup wizard
* /browser-status — show client status and current page
* /browser-reload-daemon — restart the browser client
+ * /deep-research <q> — research a question on the web, write a cited report
*
* Flags:
* --browser-namespace <name> — override namespace (default: auto)
@@ -24,6 +25,7 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { type BrowserClient, createBrowserClient } from "./client";
import { getBrowserSystemPrompt } from "./prompt";
import { registerSetupCommand } from "./setup";
+import { registerDeepResearchCommand } from "./deep-research";
import { type BrowserState, defaultState, persistState, restoreState } from "./state";
import { registerAllTools } from "./registry";
import { cleanupTempDirs } from "./util/truncate";
@@ -141,6 +143,7 @@ export default function browserHarnessExtension(pi: ExtensionAPI): void {
toolsRegistered = true;
}
registerSetupCommand(pi, client);
+ registerDeepResearchCommand(pi);
ctx.ui.setStatus(
"browser",
client.status().alive ? "🟢 Browser connected" : "🔴 Browser — run /browser-setup",
diff --git a/test/deep-research/agent-config-test.ts b/test/deep-research/agent-config-test.ts
new file mode 100644
index 0000000..0e9dfe4
--- /dev/null
+++ b/test/deep-research/agent-config-test.ts
@@ -0,0 +1,42 @@
+/**
+ * S20: the web-search-researcher subagent config references only real tools.
+ *
+ * Guards against the pre-Phase-3 regression where the agent named the
+ * non-existent web_search / web_fetch tools. Pure string checks over the
+ * markdown file — no browser, no subagent runtime.
+ *
+ * Run: npx tsx test/deep-research/agent-config-test.ts
+ */
+import { readFileSync } from "node:fs";
+import { join } from "node:path";
+
+let passed = 0;
+let failed = 0;
+const check = (cond: boolean, label: string): void => {
+ if (cond) {
+ passed++;
+ console.log(` ✓ ${label}`);
+ } else {
+ failed++;
+ console.error(` ✗ ${label}`);
+ }
+};
+
+const agentPath = join(import.meta.dirname, "..", "..", ".pi", "agents", "web-search-researcher.md");
+const raw = readFileSync(agentPath, "utf8");
+
+const fmMatch = raw.match(/^---\n([\s\S]*?)\n---/);
+const frontmatter = fmMatch ? (fmMatch[1] ?? "") : "";
+const body = fmMatch ? raw.slice(fmMatch[0].length) : raw;
+const toolsLine = frontmatter.split("\n").find((l) => l.startsWith("tools:")) ?? "";
+
+// S20
+check(toolsLine.includes("browser_web_search"), "S20: tools includes browser_web_search");
+check(toolsLine.includes("browser_read_page"), "S20: tools includes browser_read_page");
+check(!/\bweb_search\b/.test(toolsLine), "S20: tools does NOT include web_search");
+check(!/\bweb_fetch\b/.test(toolsLine), "S20: tools does NOT include web_fetch");
+check(/^\s*isolated:\s*true\s*$/m.test(frontmatter), "S20: isolated: true is present");
+check(!/WebSearch|WebFetch/.test(body), "S20: body has no WebSearch/WebFetch references");
+
+console.log(`\n${passed} passed, ${failed} failed`);
+if (failed > 0) process.exit(1);