Compare commits

...

3 Commits

Author SHA1 Message Date
celestial-vault 54cd8e54aa fix types after merge conflicts 2025-07-29 12:11:00 -07:00
celestial-vault b7bb97643b merge conflicts 2025-07-29 12:01:10 -07:00
celestial-vault 9443ae1a35 remove chatSettings object 2025-07-26 09:58:41 -07:00
72 changed files with 232 additions and 655 deletions
+5 -9
View File
@@ -13,7 +13,7 @@ service StateService {
rpc subscribeToState(EmptyRequest) returns (stream State);
rpc toggleFavoriteModel(StringRequest) returns (Empty);
rpc resetState(ResetStateRequest) returns (Empty);
rpc togglePlanActMode(TogglePlanActModeRequest) returns (Boolean);
rpc togglePlanActModeProto(TogglePlanActModeRequest) returns (Boolean);
rpc updateAutoApprovalSettings(AutoApprovalSettingsRequest) returns (Empty);
rpc updateSettings(UpdateSettingsRequest) returns (Empty);
rpc updateTelemetrySetting(TelemetrySettingRequest) returns (Empty);
@@ -43,7 +43,7 @@ message TerminalProfileUpdateResponse {
message TogglePlanActModeRequest {
Metadata metadata = 1;
ChatSettings chat_settings = 2;
PlanActMode mode = 2;
optional ChatContent chat_content = 3;
}
@@ -52,12 +52,6 @@ enum PlanActMode {
ACT = 1;
}
message ChatSettings {
PlanActMode mode = 1;
optional string preferred_language = 2;
optional string open_ai_reasoning_effort = 3;
}
message ChatContent {
optional string message = 1;
repeated string images = 2;
@@ -108,12 +102,14 @@ message UpdateSettingsRequest {
optional bool plan_act_separate_models_setting = 4;
optional bool enable_checkpoints_setting = 5;
optional bool mcp_marketplace_enabled = 6;
optional ChatSettings chat_settings = 7;
optional int32 shell_integration_timeout = 8;
optional bool terminal_reuse_enabled = 9;
optional bool mcp_responses_collapsed = 10;
optional string mcp_display_mode = 11;
optional int32 terminal_output_line_limit = 12;
optional PlanActMode mode = 13;
optional string preferred_language = 14;
optional string openai_reasoning_effort = 15;
}
// Complete API Configuration message
+1 -1
View File
@@ -29,7 +29,7 @@ import { SapAiCoreHandler } from "./providers/sapaicore"
import { ClaudeCodeHandler } from "./providers/claude-code"
import { MoonshotHandler } from "./providers/moonshot"
import { GroqHandler } from "./providers/groq"
import { Mode } from "../shared/ChatSettings"
import { Mode } from "@shared/storage/types"
import { HuggingFaceHandler } from "./providers/huggingface"
import { HuaweiCloudMaaSHandler } from "./providers/huawei-cloud-maas"
+19 -32
View File
@@ -13,7 +13,7 @@ import { ClineAccountService } from "@services/account/ClineAccountService"
import { McpHub } from "@services/mcp/McpHub"
import { ApiProvider, ModelInfo } from "@shared/api"
import { ChatContent } from "@shared/ChatContent"
import { ChatSettings, Mode, StoredChatSettings } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
import { ClineRulesToggles } from "@shared/cline-rules"
import { ExtensionMessage, ExtensionState, Platform } from "@shared/ExtensionMessage"
import { HistoryItem } from "@shared/HistoryItem"
@@ -140,7 +140,9 @@ export class Controller {
apiConfiguration,
autoApprovalSettings,
browserSettings,
chatSettings: storedChatSettings,
preferredLanguage,
openaiReasoningEffort,
mode,
shellIntegrationTimeout,
terminalReuseEnabled,
terminalOutputLineLimit,
@@ -150,15 +152,6 @@ export class Controller {
taskHistory,
} = await getAllExtensionState(this.context)
// Get current mode using helper function
const currentMode = await this.getCurrentMode()
// Reconstruct ChatSettings with mode from global state and stored preferences
const chatSettings: ChatSettings = {
...storedChatSettings, // Spread stored preferences (preferredLanguage, openAIReasoningEffort)
mode: currentMode, // Use mode from global state
}
const NEW_USER_TASK_COUNT_THRESHOLD = 10
// Check if the user has completed enough tasks to no longer be considered a "new user"
@@ -185,7 +178,9 @@ export class Controller {
apiConfiguration,
autoApprovalSettings,
browserSettings,
chatSettings,
preferredLanguage,
openaiReasoningEffort,
mode,
shellIntegrationTimeout,
terminalReuseEnabled ?? true,
terminalOutputLineLimit ?? 500,
@@ -248,28 +243,25 @@ export class Controller {
await this.postStateToWebview()
}
async togglePlanActModeWithChatSettings(chatSettings: ChatSettings, chatContent?: ChatContent): Promise<boolean> {
const didSwitchToActMode = chatSettings.mode === "act"
async togglePlanActMode(modeToSwitchTo: Mode, chatContent?: ChatContent): Promise<boolean> {
const didSwitchToActMode = modeToSwitchTo === "act"
// Store mode to global state
await updateGlobalState(this.context, "mode", chatSettings.mode)
await updateGlobalState(this.context, "mode", modeToSwitchTo)
// Capture mode switch telemetry | Capture regardless of if we know the taskId
telemetryService.captureModeSwitch(this.task?.taskId ?? "0", chatSettings.mode)
telemetryService.captureModeSwitch(this.task?.taskId ?? "0", modeToSwitchTo)
// Update API handler with new mode (buildApiHandler now selects provider based on mode)
if (this.task) {
const { apiConfiguration } = await getAllExtensionState(this.context)
this.task.api = buildApiHandler({ ...apiConfiguration, taskId: this.task.taskId }, chatSettings.mode)
this.task.api = buildApiHandler({ ...apiConfiguration, taskId: this.task.taskId }, modeToSwitchTo)
}
// Save only non-mode properties to global storage
const { mode, ...persistentChatSettings }: { mode: string } & StoredChatSettings = chatSettings
await updateGlobalState(this.context, "chatSettings", persistentChatSettings)
await this.postStateToWebview()
if (this.task) {
this.task.chatSettings = chatSettings
this.task.mode = modeToSwitchTo
if (this.task.taskState.isAwaitingPlanResponse && didSwitchToActMode) {
this.task.taskState.didRespondToPlanAskBySwitchingMode = true
// Use chatContent if provided, otherwise use default message
@@ -705,7 +697,9 @@ export class Controller {
taskHistory,
autoApprovalSettings,
browserSettings,
chatSettings: storedChatSettings,
preferredLanguage,
openaiReasoningEffort,
mode,
userInfo,
mcpMarketplaceEnabled,
mcpDisplayMode,
@@ -723,15 +717,6 @@ export class Controller {
terminalOutputLineLimit,
} = await getAllExtensionState(this.context)
// Get current mode using helper function
const currentMode = await this.getCurrentMode()
// Reconstruct ChatSettings with mode from global state and stored preferences
const chatSettings: ChatSettings = {
...storedChatSettings, // Spread stored preferences (preferredLanguage, openAIReasoningEffort)
mode: currentMode, // Use mode from global state
}
const localClineRulesToggles =
((await getWorkspaceState(this.context, "localClineRulesToggles")) as ClineRulesToggles) || {}
@@ -758,7 +743,9 @@ export class Controller {
platform: process.platform as Platform,
autoApprovalSettings,
browserSettings,
chatSettings,
preferredLanguage,
openaiReasoningEffort,
mode,
userInfo,
mcpMarketplaceEnabled,
mcpDisplayMode,
+3 -3
View File
@@ -66,9 +66,9 @@ export async function downloadMcp(controller: Controller, request: StringRequest
- Once installed, demonstrate the server's capabilities by using one of its tools.
Here is the project's README to help you get started:\n\n${mcpDetails.readmeContent}\n${mcpDetails.llmsInstallationContent}`
const { chatSettings } = await controller.getStateToPostToWebview()
if (chatSettings.mode === "plan") {
await controller.togglePlanActModeWithChatSettings({ mode: "act" })
const { mode } = await controller.getStateToPostToWebview()
if (mode === "plan") {
await controller.togglePlanActMode("act")
}
// Initialize task and show chat view
@@ -1,34 +0,0 @@
import { Controller } from ".."
import { Boolean } from "@shared/proto/cline/common"
import { TogglePlanActModeRequest } from "@shared/proto/cline/state"
import {
convertProtoChatContentToChatContent,
convertProtoChatSettingsToChatSettings,
} from "@shared/proto-conversions/state/chat-settings-conversion"
/**
* Toggles between Plan and Act modes
* @param controller The controller instance
* @param request The request containing the chat settings and optional chat content
* @returns An empty response
*/
export async function togglePlanActMode(controller: Controller, request: TogglePlanActModeRequest): Promise<Boolean> {
try {
if (!request.chatSettings) {
throw new Error("Chat settings are required")
}
const chatSettings = convertProtoChatSettingsToChatSettings(request.chatSettings)
const chatContent = request.chatContent ? convertProtoChatContentToChatContent(request.chatContent) : undefined
// Call the existing controller implementation
const sentMessage = await controller.togglePlanActModeWithChatSettings(chatSettings, chatContent)
return Boolean.create({
value: sentMessage,
})
} catch (error) {
console.error("Failed to toggle Plan/Act mode:", error)
throw error
}
}
@@ -0,0 +1,34 @@
import { Controller } from ".."
import { Boolean } from "@shared/proto/cline/common"
import { TogglePlanActModeRequest, PlanActMode } from "@shared/proto/cline/state"
import { Mode } from "@shared/storage/types"
/**
* Toggles between Plan and Act modes
* @param controller The controller instance
* @param request The request containing the chat settings and optional chat content
* @returns An empty response
*/
export async function togglePlanActModeProto(controller: Controller, request: TogglePlanActModeRequest): Promise<Boolean> {
try {
let mode: Mode
if (request.mode === PlanActMode.PLAN) {
mode = "plan"
} else if (request.mode === PlanActMode.ACT) {
mode = "act"
} else {
throw new Error(`Invalid mode value: ${request.mode}`)
}
const chatContent = request.chatContent
// Call the existing controller implementation
const sentMessage = await controller.togglePlanActMode(mode, chatContent)
return Boolean.create({
value: sentMessage,
})
} catch (error) {
console.error("Failed to toggle Plan/Act mode:", error)
throw error
}
}
+22 -18
View File
@@ -1,11 +1,11 @@
import { Controller } from ".."
import { Empty } from "@shared/proto/cline/common"
import { UpdateSettingsRequest } from "@shared/proto/cline/state"
import { PlanActMode, UpdateSettingsRequest } from "@shared/proto/cline/state"
import { updateApiConfiguration } from "../../storage/state"
import { buildApiHandler } from "../../../api"
import { convertProtoApiConfigurationToApiConfiguration } from "../../../shared/proto-conversions/state/settings-conversion"
import { convertProtoChatSettingsToChatSettings } from "../../../shared/proto-conversions/state/chat-settings-conversion"
import { TelemetrySetting } from "@/shared/TelemetrySetting"
import { convertProtoApiConfigurationToApiConfiguration } from "@shared/proto-conversions/state/settings-conversion"
import { TelemetrySetting } from "@shared/TelemetrySetting"
import { OpenaiReasoningEffort } from "@shared/storage/types"
/**
* Updates multiple extension settings in a single request
@@ -56,22 +56,26 @@ export async function updateSettings(controller: Controller, request: UpdateSett
await controller.context.globalState.update("mcpDisplayMode", request.mcpDisplayMode)
}
// Update chat settings
if (request.chatSettings) {
const chatSettings = convertProtoChatSettingsToChatSettings(request.chatSettings)
// Store mode to global state
if (chatSettings.mode !== undefined) {
await controller.context.globalState.update("mode", chatSettings.mode)
}
// Store chat settings (excluding mode) to global state
const { mode, ...globalChatSettings } = chatSettings
await controller.context.globalState.update("chatSettings", globalChatSettings)
if (request.mode !== undefined) {
const mode = request.mode === PlanActMode.PLAN ? "plan" : "act"
if (controller.task) {
controller.task.chatSettings = chatSettings
controller.task.mode = mode
}
await controller.context.globalState.update("mode", request.mode)
}
if (request.openaiReasoningEffort !== undefined) {
if (controller.task) {
controller.task.openaiReasoningEffort = request.openaiReasoningEffort as OpenaiReasoningEffort
}
await controller.context.globalState.update("openaiReasoningEffort", request.openaiReasoningEffort)
}
if (request.preferredLanguage !== undefined) {
if (controller.task) {
controller.task.preferredLanguage = request.preferredLanguage
}
await controller.context.globalState.update("preferredLanguage", request.preferredLanguage)
}
// Update terminal timeout setting
+1 -1
View File
@@ -2,7 +2,7 @@ import { Anthropic } from "@anthropic-ai/sdk"
import * as diff from "diff"
import * as path from "path"
import { ClineIgnoreController, LOCK_TEXT_SYMBOL } from "../ignore/ClineIgnoreController"
import { Mode } from "@/shared/ChatSettings"
import { Mode } from "@/shared/storage/types"
export const formatResponse = {
duplicateFileReadNotice: () =>
+2 -1
View File
@@ -84,7 +84,8 @@ export type GlobalStateKey =
| "sapAiResourceGroup"
| "claudeCodePath"
// Settings around plan/act and ephemeral model configuration
| "chatSettings"
| "preferredLanguage"
| "openaiReasoningEffort"
| "mode"
// Plan mode configurations
| "planModeApiProvider"
-39
View File
@@ -3,7 +3,6 @@ import { ensureRulesDirectoryExists } from "./disk"
import fs from "fs/promises"
import path from "path"
import { updateGlobalState, getAllExtensionState, getGlobalState } from "./state"
import { GlobalStateKey } from "./state-keys"
export async function migrateWorkspaceToGlobalStorage(context: vscode.ExtensionContext) {
// Keys to migrate from workspace storage back to global storage
@@ -13,7 +12,6 @@ export async function migrateWorkspaceToGlobalStorage(context: vscode.ExtensionC
"apiModelId",
"thinkingBudgetTokens",
"reasoningEffort",
"chatSettings",
"vsCodeLmModelSelector",
// Provider-specific model keys
@@ -136,43 +134,6 @@ export async function migrateCustomInstructionsToGlobalRules(context: vscode.Ext
}
}
export async function migrateModeFromWorkspaceStorageToControllerState(context: vscode.ExtensionContext) {
try {
// Check legacy workspace storage (use raw methods since chatSettings is now global)
const workspaceChatSettings = (await context.workspaceState.get("chatSettings")) as any
if (workspaceChatSettings && typeof workspaceChatSettings === "object" && "mode" in workspaceChatSettings) {
console.log("Cleaning up mode from legacy workspace storage...")
// Remove mode property from chatSettings
const { mode, ...cleanedChatSettings } = workspaceChatSettings
// Save cleaned chatSettings back to workspace storage (will be migrated later)
await context.workspaceState.update("chatSettings", cleanedChatSettings)
console.log("Successfully removed mode from legacy workspace storage chatSettings")
}
// Also check global storage for any mode cleanup needed
const globalChatSettings = (await context.globalState.get("chatSettings")) as any
if (globalChatSettings && typeof globalChatSettings === "object" && "mode" in globalChatSettings) {
console.log("Cleaning up mode from global storage...")
// Remove mode property from chatSettings
const { mode, ...cleanedChatSettings } = globalChatSettings
// Save cleaned chatSettings back to global storage
await updateGlobalState(context, "chatSettings", cleanedChatSettings)
console.log("Successfully removed mode from global storage chatSettings")
}
} catch (error) {
console.error("Failed to cleanup mode from storage:", error)
// Continue execution - migration failure shouldn't break extension startup
}
}
export async function migrateLegacyApiConfigurationToModeSpecific(context: vscode.ExtensionContext) {
try {
// Check if migration is needed - if planModeApiProvider already exists, skip migration
+9 -13
View File
@@ -1,5 +1,5 @@
import * as vscode from "vscode"
import { DEFAULT_CHAT_SETTINGS, Mode } from "@shared/ChatSettings"
import { Mode, OpenaiReasoningEffort } from "@shared/storage/types"
import { DEFAULT_BROWSER_SETTINGS } from "@shared/BrowserSettings"
import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "@shared/AutoApprovalSettings"
import { GlobalStateKey, LocalStateKey, SecretKey } from "./state-keys"
@@ -7,7 +7,6 @@ import { ApiConfiguration, ApiProvider, BedrockModelId, ModelInfo } from "@share
import { HistoryItem } from "@shared/HistoryItem"
import { AutoApprovalSettings } from "@shared/AutoApprovalSettings"
import { BrowserSettings } from "@shared/BrowserSettings"
import { StoredChatSettings } from "@shared/ChatSettings"
import { TelemetrySetting } from "@shared/TelemetrySetting"
import { UserInfo } from "@shared/UserInfo"
import { ClineRulesToggles } from "@shared/cline-rules"
@@ -110,7 +109,6 @@ export async function getWorkspaceState(context: vscode.ExtensionContext, key: L
}
export async function getAllExtensionState(context: vscode.ExtensionContext) {
const firstBatchStart = performance.now()
const [
isNewUser,
welcomeViewCompleted,
@@ -275,10 +273,10 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
const localClineRulesToggles = (await getWorkspaceState(context, "localClineRulesToggles")) as ClineRulesToggles
const secondBatchStart = performance.now()
const [
chatSettings,
currentMode,
preferredLanguage,
openaiReasoningEffort,
mode,
// Plan mode configurations
planModeApiProvider,
planModeApiModelId,
@@ -334,7 +332,8 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
actModeHuaweiCloudMaasModelId,
actModeHuaweiCloudMaasModelInfo,
] = await Promise.all([
getGlobalState(context, "chatSettings") as Promise<StoredChatSettings | undefined>,
getGlobalState(context, "preferredLanguage") as Promise<string | undefined>,
getGlobalState(context, "openaiReasoningEffort") as Promise<OpenaiReasoningEffort | undefined>,
getGlobalState(context, "mode") as Promise<Mode | undefined>,
// Plan mode configurations
getGlobalState(context, "planModeApiProvider") as Promise<ApiProvider | undefined>,
@@ -392,7 +391,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
getGlobalState(context, "actModeHuaweiCloudMaasModelInfo") as Promise<ModelInfo | undefined>,
])
const processingStart = performance.now()
let apiProvider: ApiProvider
if (planModeApiProvider) {
apiProvider = planModeApiProvider
@@ -554,11 +552,9 @@ 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: {
...DEFAULT_CHAT_SETTINGS, // Apply defaults first
...(chatSettings || {}), // Spread fetched global chatSettings, which includes preferredLanguage, and openAIReasoningEffort
mode: currentMode || "act", // Merge mode from global state
},
preferredLanguage: preferredLanguage || "English",
openaiReasoningEffort: (openaiReasoningEffort as OpenaiReasoningEffort) || "medium",
mode: mode || "act",
userInfo,
mcpMarketplaceEnabled: mcpMarketplaceEnabled,
mcpDisplayMode: mcpDisplayMode ?? DEFAULT_MCP_DISPLAY_MODE,
+3 -3
View File
@@ -54,7 +54,7 @@ import { TaskState } from "./TaskState"
import { MessageStateHandler } from "./message-state"
import { AutoApprove } from "./tools/autoApprove"
import { showNotificationForApprovalIfAutoApprovalEnabled } from "./utils"
import { ChatSettings } from "@/shared/ChatSettings"
import { Mode } from "@shared/storage/types"
export class ToolExecutor {
private autoApprover: AutoApprove
@@ -91,7 +91,7 @@ export class ToolExecutor {
private browserSettings: BrowserSettings,
private cwd: string,
private taskId: string,
private chatSettings: ChatSettings,
private mode: Mode,
// Callbacks to the Task (Entity)
private say: (
@@ -1919,7 +1919,7 @@ export class ToolExecutor {
const clineVersion =
vscode.extensions.getExtension("saoudrizwan.claude-dev")?.packageJSON.version || "Unknown"
const systemInfo = `VSCode: ${vscode.version}, Node.js: ${process.version}, Architecture: ${os.arch()}`
const currentMode = this.chatSettings.mode
const currentMode = this.mode
const apiProvider =
currentMode === "plan"
? await getGlobalState(this.context, "planModeApiProvider")
+21 -27
View File
@@ -19,7 +19,6 @@ import { ApiConfiguration } from "@shared/api"
import { findLast, findLastIndex } from "@shared/array"
import { AutoApprovalSettings } from "@shared/AutoApprovalSettings"
import { BrowserSettings } from "@shared/BrowserSettings"
import { ChatSettings } from "@shared/ChatSettings"
import { combineApiRequests } from "@shared/combineApiRequests"
import { combineCommandSequences } from "@shared/combineCommandSequences"
import { ClineApiReqCancelReason, ClineApiReqInfo, ClineAsk, ClineMessage, ClineSay } from "@shared/ExtensionMessage"
@@ -86,6 +85,7 @@ import { MessageStateHandler } from "./message-state"
import { TaskState } from "./TaskState"
import { ToolExecutor } from "./ToolExecutor"
import { updateApiReqMsg } from "./utils"
import { Mode, OpenaiReasoningEffort } from "@shared/storage/types"
import { ShowMessageType } from "@/shared/proto/index.host"
export const USE_EXPERIMENTAL_CLAUDE4_FEATURES = false
@@ -133,7 +133,9 @@ export class Task {
// User chat state
autoApprovalSettings: AutoApprovalSettings
browserSettings: BrowserSettings
chatSettings: ChatSettings
preferredLanguage: string
openaiReasoningEffort: OpenaiReasoningEffort
mode: Mode
// Message and conversation state
messageStateHandler: MessageStateHandler
@@ -148,7 +150,9 @@ export class Task {
apiConfiguration: ApiConfiguration,
autoApprovalSettings: AutoApprovalSettings,
browserSettings: BrowserSettings,
chatSettings: ChatSettings,
preferredLanguage: string,
openaiReasoningEffort: OpenaiReasoningEffort,
mode: Mode,
shellIntegrationTimeout: number,
terminalReuseEnabled: boolean,
terminalOutputLineLimit: number,
@@ -193,7 +197,9 @@ export class Task {
this.diffViewProvider = HostProvider.get().createDiffViewProvider()
this.autoApprovalSettings = autoApprovalSettings
this.browserSettings = browserSettings
this.chatSettings = chatSettings
this.preferredLanguage = preferredLanguage
this.openaiReasoningEffort = openaiReasoningEffort
this.mode = mode
this.enableCheckpoints = enableCheckpointsSetting
this.cwd = cwd
@@ -267,19 +273,18 @@ export class Task {
},
}
const currentProvider =
chatSettings.mode === "plan" ? apiConfiguration.planModeApiProvider : apiConfiguration.actModeApiProvider
const currentProvider = this.mode === "plan" ? apiConfiguration.planModeApiProvider : apiConfiguration.actModeApiProvider
if (currentProvider === "openai" || currentProvider === "openai-native") {
if (chatSettings.mode === "plan") {
effectiveApiConfiguration.planModeReasoningEffort = chatSettings.openAIReasoningEffort
if (this.mode === "plan") {
effectiveApiConfiguration.planModeReasoningEffort = this.openaiReasoningEffort
} else {
effectiveApiConfiguration.actModeReasoningEffort = chatSettings.openAIReasoningEffort
effectiveApiConfiguration.actModeReasoningEffort = this.openaiReasoningEffort
}
}
// Now that taskId is initialized, we can build the API handler
this.api = buildApiHandler(effectiveApiConfiguration, chatSettings.mode)
this.api = buildApiHandler(effectiveApiConfiguration, this.mode)
// Set taskId on browserSession for telemetry tracking
this.browserSession.setTaskId(this.taskId)
@@ -317,7 +322,7 @@ export class Task {
this.browserSettings,
cwd,
this.taskId,
this.chatSettings,
this.mode,
this.say.bind(this),
this.ask.bind(this),
this.saveCheckpoint.bind(this),
@@ -1178,7 +1183,7 @@ export class Task {
const hasPendingFileContextWarnings = pendingContextWarning && pendingContextWarning.length > 0
const [taskResumptionMessage, userResponseMessage] = formatResponse.taskResumption(
this.chatSettings?.mode === "plan" ? "plan" : "act",
this.mode === "plan" ? "plan" : "act",
agoText,
this.cwd,
wasRecent,
@@ -1654,20 +1659,10 @@ 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)
}
}
private async getCurrentProviderInfo(): Promise<{ modelId: string; providerId: string }> {
const modelId = this.api.getModel()?.id
const providerId =
this.chatSettings.mode === "plan"
this.mode === "plan"
? ((await getGlobalState(this.getContext(), "planModeApiProvider")) as string)
: ((await getGlobalState(this.getContext(), "actModeApiProvider")) as string)
return { modelId, providerId }
@@ -1690,8 +1685,7 @@ export class Task {
const isNextGenModel = isClaude4ModelFamily(this.api) || isGemini2dot5ModelFamily(this.api)
let systemPrompt = await SYSTEM_PROMPT(this.cwd, supportsBrowserUse, this.mcpHub, this.browserSettings, isNextGenModel)
await this.migratePreferredLanguageToolSetting()
const preferredLanguage = getLanguageKey(this.chatSettings.preferredLanguage as LanguageDisplay)
const preferredLanguage = getLanguageKey(this.preferredLanguage as LanguageDisplay)
const preferredLanguageInstructions =
preferredLanguage && preferredLanguage !== DEFAULT_LANGUAGE_SETTINGS
? `# Preferred Language\n\nSpeak in ${preferredLanguage}.`
@@ -2000,7 +1994,7 @@ export class Task {
const { modelId, providerId } = await this.getCurrentProviderInfo()
if (providerId && modelId) {
try {
await this.modelContextTracker.recordModelUsage(providerId, modelId, this.chatSettings.mode)
await this.modelContextTracker.recordModelUsage(providerId, modelId, this.mode)
} catch {}
}
@@ -2771,7 +2765,7 @@ export class Task {
details += `\n${lastApiReqTotalTokens.toLocaleString()} / ${(contextWindow / 1000).toLocaleString()}K tokens used (${usagePercentage}%)`
details += "\n\n# Current Mode"
if (this.chatSettings.mode === "plan") {
if (this.mode === "plan") {
details += "\nPLAN MODE\n" + formatResponse.planModeInstructions()
} else {
details += "\nACT MODE"
-8
View File
@@ -14,8 +14,6 @@ import { sendMcpButtonClickedEvent } from "./core/controller/ui/subscribeToMcpBu
import { sendSettingsButtonClickedEvent } from "./core/controller/ui/subscribeToSettingsButtonClicked"
import {
migrateCustomInstructionsToGlobalRules,
migrateLegacyApiConfigurationToModeSpecific,
migrateModeFromWorkspaceStorageToControllerState,
migrateWelcomeViewCompleted,
migrateWorkspaceToGlobalStorage,
} from "./core/storage/state-migrations"
@@ -60,18 +58,12 @@ export async function activate(context: vscode.ExtensionContext) {
// Migrate custom instructions to global Cline rules (one-time cleanup)
await migrateCustomInstructionsToGlobalRules(context)
// Migrate mode from workspace storage to controller state (one-time cleanup)
await migrateModeFromWorkspaceStorageToControllerState(context)
// Migrate welcomeViewCompleted setting based on existing API keys (one-time cleanup)
await migrateWelcomeViewCompleted(context)
// Migrate workspace storage values back to global storage (reverting previous migration)
await migrateWorkspaceToGlobalStorage(context)
// Migrate legacy API configuration to mode-specific keys (one-time migration)
await migrateLegacyApiConfigurationToModeSpecific(context)
// Clean up orphaned file context warnings (startup cleanup)
await FileContextTracker.cleanupOrphanedWarnings(context)
@@ -5,7 +5,7 @@ import { version as extensionVersion } from "../../../../package.json"
import type { TaskFeedbackType } from "@shared/WebviewMessage"
import type { BrowserSettings } from "@shared/BrowserSettings"
import { posthogClientProvider } from "../PostHogClientProvider"
import { Mode } from "@/shared/ChatSettings"
import { Mode } from "@/shared/storage/types"
/**
* TelemetryService handles telemetry event tracking for the Cline extension
+4 -4
View File
@@ -282,10 +282,10 @@ export function createTestServer(webviewProvider?: WebviewProvider): http.Server
}
// Ensure we're in Act mode before initiating the task
const { chatSettings } = await visibleWebview.controller.getStateToPostToWebview()
if (chatSettings.mode === "plan") {
const { mode } = await visibleWebview.controller.getStateToPostToWebview()
if (mode === "plan") {
// Switch to Act mode if currently in Plan mode
await visibleWebview.controller.togglePlanActModeWithChatSettings({ mode: "act" })
await visibleWebview.controller.togglePlanActMode("act")
}
// Initialize tool call tracker
@@ -612,7 +612,7 @@ async function autoRespondToAsk(webviewProvider: WebviewProvider, askType: Cline
try {
if (webviewProvider.controller) {
Logger.log("Auto-toggling to Act mode from Plan mode")
await webviewProvider.controller.togglePlanActModeWithChatSettings({ mode: "act" })
await webviewProvider.controller.togglePlanActMode("act")
}
} catch (error) {
Logger.log(`Error toggling to Act mode: ${error}`)
-20
View File
@@ -1,20 +0,0 @@
export type OpenAIReasoningEffort = "low" | "medium" | "high"
export type Mode = "plan" | "act"
export interface ChatSettings {
mode: Mode
preferredLanguage?: string
openAIReasoningEffort?: OpenAIReasoningEffort
}
export type PartialChatSettings = Partial<ChatSettings>
// Type for chat settings stored in workspace (excludes in-memory mode)
export type StoredChatSettings = Omit<ChatSettings, "mode">
export const DEFAULT_CHAT_SETTINGS: ChatSettings = {
mode: "act",
preferredLanguage: "English",
openAIReasoningEffort: "medium",
}
+4 -2
View File
@@ -2,7 +2,7 @@
import { ApiConfiguration } from "./api"
import { AutoApprovalSettings } from "./AutoApprovalSettings"
import { BrowserSettings } from "./BrowserSettings"
import { ChatSettings } from "./ChatSettings"
import { Mode, OpenaiReasoningEffort } from "./storage/types"
import { HistoryItem } from "./HistoryItem"
import { TelemetrySetting } from "./TelemetrySetting"
import { ClineRulesToggles } from "./cline-rules"
@@ -33,7 +33,9 @@ export interface ExtensionState {
autoApprovalSettings: AutoApprovalSettings
browserSettings: BrowserSettings
remoteBrowserHost?: string
chatSettings: ChatSettings
preferredLanguage?: string
openaiReasoningEffort?: OpenaiReasoningEffort
mode: Mode
checkpointTrackerErrorMessage?: string
clineMessages: ClineMessage[]
currentTaskItem?: HistoryItem
-2
View File
@@ -1,6 +1,5 @@
import { ApiConfiguration } from "./api"
import { BrowserSettings } from "./BrowserSettings"
import { ChatSettings } from "./ChatSettings"
import { UserInfo } from "./UserInfo"
import { ChatContent } from "./ChatContent"
import { TelemetrySetting } from "./TelemetrySetting"
@@ -23,7 +22,6 @@ export interface WebviewMessage {
bool?: boolean
number?: number
browserSettings?: BrowserSettings
chatSettings?: ChatSettings
chatContent?: ChatContent
mcpId?: string
timeout?: number
@@ -1,55 +0,0 @@
import { ChatContent } from "@shared/ChatContent"
import { ChatSettings } from "@shared/ChatSettings"
import { ChatContent as ProtoChatContent, ChatSettings as ProtoChatSettings, PlanActMode } from "@shared/proto/cline/state"
/**
* Converts domain ChatSettings objects to proto ChatSettings objects
*/
export function convertChatSettingsToProtoChatSettings(chatSettings: ChatSettings): ProtoChatSettings {
return ProtoChatSettings.create({
mode: chatSettings.mode === "plan" ? PlanActMode.PLAN : PlanActMode.ACT,
preferredLanguage: chatSettings.preferredLanguage,
openAiReasoningEffort: chatSettings.openAIReasoningEffort,
})
}
/**
* Converts proto ChatSettings objects to domain ChatSettings objects
*/
export function convertProtoChatSettingsToChatSettings(protoChatSettings: ProtoChatSettings): ChatSettings {
return {
mode: protoChatSettings.mode === PlanActMode.PLAN ? "plan" : "act",
preferredLanguage: protoChatSettings.preferredLanguage,
openAIReasoningEffort: protoChatSettings.openAiReasoningEffort as "low" | "medium" | "high" | undefined,
}
}
/**
* Converts domain ChatContent objects to proto ChatContent objects
*/
export function convertChatContentToProtoChatContent(chatContent?: ChatContent): ProtoChatContent | undefined {
if (!chatContent) {
return undefined
}
return ProtoChatContent.create({
message: chatContent.message,
images: chatContent.images || [],
files: chatContent.files || [],
})
}
/**
* Converts proto ChatContent objects to domain ChatContent objects
*/
export function convertProtoChatContentToChatContent(protoChatContent?: ProtoChatContent): ChatContent | undefined {
if (!protoChatContent) {
return undefined
}
return {
message: protoChatContent.message,
images: protoChatContent.images || [],
files: protoChatContent.files || [],
}
}
@@ -1,10 +1,5 @@
import { ApiConfiguration, ApiProvider, BedrockModelId } from "@shared/api"
import { ChatSettings } from "@shared/ChatSettings"
import {
ApiConfiguration as ProtoApiConfiguration,
ChatSettings as ProtoChatSettings,
PlanActMode,
} from "@shared/proto/cline/state"
import { ApiConfiguration as ProtoApiConfiguration } from "@shared/proto/cline/state"
/**
* Converts domain ApiConfiguration objects to proto ApiConfiguration objects
@@ -279,25 +274,3 @@ export function convertProtoApiConfigurationToApiConfiguration(protoConfig: Prot
return config
}
/**
* Converts domain ChatSettings objects to proto ChatSettings objects
*/
export function convertChatSettingsToProtoChatSettings(chatSettings: ChatSettings): ProtoChatSettings {
return ProtoChatSettings.create({
mode: chatSettings.mode === "plan" ? PlanActMode.PLAN : PlanActMode.ACT,
preferredLanguage: chatSettings.preferredLanguage,
openAiReasoningEffort: chatSettings.openAIReasoningEffort,
})
}
/**
* Converts proto ChatSettings objects to domain ChatSettings objects
*/
export function convertProtoChatSettingsToChatSettings(protoChatSettings: ProtoChatSettings): ChatSettings {
return {
mode: protoChatSettings.mode === PlanActMode.PLAN ? "plan" : "act",
preferredLanguage: protoChatSettings.preferredLanguage,
openAIReasoningEffort: protoChatSettings.openAiReasoningEffort as "low" | "medium" | "high" | undefined,
}
}
+3
View File
@@ -0,0 +1,3 @@
export type OpenaiReasoningEffort = "low" | "medium" | "high"
export type Mode = "plan" | "act"
-154
View File
@@ -1,154 +0,0 @@
import * as vscode from "vscode"
import { describe, it, beforeEach, afterEach } from "mocha"
import { strict as assert } from "assert"
describe("Chat Integration Tests", () => {
let panel: vscode.WebviewPanel
let disposables: vscode.Disposable[] = []
beforeEach(async () => {
// Create VSCode webview panel
panel = vscode.window.createWebviewPanel("testWebview", "Chat Test", vscode.ViewColumn.One, {
enableScripts: true,
retainContextWhenHidden: true,
})
// Set up minimal test webview
panel.webview.html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script>
const vscode = acquireVsCodeApi();
window.addEventListener('message', event => {
const message = event.data;
switch (message.type) {
case 'sendMessage':
vscode.postMessage({ type: 'newTask', text: message.text });
break;
case 'toggleMode':
vscode.postMessage({
type: 'togglePlanActMode',
chatSettings: { mode: 'act' },
chatContent: {
message: "message test",
}
});
break;
case 'primaryButtonClick':
vscode.postMessage({
type: 'grpc_request',
grpc_request: {
service: 'cline.TaskService',
method: 'askResponse',
message: {
responseType: 'yesButtonClicked'
},
request_id: 'test-request-id',
is_streaming: false
}
});
break;
}
});
</script>
</head>
<body>
<div id="test-webview"></div>
</body>
</html>
`
})
afterEach(() => {
panel.dispose()
disposables.forEach((d) => d.dispose())
disposables = []
})
it("should send chat messages", async () => {
// Set up message listener
const messagePromise = new Promise<any>((resolve) => {
panel.webview.onDidReceiveMessage((message) => {
if (message.type === "newTask") {
resolve(message)
}
})
})
// Trigger send message
await panel.webview.postMessage({
type: "sendMessage",
text: "Create a hello world app",
})
// Verify message was sent
const message = await messagePromise
assert.equal(message.type, "newTask")
assert.equal(message.text, "Create a hello world app")
})
it("should toggle between plan and act modes", async () => {
// Set up state change listener
const stateChangePromise = new Promise<any>((resolve) => {
panel.webview.onDidReceiveMessage((message) => {
if (message.type === "togglePlanActMode") {
resolve(message)
}
})
})
// Trigger mode toggle
await panel.webview.postMessage({ type: "toggleMode" })
// Verify mode changed
const stateChange = await stateChangePromise
assert.equal(stateChange.chatSettings.mode, "act")
})
it("should toggle between plan and act modes with messages", async () => {
// Set up state change listener
const stateChangePromise = new Promise<any>((resolve) => {
panel.webview.onDidReceiveMessage((message) => {
if (message.type === "togglePlanActMode") {
resolve(message)
}
})
})
// Trigger mode toggle
await panel.webview.postMessage({ type: "toggleMode" })
// Verify mode changed
const stateChange = await stateChangePromise
assert.equal(stateChange.chatSettings.mode, "act")
assert.equal(stateChange.chatContent.message, "message test")
})
it("should handle tool approval flow", async () => {
// Set up approval listener for gRPC request
const approvalPromise = new Promise<any>((resolve) => {
panel.webview.onDidReceiveMessage((message) => {
if (
message.type === "grpc_request" &&
message.grpc_request?.service === "cline.TaskService" &&
message.grpc_request?.method === "askResponse"
) {
resolve(message)
}
})
})
// Trigger tool approval
await panel.webview.postMessage({
type: "primaryButtonClick",
})
// Verify gRPC request was sent with correct parameters
const response = await approvalPromise
assert.equal(response.type, "grpc_request")
assert.equal(response.grpc_request.service, "cline.TaskService")
assert.equal(response.grpc_request.method, "askResponse")
assert.equal(response.grpc_request.message.responseType, "yesButtonClicked")
})
})
+22 -33
View File
@@ -1,4 +1,3 @@
import type { ChatSettings } from "@shared/ChatSettings"
import { mentionRegex, mentionRegexGlobal } from "@shared/context-mentions"
import { EmptyRequest, StringRequest } from "@shared/proto/cline/common"
import { FileSearchRequest, RelativePathsRequest } from "@shared/proto/cline/file"
@@ -44,6 +43,7 @@ import {
import { validateApiConfiguration, validateModelId } from "@/utils/validate"
import ClineRulesToggleModal from "../cline-rules/ClineRulesToggleModal"
import ServersToggleModal from "./ServersToggleModal"
import { Mode } from "@shared/storage/types"
const { MAX_IMAGES_AND_FILES_PER_MESSAGE } = CHAT_CONSTANTS
@@ -277,15 +277,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
},
ref,
) => {
const {
filePaths,
chatSettings,
apiConfiguration,
openRouterModels,
platform,
localWorkflowToggles,
globalWorkflowToggles,
} = useExtensionState()
const { filePaths, mode, apiConfiguration, openRouterModels, platform, localWorkflowToggles, globalWorkflowToggles } =
useExtensionState()
const [isTextAreaFocused, setIsTextAreaFocused] = useState(false)
const [isDraggingOver, setIsDraggingOver] = useState(false)
const [gitCommits, setGitCommits] = useState<GitCommit[]>([])
@@ -315,7 +308,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
const buttonRef = useRef<HTMLDivElement>(null)
const [arrowPosition, setArrowPosition] = useState(0)
const [menuPosition, setMenuPosition] = useState(0)
const [shownTooltipMode, setShownTooltipMode] = useState<ChatSettings["mode"] | null>(null)
const [shownTooltipMode, setShownTooltipMode] = useState<Mode | null>(null)
const [pendingInsertions, setPendingInsertions] = useState<string[]>([])
const shiftHoldTimerRef = useRef<NodeJS.Timeout | null>(null)
const [showUnsupportedFileError, setShowUnsupportedFileError] = useState(false)
@@ -971,8 +964,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
// Separate the API config submission logic
const submitApiConfig = useCallback(async () => {
const apiValidationResult = validateApiConfiguration(chatSettings.mode, apiConfiguration)
const modelIdValidationResult = validateModelId(chatSettings.mode, apiConfiguration, openRouterModels)
const apiValidationResult = validateApiConfiguration(mode, apiConfiguration)
const modelIdValidationResult = validateModelId(mode, apiConfiguration, openRouterModels)
if (!apiValidationResult && !modelIdValidationResult && apiConfiguration) {
try {
@@ -1004,18 +997,14 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
changeModeDelay = 250 // necessary to let the api config update (we send message and wait for it to be saved) FIXME: this is a hack and we ideally should check for api config changes, then wait for it to be saved, before switching modes
}
setTimeout(async () => {
const newMode = chatSettings.mode === "plan" ? PlanActMode.ACT : PlanActMode.PLAN
const response = await StateServiceClient.togglePlanActMode(
const convertedProtoMode = mode === "plan" ? PlanActMode.ACT : PlanActMode.PLAN
const response = await StateServiceClient.togglePlanActModeProto(
TogglePlanActModeRequest.create({
chatSettings: {
mode: newMode,
preferredLanguage: chatSettings.preferredLanguage,
openAiReasoningEffort: chatSettings.openAIReasoningEffort,
},
mode: convertedProtoMode,
chatContent: {
message: inputValue.trim() ? inputValue : undefined,
images: selectedImages.length > 0 ? selectedImages : undefined,
files: selectedFiles.length > 0 ? selectedFiles : undefined,
images: selectedImages,
files: selectedFiles,
},
}),
)
@@ -1027,7 +1016,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
textAreaRef.current?.focus()
}, 100)
}, changeModeDelay)
}, [chatSettings.mode, showModelSelector, submitApiConfig, inputValue, selectedImages, selectedFiles])
}, [mode, showModelSelector, submitApiConfig, inputValue, selectedImages, selectedFiles])
useShortcut("Meta+Shift+a", onModeToggle, { disableTextInputs: false }) // important that we don't disable the text input here
@@ -1094,7 +1083,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
// Get model display name
const modelDisplayName = useMemo(() => {
const { selectedProvider, selectedModelId } = normalizeApiConfiguration(apiConfiguration, chatSettings.mode)
const { selectedProvider, selectedModelId } = normalizeApiConfiguration(apiConfiguration, mode)
const {
vsCodeLmModelSelector,
togetherModelId,
@@ -1103,7 +1092,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
ollamaModelId,
liteLlmModelId,
requestyModelId,
} = getModeSpecificFields(apiConfiguration, chatSettings.mode)
} = getModeSpecificFields(apiConfiguration, mode)
const unknownModel = "unknown"
if (!apiConfiguration) return unknownModel
switch (selectedProvider) {
@@ -1130,7 +1119,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
default:
return `${selectedProvider}:${selectedModelId}`
}
}, [apiConfiguration, chatSettings.mode])
}, [apiConfiguration, mode])
// Calculate arrow position and menu position based on button location
useEffect(() => {
@@ -1586,7 +1575,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
isDraggingOver && !showUnsupportedFileError // Only show drag outline if not showing error
? "2px dashed var(--vscode-focusBorder)"
: isTextAreaFocused
? `1px solid ${chatSettings.mode === "plan" ? PLAN_MODE_COLOR : "var(--vscode-focusBorder)"}`
? `1px solid ${mode === "plan" ? PLAN_MODE_COLOR : "var(--vscode-focusBorder)"}`
: "none",
outlineOffset: isDraggingOver && !showUnsupportedFileError ? "1px" : "0px", // Add offset for drag-over outline
}}
@@ -1739,7 +1728,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
apiErrorMessage={undefined}
modelIdErrorMessage={undefined}
isPopup={true}
currentMode={chatSettings.mode}
currentMode={mode}
/>
</ModelSelectorTooltip>
)}
@@ -1753,19 +1742,19 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
tipText={`In ${shownTooltipMode === "act" ? "Act" : "Plan"} mode, Cline will ${shownTooltipMode === "act" ? "complete the task immediately" : "gather information to architect a plan"}`}
hintText={`Toggle w/ ${metaKeyChar}+Shift+A`}>
<SwitchContainer data-testid="mode-switch" disabled={false} onClick={onModeToggle}>
<Slider isAct={chatSettings.mode === "act"} isPlan={chatSettings.mode === "plan"} />
<Slider isAct={mode === "act"} isPlan={mode === "plan"} />
<SwitchOption
isActive={chatSettings.mode === "plan"}
isActive={mode === "plan"}
role="switch"
aria-checked={chatSettings.mode === "plan"}
aria-checked={mode === "plan"}
onMouseOver={() => setShownTooltipMode("plan")}
onMouseLeave={() => setShownTooltipMode(null)}>
Plan
</SwitchOption>
<SwitchOption
isActive={chatSettings.mode === "act"}
isActive={mode === "act"}
role="switch"
aria-checked={chatSettings.mode === "act"}
aria-checked={mode === "act"}
onMouseOver={() => setShownTooltipMode("act")}
onMouseLeave={() => setShownTooltipMode(null)}>
Act
+3 -3
View File
@@ -49,7 +49,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
apiConfiguration,
telemetrySetting,
navigateToChat,
chatSettings,
mode,
} = useExtensionState()
const shouldShowQuickWins = false // !taskHistory || taskHistory.length < QUICK_WINS_HISTORY_THRESHOLD
//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined
@@ -202,8 +202,8 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
messageHandlers
const { selectedModelInfo } = useMemo(() => {
return normalizeApiConfiguration(apiConfiguration, chatSettings.mode)
}, [apiConfiguration, chatSettings.mode])
return normalizeApiConfiguration(apiConfiguration, mode)
}, [apiConfiguration, mode])
const selectFilesAndImages = useCallback(async () => {
try {
@@ -43,7 +43,7 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
onClose,
onScrollToMessage,
}) => {
const { apiConfiguration, currentTaskItem, checkpointTrackerErrorMessage, clineMessages, navigateToSettings, chatSettings } =
const { apiConfiguration, currentTaskItem, checkpointTrackerErrorMessage, clineMessages, navigateToSettings, mode } =
useExtensionState()
const [isTaskExpanded, setIsTaskExpanded] = useState(true)
const [isTextExpanded, setIsTextExpanded] = useState(false)
@@ -51,10 +51,7 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
const textContainerRef = useRef<HTMLDivElement>(null)
const textRef = useRef<HTMLDivElement>(null)
const { selectedModelInfo } = useMemo(
() => normalizeApiConfiguration(apiConfiguration, chatSettings.mode),
[apiConfiguration, chatSettings.mode],
)
const { selectedModelInfo } = useMemo(() => normalizeApiConfiguration(apiConfiguration, mode), [apiConfiguration, mode])
const contextWindow = selectedModelInfo?.contextWindow
// Open task header when checkpoint tracker error message is set
@@ -133,7 +130,7 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
}, [task.text, windowWidth, isTaskExpanded])
const isCostAvailable = useMemo(() => {
const modeFields = getModeSpecificFields(apiConfiguration, chatSettings.mode)
const modeFields = getModeSpecificFields(apiConfiguration, mode)
const openAiCompatHasPricing =
modeFields.apiProvider === "openai" &&
modeFields.openAiModelInfo?.inputPrice &&
@@ -144,7 +141,7 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
return (
modeFields.apiProvider !== "vscode-lm" && modeFields.apiProvider !== "ollama" && modeFields.apiProvider !== "lmstudio"
)
}, [apiConfiguration, chatSettings.mode])
}, [apiConfiguration, mode])
const shouldShowPromptCacheInfo = () => {
// Hybrid logic: Show cache info if we have actual cache data,
@@ -14,27 +14,23 @@ import { PlanActMode, TogglePlanActModeRequest } from "@shared/proto/cline/state
// Styled component for Act Mode text with more specific styling
const ActModeHighlight: React.FC = () => {
const { chatSettings } = useExtensionState()
const { mode } = useExtensionState()
return (
<span
onClick={() => {
// Only toggle to Act mode if we're currently in Plan mode
if (chatSettings.mode === "plan") {
StateServiceClient.togglePlanActMode(
if (mode === "plan") {
StateServiceClient.togglePlanActModeProto(
TogglePlanActModeRequest.create({
chatSettings: {
mode: PlanActMode.ACT,
preferredLanguage: chatSettings.preferredLanguage,
openAiReasoningEffort: chatSettings.openAIReasoningEffort,
},
mode: PlanActMode.ACT,
}),
)
}
}}
title={chatSettings.mode === "plan" ? "Click to toggle to Act Mode" : "Already in Act Mode"}
title={mode === "plan" ? "Click to toggle to Act Mode" : "Already in Act Mode"}
className={`text-[var(--vscode-textLink-foreground)] inline-flex items-center gap-1 ${
chatSettings.mode === "plan" ? "hover:opacity-90 cursor-pointer" : "cursor-default opacity-60"
mode === "plan" ? "hover:opacity-90 cursor-pointer" : "cursor-default opacity-60"
}`}>
<div className="p-1 rounded-[12px] bg-[var(--vscode-editor-background)] flex items-center justify-end w-4 border-[1px] border-[var(--vscode-input-border)]">
<div className="rounded-full bg-[var(--vscode-textLink-foreground)] w-2 h-2" />
@@ -37,7 +37,7 @@ import { VSCodeLmProvider } from "./providers/VSCodeLmProvider"
import { LMStudioProvider } from "./providers/LMStudioProvider"
import { useApiConfigurationHandlers } from "./utils/useApiConfigurationHandlers"
import { GroqProvider } from "./providers/GroqProvider"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
import { HuaweiCloudMaasProvider } from "./providers/HuaweiCloudMaasProvider"
interface ApiOptionsProps {
@@ -1,19 +1,17 @@
import { EmptyRequest } from "@shared/proto/cline/common"
import { VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import Fuse from "fuse.js"
import React, { KeyboardEvent, memo, useEffect, useMemo, useRef, useState } from "react"
import { useRemark } from "react-remark"
import React, { KeyboardEvent, useEffect, useMemo, useRef, useState } from "react"
import { useMount } from "react-use"
import { groqDefaultModelId, groqModels } from "@shared/api"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { ModelsServiceClient } from "../../services/grpc-client"
import { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock"
import { highlight } from "../history/HistoryView"
import { ModelInfoView } from "./common/ModelInfoView"
import { normalizeApiConfiguration } from "./utils/providerUtils"
import { useApiConfigurationHandlers } from "./utils/useApiConfigurationHandlers"
import { getModeSpecificFields } from "./utils/providerUtils"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
export interface GroqModelPickerProps {
isPopup?: boolean
@@ -4,7 +4,7 @@ import Fuse from "fuse.js"
import React, { KeyboardEvent, useEffect, useMemo, useRef, useState } from "react"
import { useMount } from "react-use"
import { huggingFaceDefaultModelId, huggingFaceModels } from "@shared/api"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { ModelsServiceClient } from "../../services/grpc-client"
import { highlight } from "../history/HistoryView"
@@ -15,7 +15,7 @@ import { getModeSpecificFields, normalizeApiConfiguration } from "./utils/provid
import FeaturedModelCard from "./FeaturedModelCard"
import ThinkingBudgetSlider from "./ThinkingBudgetSlider"
import { useApiConfigurationHandlers } from "./utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
// Star icon for favorites
const StarIcon = ({ isFavorite, onClick }: { isFavorite: boolean; onClick: (e: React.MouseEvent) => void }) => {
@@ -2,21 +2,12 @@ import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import React from "react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { updateSetting } from "./utils/settingsHandlers"
import { convertChatSettingsToProtoChatSettings } from "@shared/proto-conversions/state/chat-settings-conversion"
const PreferredLanguageSetting: React.FC = () => {
const { chatSettings } = useExtensionState()
const { preferredLanguage } = useExtensionState()
const handleLanguageChange = (newLanguage: string) => {
if (!chatSettings) return
const updatedChatSettings = {
...chatSettings,
preferredLanguage: newLanguage,
}
const protoChatSettings = convertChatSettingsToProtoChatSettings(updatedChatSettings)
updateSetting("chatSettings", protoChatSettings)
updateSetting("preferredLanguage", newLanguage)
}
return (
@@ -26,7 +17,7 @@ const PreferredLanguageSetting: React.FC = () => {
</label>
<VSCodeDropdown
id="preferred-language-dropdown"
currentValue={chatSettings.preferredLanguage || "English"}
currentValue={preferredLanguage || "English"}
onChange={(e: any) => {
handleLanguageChange(e.target.value)
}}
@@ -14,7 +14,7 @@ import { ModelInfoView } from "./common/ModelInfoView"
import { getModeSpecificFields, normalizeApiConfiguration } from "./utils/providerUtils"
import ThinkingBudgetSlider from "./ThinkingBudgetSlider"
import { useApiConfigurationHandlers } from "./utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
export interface RequestyModelPickerProps {
isPopup?: boolean
@@ -2,7 +2,7 @@ import HeroTooltip from "@/components/common/HeroTooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { StateServiceClient } from "@/services/grpc-client"
import { ExtensionMessage } from "@shared/ExtensionMessage"
import { PlanActMode, ResetStateRequest, TogglePlanActModeRequest } from "@shared/proto/cline/state"
import { ResetStateRequest } from "@shared/proto/cline/state"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { CheckCheck, FlaskConical, Info, LucideIcon, Settings, SquareMousePointer, SquareTerminal, Webhook } from "lucide-react"
import { useCallback, useEffect, useRef, useState } from "react"
@@ -104,7 +104,7 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
const [activeTab, setActiveTab] = useState<string>(targetSection || SETTINGS_TABS[0].id)
// Track if we're currently switching modes
const { version, chatSettings } = useExtensionState()
const { version } = useExtensionState()
const handleMessage = useCallback((event: MessageEvent) => {
const message: ExtensionMessage = event.data
@@ -5,7 +5,7 @@ import styled from "styled-components"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "./utils/useApiConfigurationHandlers"
import { getModeSpecificFields } from "./utils/providerUtils"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
// Constants
const DEFAULT_MIN_VALID_TOKENS = 1024
@@ -7,7 +7,7 @@ import { getModeSpecificFields, normalizeApiConfiguration } from "../utils/provi
import ThinkingBudgetSlider from "../ThinkingBudgetSlider"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
// Anthropic models that support thinking/reasoning mode
export const SUPPORTED_ANTHROPIC_THINKING_MODELS = [
@@ -6,8 +6,7 @@ import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the AskSageProvider component
*/
@@ -8,8 +8,7 @@ import ThinkingBudgetSlider from "../ThinkingBudgetSlider"
import { normalizeApiConfiguration, getModeSpecificFields } from "../utils/providerUtils"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
// Z-index constants for proper dropdown layering
const DROPDOWN_Z_INDEX = 1000
@@ -5,8 +5,7 @@ import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the CerebrasProvider component
*/
@@ -7,8 +7,7 @@ import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandler
import { useExtensionState } from "@/context/ExtensionStateContext"
import { SUPPORTED_ANTHROPIC_THINKING_MODELS } from "./AnthropicProvider"
import ThinkingBudgetSlider from "../ThinkingBudgetSlider"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the ClaudeCodeProvider component
*/
@@ -6,8 +6,7 @@ import OpenRouterModelPicker, { OPENROUTER_MODEL_PICKER_Z_INDEX } from "../OpenR
import { DropdownContainer } from "../common/ModelSelector"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the ClineProvider component
*/
@@ -5,8 +5,7 @@ import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the DeepSeekProvider component
*/
@@ -5,8 +5,7 @@ import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the DoubaoProvider component
*/
@@ -4,8 +4,7 @@ import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { getModeSpecificFields } from "../utils/providerUtils"
import { ApiConfiguration } from "@shared/api"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the FireworksProvider component
*/
@@ -7,8 +7,7 @@ import { normalizeApiConfiguration } from "../utils/providerUtils"
import ThinkingBudgetSlider from "../ThinkingBudgetSlider"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
// Gemini models that support thinking/reasoning mode
const SUPPORTED_THINKING_MODELS = ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite-preview-06-17"]
@@ -1,4 +1,4 @@
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
import { ApiKeyField } from "../common/ApiKeyField"
import GroqModelPicker from "../GroqModelPicker"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
@@ -1,5 +1,5 @@
import { huaweiCloudMaasModels } from "@shared/api"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
import { ApiKeyField } from "../common/ApiKeyField"
import { ModelSelector } from "../common/ModelSelector"
import { ModelInfoView } from "../common/ModelInfoView"
@@ -1,8 +1,5 @@
import { huggingFaceModels } from "@shared/api"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
import { DebouncedTextField } from "../common/DebouncedTextField"
import { ModelSelector } from "../common/ModelSelector"
import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
@@ -8,8 +8,7 @@ import { BaseUrlField } from "../common/BaseUrlField"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { getModeSpecificFields } from "../utils/providerUtils"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the LMStudioProvider component
*/
@@ -8,8 +8,7 @@ import { ModelInfoView } from "../common/ModelInfoView"
import ThinkingBudgetSlider from "../ThinkingBudgetSlider"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the LiteLlmProvider component
*/
@@ -1,12 +1,11 @@
import { ApiConfiguration, mistralModels } from "@shared/api"
import { mistralModels } from "@shared/api"
import { ApiKeyField } from "../common/ApiKeyField"
import { ModelSelector } from "../common/ModelSelector"
import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the MistralProvider component
*/
@@ -7,8 +7,7 @@ import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandler
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useState } from "react"
import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the MoonshotProvider component
*/
@@ -5,8 +5,7 @@ import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the NebiusProvider component
*/
@@ -9,8 +9,7 @@ import { BaseUrlField } from "../common/BaseUrlField"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { getModeSpecificFields } from "../utils/providerUtils"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the OllamaProvider component
*/
@@ -11,8 +11,7 @@ import { BaseUrlField } from "../common/BaseUrlField"
import { normalizeApiConfiguration, getModeSpecificFields } from "../utils/providerUtils"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the OpenAICompatibleProvider component
*/
@@ -5,8 +5,7 @@ import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the OpenAINativeProvider component
*/
@@ -1,4 +1,3 @@
import { ApiConfiguration } from "@shared/api"
import { VSCodeCheckbox, VSCodeDropdown, VSCodeOption, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { DebouncedTextField } from "../common/DebouncedTextField"
import { DropdownContainer } from "../common/ModelSelector"
@@ -10,8 +9,7 @@ import OpenRouterModelPicker, { OPENROUTER_MODEL_PICKER_Z_INDEX } from "../OpenR
import { formatPrice } from "../utils/pricingUtils"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Component to display OpenRouter balance information
*/
@@ -8,7 +8,7 @@ import ThinkingBudgetSlider from "../ThinkingBudgetSlider"
import { DROPDOWN_Z_INDEX } from "../ApiOptions"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
import { useMemo } from "react"
const SUPPORTED_THINKING_MODELS = [
@@ -1,4 +1,4 @@
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
import { ApiKeyField } from "../common/ApiKeyField"
import RequestyModelPicker from "../RequestyModelPicker"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
@@ -5,8 +5,7 @@ import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the SambanovaProvider component
*/
@@ -6,8 +6,7 @@ import { ModelInfoView } from "../common/ModelInfoView"
import { normalizeApiConfiguration } from "../utils/providerUtils"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the SapAiCoreProvider component
*/
@@ -1,11 +1,9 @@
import { ApiConfiguration } from "@shared/api"
import { DebouncedTextField } from "../common/DebouncedTextField"
import { ApiKeyField } from "../common/ApiKeyField"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { getModeSpecificFields } from "../utils/providerUtils"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the TogetherProvider component
*/
@@ -8,8 +8,7 @@ import { DropdownContainer, DROPDOWN_Z_INDEX } from "../ApiOptions"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { getModeSpecificFields } from "../utils/providerUtils"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
interface VSCodeLmProviderProps {
currentMode: Mode
}
@@ -8,8 +8,7 @@ import { DropdownContainer, DROPDOWN_Z_INDEX } from "../ApiOptions"
import ThinkingBudgetSlider from "../ThinkingBudgetSlider"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the VertexProvider component
*/
@@ -1,6 +1,6 @@
import { xaiModels } from "@shared/api"
import { VSCodeCheckbox, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { useState, useEffect } from "react"
import { useState } from "react"
import { ApiKeyField } from "../common/ApiKeyField"
import { ModelSelector, DropdownContainer } from "../common/ModelSelector"
import { ModelInfoView } from "../common/ModelInfoView"
@@ -8,8 +8,7 @@ import { getModeSpecificFields, normalizeApiConfiguration } from "../utils/provi
import { DROPDOWN_Z_INDEX } from "../ApiOptions"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Props for the XaiProvider component
*/
@@ -8,15 +8,14 @@ import { UpdateSettingsRequest } from "@shared/proto/cline/state"
import { useState } from "react"
import { syncModeConfigurations } from "../utils/providerUtils"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
interface ApiConfigurationSectionProps {
renderSectionHeader: (tabId: string) => JSX.Element | null
}
const ApiConfigurationSection = ({ renderSectionHeader }: ApiConfigurationSectionProps) => {
const { planActSeparateModelsSetting, chatSettings, apiConfiguration } = useExtensionState()
const [currentTab, setCurrentTab] = useState<Mode>(chatSettings.mode)
const { planActSeparateModelsSetting, mode, apiConfiguration } = useExtensionState()
const [currentTab, setCurrentTab] = useState<Mode>(mode)
const { handleFieldsChange } = useApiConfigurationHandlers()
return (
<div>
@@ -54,7 +53,7 @@ const ApiConfigurationSection = ({ renderSectionHeader }: ApiConfigurationSectio
</div>
</div>
) : (
<ApiOptions showModelOptions={true} currentMode={chatSettings.mode} />
<ApiOptions showModelOptions={true} currentMode={mode} />
)}
<div className="mb-[5px]">
@@ -1,9 +1,8 @@
import { VSCodeCheckbox, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { memo } from "react"
import { OpenAIReasoningEffort } from "@shared/ChatSettings"
import { OpenaiReasoningEffort } from "@shared/storage/types"
import { updateSetting } from "../utils/settingsHandlers"
import { convertChatSettingsToProtoChatSettings } from "@shared/proto-conversions/state/chat-settings-conversion"
import { McpDisplayMode } from "@shared/McpDisplayMode"
import McpDisplayModeDropdown from "@/components/mcp/chat-display/McpDisplayModeDropdown"
import Section from "../Section"
@@ -13,19 +12,11 @@ interface FeatureSettingsSectionProps {
}
const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionProps) => {
const { enableCheckpointsSetting, mcpMarketplaceEnabled, mcpDisplayMode, mcpResponsesCollapsed, chatSettings } =
const { enableCheckpointsSetting, mcpMarketplaceEnabled, mcpDisplayMode, mcpResponsesCollapsed, openaiReasoningEffort } =
useExtensionState()
const handleReasoningEffortChange = (newValue: OpenAIReasoningEffort) => {
if (!chatSettings) return
const updatedChatSettings = {
...chatSettings,
openAIReasoningEffort: newValue,
}
const protoChatSettings = convertChatSettingsToProtoChatSettings(updatedChatSettings)
updateSetting("chatSettings", protoChatSettings)
const handleReasoningEffortChange = (newValue: OpenaiReasoningEffort) => {
updateSetting("openaiReasoningEffort", newValue)
}
return (
@@ -98,9 +89,9 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
</label>
<VSCodeDropdown
id="openai-reasoning-effort-dropdown"
currentValue={chatSettings.openAIReasoningEffort || "medium"}
currentValue={openaiReasoningEffort || "medium"}
onChange={(e: any) => {
const newValue = e.target.currentValue as OpenAIReasoningEffort
const newValue = e.target.currentValue as OpenaiReasoningEffort
handleReasoningEffortChange(newValue)
}}
className="w-full">
@@ -51,7 +51,7 @@ import {
huaweiCloudMaasModels,
huaweiCloudMaasDefaultModelId,
} from "@shared/api"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
/**
* Interface for normalized API configuration
@@ -1,10 +1,9 @@
import { useExtensionState } from "@/context/ExtensionStateContext"
import { ModelsServiceClient } from "@/services/grpc-client"
import { ApiConfiguration } from "@shared/api"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
import { convertApiConfigurationToProto } from "@shared/proto-conversions/models/api-configuration-conversion"
import { UpdateApiConfigurationRequest } from "@shared/proto/cline/models"
import { useCallback } from "react"
export const useApiConfigurationHandlers = () => {
const { apiConfiguration, planActSeparateModelsSetting } = useExtensionState()
@@ -8,7 +8,7 @@ import { AccountServiceClient, StateServiceClient } from "@/services/grpc-client
import { EmptyRequest, BooleanRequest } from "@shared/proto/cline/common"
const WelcomeView = memo(() => {
const { apiConfiguration, chatSettings } = useExtensionState()
const { apiConfiguration, mode } = useExtensionState()
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
const [showApiOptions, setShowApiOptions] = useState(false)
@@ -29,8 +29,8 @@ const WelcomeView = memo(() => {
}
useEffect(() => {
setApiErrorMessage(validateApiConfiguration(chatSettings.mode, apiConfiguration))
}, [apiConfiguration, chatSettings.mode])
setApiErrorMessage(validateApiConfiguration(mode, apiConfiguration))
}, [apiConfiguration, mode])
return (
<div className="fixed inset-0 p-0 flex flex-col">
@@ -70,7 +70,7 @@ const WelcomeView = memo(() => {
<div className="mt-4.5">
{showApiOptions && (
<div>
<ApiOptions showModelOptions={false} currentMode={chatSettings.mode} />
<ApiOptions showModelOptions={false} currentMode={mode} />
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} className="mt-0.75">
Let's go!
</VSCodeButton>
@@ -4,7 +4,6 @@ import "../../../src/shared/webview/types"
import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "@shared/AutoApprovalSettings"
import { findLastIndex } from "@shared/array"
import { DEFAULT_BROWSER_SETTINGS } from "@shared/BrowserSettings"
import { type ChatSettings, DEFAULT_CHAT_SETTINGS } from "@shared/ChatSettings"
import { DEFAULT_PLATFORM, type ExtensionState } from "@shared/ExtensionMessage"
import { DEFAULT_MCP_DISPLAY_MODE } from "@shared/McpDisplayMode"
import type { UserInfo } from "@shared/proto/cline/account"
@@ -59,7 +58,6 @@ interface ExtensionStateContextType extends ExtensionState {
// Setters
setShowAnnouncement: (value: boolean) => void
setShouldShowAnnouncement: (value: boolean) => void
setChatSettings: (value: ChatSettings) => void
setMcpServers: (value: McpServer[]) => void
setRequestyModels: (value: Record<string, ModelInfo>) => void
setGroqModels: (value: Record<string, ModelInfo>) => void
@@ -176,7 +174,9 @@ export const ExtensionStateContextProvider: React.FC<{
shouldShowAnnouncement: false,
autoApprovalSettings: DEFAULT_AUTO_APPROVAL_SETTINGS,
browserSettings: DEFAULT_BROWSER_SETTINGS,
chatSettings: DEFAULT_CHAT_SETTINGS,
preferredLanguage: "English",
openaiReasoningEffort: "medium",
mode: "act",
platform: DEFAULT_PLATFORM,
telemetrySetting: "unset",
distinctId: "",
@@ -686,38 +686,6 @@ export const ExtensionStateContextProvider: React.FC<{
setMcpMarketplaceCatalog: (catalog: McpMarketplaceCatalog) => setMcpMarketplaceCatalog(catalog),
setShowMcp,
closeMcpView,
setChatSettings: async (value) => {
setState((prevState) => ({
...prevState,
chatSettings: value,
}))
try {
// Import the conversion functions
const { convertApiConfigurationToProtoApiConfiguration } = await import(
"@shared/proto-conversions/state/settings-conversion"
)
const { convertChatSettingsToProtoChatSettings } = await import(
"@shared/proto-conversions/state/chat-settings-conversion"
)
await StateServiceClient.updateSettings(
UpdateSettingsRequest.create({
chatSettings: convertChatSettingsToProtoChatSettings(value),
apiConfiguration: state.apiConfiguration
? convertApiConfigurationToProtoApiConfiguration(state.apiConfiguration)
: undefined,
telemetrySetting: state.telemetrySetting,
planActSeparateModelsSetting: state.planActSeparateModelsSetting,
enableCheckpointsSetting: state.enableCheckpointsSetting,
mcpMarketplaceEnabled: state.mcpMarketplaceEnabled,
mcpDisplayMode: state.mcpDisplayMode,
mcpResponsesCollapsed: state.mcpResponsesCollapsed,
}),
)
} catch (error) {
console.error("Failed to update chat settings:", error)
}
},
setGlobalClineRulesToggles: (toggles) =>
setState((prevState) => ({
...prevState,
+1 -1
View File
@@ -1,6 +1,6 @@
import { ApiConfiguration, openRouterDefaultModelId, ModelInfo } from "@shared/api"
import { getModeSpecificFields } from "@/components/settings/utils/providerUtils"
import { Mode } from "@shared/ChatSettings"
import { Mode } from "@shared/storage/types"
export function validateApiConfiguration(currentMode: Mode, apiConfiguration?: ApiConfiguration): string | undefined {
if (apiConfiguration) {