@@ -17,10 +17,12 @@ import { selectImages } from "../../integrations/misc/process-images"
1717import { getTheme } from "../../integrations/theme/getTheme"
1818import WorkspaceTracker from "../../integrations/workspace/WorkspaceTracker"
1919import { ClineAccountService } from "../../services/account/ClineAccountService"
20+ import { discoverChromeInstances } from "../../services/browser/BrowserDiscovery"
21+ import { BrowserSession } from "../../services/browser/BrowserSession"
2022import { McpHub } from "../../services/mcp/McpHub"
23+ import { searchWorkspaceFiles } from "../../services/search/file-search"
2124import { telemetryService } from "../../services/telemetry/TelemetryService"
2225import { ApiProvider , ModelInfo } from "../../shared/api"
23- import { findLast } from "../../shared/array"
2426import { ChatContent } from "../../shared/ChatContent"
2527import { ChatSettings } from "../../shared/ChatSettings"
2628import { ExtensionMessage , ExtensionState , Invoke , Platform } from "../../shared/ExtensionMessage"
@@ -30,9 +32,10 @@ import { TelemetrySetting } from "../../shared/TelemetrySetting"
3032import { ClineCheckpointRestore , WebviewMessage } from "../../shared/WebviewMessage"
3133import { fileExistsAtPath } from "../../utils/fs"
3234import { searchCommits } from "../../utils/git"
35+ import { getWorkspacePath } from "../../utils/path"
3336import { getTotalTasksSize } from "../../utils/storage"
34- import { Task } from "../task"
3537import { openMention } from "../mentions"
38+ import { GlobalFileNames } from "../storage/disk"
3639import {
3740 getAllExtensionState ,
3841 getGlobalState ,
@@ -42,12 +45,7 @@ import {
4245 updateApiConfiguration ,
4346 updateGlobalState ,
4447} from "../storage/state"
45- import { WebviewProvider } from "../webview"
46- import { BrowserSession } from "../../services/browser/BrowserSession"
47- import { GlobalFileNames } from "../storage/disk"
48- import { discoverChromeInstances } from "../../services/browser/BrowserDiscovery"
49- import { searchWorkspaceFiles } from "../../services/search/file-search"
50- import { getWorkspacePath } from "../../utils/path"
48+ import { Task } from "../task"
5149
5250/*
5351https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
@@ -56,25 +54,37 @@ https://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/c
5654*/
5755
5856export class Controller {
57+ private postMessage : ( message : any ) => Thenable < boolean > | undefined
58+
5959 private disposables : vscode . Disposable [ ] = [ ]
6060 private task ?: Task
61- workspaceTracker ? : WorkspaceTracker
62- mcpHub ? : McpHub
63- accountService ? : ClineAccountService
61+ workspaceTracker : WorkspaceTracker
62+ mcpHub : McpHub
63+ accountService : ClineAccountService
6464 private latestAnnouncementId = "april-10-2025" // update to some unique identifier when we add a new announcement
65- private webviewProviderRef : WeakRef < WebviewProvider >
6665
6766 constructor (
6867 readonly context : vscode . ExtensionContext ,
6968 private readonly outputChannel : vscode . OutputChannel ,
70- webviewProvider : WebviewProvider ,
69+ postMessage : ( message : any ) => Thenable < boolean > | undefined ,
7170 ) {
7271 this . outputChannel . appendLine ( "ClineProvider instantiated" )
73- this . webviewProviderRef = new WeakRef ( webviewProvider )
74-
75- this . workspaceTracker = new WorkspaceTracker ( this )
76- this . mcpHub = new McpHub ( this )
77- this . accountService = new ClineAccountService ( this )
72+ this . postMessage = postMessage
73+
74+ this . workspaceTracker = new WorkspaceTracker ( ( msg ) => this . postMessageToWebview ( msg ) )
75+ this . mcpHub = new McpHub (
76+ ( ) => this . ensureMcpServersDirectoryExists ( ) ,
77+ ( ) => this . ensureSettingsDirectoryExists ( ) ,
78+ ( msg ) => this . postMessageToWebview ( msg ) ,
79+ this . context . extension ?. packageJSON ?. version ?? "1.0.0" ,
80+ )
81+ this . accountService = new ClineAccountService (
82+ ( msg ) => this . postMessageToWebview ( msg ) ,
83+ async ( ) => {
84+ const { apiConfiguration } = await this . getStateToPostToWebview ( )
85+ return apiConfiguration ?. clineApiKey
86+ } ,
87+ )
7888
7989 // Clean up legacy checkpoints
8090 cleanupLegacyCheckpoints ( this . context . globalStorageUri . fsPath , this . outputChannel ) . catch ( ( error ) => {
@@ -97,11 +107,8 @@ export class Controller {
97107 x . dispose ( )
98108 }
99109 }
100- this . workspaceTracker ?. dispose ( )
101- this . workspaceTracker = undefined
102- this . mcpHub ?. dispose ( )
103- this . mcpHub = undefined
104- this . accountService = undefined
110+ this . workspaceTracker . dispose ( )
111+ this . mcpHub . dispose ( )
105112 this . outputChannel . appendLine ( "Disposed all disposables" )
106113
107114 console . error ( "Controller disposed" )
@@ -124,42 +131,40 @@ export class Controller {
124131 await updateGlobalState ( this . context , "userInfo" , info )
125132 }
126133
127- async initClineWithTask ( task ?: string , images ?: string [ ] ) {
134+ async initTask ( task ?: string , images ?: string [ ] , historyItem ?: HistoryItem ) {
128135 await this . clearTask ( ) // ensures that an existing task doesn't exist before starting a new one, although this shouldn't be possible since user must clear task before starting a new one
129136 const { apiConfiguration, customInstructions, autoApprovalSettings, browserSettings, chatSettings } =
130137 await getAllExtensionState ( this . context )
131138 this . task = new Task (
132- this ,
139+ this . context ,
140+ this . mcpHub ,
141+ this . workspaceTracker ,
142+ ( historyItem ) => this . updateTaskHistory ( historyItem ) ,
143+ ( ) => this . postStateToWebview ( ) ,
144+ ( message ) => this . postMessageToWebview ( message ) ,
145+ ( taskId ) => this . reinitExistingTaskFromId ( taskId ) ,
146+ ( ) => this . cancelTask ( ) ,
133147 apiConfiguration ,
134148 autoApprovalSettings ,
135149 browserSettings ,
136150 chatSettings ,
137151 customInstructions ,
138152 task ,
139153 images ,
154+ historyItem ,
140155 )
141156 }
142157
143- async initClineWithHistoryItem ( historyItem : HistoryItem ) {
144- await this . clearTask ( )
145- const { apiConfiguration, customInstructions, autoApprovalSettings, browserSettings, chatSettings } =
146- await getAllExtensionState ( this . context )
147- this . task = new Task (
148- this ,
149- apiConfiguration ,
150- autoApprovalSettings ,
151- browserSettings ,
152- chatSettings ,
153- customInstructions ,
154- undefined ,
155- undefined ,
156- historyItem ,
157- )
158+ async reinitExistingTaskFromId ( taskId : string ) {
159+ const history = await this . getTaskWithId ( taskId )
160+ if ( history ) {
161+ await this . initTask ( undefined , undefined , history . historyItem )
162+ }
158163 }
159164
160165 // Send any JSON serializable data to the react app
161166 async postMessageToWebview ( message : ExtensionMessage ) {
162- await this . webviewProviderRef . deref ( ) ?. view ?. webview . postMessage ( message )
167+ await this . postMessage ( message )
163168 }
164169
165170 /**
@@ -259,7 +264,7 @@ export class Controller {
259264 // Could also do this in extension .ts
260265 //this.postMessageToWebview({ type: "text", text: `Extension: ${Date.now()}` })
261266 // initializing new instance of Cline will make sure that any agentically running promises in old instance don't affect our new task. this essentially creates a fresh slate for the new task
262- await this . initClineWithTask ( message . text , message . images )
267+ await this . initTask ( message . text , message . images )
263268 break
264269 case "apiConfiguration" :
265270 if ( message . apiConfiguration ) {
@@ -1083,7 +1088,7 @@ export class Controller {
10831088 // 'abandoned' will prevent this cline instance from affecting future cline instance gui. this may happen if its hanging on a streaming request
10841089 this . task . abandoned = true
10851090 }
1086- await this . initClineWithHistoryItem ( historyItem ) // clears task again, so we need to abortTask manually above
1091+ await this . initTask ( undefined , undefined , historyItem ) // clears task again, so we need to abortTask manually above
10871092 // await this.postStateToWebview() // new Cline instance will post state when it's ready. having this here sent an empty messages array to webview leading to virtuoso having to reload the entire list
10881093 }
10891094 }
@@ -1401,7 +1406,7 @@ export class Controller {
14011406Here is the project's README to help you get started:\n\n${ mcpDetails . readmeContent } \n${ mcpDetails . llmsInstallationContent } `
14021407
14031408 // Initialize task and show chat view
1404- await this . initClineWithTask ( task )
1409+ await this . initTask ( task )
14051410 await this . postMessageToWebview ( {
14061411 type : "action" ,
14071412 action : "chatButtonClicked" ,
@@ -1735,9 +1740,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
17351740
17361741 const fileMention = this . getFileMentionFromPath ( filePath )
17371742 const problemsString = this . convertDiagnosticsToProblemsString ( diagnostics )
1738- await this . initClineWithTask (
1739- `Fix the following code in ${ fileMention } \n\`\`\`\n${ code } \n\`\`\`\n\nProblems:\n${ problemsString } ` ,
1740- )
1743+ await this . initTask ( `Fix the following code in ${ fileMention } \n\`\`\`\n${ code } \n\`\`\`\n\nProblems:\n${ problemsString } ` )
17411744
17421745 console . log ( "fixWithCline" , code , filePath , languageId , diagnostics , problemsString )
17431746 }
@@ -1813,7 +1816,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
18131816 if ( id !== this . task ?. taskId ) {
18141817 // non-current task
18151818 const { historyItem } = await this . getTaskWithId ( id )
1816- await this . initClineWithHistoryItem ( historyItem ) // clears existing task
1819+ await this . initTask ( undefined , undefined , historyItem ) // clears existing task
18171820 }
18181821 await this . postMessageToWebview ( {
18191822 type : "action" ,
0 commit comments