From c104d2ed14114bec33f1e4e6fc2b7ea960289e1c Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sun, 31 May 2026 13:13:18 -0400 Subject: [PATCH] Initial commit: deep-research extension --- .gitignore | 4 + .pi-lens/cache/review-graph.json | 1 + index.ts | 521 +++++++++++++++++++++++++++++++ package-lock.json | 47 +++ package.json | 15 + src/agent.ts | 155 +++++++++ src/firecrawl.ts | 159 ++++++++++ src/queries.ts | 261 ++++++++++++++++ src/report.ts | 170 ++++++++++ src/research.ts | 254 +++++++++++++++ src/types.ts | 55 ++++ tsconfig.json | 16 + 12 files changed, 1658 insertions(+) create mode 100644 .gitignore create mode 100644 .pi-lens/cache/review-graph.json create mode 100644 index.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/agent.ts create mode 100644 src/firecrawl.ts create mode 100644 src/queries.ts create mode 100644 src/report.ts create mode 100644 src/research.ts create mode 100644 src/types.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e5c946 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +*.js +*.js.map diff --git a/.pi-lens/cache/review-graph.json b/.pi-lens/cache/review-graph.json new file mode 100644 index 0000000..27541f8 --- /dev/null +++ b/.pi-lens/cache/review-graph.json @@ -0,0 +1 @@ +{"version":"v2","builtAt":"2026-05-31T17:05:46.028Z","signature":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:18005:1780247059415.9526|/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:4052:1780247118561.034|/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:4533:1780247145473.4739|/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:7817:1780246903189.6033|/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:5697:1780246936985.1875|/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:7068:1780246975224.9976|/Users/mike/.pi/agent/extensions/deep-research/src/types.ts:1269:1780246834312.9827","fileSignatures":[["/Users/mike/.pi/agent/extensions/deep-research/index.ts","18005:1780247059415.9526"],["/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","4052:1780247118561.034"],["/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","4533:1780247145473.4739"],["/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","7817:1780246903189.6033"],["/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","5697:1780246936985.1875"],["/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","7068:1780246975224.9976"],["/Users/mike/.pi/agent/extensions/deep-research/src/types.ts","1269:1780246834312.9827"]],"nodes":[["file:/Users/mike/.pi/agent/extensions/deep-research/index.ts",{"id":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","kind":"file","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/index.ts","metadata":{"lineCount":517,"featureKind":"library","trustBoundaries":[]}}],["external:@earendil-works/pi-coding-agent",{"id":"external:@earendil-works/pi-coding-agent","kind":"external","language":"jsts","metadata":{"source":"@earendil-works/pi-coding-agent"}}],["external:typebox",{"id":"external:typebox","kind":"external","language":"jsts","metadata":{"source":"typebox"}}],["external:@earendil-works/pi-tui",{"id":"external:@earendil-works/pi-tui","kind":"external","language":"jsts","metadata":{"source":"@earendil-works/pi-tui"}}],["file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts",{"id":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","kind":"file","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","metadata":{"lineCount":243,"featureKind":"library","trustBoundaries":[]}}],["file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts",{"id":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","kind":"file","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","metadata":{"lineCount":157,"featureKind":"library","trustBoundaries":[]}}],["file:/Users/mike/.pi/agent/extensions/deep-research/src/types.ts",{"id":"file:/Users/mike/.pi/agent/extensions/deep-research/src/types.ts","kind":"file","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/types.ts","metadata":{"lineCount":56,"featureKind":"library","trustBoundaries":[]}}],["/Users/mike/.pi/agent/extensions/deep-research/index.ts:formatDuration",{"id":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:formatDuration","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/index.ts","symbolName":"formatDuration","symbolKind":"function","exported":false,"metadata":{"line":41,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:Math.floor",{"id":"external:Math.floor","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.floor"}}],["/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate",{"id":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/index.ts","symbolName":"truncate","symbolKind":"function","exported":false,"metadata":{"line":48,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:s.slice",{"id":"external:s.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"s.slice"}}],["/Users/mike/.pi/agent/extensions/deep-research/index.ts:",{"id":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/index.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":507,"column":26,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:pi.registerTool",{"id":"external:pi.registerTool","kind":"external","language":"jsts","metadata":{"unresolvedName":"pi.registerTool"}}],["symbol-name:setInterval",{"id":"symbol-name:setInterval","kind":"symbol","language":"jsts","symbolName":"setInterval","metadata":{"unresolvedName":"setInterval"}}],["symbol-name:truncate",{"id":"symbol-name:truncate","kind":"symbol","language":"jsts","symbolName":"truncate","metadata":{"unresolvedName":"truncate"}}],["external:lines.push",{"id":"external:lines.push","kind":"external","language":"jsts","metadata":{"unresolvedName":"lines.push"}}],["external:Math.round",{"id":"external:Math.round","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.round"}}],["external:\"█\".repeat",{"id":"external:\"█\".repeat","kind":"external","language":"jsts","metadata":{"unresolvedName":"\"█\".repeat"}}],["external:\"░\".repeat",{"id":"external:\"░\".repeat","kind":"external","language":"jsts","metadata":{"unresolvedName":"\"░\".repeat"}}],["external:ctx.ui.setWidget",{"id":"external:ctx.ui.setWidget","kind":"external","language":"jsts","metadata":{"unresolvedName":"ctx.ui.setWidget"}}],["external:lines.join",{"id":"external:lines.join","kind":"external","language":"jsts","metadata":{"unresolvedName":"lines.join"}}],["symbol-name:onUpdate",{"id":"symbol-name:onUpdate","kind":"symbol","language":"jsts","symbolName":"onUpdate","metadata":{"unresolvedName":"onUpdate"}}],["external:ctx.ui.setStatus",{"id":"external:ctx.ui.setStatus","kind":"external","language":"jsts","metadata":{"unresolvedName":"ctx.ui.setStatus"}}],["symbol-name:onProgress",{"id":"symbol-name:onProgress","kind":"symbol","language":"jsts","symbolName":"onProgress","metadata":{"unresolvedName":"onProgress"}}],["symbol-name:runDeepResearch",{"id":"symbol-name:runDeepResearch","kind":"symbol","language":"jsts","symbolName":"runDeepResearch","metadata":{"unresolvedName":"runDeepResearch"}}],["external:researchResult.rounds.map",{"id":"external:researchResult.rounds.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"researchResult.rounds.map"}}],["external:r.queries.map",{"id":"external:r.queries.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"r.queries.map"}}],["symbol-name:formatDuration",{"id":"symbol-name:formatDuration","kind":"symbol","language":"jsts","symbolName":"formatDuration","metadata":{"unresolvedName":"formatDuration"}}],["symbol-name:clearInterval",{"id":"symbol-name:clearInterval","kind":"symbol","language":"jsts","symbolName":"clearInterval","metadata":{"unresolvedName":"clearInterval"}}],["symbol-name:String",{"id":"symbol-name:String","kind":"symbol","language":"jsts","symbolName":"String","metadata":{"unresolvedName":"String"}}],["external:theme.fg",{"id":"external:theme.fg","kind":"external","language":"jsts","metadata":{"unresolvedName":"theme.fg"}}],["external:theme.bold",{"id":"external:theme.bold","kind":"external","language":"jsts","metadata":{"unresolvedName":"theme.bold"}}],["external:details.rounds.reduce",{"id":"external:details.rounds.reduce","kind":"external","language":"jsts","metadata":{"unresolvedName":"details.rounds.reduce"}}],["external:container.addChild",{"id":"external:container.addChild","kind":"external","language":"jsts","metadata":{"unresolvedName":"container.addChild"}}],["external:pi.registerCommand",{"id":"external:pi.registerCommand","kind":"external","language":"jsts","metadata":{"unresolvedName":"pi.registerCommand"}}],["external:args.trim",{"id":"external:args.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"args.trim"}}],["external:ctx.ui.notify",{"id":"external:ctx.ui.notify","kind":"external","language":"jsts","metadata":{"unresolvedName":"ctx.ui.notify"}}],["external:ctx.ui.select",{"id":"external:ctx.ui.select","kind":"external","language":"jsts","metadata":{"unresolvedName":"ctx.ui.select"}}],["external:depthStr?.startsWith",{"id":"external:depthStr?.startsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"depthStr?.startsWith"}}],["external:breadthStr?.startsWith",{"id":"external:breadthStr?.startsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"breadthStr?.startsWith"}}],["external:pi.sendUserMessage",{"id":"external:pi.sendUserMessage","kind":"external","language":"jsts","metadata":{"unresolvedName":"pi.sendUserMessage"}}],["external:pi.on",{"id":"external:pi.on","kind":"external","language":"jsts","metadata":{"unresolvedName":"pi.on"}}],["symbol-name:isFirecrawlReachable",{"id":"symbol-name:isFirecrawlReachable","kind":"symbol","language":"jsts","symbolName":"isFirecrawlReachable","metadata":{"unresolvedName":"isFirecrawlReachable"}}],["/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute",{"id":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/index.ts","symbolName":"execute","symbolKind":"function","exported":false,"metadata":{"line":132,"column":5,"cyclomaticComplexity":17,"maxNestingDepth":4,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress",{"id":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/index.ts","symbolName":"onProgress","symbolKind":"function","exported":false,"metadata":{"line":459,"column":46,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderCall",{"id":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderCall","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/index.ts","symbolName":"renderCall","symbolKind":"function","exported":false,"metadata":{"line":292,"column":5,"cyclomaticComplexity":5,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderResult",{"id":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderResult","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/index.ts","symbolName":"renderResult","symbolKind":"function","exported":false,"metadata":{"line":314,"column":5,"cyclomaticComplexity":8,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler",{"id":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/index.ts","symbolName":"handler","symbolKind":"function","exported":false,"metadata":{"line":413,"column":14,"cyclomaticComplexity":11,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts",{"id":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","kind":"file","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","metadata":{"lineCount":146,"featureKind":"library","trustBoundaries":[]}}],["/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","symbolName":"runAnalysisAgent","symbolKind":"function","exported":true,"metadata":{"line":36,"column":1,"cyclomaticComplexity":15,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setTimeout",{"id":"symbol-name:setTimeout","kind":"symbol","language":"jsts","symbolName":"setTimeout","metadata":{"unresolvedName":"setTimeout"}}],["external:sessionRef.session?.agent.abort",{"id":"external:sessionRef.session?.agent.abort","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessionRef.session?.agent.abort"}}],["symbol-name:getAgentDir",{"id":"symbol-name:getAgentDir","kind":"symbol","language":"jsts","symbolName":"getAgentDir","metadata":{"unresolvedName":"getAgentDir"}}],["external:loader.reload",{"id":"external:loader.reload","kind":"external","language":"jsts","metadata":{"unresolvedName":"loader.reload"}}],["symbol-name:createAgentSession",{"id":"symbol-name:createAgentSession","kind":"symbol","language":"jsts","symbolName":"createAgentSession","metadata":{"unresolvedName":"createAgentSession"}}],["external:SessionManager.inMemory",{"id":"external:SessionManager.inMemory","kind":"external","language":"jsts","metadata":{"unresolvedName":"SessionManager.inMemory"}}],["external:result.session.agent.abort",{"id":"external:result.session.agent.abort","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.session.agent.abort"}}],["external:signal?.addEventListener",{"id":"external:signal?.addEventListener","kind":"external","language":"jsts","metadata":{"unresolvedName":"signal?.addEventListener"}}],["external:result.session.subscribe",{"id":"external:result.session.subscribe","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.session.subscribe"}}],["symbol-name:onEvent",{"id":"symbol-name:onEvent","kind":"symbol","language":"jsts","symbolName":"onEvent","metadata":{"unresolvedName":"onEvent"}}],["symbol-name:extractAssistantText",{"id":"symbol-name:extractAssistantText","kind":"symbol","language":"jsts","symbolName":"extractAssistantText","metadata":{"unresolvedName":"extractAssistantText"}}],["external:result.session.prompt",{"id":"external:result.session.prompt","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.session.prompt"}}],["external:result.session.agent.waitForIdle",{"id":"external:result.session.agent.waitForIdle","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.session.agent.waitForIdle"}}],["symbol-name:unsubscribe",{"id":"symbol-name:unsubscribe","kind":"symbol","language":"jsts","symbolName":"unsubscribe","metadata":{"unresolvedName":"unsubscribe"}}],["external:result.session.dispose",{"id":"external:result.session.dispose","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.session.dispose"}}],["external:signal?.removeEventListener",{"id":"external:signal?.removeEventListener","kind":"external","language":"jsts","metadata":{"unresolvedName":"signal?.removeEventListener"}}],["symbol-name:clearTimeout",{"id":"symbol-name:clearTimeout","kind":"symbol","language":"jsts","symbolName":"clearTimeout","metadata":{"unresolvedName":"clearTimeout"}}],["external:finalText.trim",{"id":"external:finalText.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"finalText.trim"}}],["external:sessionRef.session?.dispose",{"id":"external:sessionRef.session?.dispose","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessionRef.session?.dispose"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":82,"column":50,"cyclomaticComplexity":7,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:extractAssistantText",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:extractAssistantText","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","symbolName":"extractAssistantText","symbolKind":"function","exported":false,"metadata":{"line":135,"column":1,"cyclomaticComplexity":6,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:Array.isArray",{"id":"external:Array.isArray","kind":"external","language":"jsts","metadata":{"unresolvedName":"Array.isArray"}}],["external:content\n .filter",{"id":"external:content\n .filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"content\n .filter"}}],["external:node:fs",{"id":"external:node:fs","kind":"external","language":"jsts","metadata":{"source":"node:fs"}}],["external:node:path",{"id":"external:node:path","kind":"external","language":"jsts","metadata":{"source":"node:path"}}],["external:node:os",{"id":"external:node:os","kind":"external","language":"jsts","metadata":{"source":"node:os"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:loadFirecrawlConfig",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:loadFirecrawlConfig","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","symbolName":"loadFirecrawlConfig","symbolKind":"function","exported":false,"metadata":{"line":14,"column":1,"cyclomaticComplexity":7,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["external:path.join",{"id":"external:path.join","kind":"external","language":"jsts","metadata":{"unresolvedName":"path.join"}}],["external:os.homedir",{"id":"external:os.homedir","kind":"external","language":"jsts","metadata":{"unresolvedName":"os.homedir"}}],["external:JSON.parse",{"id":"external:JSON.parse","kind":"external","language":"jsts","metadata":{"unresolvedName":"JSON.parse"}}],["external:fs.readFileSync",{"id":"external:fs.readFileSync","kind":"external","language":"jsts","metadata":{"unresolvedName":"fs.readFileSync"}}],["external:(process.env.FIRECRAWL_BASE_URL ?? \"http://localhost:3002\").replace",{"id":"external:(process.env.FIRECRAWL_BASE_URL ?? \"http://localhost:3002\").replace","kind":"external","language":"jsts","metadata":{"unresolvedName":"(process.env.FIRECRAWL_BASE_URL ?? \"http://localhost:3002\").replace"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","symbolName":"firecrawlRequest","symbolKind":"function","exported":false,"metadata":{"line":40,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:fetch",{"id":"symbol-name:fetch","kind":"symbol","language":"jsts","symbolName":"fetch","metadata":{"unresolvedName":"fetch"}}],["external:JSON.stringify",{"id":"external:JSON.stringify","kind":"external","language":"jsts","metadata":{"unresolvedName":"JSON.stringify"}}],["external:res.text",{"id":"external:res.text","kind":"external","language":"jsts","metadata":{"unresolvedName":"res.text"}}],["external:text.slice",{"id":"external:text.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"text.slice"}}],["external:res.json",{"id":"external:res.json","kind":"external","language":"jsts","metadata":{"unresolvedName":"res.json"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:isFirecrawlReachable",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:isFirecrawlReachable","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","symbolName":"isFirecrawlReachable","symbolKind":"function","exported":true,"metadata":{"line":69,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:AbortSignal.timeout",{"id":"external:AbortSignal.timeout","kind":"external","language":"jsts","metadata":{"unresolvedName":"AbortSignal.timeout"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:searchWeb",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:searchWeb","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","symbolName":"searchWeb","symbolKind":"function","exported":true,"metadata":{"line":92,"column":1,"cyclomaticComplexity":10,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:Math.min",{"id":"external:Math.min","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.min"}}],["symbol-name:firecrawlRequest",{"id":"symbol-name:firecrawlRequest","kind":"symbol","language":"jsts","symbolName":"firecrawlRequest","metadata":{"unresolvedName":"firecrawlRequest"}}],["external:res.data.map",{"id":"external:res.data.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"res.data.map"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:scrapeUrl",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:scrapeUrl","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","symbolName":"scrapeUrl","symbolKind":"function","exported":true,"metadata":{"line":131,"column":1,"cyclomaticComplexity":8,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts",{"id":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","kind":"file","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","metadata":{"lineCount":245,"featureKind":"library","trustBoundaries":[]}}],["/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","symbolName":"generateQueries","symbolKind":"function","exported":true,"metadata":{"line":51,"column":1,"cyclomaticComplexity":9,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:runAnalysisAgent",{"id":"symbol-name:runAnalysisAgent","kind":"symbol","language":"jsts","symbolName":"runAnalysisAgent","metadata":{"unresolvedName":"runAnalysisAgent"}}],["symbol-name:generateFallbackQueries",{"id":"symbol-name:generateFallbackQueries","kind":"symbol","language":"jsts","symbolName":"generateFallbackQueries","metadata":{"unresolvedName":"generateFallbackQueries"}}],["external:parsed.slice(0, count).map",{"id":"external:parsed.slice(0, count).map","kind":"external","language":"jsts","metadata":{"unresolvedName":"parsed.slice(0, count).map"}}],["external:parsed.slice",{"id":"external:parsed.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"parsed.slice"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","symbolName":"generateFollowUpQueries","symbolKind":"function","exported":true,"metadata":{"line":93,"column":1,"cyclomaticComplexity":9,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:rounds.flatMap",{"id":"external:rounds.flatMap","kind":"external","language":"jsts","metadata":{"unresolvedName":"rounds.flatMap"}}],["external:allFindings\n .map",{"id":"external:allFindings\n .map","kind":"external","language":"jsts","metadata":{"unresolvedName":"allFindings\n .map"}}],["external:rounds\n .flatMap((r) => r.queries)\n .map",{"id":"external:rounds\n .flatMap((r) => r.queries)\n .map","kind":"external","language":"jsts","metadata":{"unresolvedName":"rounds\n .flatMap((r) => r.queries)\n .map"}}],["external:rounds\n .flatMap",{"id":"external:rounds\n .flatMap","kind":"external","language":"jsts","metadata":{"unresolvedName":"rounds\n .flatMap"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFallbackQueries",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFallbackQueries","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","symbolName":"generateFallbackQueries","symbolKind":"function","exported":false,"metadata":{"line":153,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:queries.push",{"id":"external:queries.push","kind":"external","language":"jsts","metadata":{"unresolvedName":"queries.push"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","symbolName":"analyzeResults","symbolKind":"function","exported":true,"metadata":{"line":193,"column":1,"cyclomaticComplexity":11,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:results\n .map",{"id":"external:results\n .map","kind":"external","language":"jsts","metadata":{"unresolvedName":"results\n .map"}}],["external:r.markdown.slice",{"id":"external:r.markdown.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"r.markdown.slice"}}],["external:parsed\n .map",{"id":"external:parsed\n .map","kind":"external","language":"jsts","metadata":{"unresolvedName":"parsed\n .map"}}],["external:f.sources.map",{"id":"external:f.sources.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"f.sources.map"}}],["external:f.keyQuotes.map",{"id":"external:f.keyQuotes.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"f.keyQuotes.map"}}],["external:[\"high\", \"medium\", \"low\"].includes",{"id":"external:[\"high\", \"medium\", \"low\"].includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"[\"high\", \"medium\", \"low\"].includes"}}],["file:/Users/mike/.pi/agent/extensions/deep-research/src/report.ts",{"id":"file:/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","kind":"file","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","metadata":{"lineCount":172,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","symbolName":"synthesizeReport","symbolKind":"function","exported":true,"metadata":{"line":31,"column":1,"cyclomaticComplexity":17,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["external:rounds.reduce",{"id":"external:rounds.reduce","kind":"external","language":"jsts","metadata":{"unresolvedName":"rounds.reduce"}}],["external:evidenceByAngle.has",{"id":"external:evidenceByAngle.has","kind":"external","language":"jsts","metadata":{"unresolvedName":"evidenceByAngle.has"}}],["external:evidenceByAngle.set",{"id":"external:evidenceByAngle.set","kind":"external","language":"jsts","metadata":{"unresolvedName":"evidenceByAngle.set"}}],["external:evidenceByAngle.get(angle)!.push",{"id":"external:evidenceByAngle.get(angle)!.push","kind":"external","language":"jsts","metadata":{"unresolvedName":"evidenceByAngle.get(angle)!.push"}}],["external:evidenceByAngle.get",{"id":"external:evidenceByAngle.get","kind":"external","language":"jsts","metadata":{"unresolvedName":"evidenceByAngle.get"}}],["external:Array.from",{"id":"external:Array.from","kind":"external","language":"jsts","metadata":{"unresolvedName":"Array.from"}}],["external:finding.sources.map((s: string) => `[${s}](${s})`).join",{"id":"external:finding.sources.map((s: string) => `[${s}](${s})`).join","kind":"external","language":"jsts","metadata":{"unresolvedName":"finding.sources.map((s: string) => `[${s}](${s})`).join"}}],["external:finding.sources.map",{"id":"external:finding.sources.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"finding.sources.map"}}],["symbol-name:generateFallbackReport",{"id":"symbol-name:generateFallbackReport","kind":"symbol","language":"jsts","symbolName":"generateFallbackReport","metadata":{"unresolvedName":"generateFallbackReport"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:generateFallbackReport",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:generateFallbackReport","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","symbolName":"generateFallbackReport","symbolKind":"function","exported":false,"metadata":{"line":121,"column":1,"cyclomaticComplexity":7,"maxNestingDepth":4,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["external:round.queries.map((q) => `\"${q.query}\"`).join",{"id":"external:round.queries.map((q) => `\"${q.query}\"`).join","kind":"external","language":"jsts","metadata":{"unresolvedName":"round.queries.map((q) => `\"${q.query}\"`).join"}}],["external:round.queries.map",{"id":"external:round.queries.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"round.queries.map"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","symbolName":"runDeepResearch","symbolKind":"function","exported":true,"metadata":{"line":42,"column":1,"cyclomaticComplexity":19,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:Date.now",{"id":"external:Date.now","kind":"external","language":"jsts","metadata":{"unresolvedName":"Date.now"}}],["symbol-name:generateQueries",{"id":"symbol-name:generateQueries","kind":"symbol","language":"jsts","symbolName":"generateQueries","metadata":{"unresolvedName":"generateQueries"}}],["symbol-name:generateFollowUpQueries",{"id":"symbol-name:generateFollowUpQueries","kind":"symbol","language":"jsts","symbolName":"generateFollowUpQueries","metadata":{"unresolvedName":"generateFollowUpQueries"}}],["external:q.query.slice",{"id":"external:q.query.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"q.query.slice"}}],["symbol-name:searchWeb",{"id":"symbol-name:searchWeb","kind":"symbol","language":"jsts","symbolName":"searchWeb","metadata":{"unresolvedName":"searchWeb"}}],["external:searchResults.push",{"id":"external:searchResults.push","kind":"external","language":"jsts","metadata":{"unresolvedName":"searchResults.push"}}],["external:errorMsg.slice",{"id":"external:errorMsg.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"errorMsg.slice"}}],["external:searchResults.filter",{"id":"external:searchResults.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"searchResults.filter"}}],["external:seen.has",{"id":"external:seen.has","kind":"external","language":"jsts","metadata":{"unresolvedName":"seen.has"}}],["external:seen.add",{"id":"external:seen.add","kind":"external","language":"jsts","metadata":{"unresolvedName":"seen.add"}}],["external:Math.ceil",{"id":"external:Math.ceil","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.ceil"}}],["external:uniqueResults.slice",{"id":"external:uniqueResults.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"uniqueResults.slice"}}],["symbol-name:analyzeResults",{"id":"symbol-name:analyzeResults","kind":"symbol","language":"jsts","symbolName":"analyzeResults","metadata":{"unresolvedName":"analyzeResults"}}],["external:allFindings.push",{"id":"external:allFindings.push","kind":"external","language":"jsts","metadata":{"unresolvedName":"allFindings.push"}}],["external:rounds.push",{"id":"external:rounds.push","kind":"external","language":"jsts","metadata":{"unresolvedName":"rounds.push"}}],["external:allFindings\n .filter((f) => f.confidence === \"low\")\n .map",{"id":"external:allFindings\n .filter((f) => f.confidence === \"low\")\n .map","kind":"external","language":"jsts","metadata":{"unresolvedName":"allFindings\n .filter((f) => f.confidence === \"low\")\n .map"}}],["external:allFindings\n .filter",{"id":"external:allFindings\n .filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"allFindings\n .filter"}}],["symbol-name:synthesizeReport",{"id":"symbol-name:synthesizeReport","kind":"symbol","language":"jsts","symbolName":"synthesizeReport","metadata":{"unresolvedName":"synthesizeReport"}}],["/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:",{"id":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:","kind":"symbol","language":"jsts","filePath":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":147,"column":48,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}]],"edges":[{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"external:@earendil-works/pi-coding-agent","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"external:typebox","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"external:@earendil-works/pi-tui","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/types.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:formatDuration","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:formatDuration","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:formatDuration","to":"external:Math.floor","kind":"calls","metadata":{"unresolvedName":"Math.floor"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","to":"external:s.slice","kind":"calls","metadata":{"unresolvedName":"s.slice"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:pi.registerTool","kind":"calls","metadata":{"unresolvedName":"pi.registerTool"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","kind":"calls","metadata":{"unresolvedName":"truncate"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:lines.push","kind":"calls","metadata":{"unresolvedName":"lines.push"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:Math.round","kind":"calls","metadata":{"unresolvedName":"Math.round"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:\"█\".repeat","kind":"calls","metadata":{"unresolvedName":"\"█\".repeat"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:\"░\".repeat","kind":"calls","metadata":{"unresolvedName":"\"░\".repeat"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:ctx.ui.setWidget","kind":"calls","metadata":{"unresolvedName":"ctx.ui.setWidget"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:lines.join","kind":"calls","metadata":{"unresolvedName":"lines.join"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"symbol-name:onUpdate","kind":"calls","metadata":{"unresolvedName":"onUpdate"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:ctx.ui.setStatus","kind":"calls","metadata":{"unresolvedName":"ctx.ui.setStatus"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","kind":"calls","metadata":{"unresolvedName":"onProgress"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","kind":"calls","metadata":{"unresolvedName":"runDeepResearch"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:researchResult.rounds.map","kind":"calls","metadata":{"unresolvedName":"researchResult.rounds.map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:r.queries.map","kind":"calls","metadata":{"unresolvedName":"r.queries.map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:formatDuration","kind":"calls","metadata":{"unresolvedName":"formatDuration"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:theme.fg","kind":"calls","metadata":{"unresolvedName":"theme.fg"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:theme.bold","kind":"calls","metadata":{"unresolvedName":"theme.bold"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:details.rounds.reduce","kind":"calls","metadata":{"unresolvedName":"details.rounds.reduce"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:container.addChild","kind":"calls","metadata":{"unresolvedName":"container.addChild"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:pi.registerCommand","kind":"calls","metadata":{"unresolvedName":"pi.registerCommand"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:args.trim","kind":"calls","metadata":{"unresolvedName":"args.trim"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:ctx.ui.notify","kind":"calls","metadata":{"unresolvedName":"ctx.ui.notify"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:ctx.ui.select","kind":"calls","metadata":{"unresolvedName":"ctx.ui.select"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:depthStr?.startsWith","kind":"calls","metadata":{"unresolvedName":"depthStr?.startsWith"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:breadthStr?.startsWith","kind":"calls","metadata":{"unresolvedName":"breadthStr?.startsWith"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:pi.sendUserMessage","kind":"calls","metadata":{"unresolvedName":"pi.sendUserMessage"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:pi.on","kind":"calls","metadata":{"unresolvedName":"pi.on"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:isFirecrawlReachable","kind":"calls","metadata":{"unresolvedName":"isFirecrawlReachable"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","kind":"calls","metadata":{"unresolvedName":"truncate"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"external:lines.push","kind":"calls","metadata":{"unresolvedName":"lines.push"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"external:Math.round","kind":"calls","metadata":{"unresolvedName":"Math.round"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"external:\"█\".repeat","kind":"calls","metadata":{"unresolvedName":"\"█\".repeat"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"external:\"░\".repeat","kind":"calls","metadata":{"unresolvedName":"\"░\".repeat"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"external:ctx.ui.setWidget","kind":"calls","metadata":{"unresolvedName":"ctx.ui.setWidget"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"external:lines.join","kind":"calls","metadata":{"unresolvedName":"lines.join"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"symbol-name:onUpdate","kind":"calls","metadata":{"unresolvedName":"onUpdate"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"external:ctx.ui.setStatus","kind":"calls","metadata":{"unresolvedName":"ctx.ui.setStatus"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","kind":"calls","metadata":{"unresolvedName":"onProgress"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","kind":"calls","metadata":{"unresolvedName":"runDeepResearch"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"external:researchResult.rounds.map","kind":"calls","metadata":{"unresolvedName":"researchResult.rounds.map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"external:r.queries.map","kind":"calls","metadata":{"unresolvedName":"r.queries.map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:formatDuration","kind":"calls","metadata":{"unresolvedName":"formatDuration"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:execute","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","kind":"defines"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","kind":"calls","metadata":{"unresolvedName":"truncate"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:lines.push","kind":"calls","metadata":{"unresolvedName":"lines.push"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:Math.round","kind":"calls","metadata":{"unresolvedName":"Math.round"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:\"█\".repeat","kind":"calls","metadata":{"unresolvedName":"\"█\".repeat"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:\"░\".repeat","kind":"calls","metadata":{"unresolvedName":"\"░\".repeat"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:ctx.ui.setWidget","kind":"calls","metadata":{"unresolvedName":"ctx.ui.setWidget"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:lines.join","kind":"calls","metadata":{"unresolvedName":"lines.join"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"symbol-name:onUpdate","kind":"calls","metadata":{"unresolvedName":"onUpdate"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderCall","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderCall","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderCall","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","kind":"calls","metadata":{"unresolvedName":"truncate"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderCall","to":"external:theme.fg","kind":"calls","metadata":{"unresolvedName":"theme.fg"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderCall","to":"external:theme.bold","kind":"calls","metadata":{"unresolvedName":"theme.bold"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderResult","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderResult","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderResult","to":"external:details.rounds.reduce","kind":"calls","metadata":{"unresolvedName":"details.rounds.reduce"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderResult","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:formatDuration","kind":"calls","metadata":{"unresolvedName":"formatDuration"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderResult","to":"external:theme.fg","kind":"calls","metadata":{"unresolvedName":"theme.fg"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderResult","to":"external:theme.bold","kind":"calls","metadata":{"unresolvedName":"theme.bold"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderResult","to":"external:container.addChild","kind":"calls","metadata":{"unresolvedName":"container.addChild"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:renderResult","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","kind":"calls","metadata":{"unresolvedName":"truncate"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:args.trim","kind":"calls","metadata":{"unresolvedName":"args.trim"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:ctx.ui.notify","kind":"calls","metadata":{"unresolvedName":"ctx.ui.notify"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:ctx.ui.select","kind":"calls","metadata":{"unresolvedName":"ctx.ui.select"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:depthStr?.startsWith","kind":"calls","metadata":{"unresolvedName":"depthStr?.startsWith"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:breadthStr?.startsWith","kind":"calls","metadata":{"unresolvedName":"breadthStr?.startsWith"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:ctx.ui.setStatus","kind":"calls","metadata":{"unresolvedName":"ctx.ui.setStatus"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","kind":"calls","metadata":{"unresolvedName":"truncate"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:lines.push","kind":"calls","metadata":{"unresolvedName":"lines.push"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:Math.round","kind":"calls","metadata":{"unresolvedName":"Math.round"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:\"█\".repeat","kind":"calls","metadata":{"unresolvedName":"\"█\".repeat"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:\"░\".repeat","kind":"calls","metadata":{"unresolvedName":"\"░\".repeat"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:ctx.ui.setWidget","kind":"calls","metadata":{"unresolvedName":"ctx.ui.setWidget"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","kind":"calls","metadata":{"unresolvedName":"runDeepResearch"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:formatDuration","kind":"calls","metadata":{"unresolvedName":"formatDuration"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:handler","to":"external:pi.sendUserMessage","kind":"calls","metadata":{"unresolvedName":"pi.sendUserMessage"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","kind":"defines"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:truncate","kind":"calls","metadata":{"unresolvedName":"truncate"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:lines.push","kind":"calls","metadata":{"unresolvedName":"lines.push"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:Math.round","kind":"calls","metadata":{"unresolvedName":"Math.round"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:\"█\".repeat","kind":"calls","metadata":{"unresolvedName":"\"█\".repeat"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:\"░\".repeat","kind":"calls","metadata":{"unresolvedName":"\"░\".repeat"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","to":"external:ctx.ui.setWidget","kind":"calls","metadata":{"unresolvedName":"ctx.ui.setWidget"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/index.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:isFirecrawlReachable","kind":"calls","metadata":{"unresolvedName":"isFirecrawlReachable"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:","to":"external:ctx.ui.notify","kind":"calls","metadata":{"unresolvedName":"ctx.ui.notify"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","to":"external:@earendil-works/pi-coding-agent","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","to":"external:@earendil-works/pi-coding-agent","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:sessionRef.session?.agent.abort","kind":"calls","metadata":{"unresolvedName":"sessionRef.session?.agent.abort"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"symbol-name:getAgentDir","kind":"calls","metadata":{"unresolvedName":"getAgentDir"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:loader.reload","kind":"calls","metadata":{"unresolvedName":"loader.reload"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"symbol-name:createAgentSession","kind":"calls","metadata":{"unresolvedName":"createAgentSession"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:SessionManager.inMemory","kind":"calls","metadata":{"unresolvedName":"SessionManager.inMemory"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:result.session.agent.abort","kind":"calls","metadata":{"unresolvedName":"result.session.agent.abort"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:signal?.addEventListener","kind":"calls","metadata":{"unresolvedName":"signal?.addEventListener"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:result.session.subscribe","kind":"calls","metadata":{"unresolvedName":"result.session.subscribe"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"symbol-name:onEvent","kind":"calls","metadata":{"unresolvedName":"onEvent"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:extractAssistantText","kind":"calls","metadata":{"unresolvedName":"extractAssistantText"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:result.session.prompt","kind":"calls","metadata":{"unresolvedName":"result.session.prompt"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:result.session.agent.waitForIdle","kind":"calls","metadata":{"unresolvedName":"result.session.agent.waitForIdle"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"symbol-name:unsubscribe","kind":"calls","metadata":{"unresolvedName":"unsubscribe"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:result.session.dispose","kind":"calls","metadata":{"unresolvedName":"result.session.dispose"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:signal?.removeEventListener","kind":"calls","metadata":{"unresolvedName":"signal?.removeEventListener"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:finalText.trim","kind":"calls","metadata":{"unresolvedName":"finalText.trim"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","to":"external:sessionRef.session?.dispose","kind":"calls","metadata":{"unresolvedName":"sessionRef.session?.dispose"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:","to":"external:sessionRef.session?.agent.abort","kind":"calls","metadata":{"unresolvedName":"sessionRef.session?.agent.abort"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:","to":"symbol-name:onEvent","kind":"calls","metadata":{"unresolvedName":"onEvent"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:extractAssistantText","kind":"calls","metadata":{"unresolvedName":"extractAssistantText"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:extractAssistantText","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:extractAssistantText","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:extractAssistantText","to":"external:Array.isArray","kind":"calls","metadata":{"unresolvedName":"Array.isArray"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:extractAssistantText","to":"external:content\n .filter","kind":"calls","metadata":{"unresolvedName":"content\n .filter"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"external:node:fs","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"external:node:path","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"external:node:os","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/types.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:loadFirecrawlConfig","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:loadFirecrawlConfig","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:loadFirecrawlConfig","to":"external:path.join","kind":"calls","metadata":{"unresolvedName":"path.join"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:loadFirecrawlConfig","to":"external:os.homedir","kind":"calls","metadata":{"unresolvedName":"os.homedir"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:loadFirecrawlConfig","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:loadFirecrawlConfig","to":"external:fs.readFileSync","kind":"calls","metadata":{"unresolvedName":"fs.readFileSync"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:loadFirecrawlConfig","to":"external:(process.env.FIRECRAWL_BASE_URL ?? \"http://localhost:3002\").replace","kind":"calls","metadata":{"unresolvedName":"(process.env.FIRECRAWL_BASE_URL ?? \"http://localhost:3002\").replace"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest","to":"symbol-name:fetch","kind":"calls","metadata":{"unresolvedName":"fetch"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest","to":"external:res.text","kind":"calls","metadata":{"unresolvedName":"res.text"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest","to":"external:text.slice","kind":"calls","metadata":{"unresolvedName":"text.slice"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest","to":"external:res.json","kind":"calls","metadata":{"unresolvedName":"res.json"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:isFirecrawlReachable","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:isFirecrawlReachable","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:isFirecrawlReachable","to":"symbol-name:fetch","kind":"calls","metadata":{"unresolvedName":"fetch"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:isFirecrawlReachable","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:isFirecrawlReachable","to":"external:AbortSignal.timeout","kind":"calls","metadata":{"unresolvedName":"AbortSignal.timeout"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:searchWeb","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:searchWeb","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:searchWeb","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:searchWeb","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest","kind":"calls","metadata":{"unresolvedName":"firecrawlRequest"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:searchWeb","to":"external:res.data.map","kind":"calls","metadata":{"unresolvedName":"res.data.map"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:scrapeUrl","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:scrapeUrl","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:scrapeUrl","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:firecrawlRequest","kind":"calls","metadata":{"unresolvedName":"firecrawlRequest"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/types.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","kind":"calls","metadata":{"unresolvedName":"runAnalysisAgent"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFallbackQueries","kind":"calls","metadata":{"unresolvedName":"generateFallbackQueries"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","to":"external:Array.isArray","kind":"calls","metadata":{"unresolvedName":"Array.isArray"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","to":"external:parsed.slice(0, count).map","kind":"calls","metadata":{"unresolvedName":"parsed.slice(0, count).map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","to":"external:parsed.slice","kind":"calls","metadata":{"unresolvedName":"parsed.slice"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","to":"external:rounds.flatMap","kind":"calls","metadata":{"unresolvedName":"rounds.flatMap"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","to":"external:allFindings\n .map","kind":"calls","metadata":{"unresolvedName":"allFindings\n .map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","to":"external:rounds\n .flatMap((r) => r.queries)\n .map","kind":"calls","metadata":{"unresolvedName":"rounds\n .flatMap((r) => r.queries)\n .map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","to":"external:rounds\n .flatMap","kind":"calls","metadata":{"unresolvedName":"rounds\n .flatMap"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","kind":"calls","metadata":{"unresolvedName":"runAnalysisAgent"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","to":"external:Array.isArray","kind":"calls","metadata":{"unresolvedName":"Array.isArray"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","to":"external:parsed.slice(0, count).map","kind":"calls","metadata":{"unresolvedName":"parsed.slice(0, count).map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","to":"external:parsed.slice","kind":"calls","metadata":{"unresolvedName":"parsed.slice"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFallbackQueries","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFallbackQueries","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFallbackQueries","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFallbackQueries","to":"external:queries.push","kind":"calls","metadata":{"unresolvedName":"queries.push"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","to":"external:results\n .map","kind":"calls","metadata":{"unresolvedName":"results\n .map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","to":"external:r.markdown.slice","kind":"calls","metadata":{"unresolvedName":"r.markdown.slice"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","kind":"calls","metadata":{"unresolvedName":"runAnalysisAgent"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","to":"external:Array.isArray","kind":"calls","metadata":{"unresolvedName":"Array.isArray"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","to":"external:parsed\n .map","kind":"calls","metadata":{"unresolvedName":"parsed\n .map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","to":"external:f.sources.map","kind":"calls","metadata":{"unresolvedName":"f.sources.map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","to":"external:f.keyQuotes.map","kind":"calls","metadata":{"unresolvedName":"f.keyQuotes.map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","to":"external:[\"high\", \"medium\", \"low\"].includes","kind":"calls","metadata":{"unresolvedName":"[\"high\", \"medium\", \"low\"].includes"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/types.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"external:rounds.flatMap","kind":"calls","metadata":{"unresolvedName":"rounds.flatMap"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"external:rounds.reduce","kind":"calls","metadata":{"unresolvedName":"rounds.reduce"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"external:evidenceByAngle.has","kind":"calls","metadata":{"unresolvedName":"evidenceByAngle.has"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"external:evidenceByAngle.set","kind":"calls","metadata":{"unresolvedName":"evidenceByAngle.set"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"external:evidenceByAngle.get(angle)!.push","kind":"calls","metadata":{"unresolvedName":"evidenceByAngle.get(angle)!.push"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"external:evidenceByAngle.get","kind":"calls","metadata":{"unresolvedName":"evidenceByAngle.get"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"external:Array.from","kind":"calls","metadata":{"unresolvedName":"Array.from"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"external:finding.sources.map((s: string) => `[${s}](${s})`).join","kind":"calls","metadata":{"unresolvedName":"finding.sources.map((s: string) => `[${s}](${s})`).join"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"external:finding.sources.map","kind":"calls","metadata":{"unresolvedName":"finding.sources.map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"/Users/mike/.pi/agent/extensions/deep-research/src/agent.ts:runAnalysisAgent","kind":"calls","metadata":{"unresolvedName":"runAnalysisAgent"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","to":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:generateFallbackReport","kind":"calls","metadata":{"unresolvedName":"generateFallbackReport"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:generateFallbackReport","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:generateFallbackReport","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:generateFallbackReport","to":"external:lines.push","kind":"calls","metadata":{"unresolvedName":"lines.push"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:generateFallbackReport","to":"external:rounds.flatMap","kind":"calls","metadata":{"unresolvedName":"rounds.flatMap"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:generateFallbackReport","to":"external:round.queries.map((q) => `\"${q.query}\"`).join","kind":"calls","metadata":{"unresolvedName":"round.queries.map((q) => `\"${q.query}\"`).join"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:generateFallbackReport","to":"external:round.queries.map","kind":"calls","metadata":{"unresolvedName":"round.queries.map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:generateFallbackReport","to":"external:lines.join","kind":"calls","metadata":{"unresolvedName":"lines.join"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","to":"external:@earendil-works/pi-coding-agent","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/types.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","to":"file:/Users/mike/.pi/agent/extensions/deep-research/src/report.ts","kind":"imports"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"/Users/mike/.pi/agent/extensions/deep-research/index.ts:onProgress","kind":"calls","metadata":{"unresolvedName":"onProgress"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateQueries","kind":"calls","metadata":{"unresolvedName":"generateQueries"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:generateFollowUpQueries","kind":"calls","metadata":{"unresolvedName":"generateFollowUpQueries"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:q.query.slice","kind":"calls","metadata":{"unresolvedName":"q.query.slice"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"/Users/mike/.pi/agent/extensions/deep-research/src/firecrawl.ts:searchWeb","kind":"calls","metadata":{"unresolvedName":"searchWeb"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:searchResults.push","kind":"calls","metadata":{"unresolvedName":"searchResults.push"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:errorMsg.slice","kind":"calls","metadata":{"unresolvedName":"errorMsg.slice"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:searchResults.filter","kind":"calls","metadata":{"unresolvedName":"searchResults.filter"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:seen.has","kind":"calls","metadata":{"unresolvedName":"seen.has"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:seen.add","kind":"calls","metadata":{"unresolvedName":"seen.add"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:Math.ceil","kind":"calls","metadata":{"unresolvedName":"Math.ceil"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:uniqueResults.slice","kind":"calls","metadata":{"unresolvedName":"uniqueResults.slice"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"/Users/mike/.pi/agent/extensions/deep-research/src/queries.ts:analyzeResults","kind":"calls","metadata":{"unresolvedName":"analyzeResults"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:allFindings.push","kind":"calls","metadata":{"unresolvedName":"allFindings.push"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:rounds.push","kind":"calls","metadata":{"unresolvedName":"rounds.push"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:allFindings\n .filter((f) => f.confidence === \"low\")\n .map","kind":"calls","metadata":{"unresolvedName":"allFindings\n .filter((f) => f.confidence === \"low\")\n .map"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"external:allFindings\n .filter","kind":"calls","metadata":{"unresolvedName":"allFindings\n .filter"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:runDeepResearch","to":"/Users/mike/.pi/agent/extensions/deep-research/src/report.ts:synthesizeReport","kind":"calls","metadata":{"unresolvedName":"synthesizeReport"}},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:","kind":"contains"},{"from":"file:/Users/mike/.pi/agent/extensions/deep-research/src/research.ts","to":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:","kind":"defines"},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:","to":"external:seen.has","kind":"calls","metadata":{"unresolvedName":"seen.has"}},{"from":"/Users/mike/.pi/agent/extensions/deep-research/src/research.ts:","to":"external:seen.add","kind":"calls","metadata":{"unresolvedName":"seen.add"}}]} \ No newline at end of file diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..d799950 --- /dev/null +++ b/index.ts @@ -0,0 +1,521 @@ +/** + * deep-research — Multi-round deep web research powered by Firecrawl + * + * Registers: + * - `deep_research` tool — callable by the LLM to conduct deep research + * - `/deep-research` command — interactive session invocation + * + * Architecture: + * Each research round generates queries, searches in parallel via + * Firecrawl, analyzes results with agent sessions, then generates + * follow-up queries. A final synthesis step produces the report. + * + * Patterns borrowed from: + * - firecrawl.ts extension (direct Firecrawl HTTP calls) + * - ralpi executor (agent sessions, widget updates, progress UX) + * - subagent extension (structured tool rendering) + */ +import type { + ExtensionAPI, + ExtensionCommandContext, + ExtensionContext, +} from "@earendil-works/pi-coding-agent"; +import { Type } from "typebox"; +import { Box, Text } from "@earendil-works/pi-tui"; +import { runDeepResearch, type ResearchProgress } from "./src/research"; +import { isFirecrawlReachable } from "./src/firecrawl"; +import type { ResearchConfig, ResearchReport } from "./src/types"; + +/* ── Constants ────────────────────────────────────────────────────── */ + +const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; +const PHASE_ICONS: Record = { + generating_queries: "🔍", + searching: "🌐", + analyzing: "📊", + synthesizing: "📝", + complete: "✅", +}; + +/* ── Helpers ──────────────────────────────────────────────────────── */ + +function formatDuration(ms: number): string { + const seconds = Math.floor(ms / 1000); + const minutes = Math.floor(seconds / 60); + if (minutes > 0) return `${minutes}m ${seconds % 60}s`; + return `${seconds}s`; +} + +function truncate(s: string, max: number): string { + if (s.length <= max) return s; + return s.slice(0, max - 3) + "..."; +} + +/* ── Tool Definition ──────────────────────────────────────────────── */ + +const DeepResearchParams = Type.Object({ + question: Type.String({ + description: "The research question to investigate", + }), + depth: Type.Optional( + Type.Integer({ + description: + "Number of research rounds (1-3). Each round uses findings from the previous to generate deeper follow-up queries. Default: 2", + minimum: 1, + maximum: 3, + default: 2, + }), + ), + breadth: Type.Optional( + Type.Integer({ + description: + "Number of search queries per round (1-5). More queries = broader coverage. Default: 3", + minimum: 1, + maximum: 5, + default: 3, + }), + ), + format: Type.Optional( + Type.Union([Type.Literal("markdown"), Type.Literal("structured")], { + description: + 'Output format for the research report. "markdown" for prose, "structured" for detailed sections. Default: "markdown"', + default: "markdown", + }), + ), + details: Type.Optional( + Type.Object({ + showRoundDetails: Type.Optional( + Type.Boolean({ + description: + "Include per-round search details in the output. Default: false", + }), + ), + }), + ), +}); + +interface ResearchDetails { + rounds: Array<{ + round: number; + queries: string[]; + findingsCount: number; + resultsCount: number; + }>; + totalSearches: number; + totalPagesScraped: number; + durationMs: number; +} + +/* ── Extension Entry ───────────────────────────────────────────────── */ + +export default function (pi: ExtensionAPI) { + pi.registerTool({ + name: "deep_research", + label: "Deep Research", + description: [ + "Conduct multi-round deep web research on any topic using Firecrawl.", + "Generates diverse search queries, searches the web in parallel, analyzes results, and produces a comprehensive report.", + "Supports iterative refinement: each round builds on findings from the previous one.", + "Parameters: question (required), depth (1-3, default 2), breadth (1-5, default 3), format (markdown|structured).", + ].join(" "), + promptSnippet: + "deep_research — multi-round deep web research via Firecrawl with iterative query refinement", + promptGuidelines: [ + "Use deep_research for complex, multi-faceted questions that benefit from multiple search angles and iterative refinement.", + "The tool handles query generation, web search, result analysis, and report synthesis automatically.", + "For simple fact-finding questions, use firecrawl_search directly instead.", + ], + parameters: DeepResearchParams, + + async execute( + _toolCallId: string, + params: { + question: string; + depth?: number; + breadth?: number; + format?: "markdown" | "structured"; + details?: { showRoundDetails?: boolean }; + }, + signal: AbortSignal | undefined, + onUpdate: ((partial: any) => void) | undefined, + ctx: any, + ) { + const config: ResearchConfig = { + question: params.question, + depth: params.depth ?? 2, + breadth: params.breadth ?? 3, + format: params.format ?? "markdown", + }; + + // Use provided signals + const abortSignal = signal; + + // Wire progress updates to both the widget and onUpdate + let spinnerIdx = 0; + const spinnerTimer = setInterval(() => { + spinnerIdx = (spinnerIdx + 1) % SPINNER_FRAMES.length; + }, 100); + + let researchResult: ResearchReport | null = null; + let lastError: string | null = null; + + const onProgress: ResearchProgress = (update) => { + const icon = PHASE_ICONS[update.phase] ?? ""; + const spinner = SPINNER_FRAMES[spinnerIdx]; + const roundInfo = + update.round && update.totalRounds + ? ` Round ${update.round}/${update.totalRounds}` + : ""; + + // Update widget + const lines: string[] = [ + `${spinner} ${icon} ${truncate(update.message, 80)}${roundInfo}`, + ]; + if (update.detail) { + lines.push(` ${truncate(update.detail, 76)}`); + } + if (update.fraction !== undefined) { + const barLen = 15; + const filled = Math.round(barLen * update.fraction); + const bar = "█".repeat(filled) + "░".repeat(barLen - filled); + lines.push(` ${bar}`); + } + ctx.ui.setWidget("deep-research", lines); + + // Stream partial results via onUpdate + if (onUpdate) { + const partialText = lines.join("\n"); + onUpdate({ + content: [{ type: "text", text: partialText }], + details: { + phase: update.phase, + round: update.round, + message: update.message, + fraction: update.fraction, + }, + }); + } + }; + + try { + // Initial status + ctx.ui.setStatus( + "deep-research", + `🌐 Researching: ${truncate(config.question, 40)}`, + ); + + onProgress({ + phase: "generating_queries", + message: "Starting deep research...", + fraction: 0, + }); + + researchResult = await runDeepResearch( + config, + ctx, + onProgress, + abortSignal, + ); + + // ── Build the tool result ────────────────────────────────── + + const details: ResearchDetails = { + rounds: researchResult.rounds.map((r) => ({ + round: r.round, + queries: r.queries.map((q) => q.query), + findingsCount: r.findings.length, + resultsCount: r.results.length, + })), + totalSearches: researchResult.totalSearches, + totalPagesScraped: researchResult.totalPagesScraped, + durationMs: researchResult.durationMs, + }; + + const showRoundDetails = params.details?.showRoundDetails ?? false; + + let output = researchResult.finalReport; + if (showRoundDetails) { + output += `\n\n---\n\n## Research Methodology\n\n`; + for (const round of researchResult.rounds) { + output += `### Round ${round.round}\n\n`; + output += `**Queries:**\n`; + for (const q of round.queries) { + output += `- "${q.query}" (${q.angle}) — ${q.rationale}\n`; + } + output += `\n**Results scraped:** ${round.results.length}\n`; + output += `**Findings extracted:** ${round.findings.length}\n\n`; + } + output += `**Total searches:** ${researchResult.totalSearches}\n`; + output += `**Total pages scraped:** ${researchResult.totalPagesScraped}\n`; + output += `**Duration:** ${formatDuration(researchResult.durationMs)}\n`; + } + + // Clean up widget + clearInterval(spinnerTimer); + ctx.ui.setWidget("deep-research", undefined); + ctx.ui.setStatus("deep-research", undefined); + + return { + content: [{ type: "text", text: output }], + details, + }; + } catch (error) { + clearInterval(spinnerTimer); + ctx.ui.setWidget("deep-research", undefined); + ctx.ui.setStatus("deep-research", undefined); + + lastError = error instanceof Error ? error.message : String(error); + + return { + content: [ + { + type: "text", + text: `Research failed: ${lastError}`, + }, + ], + details: { + error: lastError, + phase: researchResult + ? `completed ${researchResult.rounds.length} rounds` + : "preparation", + }, + isError: true, + }; + } + }, + + // ── TUI: Render the tool call (collapsed view) ────────────────── + + renderCall( + args: { + question: string; + depth?: number; + breadth?: number; + format?: string; + }, + theme: any, + _context: any, + ) { + const question = truncate(args.question ?? "?", 70); + const depth = args.depth ?? 2; + const breadth = args.breadth ?? 3; + const format = args.format ?? "markdown"; + + const text = + theme.fg("toolTitle", theme.bold("deep_research ")) + + theme.fg("accent", `"${question}"`) + + theme.fg("muted", ` [depth:${depth} breadth:${breadth} ${format}]`); + return new Text(text, 0, 0); + }, + + // ── TUI: Render the tool result (expanded/collapsed) ───────────── + + renderResult( + result: any, + { expanded }: { expanded: boolean }, + theme: any, + _context: any, + ) { + const details = result.details as ResearchDetails | undefined; + + if (!details) { + const text = result.content?.[0]?.text ?? "(no output)"; + return new Text(text, 0, 0); + } + + const container = new Box(); + + // ── Collapsed view ──────────────────────────────────────────── + + if (!expanded) { + const totalRounds = details.rounds.length; + const totalFindings = details.rounds.reduce( + (s, r) => s + r.findingsCount, + 0, + ); + const duration = formatDuration(details.durationMs); + + let text = ""; + text += + theme.fg("success", "✓ ") + + theme.fg("toolTitle", theme.bold("deep research")); + text += theme.fg( + "muted", + ` — ${totalRounds} rounds, ${totalFindings} findings`, + ); + text += theme.fg("dim", ` (${duration})`); + text += "\n"; + + for (const round of details.rounds) { + const icon = + round.findingsCount > 0 + ? theme.fg("success", "✓") + : theme.fg("muted", "·"); + text += ` ${icon} ${theme.fg("accent", `Round ${round.round}:`)} `; + text += theme.fg( + "dim", + `${round.queries.length} queries, ${round.resultsCount} pages, ${round.findingsCount} findings`, + ); + text += "\n"; + } + + text += theme.fg("muted", "(Ctrl+O to expand)"); + container.addChild(new Text(text, 0, 0)); + return container; + } + + // ── Expanded view ───────────────────────────────────────────── + + const headerText = + theme.fg("toolTitle", theme.bold("Deep Research Results")) + + "\n" + + theme.fg("dim", `Duration: ${formatDuration(details.durationMs)} | `) + + theme.fg("dim", `Searches: ${details.totalSearches} | `) + + theme.fg("dim", `Pages scraped: ${details.totalPagesScraped}`); + container.addChild(new Text(headerText, 0, 0)); + + for (const round of details.rounds) { + container.addChild(new Text("", 0, 0)); // Spacer + const roundHeader = `Round ${round.round}`; + container.addChild( + new Text(theme.fg("toolTitle", theme.bold(roundHeader)), 0, 0), + ); + container.addChild( + new Text( + theme.fg( + "dim", + `${round.queries.length} queries → ${round.resultsCount} pages → ${round.findingsCount} findings`, + ), + 0, + 0, + ), + ); + for (const q of round.queries) { + container.addChild( + new Text( + theme.fg("muted", " · ") + theme.fg("accent", truncate(q, 70)), + 0, + 0, + ), + ); + } + } + + return container; + }, + }); + + // ── Command ─────────────────────────────────────────────────────── + + pi.registerCommand("deep-research", { + description: + "Conduct multi-round deep web research on any topic via Firecrawl. Usage: /deep-research ", + handler: async (args: string, ctx: ExtensionCommandContext) => { + if (!args || args.trim().length === 0) { + ctx.ui.notify( + "Usage: /deep-research ", + "error", + ); + return; + } + + // Ask about depth/breadth + const depthStr = await ctx.ui.select("Research depth?", [ + "1 round (quick survey)", + "2 rounds (standard)", + "3 rounds (deep dive)", + ]); + const depth = depthStr?.startsWith("1") + ? 1 + : depthStr?.startsWith("3") + ? 3 + : 2; + + const breadthStr = await ctx.ui.select("Research breadth?", [ + "1 query/round (narrow)", + "3 queries/round (balanced)", + "5 queries/round (broad)", + ]); + const breadth = breadthStr?.startsWith("1") + ? 1 + : breadthStr?.startsWith("5") + ? 5 + : 3; + + // Create a promise-based interaction + ctx.ui.setStatus( + "deep-research", + `🌐 Researching: ${truncate(args, 40)}`, + ); + + const config: ResearchConfig = { + question: args, + depth, + breadth, + format: "markdown", + }; + + let spinnerIdx = 0; + const spinnerTimer = setInterval(() => { + spinnerIdx = (spinnerIdx + 1) % SPINNER_FRAMES.length; + }, 100); + + try { + const onProgress: ResearchProgress = (update) => { + const icon = PHASE_ICONS[update.phase] ?? ""; + const spinner = SPINNER_FRAMES[spinnerIdx]; + const lines: string[] = [ + `${spinner} ${icon} ${truncate(update.message, 80)}`, + ]; + if (update.detail) { + lines.push(` ${truncate(update.detail, 76)}`); + } + if (update.fraction !== undefined) { + const barLen = 15; + const filled = Math.round(barLen * update.fraction); + const bar = "█".repeat(filled) + "░".repeat(barLen - filled); + lines.push(` ${bar}`); + } + ctx.ui.setWidget("deep-research", lines); + }; + + const report = await runDeepResearch(config, ctx, onProgress); + + clearInterval(spinnerTimer); + ctx.ui.setWidget("deep-research", undefined); + ctx.ui.setStatus("deep-research", undefined); + + // Show notification + ctx.ui.notify( + `Research complete: ${report.rounds.length} rounds, ${report.totalSearches} searches, ${report.totalPagesScraped} pages in ${formatDuration(report.durationMs)}`, + "info", + ); + + // Send the report as a user message + pi.sendUserMessage( + `## Deep Research: ${args}\n\n${report.finalReport}\n\n---\n*${report.rounds.length} rounds · ${report.totalSearches} searches · ${report.totalPagesScraped} pages · ${formatDuration(report.durationMs)}*`, + ); + } catch (error) { + clearInterval(spinnerTimer); + ctx.ui.setWidget("deep-research", undefined); + ctx.ui.setStatus("deep-research", undefined); + ctx.ui.notify( + `Research failed: error instanceof Error ? error.message : String(error)`, + "error", + ); + } + }, + }); + + // ── Startup check ───────────────────────────────────────────────── + + pi.on("session_start", async (_event: unknown, ctx: ExtensionContext) => { + const reachable = await isFirecrawlReachable(); + if (!reachable) { + ctx.ui.notify( + "Deep Research: Firecrawl endpoint unreachable — searches will fail. Check FIRECRAWL_BASE_URL in settings.json or env.", + "warning", + ); + } + }); +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2ad565c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,47 @@ +{ + "name": "deep-research", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "deep-research", + "version": "1.0.0", + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^5.3.0" + } + }, + "node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..366c237 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "deep-research", + "version": "1.0.0", + "description": "Deep research extension for pi — parallel web research via Firecrawl with iterative query refinement", + "private": true, + "pi": { + "extensions": [ + "./index.ts" + ] + }, + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^5.3.0" + } +} diff --git a/src/agent.ts b/src/agent.ts new file mode 100644 index 0000000..9835677 --- /dev/null +++ b/src/agent.ts @@ -0,0 +1,155 @@ +/** + * Deep Research — Agent Session helper + * + * Uses pi's in-process `createAgentSession` for LLM subtasks + * (query generation, result analysis, report synthesis). + * Pattern borrowed from ralpi's runAgentSession(). + */ +import { + createAgentSession, + DefaultResourceLoader, + getAgentDir, + SessionManager, +} from "@earendil-works/pi-coding-agent"; +import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent"; + +/** Aggregate tool usage stats */ +export interface ToolUsage { + read: number; + write: number; + edit: number; + bash: number; + other: number; +} + +export interface AgentResult { + success: boolean; + text: string; + error?: string; + toolUsage: ToolUsage; +} + +/** + * Run a prompt through an in-process Pi agent session. + * Non-blocking — the event loop stays responsive. + */ +export async function runAnalysisAgent( + systemPrompt: string, + taskPrompt: string, + cwd: string, + timeoutMs: number = 120_000, + onEvent?: (event: AgentSessionEvent) => void, + signal?: AbortSignal, +): Promise { + const toolUsage: ToolUsage = { + read: 0, + write: 0, + edit: 0, + bash: 0, + other: 0, + }; + + let timeoutHandle: ReturnType | null = null; + if (timeoutMs > 0) { + timeoutHandle = setTimeout(() => { + sessionRef.session?.agent.abort(); + }, timeoutMs); + } + + const sessionRef: { + session?: Awaited>["session"]; + } = {}; + + try { + const loader = new DefaultResourceLoader({ + cwd, + agentDir: getAgentDir(), + noExtensions: true, + noSkills: true, + noPromptTemplates: true, + noThemes: true, + noContextFiles: true, + }); + await loader.reload(); + + const result = await createAgentSession({ + cwd, + sessionManager: SessionManager.inMemory(), + resourceLoader: loader, + tools: ["read", "grep", "find", "ls"], + systemPrompt, + }); + sessionRef.session = result.session; + + const abortHandler = () => result.session.agent.abort(); + signal?.addEventListener("abort", abortHandler, { once: true }); + + let finalText = ""; + let errorMessage: string | undefined; + + const unsubscribe = result.session.subscribe((event: AgentSessionEvent) => { + onEvent?.(event); + + if (event.type === "message_end") { + const message = event.message as { + role?: string; + content?: unknown; + errorMessage?: string; + }; + if (message.role !== "assistant") return; + if (message.errorMessage) errorMessage = message.errorMessage; + const text = extractAssistantText(message.content); + if (text) finalText = text; + } + + if (event.type === "tool_execution_start") { + const name = event.toolName; + if (name in toolUsage) { + (toolUsage as unknown as Record)[name]++; + } else { + toolUsage.other++; + } + } + }); + + if (signal?.aborted) throw new Error("Aborted"); + + await result.session.prompt(taskPrompt); + await result.session.agent.waitForIdle(); + + unsubscribe(); + result.session.dispose(); + signal?.removeEventListener("abort", abortHandler); + if (timeoutHandle) clearTimeout(timeoutHandle); + + if (errorMessage && !finalText) { + return { success: false, text: "", error: errorMessage, toolUsage }; + } + + return { success: true, text: finalText.trim(), toolUsage }; + } catch (error) { + if (timeoutHandle) clearTimeout(timeoutHandle); + return { + success: false, + text: "", + error: error instanceof Error ? error.message : String(error), + toolUsage, + }; + } finally { + sessionRef.session?.dispose(); + } +} + +function extractAssistantText(content: unknown): string { + if (typeof content === "string") return content; + if (!Array.isArray(content)) return ""; + return content + .filter( + (c): c is { type: string; text?: string } => + !!c && + typeof c === "object" && + (c as { type?: string }).type === "text", + ) + .map((c) => (c as { text?: string }).text ?? "") + .join(""); +} diff --git a/src/firecrawl.ts b/src/firecrawl.ts new file mode 100644 index 0000000..70308bc --- /dev/null +++ b/src/firecrawl.ts @@ -0,0 +1,159 @@ +/** + * Deep Research — direct Firecrawl HTTP client + * + * Calls the self-hosted Firecrawl API directly (same approach as the + * firecrawl.ts extension) + */ +import * as fs from "node:fs"; +import * as path from "node:path"; +import * as os from "node:os"; +import type { SearchResult } from "./types"; + +/* ── Config ──────────────────────────────────────────────────────── */ + +function loadFirecrawlConfig() { + const settingsPath = path.join(os.homedir(), ".pi", "agent", "settings.json"); + try { + const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8")); + const fc = settings.firecrawl ?? {}; + return { + baseUrl: ( + fc.baseUrl ?? + process.env.FIRECRAWL_BASE_URL ?? + "http://localhost:3002" + ).replace(/\/+$/, ""), + apiKey: fc.apiKey ?? process.env.FIRECRAWL_API_KEY, + }; + } catch { + return { + baseUrl: ( + process.env.FIRECRAWL_BASE_URL ?? "http://localhost:3002" + ).replace(/\/+$/, ""), + apiKey: process.env.FIRECRAWL_API_KEY, + }; + } +} + +const { baseUrl: BASE_URL, apiKey: API_KEY } = loadFirecrawlConfig(); + +/* ── Helpers ──────────────────────────────────────────────────────── */ + +async function firecrawlRequest( + endpoint: string, + body: Record, + signal?: AbortSignal, +): Promise { + const headers: Record = { + "Content-Type": "application/json", + }; + if (API_KEY) { + headers["Authorization"] = `Bearer ${API_KEY}`; + } + + const res = await fetch(`${BASE_URL}/v1/${endpoint}`, { + method: "POST", + headers, + body: JSON.stringify(body), + signal, + }); + + if (!res.ok) { + const text = await res.text(); + throw new Error( + `Firecrawl ${endpoint} failed (${res.status}): ${text.slice(0, 500)}`, + ); + } + + return res.json(); +} + +export async function isFirecrawlReachable(): Promise { + try { + const res = await fetch(`${BASE_URL}/v1/scrape`, { + method: "POST", + headers: { + "Content-Type": "application/json", + ...(API_KEY ? { Authorization: `Bearer ${API_KEY}` } : {}), + }, + body: JSON.stringify({ url: "https://example.com", formats: ["links"] }), + signal: AbortSignal.timeout(10_000), + }); + return res.ok; + } catch { + return false; + } +} + +/* ── Search ───────────────────────────────────────────────────────── */ + +/** + * Search the web and return structured results. + * Uses Firecrawl's search endpoint with scrape to get full page content. + */ +export async function searchWeb( + query: string, + limit: number = 5, + signal?: AbortSignal, +): Promise { + const body: Record = { + query, + limit: Math.min(limit, 10), + scrapeOptions: { + formats: ["markdown"], + onlyMainContent: true, + }, + }; + + const result = await firecrawlRequest("search", body, signal); + + if (!result || typeof result !== "object") return []; + + const res = result as { + success?: boolean; + data?: Record[]; + error?: string; + }; + + if (!res.success || !res.data) return []; + + return res.data + .map((doc) => ({ + title: (doc.title as string) ?? "", + url: (doc.url as string) ?? "", + description: (doc.description as string) ?? "", + markdown: (doc.markdown as string) ?? "", + })) + .filter((r) => r.markdown || r.description); +} + +/* ── Scrape ───────────────────────────────────────────────────────── */ + +/** + * Scrape a single URL and return its markdown content. + */ +export async function scrapeUrl( + url: string, + signal?: AbortSignal, +): Promise<{ title: string; markdown: string; links: string[] } | null> { + const result = await firecrawlRequest( + "scrape", + { url, formats: ["markdown"] }, + signal, + ); + + if (!result || typeof result !== "object") return null; + + const res = result as { + success?: boolean; + data?: Record; + error?: string; + }; + + if (!res.success || !res.data) return null; + + return { + title: (res.data.title as string) ?? "", + markdown: (res.data.markdown as string) ?? "", + links: (res.data.links as string[]) ?? [], + }; +} diff --git a/src/queries.ts b/src/queries.ts new file mode 100644 index 0000000..844c4ca --- /dev/null +++ b/src/queries.ts @@ -0,0 +1,261 @@ +/** + * Deep Research — Search query generation & refinement + * + * Uses an LLM agent to generate search queries from different research + * angles, then analyzes results to produce follow-up queries. + */ +import type { SearchQuery, Finding, ResearchRound } from "./types"; +import { runAnalysisAgent } from "./agent"; + +const GENERATE_QUERIES_SYSTEM = `You are a research methodology expert. Your role is to generate effective web search queries that will yield high-quality, diverse information about a research topic. + +Guidelines: +- Create queries from DIFFERENT angles (technical, practical, comparative, critical, forward-looking) +- Each query should target a specific facet of the question +- Queries should use keywords that search engines rank well (avoid overly long questions) +- Cover contrasting viewpoints and alternative approaches +- Include queries for finding authoritative sources (docs, papers, official sites) +- Prioritize recent information where relevant + +Output ONLY a JSON array of objects with fields: +- "query": the search query string +- "rationale": why this query will help answer the research question +- "angle": one of "technical" | "practical" | "comparative" | "critical" | "forward-looking" | "authoritative" + +Example: +[ + {"query": "Rust async/await performance benchmarks 2024", "rationale": "Understanding current performance characteristics", "angle": "technical"}, + {"query": "Rust vs Go concurrency patterns comparison", "rationale": "Comparative analysis helps contextualize trade-offs", "angle": "comparative"} +] +`; + +const FOLLOWUP_SYSTEM = `You are a research analyst. Given the research question and findings so far, your job is to identify what's still unknown and generate follow-up search queries to fill those gaps. + +Look for: +- Claims made without sufficient evidence +- Conflicting information that needs resolution +- Angles that haven't been explored yet +- Missing authoritative sources +- Practical implications that need more detail +- Recent developments that might have updated findings + +Output ONLY a JSON array of objects with fields: +- "query": the search query string +- "rationale": what gap this query fills or what angle it explores +- "angle": one of "technical" | "practical" | "comparative" | "critical" | "forward-looking" | "authoritative" +`; + +/** + * Generate initial search queries for a research question. + */ +export async function generateQueries( + question: string, + count: number, + cwd: string, + signal?: AbortSignal, +): Promise { + const taskPrompt = `Research question: ${question} + +Generate ${count} diverse search queries to research this topic effectively. Cover different angles.`; + + const result = await runAnalysisAgent( + GENERATE_QUERIES_SYSTEM, + taskPrompt, + cwd, + 60_000, + undefined, + signal, + ); + + if (!result.success || !result.text) { + return generateFallbackQueries(question, count); + } + + try { + const parsed = JSON.parse(result.text); + if (Array.isArray(parsed) && parsed.length > 0) { + return parsed + .slice(0, count) + .map((q: Record) => ({ + query: String(q.query ?? ""), + rationale: String(q.rationale ?? ""), + angle: String(q.angle ?? "technical"), + })) + .filter((q) => q.query.length > 0); + } + } catch { + // JSON parse failed, fall back + } + + return generateFallbackQueries(question, count); +} + +/** + * Generate follow-up queries based on findings from previous rounds. + */ +export async function generateFollowUpQueries( + question: string, + rounds: ResearchRound[], + count: number, + cwd: string, + signal?: AbortSignal, +): Promise { + // Build a summary of findings so far + const allFindings = rounds.flatMap((r) => r.findings); + const findingsSummary = allFindings + .map((f) => `- ${f.title}: ${f.summary} (confidence: ${f.confidence})`) + .join("\n"); + + const exploredAngles = rounds + .flatMap((r) => r.queries) + .map((q) => `[${q.angle}] ${q.query} — ${q.rationale}`) + .join("\n"); + + const taskPrompt = `Research question: ${question} + +Queries already explored: +${exploredAngles} + +Findings so far: +${findingsSummary} + +Generate ${count} follow-up search queries to fill remaining gaps and deepen the research.`; + + const result = await runAnalysisAgent( + FOLLOWUP_SYSTEM, + taskPrompt, + cwd, + 60_000, + undefined, + signal, + ); + + if (!result.success || !result.text) { + return []; + } + + try { + const parsed = JSON.parse(result.text); + if (Array.isArray(parsed) && parsed.length > 0) { + return parsed + .slice(0, count) + .map((q: Record) => ({ + query: String(q.query ?? ""), + rationale: String(q.rationale ?? ""), + angle: String(q.angle ?? "technical"), + })) + .filter((q) => q.query.length > 0); + } + } catch { + // parse failed + } + + return []; +} + +/** + * Fallback query generation when the LLM call fails. + */ +function generateFallbackQueries( + question: string, + count: number, +): SearchQuery[] { + const queries: SearchQuery[] = []; + const angles = [ + { angle: "technical", desc: "technical details and specifications" }, + { + angle: "practical", + desc: "practical examples, tutorials, and best practices", + }, + { angle: "comparative", desc: "comparisons with alternatives" }, + { angle: "critical", desc: "limitations, challenges, and criticisms" }, + { angle: "forward-looking", desc: "future trends and developments" }, + ]; + + for (let i = 0; i < Math.min(count, angles.length); i++) { + queries.push({ + query: `${question} ${angles[i].desc}`, + rationale: `Exploring ${angles[i].desc} related to the research question`, + angle: angles[i].angle as SearchQuery["angle"], + }); + } + + return queries; +} + +const ANALYZE_SYSTEM = `You are a research analyst. Given search results for a specific query, extract key findings. + +For each finding: +- Give it a concise title +- Summarize what was found in 1-3 sentences +- List which source URLs support this finding +- Include 1-2 key quotes from the sources +- Rate your confidence (high/medium/low) based on source authority and consistency + +Output ONLY a JSON array of objects with fields: +- "title": concise finding title +- "summary": 1-3 sentence summary +- "sources": array of source URLs +- "keyQuotes": array of 1-2 key quotes +- "confidence": "high" | "medium" | "low"`; + +/** + * Analyze search results for a specific query and extract findings. + */ +export async function analyzeResults( + query: string, + results: { + title: string; + url: string; + description: string; + markdown: string; + }[], + cwd: string, + signal?: AbortSignal, +): Promise { + const resultsText = results + .map( + (r, i) => + `--- Result ${i + 1} ---\nTitle: ${r.title}\nURL: ${r.url}\nDescription: ${r.description}\nContent:\n${r.markdown.slice(0, 3000)}`, + ) + .join("\n\n"); + + const taskPrompt = `Search query: "${query}" + +Search results: +${resultsText} + +Extract key findings from these results.`; + + const result = await runAnalysisAgent( + ANALYZE_SYSTEM, + taskPrompt, + cwd, + 90_000, + undefined, + signal, + ); + + if (!result.success || !result.text) return []; + + try { + const parsed = JSON.parse(result.text); + if (Array.isArray(parsed)) { + return parsed + .map((f: Record) => ({ + title: String(f.title ?? ""), + summary: String(f.summary ?? ""), + sources: Array.isArray(f.sources) ? f.sources.map(String) : [], + keyQuotes: Array.isArray(f.keyQuotes) ? f.keyQuotes.map(String) : [], + confidence: (["high", "medium", "low"].includes(String(f.confidence)) + ? String(f.confidence) + : "medium") as Finding["confidence"], + })) + .filter((f) => f.title && f.summary); + } + } catch { + // parse failed + } + + return []; +} diff --git a/src/report.ts b/src/report.ts new file mode 100644 index 0000000..f8bcb18 --- /dev/null +++ b/src/report.ts @@ -0,0 +1,170 @@ +/** + * Deep Research — Report synthesis + * + * Takes all research rounds and synthesizes a comprehensive report + * using an LLM agent. + */ +import type { ResearchRound, ResearchConfig } from "./types"; +import { runAnalysisAgent } from "./agent"; + +const SYNTHESIS_SYSTEM = `You are a senior research analyst synthesizing findings from multiple web searches into a comprehensive, well-structured report. + +Your report should: +1. Start with an executive summary (2-3 paragraphs covering the key answer to the research question) +2. Organize findings by theme, not by search query +3. Include specific evidence from sources (cite URLs in [brackets]) +4. Note areas of disagreement or uncertainty +5. Identify knowledge gaps that remain +6. End with actionable conclusions + +Style guidelines: +- Use clear section headings (## level) +- Write in an objective, authoritative tone +- Include bullet points for listing evidence +- Use inline citations like [source](url) +- Note the confidence level for key claims +- Be thorough but concise — every paragraph should add value`; + +/** + * Synthesize a research report from all rounds. + */ +export async function synthesizeReport( + question: string, + rounds: ResearchRound[], + config: ResearchConfig, + cwd: string, + signal?: AbortSignal, +): Promise { + // Build the evidence summary + const allFindings = rounds.flatMap((r) => r.findings); + const totalSearches = rounds.reduce((sum, r) => sum + r.queries.length, 0); + const totalPages = rounds.reduce((sum, r) => sum + r.results.length, 0); + + const evidenceByAngle = new Map(); + for (const round of rounds) { + for (const query of round.queries) { + const key = query.angle; + if (!evidenceByAngle.has(key)) evidenceByAngle.set(key, []); + } + for (const finding of round.findings) { + // Try to determine angle from the round's queries + const angle = round.queries[0]?.angle ?? "technical"; + if (!evidenceByAngle.has(angle)) evidenceByAngle.set(angle, []); + evidenceByAngle.get(angle)!.push(finding); + } + } + + // Build structured evidence text + let evidenceText = `## Research Question\n${question}\n\n`; + evidenceText += `## Overview\n- Rounds of research: ${rounds.length}\n`; + evidenceText += `- Total searches executed: ${totalSearches}\n`; + evidenceText += `- Total pages analyzed: ${totalPages}\n`; + evidenceText += `- Key findings extracted: ${allFindings.length}\n\n`; + + for (const [angle, findings] of Array.from(evidenceByAngle)) { + if (findings.length === 0) continue; + evidenceText += `## Angle: ${angle}\n\n`; + for (const finding of findings) { + evidenceText += `### ${finding.title}\n`; + evidenceText += `**Confidence:** ${finding.confidence}\n`; + evidenceText += `${finding.summary}\n\n`; + if (finding.keyQuotes.length > 0) { + evidenceText += `> ${finding.keyQuotes[0]}\n\n`; + } + if (finding.sources.length > 0) { + evidenceText += `Sources: ${finding.sources.map((s: string) => `[${s}](${s})`).join(", ")}\n\n`; + } + } + } + + // Also include raw search context for depth + evidenceText += `## Raw Search Context\n\n`; + for (const round of rounds) { + evidenceText += `### Round ${round.round}\n`; + for (const q of round.queries) { + evidenceText += `- **"${q.query}"** (${q.angle}) — ${q.rationale}\n`; + } + evidenceText += `\n`; + } + + const taskPrompt = `Synthesize the following research findings into a comprehensive, well-structured report. + +${evidenceText} + +Write a thorough report that answers the original question: "${question}" + +Format: ${config.format === "structured" ? "Structured report with numbered sections, clear hierarchies, and data tables where appropriate." : "Well-formatted markdown report with ## headings, bullet points, and inline citations."}`; + + const result = await runAnalysisAgent( + SYNTHESIS_SYSTEM, + taskPrompt, + cwd, + 120_000, + undefined, + signal, + ); + + if (result.success && result.text) { + return result.text; + } + + // Fallback: generate a simple report from the evidence + return generateFallbackReport(question, rounds); +} + +/** + * Fallback report when the LLM synthesis fails. + */ +function generateFallbackReport( + question: string, + rounds: ResearchRound[], +): string { + const lines: string[] = []; + lines.push(`# Research Report: ${question}`); + lines.push(""); + lines.push("## Executive Summary"); + lines.push(""); + lines.push( + `This report summarizes findings from ${rounds.length} research round(s) exploring the question above.`, + ); + lines.push(""); + + const allFindings = rounds.flatMap((r) => r.findings); + + if (allFindings.length > 0) { + lines.push("## Key Findings"); + lines.push(""); + for (const finding of allFindings) { + lines.push(`### ${finding.title}`); + lines.push(`*Confidence: ${finding.confidence}*`); + lines.push(""); + lines.push(finding.summary); + lines.push(""); + if (finding.keyQuotes.length > 0) { + lines.push(`> ${finding.keyQuotes[0]}`); + lines.push(""); + } + if (finding.sources.length > 0) { + lines.push("Sources:"); + for (const src of finding.sources) { + lines.push(`- [${src}](${src})`); + } + lines.push(""); + } + } + } + + lines.push("## Search Methodology"); + lines.push(""); + for (const round of rounds) { + lines.push(`### Round ${round.round}`); + lines.push( + `Queries: ${round.queries.map((q) => `"${q.query}"`).join(", ")}`, + ); + lines.push(`Pages scraped: ${round.results.length}`); + lines.push(`Findings: ${round.findings.length}`); + lines.push(""); + } + + return lines.join("\n"); +} diff --git a/src/research.ts b/src/research.ts new file mode 100644 index 0000000..519f45d --- /dev/null +++ b/src/research.ts @@ -0,0 +1,254 @@ +/** + * Deep Research — Core research orchestration + * + * Manages the multi-round deep research process: + * 1. Generate initial search queries + * 2. Execute all queries in parallel via Firecrawl + * 3. Analyze results and extract findings + * 4. Generate follow-up queries + * 5. Iterate for depth rounds + * 6. Synthesize final report + * + * Widget and progress callback patterns borrowed from ralpi's executor. + */ +import type { ExtensionContext } from "@earendil-works/pi-coding-agent"; +import type { + ResearchConfig, + SearchResult, + ResearchRound, + ResearchReport, +} from "./types"; +import { searchWeb } from "./firecrawl"; +import { + generateQueries, + generateFollowUpQueries, + analyzeResults, +} from "./queries"; +import { synthesizeReport } from "./report"; + +/** Progress callback for UI updates */ +export type ResearchProgress = (update: { + phase: + | "generating_queries" + | "searching" + | "analyzing" + | "synthesizing" + | "complete"; + round?: number; + totalRounds?: number; + message: string; + detail?: string; + fraction?: number; // 0-1 +}) => void; + +/** + * Run a complete deep research session. + */ +export async function runDeepResearch( + config: ResearchConfig, + ctx: ExtensionContext, + onProgress: ResearchProgress, + signal?: AbortSignal, +): Promise { + const startTime = Date.now(); + const rounds: ResearchRound[] = []; + let totalSearches = 0; + let totalPages = 0; + + // ── Round 1: Generate initial queries ────────────────────────────── + + onProgress({ + phase: "generating_queries", + round: 1, + totalRounds: config.depth, + message: "Generating initial search queries...", + fraction: 0, + }); + + if (signal?.aborted) throw new Error("Research cancelled"); + + const queries = await generateQueries( + config.question, + config.breadth, + ctx.cwd, + signal, + ); + + if (queries.length === 0) { + throw new Error("Failed to generate any search queries"); + } + + // ── Execute rounds ───────────────────────────────────────────────── + + for (let round = 1; round <= config.depth; round++) { + if (signal?.aborted) throw new Error("Research cancelled"); + + const isFirstRound = round === 1; + const currentQueries = isFirstRound + ? queries + : await generateFollowUpQueries( + config.question, + rounds, + config.breadth, + ctx.cwd, + signal, + ); + + if (!currentQueries || currentQueries.length === 0) { + // No follow-up queries to generate — stop here + break; + } + + // ── Search phase ────────────────────────────────────────────────── + + onProgress({ + phase: "searching", + round, + totalRounds: config.depth, + message: `Searching with ${currentQueries.length} queries...`, + fraction: 0.25, + }); + + const searchResults: SearchResult[] = []; + + for (let i = 0; i < currentQueries.length; i++) { + if (signal?.aborted) throw new Error("Research cancelled"); + + const q = currentQueries[i]; + onProgress({ + phase: "searching", + round, + totalRounds: config.depth, + message: `Searching: "${q.query.slice(0, 60)}..."`, + detail: q.rationale, + fraction: 0.25 + (i / currentQueries.length) * 0.25, + }); + + try { + const results = await searchWeb(q.query, 5, signal); + searchResults.push(...results); + } catch (error) { + // Individual search failure shouldn't crash the whole round + const errorMsg = error instanceof Error ? error.message : String(error); + onProgress({ + phase: "searching", + round, + totalRounds: config.depth, + message: `Search failed: ${errorMsg.slice(0, 80)}`, + fraction: 0.25 + ((i + 1) / currentQueries.length) * 0.25, + }); + } + + // Small delay between searches to avoid rate limits + if (i < currentQueries.length - 1) { + await new Promise((r) => setTimeout(r, 300)); + } + } + + totalSearches += currentQueries.length; + + // Deduplicate results by URL + const seen = new Set(); + const uniqueResults = searchResults.filter((r) => { + if (seen.has(r.url)) return false; + seen.add(r.url); + return true; + }); + + totalPages += uniqueResults.length; + + // ── Analyze phase ────────────────────────────────────────────────── + + onProgress({ + phase: "analyzing", + round, + totalRounds: config.depth, + message: `Analyzing ${uniqueResults.length} search results...`, + fraction: 0.6, + }); + + // Analyze results per query group + const allFindings: ResearchRound["findings"] = []; + + for (let i = 0; i < currentQueries.length; i++) { + if (signal?.aborted) throw new Error("Research cancelled"); + + const q = currentQueries[i]; + // Find results that match this query (loosely: take a portion of results) + const resultsPerQuery = Math.ceil( + uniqueResults.length / currentQueries.length, + ); + const startIdx = i * resultsPerQuery; + const endIdx = Math.min(startIdx + resultsPerQuery, uniqueResults.length); + const queryResults = uniqueResults.slice(startIdx, endIdx); + + if (queryResults.length === 0) continue; + + onProgress({ + phase: "analyzing", + round, + totalRounds: config.depth, + message: `Analyzing results for "${q.query.slice(0, 40)}..."`, + fraction: 0.6 + (i / currentQueries.length) * 0.2, + }); + + try { + const findings = await analyzeResults( + q.query, + queryResults, + ctx.cwd, + signal, + ); + allFindings.push(...findings); + } catch { + // Analysis failure shouldn't crash the round + } + } + + // Record this round + rounds.push({ + round, + queries: currentQueries, + results: uniqueResults, + findings: allFindings, + followUpTopics: allFindings + .filter((f) => f.confidence === "low") + .map((f) => f.title), + }); + } + + // ── Synthesis phase ───────────────────────────────────────────────── + + onProgress({ + phase: "synthesizing", + message: "Synthesizing research into final report...", + fraction: 0.9, + }); + + if (signal?.aborted) throw new Error("Research cancelled"); + + const finalReport = await synthesizeReport( + config.question, + rounds, + config, + ctx.cwd, + signal, + ); + + const durationMs = Date.now() - startTime; + + onProgress({ + phase: "complete", + message: "Research complete!", + fraction: 1.0, + }); + + return { + question: config.question, + rounds, + finalReport, + totalSearches, + totalPagesScraped: totalPages, + durationMs, + }; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..ed018c9 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,55 @@ +/** + * Deep Research — type definitions + */ + +/** A single search result from Firecrawl */ +export interface SearchResult { + title: string; + url: string; + description: string; + markdown: string; +} + +/** A finding extracted from search results by an analysis agent */ +export interface Finding { + title: string; + summary: string; + sources: string[]; + keyQuotes: string[]; + confidence: "high" | "medium" | "low"; +} + +/** A generated search query with its intent/rationale */ +export interface SearchQuery { + query: string; + rationale: string; + angle: string; +} + +/** Output from one research round */ +export interface ResearchRound { + round: number; + queries: SearchQuery[]; + results: SearchResult[]; + findings: Finding[]; + /** Any follow-up questions/angles the analysis suggests */ + followUpTopics: string[]; +} + +/** Configuration for a research session */ +export interface ResearchConfig { + question: string; + depth: number; // 1-3 rounds + breadth: number; // queries per round (1-5) + format: "markdown" | "structured"; +} + +/** Final research report */ +export interface ResearchReport { + question: string; + rounds: ResearchRound[]; + finalReport: string; + totalSearches: number; + totalPagesScraped: number; + durationMs: number; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7e287c7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "node", + "lib": ["ES2022"], + "noEmit": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "include": ["index.ts", "src/**/*"], + "exclude": ["node_modules", "dist"] +}