Skip to content

Commit 70ad2c5

Browse files
committed
Deep research
1 parent 91ca35d commit 70ad2c5

Some content is hidden

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

41 files changed

+3334
-92
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: 23 additions & 7 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
},
@@ -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/diff/strategies/new-unified/__tests__/edit-strategies.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/// <reference types="jest" />
2+
13
import { applyContextMatching, applyDMP, applyGitFallback } from "../edit-strategies"
24
import { Hunk } from "../types"
35

@@ -275,7 +277,7 @@ describe("applyGitFallback", () => {
275277
expect(result.result.join("\n")).toEqual("line1\nnew line2\nline3")
276278
expect(result.confidence).toBe(1)
277279
expect(result.strategy).toBe("git-fallback")
278-
})
280+
}, 10_000)
279281

280282
it("should return original content with 0 confidence when changes cannot be applied", async () => {
281283
const hunk = {
@@ -291,5 +293,5 @@ describe("applyGitFallback", () => {
291293
expect(result.result).toEqual(content)
292294
expect(result.confidence).toBe(0)
293295
expect(result.strategy).toBe("git-fallback")
294-
})
296+
}, 10_000)
295297
})

src/core/webview/ClineProvider.ts

Lines changed: 118 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
@@ -1526,6 +1536,107 @@ export class ClineProvider implements vscode.WebviewViewProvider {
15261536
await this.updateGlobalState("mode", defaultModeSlug)
15271537
await this.postStateToWebview()
15281538
}
1539+
break
1540+
case "research.task": {
1541+
const result = researchTaskPayloadSchema.safeParse(message.payload)
1542+
1543+
if (!result.success) {
1544+
console.warn(
1545+
`[ClineProvider#research.task] Invalid payload: ${JSON.stringify(message.payload)}`,
1546+
)
1547+
break
1548+
}
1549+
1550+
if (result.success && !this.deepResearchService) {
1551+
const { session } = result.data
1552+
this.deepResearchService = new DeepResearchService(session, this)
1553+
await this.deepResearchService.input(session.query)
1554+
}
1555+
1556+
break
1557+
}
1558+
case "research.input": {
1559+
const result = researchInputPayloadSchema.safeParse(message.payload)
1560+
1561+
if (!result.success) {
1562+
console.warn(
1563+
`[ClineProvider#research.input] Invalid payload: ${JSON.stringify(message.payload)}`,
1564+
)
1565+
break
1566+
}
1567+
1568+
if (result.success && this.deepResearchService) {
1569+
const { content } = result.data.message
1570+
this.deepResearchService.input(content)
1571+
}
1572+
1573+
break
1574+
}
1575+
case "research.viewReport":
1576+
await this.deepResearchService?.viewReport()
1577+
break
1578+
case "research.createTask":
1579+
await this.deepResearchService?.createTask()
1580+
break
1581+
case "research.getTasks":
1582+
const tasks = await DeepResearchService.getTasks(this.context.globalStorageUri.fsPath)
1583+
1584+
await this.postMessageToWebview({
1585+
type: "research.history",
1586+
text: JSON.stringify(
1587+
tasks
1588+
.sort((a, b) => b.createdAt - a.createdAt)
1589+
.map((task) => ({
1590+
taskId: task.taskId,
1591+
query: task.inquiry.initialQuery,
1592+
createdAt: task.createdAt,
1593+
})),
1594+
),
1595+
})
1596+
1597+
break
1598+
case "research.getTask":
1599+
if (message.text) {
1600+
const task = await DeepResearchService.getTask(
1601+
this.context.globalStorageUri.fsPath,
1602+
message.text,
1603+
)
1604+
1605+
await this.postMessageToWebview({ type: "research.task", text: JSON.stringify(task) })
1606+
}
1607+
1608+
break
1609+
case "research.deleteTask":
1610+
if (message.text) {
1611+
try {
1612+
await DeepResearchService.deleteTask(this.context.globalStorageUri.fsPath, message.text)
1613+
const tasks = await DeepResearchService.getTasks(this.context.globalStorageUri.fsPath)
1614+
1615+
await this.postMessageToWebview({
1616+
type: "research.history",
1617+
text: JSON.stringify(
1618+
tasks
1619+
.sort((a, b) => b.createdAt - a.createdAt)
1620+
.map((task) => ({
1621+
taskId: task.taskId,
1622+
query: task.inquiry.initialQuery,
1623+
createdAt: task.createdAt,
1624+
})),
1625+
),
1626+
})
1627+
} catch (error) {
1628+
vscode.window.showErrorMessage("Failed to delete research task.")
1629+
}
1630+
}
1631+
1632+
break
1633+
case "research.abort":
1634+
await this.deepResearchService?.abort()
1635+
break
1636+
case "research.reset":
1637+
await this.deepResearchService?.abort()
1638+
this.deepResearchService = undefined
1639+
break
15291640
}
15301641
},
15311642
null,
@@ -1673,6 +1784,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
16731784
requestyModelId,
16741785
requestyModelInfo,
16751786
modelTemperature,
1787+
firecrawlApiKey,
16761788
} = apiConfiguration
16771789
await Promise.all([
16781790
this.updateGlobalState("apiProvider", apiProvider),
@@ -1720,6 +1832,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
17201832
this.updateGlobalState("requestyModelId", requestyModelId),
17211833
this.updateGlobalState("requestyModelInfo", requestyModelInfo),
17221834
this.updateGlobalState("modelTemperature", modelTemperature),
1835+
this.storeSecret("firecrawlApiKey", firecrawlApiKey),
17231836
])
17241837
if (this.cline) {
17251838
this.cline.api = buildApiHandler(apiConfiguration)
@@ -2603,6 +2716,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
26032716
requestyModelInfo,
26042717
modelTemperature,
26052718
maxOpenTabsContext,
2719+
firecrawlApiKey,
26062720
] = await Promise.all([
26072721
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
26082722
this.getGlobalState("apiModelId") as Promise<string | undefined>,
@@ -2685,6 +2799,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
26852799
this.getGlobalState("requestyModelInfo") as Promise<ModelInfo | undefined>,
26862800
this.getGlobalState("modelTemperature") as Promise<number | undefined>,
26872801
this.getGlobalState("maxOpenTabsContext") as Promise<number | undefined>,
2802+
this.getSecret("firecrawlApiKey") as Promise<string | undefined>,
26882803
])
26892804

26902805
let apiProvider: ApiProvider
@@ -2748,6 +2863,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
27482863
requestyModelId,
27492864
requestyModelInfo,
27502865
modelTemperature,
2866+
firecrawlApiKey,
27512867
},
27522868
lastShownAnnouncementId,
27532869
customInstructions,
@@ -2905,6 +3021,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
29053021
"mistralApiKey",
29063022
"unboundApiKey",
29073023
"requestyApiKey",
3024+
"firecrawlApiKey",
29083025
]
29093026
for (const key of secretKeys) {
29103027
await this.storeSecret(key, undefined)

0 commit comments

Comments
 (0)