Skip to content

Commit 4c19fbd

Browse files
committed
Deep research
1 parent 7171a0e commit 4c19fbd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3405
-239
lines changed

package-lock.json

Lines changed: 269 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@
7474
"title": "New Task",
7575
"icon": "$(add)"
7676
},
77+
{
78+
"command": "roo-cline.researchButtonClicked",
79+
"title": "Deep Research (β)",
80+
"icon": "$(telescope)"
81+
},
7782
{
7883
"command": "roo-cline.mcpButtonClicked",
7984
"title": "MCP Servers",
@@ -207,34 +212,39 @@
207212
"when": "view == roo-cline.SidebarProvider"
208213
},
209214
{
210-
"command": "roo-cline.promptsButtonClicked",
215+
"command": "roo-cline.researchButtonClicked",
211216
"group": "navigation@2",
212217
"when": "view == roo-cline.SidebarProvider"
213218
},
214219
{
215-
"command": "roo-cline.mcpButtonClicked",
220+
"command": "roo-cline.promptsButtonClicked",
216221
"group": "navigation@3",
217222
"when": "view == roo-cline.SidebarProvider"
218223
},
219224
{
220-
"command": "roo-cline.historyButtonClicked",
225+
"command": "roo-cline.mcpButtonClicked",
221226
"group": "navigation@4",
222227
"when": "view == roo-cline.SidebarProvider"
223228
},
224229
{
225-
"command": "roo-cline.popoutButtonClicked",
230+
"command": "roo-cline.historyButtonClicked",
226231
"group": "navigation@5",
227232
"when": "view == roo-cline.SidebarProvider"
228233
},
229234
{
230-
"command": "roo-cline.settingsButtonClicked",
235+
"command": "roo-cline.popoutButtonClicked",
231236
"group": "navigation@6",
232237
"when": "view == roo-cline.SidebarProvider"
233238
},
234239
{
235-
"command": "roo-cline.helpButtonClicked",
240+
"command": "roo-cline.settingsButtonClicked",
236241
"group": "navigation@7",
237242
"when": "view == roo-cline.SidebarProvider"
243+
},
244+
{
245+
"command": "roo-cline.helpButtonClicked",
246+
"group": "navigation@8",
247+
"when": "view == roo-cline.SidebarProvider"
238248
}
239249
]
240250
},
@@ -277,7 +287,7 @@
277287
"build": "npm run build:webview && npm run vsix",
278288
"build:webview": "cd webview-ui && npm run build",
279289
"changeset": "changeset",
280-
"check-types": "tsc --noEmit",
290+
"check-types": "tsc --noEmit && cd webview-ui && npm run check-types",
281291
"compile": "tsc -p . --outDir out && node esbuild.js",
282292
"compile:integration": "tsc -p tsconfig.integration.json",
283293
"install:all": "npm install && cd webview-ui && npm install",
@@ -308,6 +318,8 @@
308318
"@anthropic-ai/vertex-sdk": "^0.4.1",
309319
"@aws-sdk/client-bedrock-runtime": "^3.706.0",
310320
"@google/generative-ai": "^0.18.0",
321+
"@hookform/resolvers": "^4.0.0",
322+
"@mendable/firecrawl-js": "^1.16.0",
311323
"@mistralai/mistralai": "^1.3.6",
312324
"@modelcontextprotocol/sdk": "^1.0.1",
313325
"@types/clone-deep": "^4.0.4",
@@ -329,15 +341,18 @@
329341
"get-folder-size": "^5.0.0",
330342
"globby": "^14.0.2",
331343
"isbinaryfile": "^5.0.2",
344+
"js-tiktoken": "^1.0.18",
332345
"mammoth": "^1.8.0",
333346
"monaco-vscode-textmate-theme-converter": "^0.1.7",
334347
"openai": "^4.78.1",
335348
"os-name": "^6.0.0",
349+
"p-limit": "^6.2.0",
336350
"p-wait-for": "^5.0.2",
337351
"pdf-parse": "^1.1.1",
338352
"pretty-bytes": "^6.1.1",
339353
"puppeteer-chromium-resolver": "^23.0.0",
340354
"puppeteer-core": "^23.4.0",
355+
"react-hook-form": "^7.54.2",
341356
"serialize-error": "^11.0.3",
342357
"simple-git": "^3.27.0",
343358
"sound-play": "^1.1.0",
@@ -346,8 +361,9 @@
346361
"tmp": "^0.2.3",
347362
"tree-sitter-wasms": "^0.1.11",
348363
"turndown": "^7.2.0",
364+
"use-callback-ref": "^1.3.3",
349365
"web-tree-sitter": "^0.22.6",
350-
"zod": "^3.23.8"
366+
"zod": "^3.24.2"
351367
},
352368
"devDependencies": {
353369
"@changesets/cli": "^2.27.10",

src/__mocks__/p-limit.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const pLimit = (concurrency) => async (fn) => fn()
2+
3+
module.exports = pLimit

src/activate/registerCommands.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
2424
await provider.postStateToWebview()
2525
await provider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
2626
},
27+
"roo-cline.researchButtonClicked": () => {
28+
provider.postMessageToWebview({ type: "action", action: "researchButtonClicked" })
29+
},
2730
"roo-cline.mcpButtonClicked": () => {
2831
provider.postMessageToWebview({ type: "action", action: "mcpButtonClicked" })
2932
},

src/core/Cline.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2836,7 +2836,7 @@ export class Cline {
28362836
}
28372837

28382838
const abortStream = async (cancelReason: ClineApiReqCancelReason, streamingFailedMessage?: string) => {
2839-
console.log(`[Cline#abortStream] cancelReason = ${cancelReason}`)
2839+
// console.log(`[Cline#abortStream] cancelReason = ${cancelReason}`)
28402840

28412841
if (this.diffViewProvider.isEditing) {
28422842
await this.diffViewProvider.revertChanges() // closes diff view

src/core/webview/ClineProvider.ts

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,18 @@ import { ApiConfiguration, ApiProvider, ModelInfo } from "../../shared/api"
2020
import { findLast } from "../../shared/array"
2121
import { ApiConfigMeta, ExtensionMessage } from "../../shared/ExtensionMessage"
2222
import { HistoryItem } from "../../shared/HistoryItem"
23-
import { checkoutDiffPayloadSchema, checkoutRestorePayloadSchema, WebviewMessage } from "../../shared/WebviewMessage"
23+
import {
24+
checkoutDiffPayloadSchema,
25+
checkoutRestorePayloadSchema,
26+
researchTaskPayloadSchema,
27+
researchInputPayloadSchema,
28+
WebviewMessage,
29+
} from "../../shared/WebviewMessage"
2430
import { Mode, CustomModePrompts, PromptComponent, defaultModeSlug } from "../../shared/modes"
2531
import { SYSTEM_PROMPT } from "../prompts/system"
2632
import { fileExistsAtPath } from "../../utils/fs"
2733
import { Cline } from "../Cline"
34+
import { DeepResearchService } from "../../services/deep-research/DeepResearchService"
2835
import { openMention } from "../mentions"
2936
import { getNonce } from "./getNonce"
3037
import { getUri } from "./getUri"
@@ -60,6 +67,8 @@ type SecretKey =
6067
| "mistralApiKey"
6168
| "unboundApiKey"
6269
| "requestyApiKey"
70+
| "firecrawlApiKey"
71+
6372
type GlobalStateKey =
6473
| "apiProvider"
6574
| "apiModelId"
@@ -148,6 +157,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
148157
private view?: vscode.WebviewView | vscode.WebviewPanel
149158
private isViewLaunched = false
150159
private cline?: Cline
160+
private deepResearchService?: DeepResearchService
151161
private workspaceTracker?: WorkspaceTracker
152162
protected mcpHub?: McpHub // Change from private to protected
153163
private latestAnnouncementId = "jan-21-2025-custom-modes" // update to some unique identifier when we add a new announcement
@@ -1529,6 +1539,82 @@ export class ClineProvider implements vscode.WebviewViewProvider {
15291539
await this.updateGlobalState("mode", defaultModeSlug)
15301540
await this.postStateToWebview()
15311541
}
1542+
break
1543+
case "research.task": {
1544+
const result = researchTaskPayloadSchema.safeParse(message.payload)
1545+
1546+
if (!result.success) {
1547+
console.warn(
1548+
`[ClineProvider#research.task] Invalid payload: ${JSON.stringify(message.payload)}`,
1549+
)
1550+
break
1551+
}
1552+
1553+
if (result.success && !this.deepResearchService) {
1554+
const { session } = result.data
1555+
this.deepResearchService = new DeepResearchService(session, this)
1556+
await this.deepResearchService.input(session.query)
1557+
}
1558+
1559+
break
1560+
}
1561+
case "research.input": {
1562+
const result = researchInputPayloadSchema.safeParse(message.payload)
1563+
1564+
if (!result.success) {
1565+
console.warn(
1566+
`[ClineProvider#research.input] Invalid payload: ${JSON.stringify(message.payload)}`,
1567+
)
1568+
break
1569+
}
1570+
1571+
if (result.success && this.deepResearchService) {
1572+
const { content } = result.data.message
1573+
this.deepResearchService.input(content)
1574+
}
1575+
1576+
break
1577+
}
1578+
case "research.viewReport":
1579+
await this.deepResearchService?.viewReport()
1580+
break
1581+
case "research.createTask":
1582+
await this.deepResearchService?.createTask()
1583+
break
1584+
case "research.getTasks":
1585+
const tasks = await DeepResearchService.getTasks(this.context.globalStorageUri.fsPath)
1586+
1587+
await this.postMessageToWebview({
1588+
type: "research.history",
1589+
text: JSON.stringify(
1590+
tasks
1591+
.sort((a, b) => b.createdAt - a.createdAt)
1592+
.map((task) => ({
1593+
taskId: task.taskId,
1594+
query: task.inquiry.initialQuery,
1595+
createdAt: task.createdAt,
1596+
})),
1597+
),
1598+
})
1599+
1600+
break
1601+
case "research.getTask":
1602+
if (message.text) {
1603+
const task = await DeepResearchService.getTask(
1604+
this.context.globalStorageUri.fsPath,
1605+
message.text,
1606+
)
1607+
await this.postMessageToWebview({ type: "research.task", text: JSON.stringify(task) })
1608+
}
1609+
1610+
break
1611+
case "research.abort":
1612+
await this.deepResearchService?.abort()
1613+
break
1614+
case "research.reset":
1615+
await this.deepResearchService?.abort()
1616+
this.deepResearchService = undefined
1617+
break
15321618
}
15331619
},
15341620
null,
@@ -1676,6 +1762,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
16761762
requestyModelId,
16771763
requestyModelInfo,
16781764
modelTemperature,
1765+
firecrawlApiKey,
16791766
} = apiConfiguration
16801767
await Promise.all([
16811768
this.updateGlobalState("apiProvider", apiProvider),
@@ -1723,6 +1810,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
17231810
this.updateGlobalState("requestyModelId", requestyModelId),
17241811
this.updateGlobalState("requestyModelInfo", requestyModelInfo),
17251812
this.updateGlobalState("modelTemperature", modelTemperature),
1813+
this.storeSecret("firecrawlApiKey", firecrawlApiKey),
17261814
])
17271815
if (this.cline) {
17281816
this.cline.api = buildApiHandler(apiConfiguration)
@@ -2606,6 +2694,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
26062694
requestyModelInfo,
26072695
modelTemperature,
26082696
maxOpenTabsContext,
2697+
firecrawlApiKey,
26092698
] = await Promise.all([
26102699
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
26112700
this.getGlobalState("apiModelId") as Promise<string | undefined>,
@@ -2688,6 +2777,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
26882777
this.getGlobalState("requestyModelInfo") as Promise<ModelInfo | undefined>,
26892778
this.getGlobalState("modelTemperature") as Promise<number | undefined>,
26902779
this.getGlobalState("maxOpenTabsContext") as Promise<number | undefined>,
2780+
this.getSecret("firecrawlApiKey") as Promise<string | undefined>,
26912781
])
26922782

26932783
let apiProvider: ApiProvider
@@ -2751,6 +2841,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
27512841
requestyModelId,
27522842
requestyModelInfo,
27532843
modelTemperature,
2844+
firecrawlApiKey,
27542845
},
27552846
lastShownAnnouncementId,
27562847
customInstructions,
@@ -2908,6 +2999,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
29082999
"mistralApiKey",
29093000
"unboundApiKey",
29103001
"requestyApiKey",
3002+
"firecrawlApiKey",
29113003
]
29123004
for (const key of secretKeys) {
29133005
await this.storeSecret(key, undefined)

0 commit comments

Comments
 (0)