mirror of
https://github.com/cline/cline.git
synced 2025-06-03 03:59:07 +00:00
Refactor reasoning effort option and checkpoint handling (#3454)
• Replace "o3MiniReasoningEffort" with "reasoningEffort" in API providers • Remove deprecated configuration properties from package.json • Guard checkpoint tracker initialization and saving using the enableCheckpoints flag Co-authored-by: Cline Evaluation <cline@example.com>
This commit is contained in:
parent
d2979631d8
commit
cd1ff2ad25
5
.changeset/nine-spiders-kneel.md
Normal file
5
.changeset/nine-spiders-kneel.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Finishing the migration of Vscode Advanced settings to Settings Webview
|
38
package.json
38
package.json
@ -236,43 +236,7 @@
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Cline",
|
||||
"properties": {
|
||||
"cline.enableCheckpoints": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Enables extension to save checkpoints of workspace throughout the task. Uses git under the hood which may not work well with large workspaces."
|
||||
},
|
||||
"cline.preferredLanguage": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"English",
|
||||
"Arabic - العربية",
|
||||
"Portuguese - Português (Brasil)",
|
||||
"Czech - Čeština",
|
||||
"French - Français",
|
||||
"German - Deutsch",
|
||||
"Hindi - हिन्दी",
|
||||
"Hungarian - Magyar",
|
||||
"Italian - Italiano",
|
||||
"Japanese - 日本語",
|
||||
"Korean - 한국어",
|
||||
"Polish - Polski",
|
||||
"Portuguese - Português (Portugal)",
|
||||
"Russian - Русский",
|
||||
"Simplified Chinese - 简体中文",
|
||||
"Spanish - Español",
|
||||
"Traditional Chinese - 繁體中文",
|
||||
"Turkish - Türkçe"
|
||||
],
|
||||
"default": "English",
|
||||
"description": "The language that Cline should use for communication."
|
||||
},
|
||||
"cline.mcpMarketplace.enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Controls whether the MCP Marketplace is enabled."
|
||||
}
|
||||
}
|
||||
"properties": {}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -33,7 +33,7 @@ export class ClineHandler implements ApiHandler {
|
||||
systemPrompt,
|
||||
messages,
|
||||
this.getModel(),
|
||||
this.options.o3MiniReasoningEffort,
|
||||
this.options.reasoningEffort,
|
||||
this.options.thinkingBudgetTokens,
|
||||
this.options.openRouterProviderSorting,
|
||||
)
|
||||
|
@ -66,8 +66,9 @@ export class OpenAiNativeHandler implements ApiHandler {
|
||||
messages: [{ role: "developer", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
|
||||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
reasoning_effort: (this.options.o3MiniReasoningEffort as ChatCompletionReasoningEffort) || "medium",
|
||||
reasoning_effort: (this.options.reasoningEffort as ChatCompletionReasoningEffort) || "medium",
|
||||
})
|
||||
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
if (delta?.content) {
|
||||
|
@ -65,7 +65,7 @@ export class OpenAiHandler implements ApiHandler {
|
||||
if (isReasoningModelFamily) {
|
||||
openAiMessages = [{ role: "developer", content: systemPrompt }, ...convertToOpenAiMessages(messages)]
|
||||
temperature = undefined // does not support temperature
|
||||
reasoningEffort = (this.options.o3MiniReasoningEffort as ChatCompletionReasoningEffort) || "medium"
|
||||
reasoningEffort = (this.options.reasoningEffort as ChatCompletionReasoningEffort) || "medium"
|
||||
}
|
||||
|
||||
const stream = await this.client.chat.completions.create({
|
||||
|
@ -35,7 +35,7 @@ export class OpenRouterHandler implements ApiHandler {
|
||||
systemPrompt,
|
||||
messages,
|
||||
this.getModel(),
|
||||
this.options.o3MiniReasoningEffort,
|
||||
this.options.reasoningEffort,
|
||||
this.options.thinkingBudgetTokens,
|
||||
this.options.openRouterProviderSorting,
|
||||
)
|
||||
|
@ -32,7 +32,7 @@ export class RequestyHandler implements ApiHandler {
|
||||
...convertToOpenAiMessages(messages),
|
||||
]
|
||||
|
||||
const reasoningEffort = this.options.o3MiniReasoningEffort || "medium"
|
||||
const reasoningEffort = this.options.reasoningEffort || "medium"
|
||||
const reasoning = { reasoning_effort: reasoningEffort }
|
||||
const reasoningArgs = model.id.startsWith("openai/o") ? reasoning : {}
|
||||
|
||||
|
@ -9,7 +9,7 @@ export async function createOpenRouterStream(
|
||||
systemPrompt: string,
|
||||
messages: Anthropic.Messages.MessageParam[],
|
||||
model: { id: string; info: ModelInfo },
|
||||
o3MiniReasoningEffort?: string,
|
||||
reasoningEffort?: string,
|
||||
thinkingBudgetTokens?: number,
|
||||
openRouterProviderSorting?: string,
|
||||
) {
|
||||
@ -144,7 +144,7 @@ export async function createOpenRouterStream(
|
||||
stream_options: { include_usage: true },
|
||||
transforms: shouldApplyMiddleOutTransform ? ["middle-out"] : undefined,
|
||||
include_reasoning: true,
|
||||
...(model.id.startsWith("openai/o") ? { reasoning_effort: o3MiniReasoningEffort || "medium" } : {}),
|
||||
...(model.id.startsWith("openai/o") ? { reasoning_effort: reasoningEffort || "medium" } : {}),
|
||||
...(reasoning ? { reasoning } : {}),
|
||||
...(openRouterProviderSorting ? { provider: { sort: openRouterProviderSorting } } : {}),
|
||||
})
|
||||
|
@ -145,6 +145,7 @@ export class Controller {
|
||||
browserSettings,
|
||||
chatSettings,
|
||||
shellIntegrationTimeout,
|
||||
enableCheckpointsSetting,
|
||||
} = await getAllExtensionState(this.context)
|
||||
|
||||
if (autoApprovalSettings) {
|
||||
@ -168,6 +169,7 @@ export class Controller {
|
||||
browserSettings,
|
||||
chatSettings,
|
||||
shellIntegrationTimeout,
|
||||
enableCheckpointsSetting ?? true,
|
||||
customInstructions,
|
||||
task,
|
||||
images,
|
||||
@ -563,6 +565,22 @@ export class Controller {
|
||||
// plan act setting
|
||||
await updateGlobalState(this.context, "planActSeparateModelsSetting", message.planActSeparateModelsSetting)
|
||||
|
||||
if (typeof message.enableCheckpointsSetting === "boolean") {
|
||||
await updateGlobalState(this.context, "enableCheckpointsSetting", message.enableCheckpointsSetting)
|
||||
}
|
||||
|
||||
if (typeof message.mcpMarketplaceEnabled === "boolean") {
|
||||
await updateGlobalState(this.context, "mcpMarketplaceEnabled", message.mcpMarketplaceEnabled)
|
||||
}
|
||||
|
||||
// chat settings (including preferredLanguage and openAIReasoningEffort)
|
||||
if (message.chatSettings) {
|
||||
await updateGlobalState(this.context, "chatSettings", message.chatSettings)
|
||||
if (this.task) {
|
||||
this.task.chatSettings = message.chatSettings
|
||||
}
|
||||
}
|
||||
|
||||
// after settings are updated, post state to webview
|
||||
await this.postStateToWebview()
|
||||
|
||||
@ -1332,6 +1350,7 @@ export class Controller {
|
||||
mcpMarketplaceEnabled,
|
||||
telemetrySetting,
|
||||
planActSeparateModelsSetting,
|
||||
enableCheckpointsSetting,
|
||||
globalClineRulesToggles,
|
||||
shellIntegrationTimeout,
|
||||
} = await getAllExtensionState(this.context)
|
||||
@ -1366,6 +1385,7 @@ export class Controller {
|
||||
mcpMarketplaceEnabled,
|
||||
telemetrySetting,
|
||||
planActSeparateModelsSetting,
|
||||
enableCheckpointsSetting: enableCheckpointsSetting ?? true,
|
||||
vscMachineId: vscode.env.machineId,
|
||||
globalClineRulesToggles: globalClineRulesToggles || {},
|
||||
localClineRulesToggles: localClineRulesToggles || {},
|
||||
|
@ -83,6 +83,8 @@ export type GlobalStateKey =
|
||||
| "thinkingBudgetTokens"
|
||||
| "reasoningEffort"
|
||||
| "planActSeparateModelsSetting"
|
||||
| "enableCheckpointsSetting"
|
||||
| "mcpMarketplaceEnabled"
|
||||
| "favoritedModelIds"
|
||||
| "requestTimeoutMs"
|
||||
| "shellIntegrationTimeout"
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as vscode from "vscode"
|
||||
import { DEFAULT_CHAT_SETTINGS } from "@shared/ChatSettings"
|
||||
import { DEFAULT_CHAT_SETTINGS, OpenAIReasoningEffort } from "@shared/ChatSettings"
|
||||
import { DEFAULT_BROWSER_SETTINGS } from "@shared/BrowserSettings"
|
||||
import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "@shared/AutoApprovalSettings"
|
||||
import { GlobalStateKey, SecretKey } from "./state-keys"
|
||||
@ -51,6 +51,29 @@ export async function getWorkspaceState(context: vscode.ExtensionContext, key: s
|
||||
return await context.workspaceState.get(key)
|
||||
}
|
||||
|
||||
async function migrateMcpMarketplaceEnableSetting(mcpMarketplaceEnabledRaw: boolean | undefined): Promise<boolean> {
|
||||
const config = vscode.workspace.getConfiguration("cline")
|
||||
const mcpMarketplaceEnabled = config.get<boolean>("mcpMarketplace.enabled")
|
||||
if (mcpMarketplaceEnabled !== undefined) {
|
||||
// Remove from VSCode configuration
|
||||
await config.update("mcpMarketplace.enabled", undefined, true)
|
||||
|
||||
return !mcpMarketplaceEnabled
|
||||
}
|
||||
return mcpMarketplaceEnabledRaw ?? true
|
||||
}
|
||||
|
||||
async function migrateEnableCheckpointsSetting(enableCheckpointsSettingRaw: boolean | undefined): Promise<boolean> {
|
||||
const config = vscode.workspace.getConfiguration("cline")
|
||||
const enableCheckpoints = config.get<boolean>("enableCheckpoints")
|
||||
if (enableCheckpoints !== undefined) {
|
||||
// Remove from VSCode configuration
|
||||
await config.update("enableCheckpoints", undefined, true)
|
||||
return enableCheckpoints
|
||||
}
|
||||
return enableCheckpointsSettingRaw ?? true
|
||||
}
|
||||
|
||||
export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
const [
|
||||
storedApiProvider,
|
||||
@ -136,6 +159,8 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
globalClineRulesToggles,
|
||||
requestTimeoutMs,
|
||||
shellIntegrationTimeout,
|
||||
enableCheckpointsSettingRaw,
|
||||
mcpMarketplaceEnabledRaw,
|
||||
] = await Promise.all([
|
||||
getGlobalState(context, "apiProvider") as Promise<ApiProvider | undefined>,
|
||||
getGlobalState(context, "apiModelId") as Promise<string | undefined>,
|
||||
@ -220,6 +245,9 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
getGlobalState(context, "globalClineRulesToggles") as Promise<ClineRulesToggles | undefined>,
|
||||
getGlobalState(context, "requestTimeoutMs") as Promise<number | undefined>,
|
||||
getGlobalState(context, "shellIntegrationTimeout") as Promise<number | undefined>,
|
||||
getGlobalState(context, "enableCheckpointsSetting") as Promise<boolean | undefined>,
|
||||
getGlobalState(context, "mcpMarketplaceEnabled") as Promise<boolean | undefined>,
|
||||
fetch,
|
||||
])
|
||||
|
||||
let apiProvider: ApiProvider
|
||||
@ -238,9 +266,8 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
|
||||
const localClineRulesToggles = (await getWorkspaceState(context, "localClineRulesToggles")) as ClineRulesToggles
|
||||
|
||||
const o3MiniReasoningEffort = vscode.workspace.getConfiguration("cline.modelSettings.o3Mini").get("reasoningEffort", "medium")
|
||||
|
||||
const mcpMarketplaceEnabled = vscode.workspace.getConfiguration("cline").get<boolean>("mcpMarketplace.enabled", true)
|
||||
const mcpMarketplaceEnabled = await migrateMcpMarketplaceEnableSetting(mcpMarketplaceEnabledRaw)
|
||||
const enableCheckpointsSetting = await migrateEnableCheckpointsSetting(enableCheckpointsSettingRaw)
|
||||
|
||||
// Plan/Act separate models setting is a boolean indicating whether the user wants to use different models for plan and act. Existing users expect this to be enabled, while we want new users to opt in to this being disabled by default.
|
||||
// On win11 state sometimes initializes as empty string instead of undefined
|
||||
@ -309,7 +336,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
openRouterModelInfo,
|
||||
openRouterProviderSorting,
|
||||
vsCodeLmModelSelector,
|
||||
o3MiniReasoningEffort,
|
||||
thinkingBudgetTokens,
|
||||
reasoningEffort,
|
||||
liteLlmBaseUrl,
|
||||
@ -335,7 +361,10 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
globalClineRulesToggles: globalClineRulesToggles || {},
|
||||
localClineRulesToggles: localClineRulesToggles || {},
|
||||
browserSettings: { ...DEFAULT_BROWSER_SETTINGS, ...browserSettings }, // this will ensure that older versions of browserSettings (e.g. before remoteBrowserEnabled was added) are merged with the default values (false for remoteBrowserEnabled)
|
||||
chatSettings: chatSettings || DEFAULT_CHAT_SETTINGS,
|
||||
chatSettings: {
|
||||
...DEFAULT_CHAT_SETTINGS, // Apply defaults first
|
||||
...(chatSettings || {}), // Spread fetched chatSettings, which includes preferredLanguage, and openAIReasoningEffort
|
||||
},
|
||||
userInfo,
|
||||
previousModeApiProvider,
|
||||
previousModeModelId,
|
||||
@ -345,9 +374,10 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
previousModeReasoningEffort,
|
||||
previousModeAwsBedrockCustomSelected,
|
||||
previousModeAwsBedrockCustomModelBaseId,
|
||||
mcpMarketplaceEnabled,
|
||||
mcpMarketplaceEnabled: mcpMarketplaceEnabled,
|
||||
telemetrySetting: telemetrySetting || "unset",
|
||||
planActSeparateModelsSetting,
|
||||
enableCheckpointsSetting: enableCheckpointsSetting,
|
||||
shellIntegrationTimeout: shellIntegrationTimeout || 4000,
|
||||
}
|
||||
}
|
||||
|
@ -167,6 +167,7 @@ export class Task {
|
||||
private didAlreadyUseTool = false
|
||||
private didCompleteReadingStream = false
|
||||
private didAutomaticallyRetryFailedApiRequest = false
|
||||
private enableCheckpoints: boolean
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext,
|
||||
@ -182,6 +183,7 @@ export class Task {
|
||||
browserSettings: BrowserSettings,
|
||||
chatSettings: ChatSettings,
|
||||
shellIntegrationTimeout: number,
|
||||
enableCheckpointsSetting: boolean,
|
||||
customInstructions?: string,
|
||||
task?: string,
|
||||
images?: string[],
|
||||
@ -207,6 +209,7 @@ export class Task {
|
||||
this.autoApprovalSettings = autoApprovalSettings
|
||||
this.browserSettings = browserSettings
|
||||
this.chatSettings = chatSettings
|
||||
this.enableCheckpoints = enableCheckpointsSetting
|
||||
|
||||
// Initialize taskId first
|
||||
if (historyItem) {
|
||||
@ -222,11 +225,19 @@ export class Task {
|
||||
// Initialize file context tracker
|
||||
this.fileContextTracker = new FileContextTracker(context, this.taskId)
|
||||
this.modelContextTracker = new ModelContextTracker(context, this.taskId)
|
||||
// Now that taskId is initialized, we can build the API handler
|
||||
this.api = buildApiHandler({
|
||||
|
||||
// Prepare effective API configuration
|
||||
let effectiveApiConfiguration: ApiConfiguration = {
|
||||
...apiConfiguration,
|
||||
taskId: this.taskId,
|
||||
})
|
||||
}
|
||||
|
||||
if (apiConfiguration.apiProvider === "openai" || apiConfiguration.apiProvider === "openai-native") {
|
||||
effectiveApiConfiguration.reasoningEffort = chatSettings.openAIReasoningEffort
|
||||
}
|
||||
|
||||
// Now that taskId is initialized, we can build the API handler
|
||||
this.api = buildApiHandler(effectiveApiConfiguration)
|
||||
|
||||
// Set taskId on browserSession for telemetry tracking
|
||||
this.browserSession.setTaskId(this.taskId)
|
||||
@ -342,9 +353,19 @@ export class Task {
|
||||
break
|
||||
case "taskAndWorkspace":
|
||||
case "workspace":
|
||||
if (!this.enableCheckpoints) {
|
||||
vscode.window.showErrorMessage("Checkpoints are disabled in settings.")
|
||||
didWorkspaceRestoreFail = true
|
||||
break
|
||||
}
|
||||
|
||||
if (!this.checkpointTracker && !this.checkpointTrackerErrorMessage) {
|
||||
try {
|
||||
this.checkpointTracker = await CheckpointTracker.create(this.taskId, this.context.globalStorageUri.fsPath)
|
||||
this.checkpointTracker = await CheckpointTracker.create(
|
||||
this.taskId,
|
||||
this.context.globalStorageUri.fsPath,
|
||||
this.enableCheckpoints,
|
||||
)
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : "Unknown error"
|
||||
console.error("Failed to initialize checkpoint tracker:", errorMessage)
|
||||
@ -451,6 +472,11 @@ export class Task {
|
||||
const relinquishButton = () => {
|
||||
this.postMessageToWebview({ type: "relinquishControl" })
|
||||
}
|
||||
if (!this.enableCheckpoints) {
|
||||
vscode.window.showInformationMessage("Checkpoints are disabled in settings. Cannot show diff.")
|
||||
relinquishButton()
|
||||
return
|
||||
}
|
||||
|
||||
console.log("presentMultifileDiff", messageTs)
|
||||
const messageIndex = this.clineMessages.findIndex((m) => m.ts === messageTs)
|
||||
@ -468,9 +494,13 @@ export class Task {
|
||||
}
|
||||
|
||||
// TODO: handle if this is called from outside original workspace, in which case we need to show user error message we can't show diff outside of workspace?
|
||||
if (!this.checkpointTracker && !this.checkpointTrackerErrorMessage) {
|
||||
if (!this.checkpointTracker && this.enableCheckpoints && !this.checkpointTrackerErrorMessage) {
|
||||
try {
|
||||
this.checkpointTracker = await CheckpointTracker.create(this.taskId, this.context.globalStorageUri.fsPath)
|
||||
this.checkpointTracker = await CheckpointTracker.create(
|
||||
this.taskId,
|
||||
this.context.globalStorageUri.fsPath,
|
||||
this.enableCheckpoints,
|
||||
)
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : "Unknown error"
|
||||
console.error("Failed to initialize checkpoint tracker:", errorMessage)
|
||||
@ -568,6 +598,8 @@ export class Task {
|
||||
}
|
||||
|
||||
async doesLatestTaskCompletionHaveNewChanges() {
|
||||
if (!this.enableCheckpoints) return false
|
||||
|
||||
const messageIndex = findLastIndex(this.clineMessages, (m) => m.say === "completion_result")
|
||||
const message = this.clineMessages[messageIndex]
|
||||
if (!message) {
|
||||
@ -580,9 +612,13 @@ export class Task {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.checkpointTracker && !this.checkpointTrackerErrorMessage) {
|
||||
if (this.enableCheckpoints && !this.checkpointTracker && !this.checkpointTrackerErrorMessage) {
|
||||
try {
|
||||
this.checkpointTracker = await CheckpointTracker.create(this.taskId, this.context.globalStorageUri.fsPath)
|
||||
this.checkpointTracker = await CheckpointTracker.create(
|
||||
this.taskId,
|
||||
this.context.globalStorageUri.fsPath,
|
||||
this.enableCheckpoints,
|
||||
)
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : "Unknown error"
|
||||
console.error("Failed to initialize checkpoint tracker:", errorMessage)
|
||||
@ -1081,6 +1117,10 @@ export class Task {
|
||||
// Checkpoints
|
||||
|
||||
async saveCheckpoint(isAttemptCompletionMessage: boolean = false) {
|
||||
if (!this.enableCheckpoints) {
|
||||
// If checkpoints are disabled, do nothing.
|
||||
return
|
||||
}
|
||||
// Set isCheckpointCheckedOut to false for all checkpoint_created messages
|
||||
this.clineMessages.forEach((message) => {
|
||||
if (message.say === "checkpoint_created") {
|
||||
@ -1448,7 +1488,7 @@ export class Task {
|
||||
*/
|
||||
private async migrateDisableBrowserToolSetting(): Promise<void> {
|
||||
const config = vscode.workspace.getConfiguration("cline")
|
||||
const disableBrowserTool = vscode.workspace.getConfiguration("cline").get<boolean>("disableBrowserTool")
|
||||
const disableBrowserTool = config.get<boolean>("disableBrowserTool")
|
||||
|
||||
if (disableBrowserTool !== undefined) {
|
||||
this.browserSettings.disableToolUse = disableBrowserTool
|
||||
@ -1457,6 +1497,16 @@ export class Task {
|
||||
}
|
||||
}
|
||||
|
||||
private async migratePreferredLanguageToolSetting(): Promise<void> {
|
||||
const config = vscode.workspace.getConfiguration("cline")
|
||||
const preferredLanguage = config.get<LanguageDisplay>("preferredLanguage")
|
||||
if (preferredLanguage !== undefined) {
|
||||
this.chatSettings.preferredLanguage = preferredLanguage
|
||||
// Remove from VSCode configuration
|
||||
await config.update("preferredLanguage", undefined, true)
|
||||
}
|
||||
}
|
||||
|
||||
async *attemptApiRequest(previousApiReqIndex: number): ApiStream {
|
||||
// Wait for MCP servers to be connected before generating system prompt
|
||||
await pWaitFor(() => this.mcpHub.isConnecting !== true, { timeout: 10_000 }).catch(() => {
|
||||
@ -1473,9 +1523,8 @@ export class Task {
|
||||
let systemPrompt = await SYSTEM_PROMPT(cwd, supportsBrowserUse, this.mcpHub, this.browserSettings)
|
||||
|
||||
let settingsCustomInstructions = this.customInstructions?.trim()
|
||||
const preferredLanguage = getLanguageKey(
|
||||
vscode.workspace.getConfiguration("cline").get<LanguageDisplay>("preferredLanguage"),
|
||||
)
|
||||
await this.migratePreferredLanguageToolSetting()
|
||||
const preferredLanguage = getLanguageKey(this.chatSettings.preferredLanguage as LanguageDisplay)
|
||||
const preferredLanguageInstructions =
|
||||
preferredLanguage && preferredLanguage !== DEFAULT_LANGUAGE_SETTINGS
|
||||
? `# Preferred Language\n\nSpeak in ${preferredLanguage}.`
|
||||
@ -3670,17 +3719,11 @@ export class Task {
|
||||
}),
|
||||
)
|
||||
|
||||
if (isFirstRequest) {
|
||||
await this.say("checkpoint_created") // no hash since we need to wait for CheckpointTracker to be initialized
|
||||
}
|
||||
|
||||
// use this opportunity to initialize the checkpoint tracker (can be expensive to initialize in the constructor)
|
||||
// FIXME: right now we're letting users init checkpoints for old tasks, but this could be a problem if opening a task in the wrong workspace
|
||||
// isNewTask &&
|
||||
if (!this.checkpointTracker && !this.checkpointTrackerErrorMessage) {
|
||||
// Initialize checkpoint tracker first if enabled and it's the first request
|
||||
if (isFirstRequest && this.enableCheckpoints && !this.checkpointTracker && !this.checkpointTrackerErrorMessage) {
|
||||
try {
|
||||
this.checkpointTracker = await pTimeout(
|
||||
CheckpointTracker.create(this.taskId, this.context.globalStorageUri.fsPath),
|
||||
CheckpointTracker.create(this.taskId, this.context.globalStorageUri.fsPath, this.enableCheckpoints),
|
||||
{
|
||||
milliseconds: 15_000,
|
||||
message:
|
||||
@ -3694,14 +3737,22 @@ export class Task {
|
||||
}
|
||||
}
|
||||
|
||||
// Now that checkpoint tracker is initialized, update the dummy checkpoint_created message with the commit hash. (This is necessary since we use the API request loading as an opportunity to initialize the checkpoint tracker, which can take some time)
|
||||
if (isFirstRequest) {
|
||||
const commitHash = await this.checkpointTracker?.commit()
|
||||
// Now, if it's the first request AND checkpoints are enabled AND tracker was successfully initialized,
|
||||
// then say "checkpoint_created" and perform the commit.
|
||||
if (isFirstRequest && this.enableCheckpoints && this.checkpointTracker) {
|
||||
await this.say("checkpoint_created") // Now this is conditional
|
||||
const commitHash = await this.checkpointTracker.commit() // Actual commit
|
||||
const lastCheckpointMessage = findLast(this.clineMessages, (m) => m.say === "checkpoint_created")
|
||||
if (lastCheckpointMessage) {
|
||||
lastCheckpointMessage.lastCheckpointHash = commitHash
|
||||
await this.saveClineMessagesAndUpdateHistory()
|
||||
// saveClineMessagesAndUpdateHistory will be called later after API response,
|
||||
// so no need to call it here unless this is the only modification to this message.
|
||||
// For now, assuming it's handled later.
|
||||
}
|
||||
} else if (isFirstRequest && this.enableCheckpoints && !this.checkpointTracker && this.checkpointTrackerErrorMessage) {
|
||||
// Checkpoints are enabled, but tracker failed to initialize.
|
||||
// checkpointTrackerErrorMessage is already set and will be part of the state.
|
||||
// No explicit UI message here, error message will be in ExtensionState.
|
||||
}
|
||||
|
||||
const [parsedUserContent, environmentDetails, clinerulesError] = await this.loadContext(userContent, includeFileDetails)
|
||||
|
@ -21,15 +21,17 @@ class CheckpointTracker {
|
||||
this.cwd = cwd
|
||||
}
|
||||
|
||||
public static async create(taskId: string, provider?: ClineProvider): Promise<CheckpointTracker | undefined> {
|
||||
public static async create(
|
||||
taskId: string,
|
||||
enableCheckpointsSetting: boolean,
|
||||
provider?: ClineProvider,
|
||||
): Promise<CheckpointTracker | undefined> {
|
||||
try {
|
||||
if (!provider) {
|
||||
throw new Error("Provider is required to create a checkpoint tracker")
|
||||
}
|
||||
|
||||
// Check if checkpoints are disabled in VS Code settings
|
||||
const enableCheckpoints = vscode.workspace.getConfiguration("cline").get<boolean>("enableCheckpoints") ?? true
|
||||
if (!enableCheckpoints) {
|
||||
if (!enableCheckpointsSetting) {
|
||||
return undefined // Don't create tracker when disabled
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,11 @@ class CheckpointTracker {
|
||||
* Configuration:
|
||||
* - Respects 'cline.enableCheckpoints' VS Code setting
|
||||
*/
|
||||
public static async create(taskId: string, globalStoragePath: string | undefined): Promise<CheckpointTracker | undefined> {
|
||||
public static async create(
|
||||
taskId: string,
|
||||
globalStoragePath: string | undefined,
|
||||
enableCheckpointsSetting: boolean,
|
||||
): Promise<CheckpointTracker | undefined> {
|
||||
if (!globalStoragePath) {
|
||||
throw new Error("Global storage path is required to create a checkpoint tracker")
|
||||
}
|
||||
@ -98,9 +102,9 @@ class CheckpointTracker {
|
||||
console.info(`Creating new CheckpointTracker for task ${taskId}`)
|
||||
const startTime = performance.now()
|
||||
|
||||
// Check if checkpoints are disabled in VS Code settings
|
||||
const enableCheckpoints = vscode.workspace.getConfiguration("cline").get<boolean>("enableCheckpoints") ?? true
|
||||
if (!enableCheckpoints) {
|
||||
// Check if checkpoints are disabled by setting
|
||||
if (!enableCheckpointsSetting) {
|
||||
console.info(`Checkpoints disabled by setting for task ${taskId}`)
|
||||
return undefined // Don't create tracker when disabled
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,15 @@
|
||||
export type OpenAIReasoningEffort = "low" | "medium" | "high"
|
||||
|
||||
export interface ChatSettings {
|
||||
mode: "plan" | "act"
|
||||
preferredLanguage?: string
|
||||
openAIReasoningEffort?: OpenAIReasoningEffort
|
||||
}
|
||||
|
||||
export type PartialChatSettings = Partial<ChatSettings>
|
||||
|
||||
export const DEFAULT_CHAT_SETTINGS: ChatSettings = {
|
||||
mode: "act",
|
||||
preferredLanguage: "English",
|
||||
openAIReasoningEffort: "medium",
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ export interface ExtensionState {
|
||||
customInstructions?: string
|
||||
mcpMarketplaceEnabled?: boolean
|
||||
planActSeparateModelsSetting: boolean
|
||||
enableCheckpointsSetting?: boolean
|
||||
platform: Platform
|
||||
shouldShowAnnouncement: boolean
|
||||
taskHistory: HistoryItem[]
|
||||
|
@ -90,6 +90,8 @@ export interface WebviewMessage {
|
||||
// For openInBrowser
|
||||
url?: string
|
||||
planActSeparateModelsSetting?: boolean
|
||||
enableCheckpointsSetting?: boolean
|
||||
mcpMarketplaceEnabled?: boolean
|
||||
telemetrySetting?: TelemetrySetting
|
||||
customInstructionsSetting?: string
|
||||
// For task feedback
|
||||
|
@ -80,7 +80,6 @@ export interface ApiHandlerOptions {
|
||||
mistralApiKey?: string
|
||||
azureApiVersion?: string
|
||||
vsCodeLmModelSelector?: LanguageModelChatSelector
|
||||
o3MiniReasoningEffort?: string
|
||||
qwenApiLine?: string
|
||||
asksageApiUrl?: string
|
||||
asksageApiKey?: string
|
||||
|
@ -0,0 +1,75 @@
|
||||
import { VSCodeCheckbox, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { memo } from "react"
|
||||
import { OpenAIReasoningEffort } from "@shared/ChatSettings"
|
||||
|
||||
const FeatureSettingsSection = () => {
|
||||
const {
|
||||
enableCheckpointsSetting,
|
||||
setEnableCheckpointsSetting,
|
||||
mcpMarketplaceEnabled,
|
||||
setMcpMarketplaceEnabled,
|
||||
chatSettings,
|
||||
setChatSettings,
|
||||
} = useExtensionState()
|
||||
|
||||
return (
|
||||
<div style={{ marginBottom: 20, borderTop: "1px solid var(--vscode-panel-border)", paddingTop: 15 }}>
|
||||
<h3 style={{ color: "var(--vscode-foreground)", margin: "0 0 10px 0", fontSize: "14px" }}>Feature Settings</h3>
|
||||
<div>
|
||||
<VSCodeCheckbox
|
||||
checked={enableCheckpointsSetting}
|
||||
onChange={(e: any) => {
|
||||
const checked = e.target.checked === true
|
||||
setEnableCheckpointsSetting(checked)
|
||||
}}>
|
||||
Enable Checkpoints
|
||||
</VSCodeCheckbox>
|
||||
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
|
||||
Enables extension to save checkpoints of workspace throughout the task. Uses git under the hood which may not
|
||||
work well with large workspaces.
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<VSCodeCheckbox
|
||||
checked={mcpMarketplaceEnabled}
|
||||
onChange={(e: any) => {
|
||||
const checked = e.target.checked === true
|
||||
setMcpMarketplaceEnabled(checked)
|
||||
}}>
|
||||
Enable MCP Marketplace
|
||||
</VSCodeCheckbox>
|
||||
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
|
||||
Enables the MCP Marketplace tab for discovering and installing MCP servers.
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<label
|
||||
htmlFor="openai-reasoning-effort-dropdown"
|
||||
className="block text-sm font-medium text-[var(--vscode-foreground)] mb-1">
|
||||
OpenAI Reasoning Effort
|
||||
</label>
|
||||
<VSCodeDropdown
|
||||
id="openai-reasoning-effort-dropdown"
|
||||
currentValue={chatSettings.openAIReasoningEffort || "medium"}
|
||||
onChange={(e: any) => {
|
||||
const newValue = e.target.currentValue as OpenAIReasoningEffort
|
||||
setChatSettings({
|
||||
...chatSettings,
|
||||
openAIReasoningEffort: newValue,
|
||||
})
|
||||
}}
|
||||
className="w-full">
|
||||
<VSCodeOption value="low">Low</VSCodeOption>
|
||||
<VSCodeOption value="medium">Medium</VSCodeOption>
|
||||
<VSCodeOption value="high">High</VSCodeOption>
|
||||
</VSCodeDropdown>
|
||||
<p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
|
||||
Reasoning effort for the OpenAI family of models(applies to all OpenAI model providers)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(FeatureSettingsSection)
|
@ -0,0 +1,53 @@
|
||||
import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
|
||||
import React from "react"
|
||||
import { ChatSettings } from "@shared/ChatSettings"
|
||||
|
||||
interface PreferredLanguageSettingProps {
|
||||
chatSettings: ChatSettings
|
||||
setChatSettings: (settings: ChatSettings) => void
|
||||
}
|
||||
|
||||
const PreferredLanguageSetting: React.FC<PreferredLanguageSettingProps> = ({ chatSettings, setChatSettings }) => {
|
||||
return (
|
||||
<div style={{ marginTop: 10, marginBottom: 10 }}>
|
||||
<label htmlFor="preferred-language-dropdown" className="block mb-1 text-sm font-medium">
|
||||
Preferred Language
|
||||
</label>
|
||||
<VSCodeDropdown
|
||||
id="preferred-language-dropdown"
|
||||
currentValue={chatSettings.preferredLanguage || "English"}
|
||||
onChange={(e: any) => {
|
||||
const newLanguage = e.target.value
|
||||
setChatSettings({
|
||||
...chatSettings,
|
||||
preferredLanguage: newLanguage,
|
||||
}) // This constructs a full ChatSettings object
|
||||
}}
|
||||
style={{ width: "100%" }}>
|
||||
<VSCodeOption value="English">English</VSCodeOption>
|
||||
<VSCodeOption value="Arabic - العربية">Arabic - العربية</VSCodeOption>
|
||||
<VSCodeOption value="Portuguese - Português (Brasil)">Portuguese - Português (Brasil)</VSCodeOption>
|
||||
<VSCodeOption value="Czech - Čeština">Czech - Čeština</VSCodeOption>
|
||||
<VSCodeOption value="French - Français">French - Français</VSCodeOption>
|
||||
<VSCodeOption value="German - Deutsch">German - Deutsch</VSCodeOption>
|
||||
<VSCodeOption value="Hindi - हिन्दी">Hindi - हिन्दी</VSCodeOption>
|
||||
<VSCodeOption value="Hungarian - Magyar">Hungarian - Magyar</VSCodeOption>
|
||||
<VSCodeOption value="Italian - Italiano">Italian - Italiano</VSCodeOption>
|
||||
<VSCodeOption value="Japanese - 日本語">Japanese - 日本語</VSCodeOption>
|
||||
<VSCodeOption value="Korean - 한국어">Korean - 한국어</VSCodeOption>
|
||||
<VSCodeOption value="Polish - Polski">Polish - Polski</VSCodeOption>
|
||||
<VSCodeOption value="Portuguese - Português (Portugal)">Portuguese - Português (Portugal)</VSCodeOption>
|
||||
<VSCodeOption value="Russian - Русский">Russian - Русский</VSCodeOption>
|
||||
<VSCodeOption value="Simplified Chinese - 简体中文">Simplified Chinese - 简体中文</VSCodeOption>
|
||||
<VSCodeOption value="Spanish - Español">Spanish - Español</VSCodeOption>
|
||||
<VSCodeOption value="Traditional Chinese - 繁體中文">Traditional Chinese - 繁體中文</VSCodeOption>
|
||||
<VSCodeOption value="Turkish - Türkçe">Turkish - Türkçe</VSCodeOption>
|
||||
</VSCodeDropdown>
|
||||
<p className="text-xs text-[var(--vscode-descriptionForeground)] mt-1">
|
||||
The language that Cline should use for communication.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(PreferredLanguageSetting)
|
@ -1,5 +1,14 @@
|
||||
import { VSCodeButton, VSCodeCheckbox, VSCodeLink, VSCodeTextArea } from "@vscode/webview-ui-toolkit/react"
|
||||
import {
|
||||
VSCodeButton,
|
||||
VSCodeCheckbox,
|
||||
VSCodeDropdown,
|
||||
VSCodeLink,
|
||||
VSCodeOption,
|
||||
VSCodeTextArea,
|
||||
} from "@vscode/webview-ui-toolkit/react"
|
||||
import { memo, useCallback, useEffect, useState } from "react"
|
||||
import PreferredLanguageSetting from "./PreferredLanguageSetting" // Added import
|
||||
import { OpenAIReasoningEffort } from "@shared/ChatSettings"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { validateApiConfiguration, validateModelId } from "@/utils/validate"
|
||||
import { vscode } from "@/utils/vscode"
|
||||
@ -8,6 +17,7 @@ import ApiOptions from "./ApiOptions"
|
||||
import { TabButton } from "../mcp/configuration/McpConfigurationView"
|
||||
import { useEvent } from "react-use"
|
||||
import { ExtensionMessage } from "@shared/ExtensionMessage"
|
||||
import FeatureSettingsSection from "./FeatureSettingsSection"
|
||||
import BrowserSettingsSection from "./BrowserSettingsSection"
|
||||
import TerminalSettingsSection from "./TerminalSettingsSection"
|
||||
import { FEATURE_FLAGS } from "@shared/services/feature-flags/feature-flags"
|
||||
@ -27,8 +37,11 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
telemetrySetting,
|
||||
setTelemetrySetting,
|
||||
chatSettings,
|
||||
setChatSettings,
|
||||
planActSeparateModelsSetting,
|
||||
setPlanActSeparateModelsSetting,
|
||||
enableCheckpointsSetting,
|
||||
mcpMarketplaceEnabled,
|
||||
} = useExtensionState()
|
||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
||||
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
|
||||
@ -67,6 +80,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
planActSeparateModelsSetting,
|
||||
customInstructionsSetting: customInstructions,
|
||||
telemetrySetting,
|
||||
enableCheckpointsSetting,
|
||||
mcpMarketplaceEnabled,
|
||||
apiConfiguration: apiConfigurationToSubmit,
|
||||
})
|
||||
|
||||
@ -198,6 +213,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{chatSettings && <PreferredLanguageSetting chatSettings={chatSettings} setChatSettings={setChatSettings} />}
|
||||
|
||||
<div className="mb-[5px]">
|
||||
<VSCodeCheckbox
|
||||
className="mb-[5px]"
|
||||
@ -238,21 +255,15 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Feature Settings Section */}
|
||||
<FeatureSettingsSection />
|
||||
|
||||
{/* Browser Settings Section */}
|
||||
<BrowserSettingsSection />
|
||||
|
||||
{/* Terminal Settings Section */}
|
||||
<TerminalSettingsSection />
|
||||
|
||||
<div className="mt-auto pr-2 flex justify-center">
|
||||
<SettingsButton
|
||||
onClick={() => vscode.postMessage({ type: "openExtensionSettings" })}
|
||||
className="mt-0 mr-0 mb-4 ml-0">
|
||||
<i className="codicon codicon-settings-gear" />
|
||||
Advanced Settings
|
||||
</SettingsButton>
|
||||
</div>
|
||||
|
||||
{IS_DEV && (
|
||||
<>
|
||||
<div className="mt-[10px] mb-1">Debug</div>
|
||||
|
@ -17,7 +17,7 @@ import { McpMarketplaceCatalog, McpServer, McpViewTab } from "../../../src/share
|
||||
import { convertTextMateToHljs } from "../utils/textMateToHljs"
|
||||
import { vscode } from "../utils/vscode"
|
||||
import { DEFAULT_BROWSER_SETTINGS } from "@shared/BrowserSettings"
|
||||
import { DEFAULT_CHAT_SETTINGS } from "@shared/ChatSettings"
|
||||
import { ChatSettings, DEFAULT_CHAT_SETTINGS } from "@shared/ChatSettings"
|
||||
import { TelemetrySetting } from "@shared/TelemetrySetting"
|
||||
|
||||
interface ExtensionStateContextType extends ExtensionState {
|
||||
@ -41,7 +41,10 @@ interface ExtensionStateContextType extends ExtensionState {
|
||||
setTelemetrySetting: (value: TelemetrySetting) => void
|
||||
setShowAnnouncement: (value: boolean) => void
|
||||
setPlanActSeparateModelsSetting: (value: boolean) => void
|
||||
setEnableCheckpointsSetting: (value: boolean) => void
|
||||
setMcpMarketplaceEnabled: (value: boolean) => void
|
||||
setShellIntegrationTimeout: (value: number) => void
|
||||
setChatSettings: (value: ChatSettings) => void
|
||||
setMcpServers: (value: McpServer[]) => void
|
||||
|
||||
// Navigation
|
||||
@ -70,6 +73,7 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
telemetrySetting: "unset",
|
||||
vscMachineId: "",
|
||||
planActSeparateModelsSetting: true,
|
||||
enableCheckpointsSetting: true,
|
||||
globalClineRulesToggles: {},
|
||||
localClineRulesToggles: {},
|
||||
localCursorRulesToggles: {},
|
||||
@ -268,6 +272,7 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
localClineRulesToggles: state.localClineRulesToggles || {},
|
||||
localCursorRulesToggles: state.localCursorRulesToggles || {},
|
||||
localWindsurfRulesToggles: state.localWindsurfRulesToggles || {},
|
||||
enableCheckpointsSetting: state.enableCheckpointsSetting,
|
||||
setApiConfiguration: (value) =>
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
@ -288,6 +293,16 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
...prevState,
|
||||
planActSeparateModelsSetting: value,
|
||||
})),
|
||||
setEnableCheckpointsSetting: (value) =>
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
enableCheckpointsSetting: value,
|
||||
})),
|
||||
setMcpMarketplaceEnabled: (value) =>
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
mcpMarketplaceEnabled: value,
|
||||
})),
|
||||
setShowAnnouncement: (value) =>
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
@ -300,6 +315,22 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
})),
|
||||
setMcpServers: (mcpServers: McpServer[]) => setMcpServers(mcpServers),
|
||||
setShowMcp,
|
||||
setChatSettings: (value) => {
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
chatSettings: value,
|
||||
}))
|
||||
vscode.postMessage({
|
||||
type: "updateSettings",
|
||||
chatSettings: value,
|
||||
apiConfiguration: state.apiConfiguration,
|
||||
customInstructionsSetting: state.customInstructions,
|
||||
telemetrySetting: state.telemetrySetting,
|
||||
planActSeparateModelsSetting: state.planActSeparateModelsSetting,
|
||||
enableCheckpointsSetting: state.enableCheckpointsSetting,
|
||||
mcpMarketplaceEnabled: state.mcpMarketplaceEnabled,
|
||||
})
|
||||
},
|
||||
setMcpTab,
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user