Compare commits

...

3 Commits

Author SHA1 Message Date
Cline Evaluation 04014541a8 Remove TaskTimeLine altogether 2025-05-18 03:00:21 +04:00
Cline Evaluation f466034c64 Remove TaskTimeLine altogether 2025-05-18 02:58:38 +04:00
Cline Evaluation 1272fce191 fix: Address memory leak by bypassing subscribeToState gRPC stream for state updates 2025-05-18 02:58:16 +04:00
5 changed files with 109 additions and 36 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
fix: Address memory leak by bypassing subscribeToState gRPC stream for state updates
+4 -1
View File
@@ -1345,7 +1345,10 @@ export class Controller {
async postStateToWebview() {
const state = await this.getStateToPostToWebview()
await sendStateUpdate(state)
// For testing: Bypass gRPC stream and send state directly
console.log("[Controller Test Revert] Posting full state via direct 'state' message.")
await this.postMessageToWebview({ type: "state", state: state })
// await sendStateUpdate(state) // Original line for the GrPC stream
}
async getStateToPostToWebview(): Promise<ExtensionState> {
+9 -9
View File
@@ -915,15 +915,6 @@ export const FileServiceDefinition = {
responseStream: false,
options: {},
},
/** Select images from the file system and return as data URLs */
selectImages: {
name: "selectImages",
requestType: EmptyRequest,
requestStream: false,
responseType: StringArray,
responseStream: false,
options: {},
},
/** Opens an image in the system viewer */
openImage: {
name: "openImage",
@@ -960,6 +951,15 @@ export const FileServiceDefinition = {
responseStream: false,
options: {},
},
/** Select images from the file system and return as data URLs */
selectImages: {
name: "selectImages",
requestType: EmptyRequest,
requestStream: false,
responseType: StringArray,
responseStream: false,
options: {},
},
/** Convert URIs to workspace-relative paths */
getRelativePaths: {
name: "getRelativePaths",
+2 -2
View File
@@ -20,11 +20,11 @@ import { checkpointRestore } from "../core/controller/checkpoints/checkpointRest
// File Service
import { openFile } from "../core/controller/file/openFile"
import { selectImages } from "../core/controller/file/selectImages"
import { openImage } from "../core/controller/file/openImage"
import { deleteRuleFile } from "../core/controller/file/deleteRuleFile"
import { createRuleFile } from "../core/controller/file/createRuleFile"
import { searchCommits } from "../core/controller/file/searchCommits"
import { selectImages } from "../core/controller/file/selectImages"
import { getRelativePaths } from "../core/controller/file/getRelativePaths"
import { searchFiles } from "../core/controller/file/searchFiles"
@@ -96,11 +96,11 @@ export function addServices(
// File Service
server.addService(proto.cline.FileService.service, {
openFile: wrapper(openFile, controller),
selectImages: wrapper(selectImages, controller),
openImage: wrapper(openImage, controller),
deleteRuleFile: wrapper(deleteRuleFile, controller),
createRuleFile: wrapper(createRuleFile, controller),
searchCommits: wrapper(searchCommits, controller),
selectImages: wrapper(selectImages, controller),
getRelativePaths: wrapper(getRelativePaths, controller),
searchFiles: wrapper(searchFiles, controller),
})
@@ -100,6 +100,59 @@ export const ExtensionStateContextProvider: React.FC<{
const handleMessage = useCallback((event: MessageEvent) => {
const message: ExtensionMessage = event.data
switch (message.type) {
case "state": {
// Handler for direct state messages
if (message.state) {
const stateData = message.state as ExtensionState
console.log("[Webview Context Test Revert] Received direct 'state' message, updating state.")
setState((prevState) => {
// Versioning logic for autoApprovalSettings (copied from original onResponse)
const incomingVersion = stateData.autoApprovalSettings?.version ?? 1
const currentVersion = prevState.autoApprovalSettings?.version ?? 1
const shouldUpdateAutoApproval = incomingVersion > currentVersion
const newState = {
...stateData,
autoApprovalSettings: shouldUpdateAutoApproval
? stateData.autoApprovalSettings
: prevState.autoApprovalSettings,
}
// Update welcome screen state based on API configuration (copied from original onResponse)
const config = stateData.apiConfiguration
const hasKey = config
? [
config.apiKey,
config.openRouterApiKey,
config.awsRegion,
config.vertexProjectId,
config.openAiApiKey,
config.ollamaModelId,
config.lmStudioModelId,
config.liteLlmApiKey,
config.geminiApiKey,
config.openAiNativeApiKey,
config.deepSeekApiKey,
config.requestyApiKey,
config.togetherApiKey,
config.qwenApiKey,
config.doubaoApiKey,
config.mistralApiKey,
config.vsCodeLmModelSelector,
config.clineApiKey,
config.asksageApiKey,
config.xaiApiKey,
config.sambanovaApiKey,
].some((key) => key !== undefined)
: false
setShowWelcome(!hasKey)
setDidHydrateState(true)
return newState
})
}
break
}
case "theme": {
if (message.text) {
setTheme(convertTextMateToHljs(JSON.parse(message.text)))
@@ -169,32 +222,33 @@ export const ExtensionStateContextProvider: React.FC<{
const stateSubscriptionRef = useRef<(() => void) | null>(null)
// Subscribe to state updates using the new gRPC streaming API
/* // TEST REVERT: Commenting out gRPC state subscription
useEffect(() => {
// Set up state subscription
stateSubscriptionRef.current = StateServiceClient.subscribeToState(
{},
{
onResponse: (response) => {
console.log("[DEBUG] got state update via subscription", response)
console.log("[DEBUG] got state update via subscription", response);
if (response.stateJson) {
try {
const stateData = JSON.parse(response.stateJson) as ExtensionState
console.log("[DEBUG] parsed state JSON, updating state")
const stateData = JSON.parse(response.stateJson) as ExtensionState;
console.log("[DEBUG] parsed state JSON, updating state");
setState((prevState) => {
// Versioning logic for autoApprovalSettings
const incomingVersion = stateData.autoApprovalSettings?.version ?? 1
const currentVersion = prevState.autoApprovalSettings?.version ?? 1
const shouldUpdateAutoApproval = incomingVersion > currentVersion
const incomingVersion = stateData.autoApprovalSettings?.version ?? 1;
const currentVersion = prevState.autoApprovalSettings?.version ?? 1;
const shouldUpdateAutoApproval = incomingVersion > currentVersion;
const newState = {
...stateData,
autoApprovalSettings: shouldUpdateAutoApproval
? stateData.autoApprovalSettings
: prevState.autoApprovalSettings,
}
};
// Update welcome screen state based on API configuration
const config = stateData.apiConfiguration
const config = stateData.apiConfiguration;
const hasKey = config
? [
config.apiKey,
@@ -219,41 +273,52 @@ export const ExtensionStateContextProvider: React.FC<{
config.xaiApiKey,
config.sambanovaApiKey,
].some((key) => key !== undefined)
: false
: false;
setShowWelcome(!hasKey)
setDidHydrateState(true)
setShowWelcome(!hasKey);
setDidHydrateState(true);
console.log("[DEBUG] returning new state in ESC")
console.log("[DEBUG] returning new state in ESC");
return newState
})
return newState;
});
} catch (error) {
console.error("Error parsing state JSON:", error)
console.log("[DEBUG] ERR getting state", error)
console.error("Error parsing state JSON:", error);
console.log("[DEBUG] ERR getting state", error);
}
}
console.log('[DEBUG] ended "got subscribed state"')
console.log('[DEBUG] ended "got subscribed state"');
},
onError: (error) => {
console.error("Error in state subscription:", error)
console.error("Error in state subscription:", error);
},
onComplete: () => {
console.log("State subscription completed")
console.log("State subscription completed");
},
},
)
);
// Still send the webviewDidLaunch message for other initialization
vscode.postMessage({ type: "webviewDidLaunch" })
vscode.postMessage({ type: "webviewDidLaunch" });
// Clean up subscription when component unmounts
return () => {
if (stateSubscriptionRef.current) {
stateSubscriptionRef.current()
stateSubscriptionRef.current = null
stateSubscriptionRef.current();
stateSubscriptionRef.current = null;
}
}
};
}, []);
*/ // END TEST REVERT
// For the test revert, ensure webviewDidLaunch is still sent if not done by the above useEffect
useEffect(() => {
// This effect now only sends webviewDidLaunch if the gRPC subscription is commented out.
// If the gRPC subscription is active, it sends webviewDidLaunch.
// To avoid sending it twice if you uncomment the above, you might add a flag.
// For this specific test (gRPC sub commented out), this is fine.
console.log("[Webview Context Test Revert] Sending webviewDidLaunch from separate useEffect.")
vscode.postMessage({ type: "webviewDidLaunch" })
}, [])
const contextValue: ExtensionStateContextType = {