Merge branch 'main' into new_unified

This commit is contained in:
Daniel
2025-01-15 11:53:27 -05:00
committed by GitHub
116 changed files with 16285 additions and 2361 deletions

View File

@@ -12,19 +12,25 @@ import { selectImages } from "../../integrations/misc/process-images"
import { getTheme } from "../../integrations/theme/getTheme"
import WorkspaceTracker from "../../integrations/workspace/WorkspaceTracker"
import { McpHub } from "../../services/mcp/McpHub"
import { ApiProvider, ModelInfo } from "../../shared/api"
import { ApiConfiguration, ApiProvider, ModelInfo } from "../../shared/api"
import { findLast } from "../../shared/array"
import { ExtensionMessage } from "../../shared/ExtensionMessage"
import { ApiConfigMeta, ExtensionMessage } from "../../shared/ExtensionMessage"
import { HistoryItem } from "../../shared/HistoryItem"
import { WebviewMessage } from "../../shared/WebviewMessage"
import { WebviewMessage, PromptMode } from "../../shared/WebviewMessage"
import { defaultPrompts } from "../../shared/modes"
import { SYSTEM_PROMPT, addCustomInstructions } from "../prompts/system"
import { fileExistsAtPath } from "../../utils/fs"
import { Cline } from "../Cline"
import { openMention } from "../mentions"
import { getNonce } from "./getNonce"
import { getUri } from "./getUri"
import { playSound, setSoundEnabled, setSoundVolume } from "../../utils/sound"
import { checkExistKey } from "../../shared/checkExistApiConfig"
import { enhancePrompt } from "../../utils/enhance-prompt"
import { getCommitInfo, searchCommits, getWorkingState } from "../../utils/git"
import { ConfigManager } from "../config/ConfigManager"
import { Mode } from "../prompts/types"
import { codeMode, CustomPrompts } from "../../shared/modes"
/*
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
@@ -85,7 +91,14 @@ type GlobalStateKey =
| "mcpEnabled"
| "alwaysApproveResubmit"
| "requestDelaySeconds"
| "experimentalDiffStrategy"
| "currentApiConfigName"
| "listApiConfigMeta"
| "mode"
| "modeApiConfigs"
| "customPrompts"
| "enhancementApiConfigId",
| "experimentalDiffStrategy"
export const GlobalFileNames = {
apiConversationHistory: "api_conversation_history.json",
uiMessages: "ui_messages.json",
@@ -103,7 +116,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
private cline?: Cline
private workspaceTracker?: WorkspaceTracker
mcpHub?: McpHub
private latestAnnouncementId = "dec-10-2024" // update to some unique identifier when we add a new announcement
private latestAnnouncementId = "jan-13-2025-custom-prompt" // update to some unique identifier when we add a new announcement
configManager: ConfigManager
constructor(
readonly context: vscode.ExtensionContext,
@@ -113,6 +127,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
ClineProvider.activeInstances.add(this)
this.workspaceTracker = new WorkspaceTracker(this)
this.mcpHub = new McpHub(this)
this.configManager = new ConfigManager(this.context)
}
/*
@@ -228,20 +243,27 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.outputChannel.appendLine("Webview view resolved")
}
async initClineWithTask(task?: string, images?: string[]) {
public async initClineWithTask(task?: string, images?: string[]) {
await this.clearTask()
const {
apiConfiguration,
customInstructions,
customPrompts,
diffEnabled,
fuzzyMatchThreshold,
experimentalDiffStrategy
mode,
customInstructions: globalInstructions,
experimentalDiffStrategy
} = await this.getState()
const modeInstructions = customPrompts?.[mode]?.customInstructions
const effectiveInstructions = [globalInstructions, modeInstructions]
.filter(Boolean)
.join('\n\n')
this.cline = new Cline(
this,
apiConfiguration,
customInstructions,
effectiveInstructions,
diffEnabled,
fuzzyMatchThreshold,
task,
@@ -251,20 +273,27 @@ export class ClineProvider implements vscode.WebviewViewProvider {
)
}
async initClineWithHistoryItem(historyItem: HistoryItem) {
public async initClineWithHistoryItem(historyItem: HistoryItem) {
await this.clearTask()
const {
apiConfiguration,
customInstructions,
customPrompts,
diffEnabled,
fuzzyMatchThreshold,
experimentalDiffStrategy
mode,
customInstructions: globalInstructions,
experimentalDiffStrategy
} = await this.getState()
const modeInstructions = customPrompts?.[mode]?.customInstructions
const effectiveInstructions = [globalInstructions, modeInstructions]
.filter(Boolean)
.join('\n\n')
this.cline = new Cline(
this,
apiConfiguration,
customInstructions,
effectiveInstructions,
diffEnabled,
fuzzyMatchThreshold,
undefined,
@@ -274,8 +303,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
)
}
// Send any JSON serializable data to the react app
async postMessageToWebview(message: ExtensionMessage) {
public async postMessageToWebview(message: ExtensionMessage) {
await this.view?.webview.postMessage(message)
}
@@ -327,15 +355,15 @@ export class ClineProvider implements vscode.WebviewViewProvider {
// Use a nonce to only allow a specific script to be run.
/*
content security policy of your webview to only allow scripts that have a specific nonce
create a content security policy meta tag so that only loading scripts with a nonce is allowed
As your extension grows you will likely want to add custom styles, fonts, and/or images to your webview. If you do, you will need to update the content security policy meta tag to explicity allow for these resources. E.g.
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; font-src ${webview.cspSource}; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}';">
content security policy of your webview to only allow scripts that have a specific nonce
create a content security policy meta tag so that only loading scripts with a nonce is allowed
As your extension grows you will likely want to add custom styles, fonts, and/or images to your webview. If you do, you will need to update the content security policy meta tag to explicity allow for these resources. E.g.
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; font-src ${webview.cspSource}; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}';">
- 'unsafe-inline' is required for styles due to vscode-webview-toolkit's dynamic style injection
- since we pass base64 images to the webview, we need to specify img-src ${webview.cspSource} data:;
in meta tag we add nonce attribute: A cryptographic nonce (only used once) to allow scripts. The server must generate a unique nonce value each time it transmits a policy. It is critical to provide a nonce that cannot be guessed as bypassing a resource's policy is otherwise trivial.
*/
in meta tag we add nonce attribute: A cryptographic nonce (only used once) to allow scripts. The server must generate a unique nonce value each time it transmits a policy. It is critical to provide a nonce that cannot be guessed as bypassing a resource's policy is otherwise trivial.
*/
const nonce = getNonce()
// Tip: Install the es6-string-html VS Code extension to enable code highlighting below
@@ -371,6 +399,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
async (message: WebviewMessage) => {
switch (message.type) {
case "webviewDidLaunch":
this.postStateToWebview()
this.workspaceTracker?.initializeFilePaths() // don't await
getTheme().then((theme) =>
@@ -416,6 +445,55 @@ export class ClineProvider implements vscode.WebviewViewProvider {
}
}
})
this.configManager.ListConfig().then(async (listApiConfig) => {
if (!listApiConfig) {
return
}
if (listApiConfig.length === 1) {
// check if first time init then sync with exist config
if (!checkExistKey(listApiConfig[0])) {
const {
apiConfiguration,
} = await this.getState()
await this.configManager.SaveConfig(listApiConfig[0].name ?? "default", apiConfiguration)
listApiConfig[0].apiProvider = apiConfiguration.apiProvider
}
}
let currentConfigName = await this.getGlobalState("currentApiConfigName") as string
if (currentConfigName) {
if (!await this.configManager.HasConfig(currentConfigName)) {
// current config name not valid, get first config in list
await this.updateGlobalState("currentApiConfigName", listApiConfig?.[0]?.name)
if (listApiConfig?.[0]?.name) {
const apiConfig = await this.configManager.LoadConfig(listApiConfig?.[0]?.name);
await Promise.all([
this.updateGlobalState("listApiConfigMeta", listApiConfig),
this.postMessageToWebview({ type: "listApiConfig", listApiConfig }),
this.updateApiConfiguration(apiConfig),
])
await this.postStateToWebview()
return
}
}
}
await Promise.all(
[
await this.updateGlobalState("listApiConfigMeta", listApiConfig),
await this.postMessageToWebview({ type: "listApiConfig", listApiConfig })
]
)
}).catch(console.error);
break
case "newTask":
// Code that should run in response to the hello message command
@@ -430,70 +508,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
break
case "apiConfiguration":
if (message.apiConfiguration) {
const {
apiProvider,
apiModelId,
apiKey,
glamaModelId,
glamaModelInfo,
glamaApiKey,
openRouterApiKey,
awsAccessKey,
awsSecretKey,
awsSessionToken,
awsRegion,
awsUseCrossRegionInference,
vertexProjectId,
vertexRegion,
openAiBaseUrl,
openAiApiKey,
openAiModelId,
ollamaModelId,
ollamaBaseUrl,
lmStudioModelId,
lmStudioBaseUrl,
anthropicBaseUrl,
geminiApiKey,
openAiNativeApiKey,
azureApiVersion,
openAiStreamingEnabled,
openRouterModelId,
openRouterModelInfo,
openRouterUseMiddleOutTransform,
} = message.apiConfiguration
await this.updateGlobalState("apiProvider", apiProvider)
await this.updateGlobalState("apiModelId", apiModelId)
await this.storeSecret("apiKey", apiKey)
await this.updateGlobalState("glamaModelId", glamaModelId)
await this.updateGlobalState("glamaModelInfo", glamaModelInfo)
await this.storeSecret("glamaApiKey", glamaApiKey)
await this.storeSecret("openRouterApiKey", openRouterApiKey)
await this.storeSecret("awsAccessKey", awsAccessKey)
await this.storeSecret("awsSecretKey", awsSecretKey)
await this.storeSecret("awsSessionToken", awsSessionToken)
await this.updateGlobalState("awsRegion", awsRegion)
await this.updateGlobalState("awsUseCrossRegionInference", awsUseCrossRegionInference)
await this.updateGlobalState("vertexProjectId", vertexProjectId)
await this.updateGlobalState("vertexRegion", vertexRegion)
await this.updateGlobalState("openAiBaseUrl", openAiBaseUrl)
await this.storeSecret("openAiApiKey", openAiApiKey)
await this.updateGlobalState("openAiModelId", openAiModelId)
await this.updateGlobalState("ollamaModelId", ollamaModelId)
await this.updateGlobalState("ollamaBaseUrl", ollamaBaseUrl)
await this.updateGlobalState("lmStudioModelId", lmStudioModelId)
await this.updateGlobalState("lmStudioBaseUrl", lmStudioBaseUrl)
await this.updateGlobalState("anthropicBaseUrl", anthropicBaseUrl)
await this.storeSecret("geminiApiKey", geminiApiKey)
await this.storeSecret("openAiNativeApiKey", openAiNativeApiKey)
await this.storeSecret("deepSeekApiKey", message.apiConfiguration.deepSeekApiKey)
await this.updateGlobalState("azureApiVersion", azureApiVersion)
await this.updateGlobalState("openAiStreamingEnabled", openAiStreamingEnabled)
await this.updateGlobalState("openRouterModelId", openRouterModelId)
await this.updateGlobalState("openRouterModelInfo", openRouterModelInfo)
await this.updateGlobalState("openRouterUseMiddleOutTransform", openRouterUseMiddleOutTransform)
if (this.cline) {
this.cline.api = buildApiHandler(message.apiConfiguration)
}
await this.updateApiConfiguration(message.apiConfiguration)
}
await this.postStateToWebview()
break
@@ -572,13 +587,13 @@ export class ClineProvider implements vscode.WebviewViewProvider {
if (message?.values?.baseUrl && message?.values?.apiKey) {
const openAiModels = await this.getOpenAiModels(message?.values?.baseUrl, message?.values?.apiKey)
this.postMessageToWebview({ type: "openAiModels", openAiModels })
}
}
break
case "openImage":
openImage(message.text!)
break
case "openFile":
openFile(message.text!)
openFile(message.text!, message.values as { create?: boolean; content?: string })
break
case "openMention":
openMention(message.text)
@@ -703,6 +718,90 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("terminalOutputLineLimit", message.value)
await this.postStateToWebview()
break
case "mode":
const newMode = message.text as Mode
await this.updateGlobalState("mode", newMode)
// Load the saved API config for the new mode if it exists
const savedConfigId = await this.configManager.GetModeConfigId(newMode)
const listApiConfig = await this.configManager.ListConfig()
// Update listApiConfigMeta first to ensure UI has latest data
await this.updateGlobalState("listApiConfigMeta", listApiConfig)
// If this mode has a saved config, use it
if (savedConfigId) {
const config = listApiConfig?.find(c => c.id === savedConfigId)
if (config?.name) {
const apiConfig = await this.configManager.LoadConfig(config.name)
await Promise.all([
this.updateGlobalState("currentApiConfigName", config.name),
this.updateApiConfiguration(apiConfig)
])
}
} else {
// If no saved config for this mode, save current config as default
const currentApiConfigName = await this.getGlobalState("currentApiConfigName")
if (currentApiConfigName) {
const config = listApiConfig?.find(c => c.name === currentApiConfigName)
if (config?.id) {
await this.configManager.SetModeConfig(newMode, config.id)
}
}
}
await this.postStateToWebview()
break
case "updateEnhancedPrompt":
const existingPrompts = await this.getGlobalState("customPrompts") || {}
const updatedPrompts = {
...existingPrompts,
enhance: message.text
}
await this.updateGlobalState("customPrompts", updatedPrompts)
// Get current state and explicitly include customPrompts
const currentState = await this.getState()
const stateWithPrompts = {
...currentState,
customPrompts: updatedPrompts
}
// Post state with prompts
this.view?.webview.postMessage({
type: "state",
state: stateWithPrompts
})
break
case "updatePrompt":
if (message.promptMode && message.customPrompt !== undefined) {
const existingPrompts = await this.getGlobalState("customPrompts") || {}
const updatedPrompts = {
...existingPrompts,
[message.promptMode]: message.customPrompt
}
await this.updateGlobalState("customPrompts", updatedPrompts)
// Get current state and explicitly include customPrompts
const currentState = await this.getState()
const stateWithPrompts = {
...currentState,
customPrompts: updatedPrompts
}
// Post state with prompts
this.view?.webview.postMessage({
type: "state",
state: stateWithPrompts
})
}
break
case "deleteMessage": {
const answer = await vscode.window.showInformationMessage(
"What would you like to delete?",
@@ -773,16 +872,28 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("screenshotQuality", message.value)
await this.postStateToWebview()
break
case "enhancementApiConfigId":
await this.updateGlobalState("enhancementApiConfigId", message.text)
await this.postStateToWebview()
break
case "enhancePrompt":
if (message.text) {
try {
const { apiConfiguration } = await this.getState()
const enhanceConfig = {
...apiConfiguration,
apiProvider: "openrouter" as const,
openRouterModelId: "gpt-4o",
const { apiConfiguration, customPrompts, listApiConfigMeta, enhancementApiConfigId } = await this.getState()
// Try to get enhancement config first, fall back to current config
let configToUse: ApiConfiguration = apiConfiguration
if (enhancementApiConfigId) {
const config = listApiConfigMeta?.find(c => c.id === enhancementApiConfigId)
if (config?.name) {
const loadedConfig = await this.configManager.LoadConfig(config.name)
if (loadedConfig.apiProvider) {
configToUse = loadedConfig
}
}
}
const enhancedPrompt = await enhancePrompt(enhanceConfig, message.text)
const enhancedPrompt = await enhancePrompt(configToUse, message.text, customPrompts?.enhance)
await this.postMessageToWebview({
type: "enhancedPrompt",
text: enhancedPrompt
@@ -790,11 +901,45 @@ export class ClineProvider implements vscode.WebviewViewProvider {
} catch (error) {
console.error("Error enhancing prompt:", error)
vscode.window.showErrorMessage("Failed to enhance prompt")
await this.postMessageToWebview({
type: "enhancedPrompt"
})
}
}
break
case "getSystemPrompt":
try {
const { apiConfiguration, customPrompts, customInstructions, preferredLanguage, browserViewportSize, mcpEnabled } = await this.getState()
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) || ''
const mode = message.mode ?? codeMode
const instructions = await addCustomInstructions(
{ customInstructions, customPrompts, preferredLanguage },
cwd,
mode
)
const systemPrompt = await SYSTEM_PROMPT(
cwd,
apiConfiguration.openRouterModelInfo?.supportsComputerUse ?? false,
mcpEnabled ? this.mcpHub : undefined,
undefined,
browserViewportSize ?? "900x600",
mode,
customPrompts
)
const fullPrompt = instructions ? `${systemPrompt}${instructions}` : systemPrompt
await this.postMessageToWebview({
type: "systemPrompt",
text: fullPrompt,
mode: message.mode
})
} catch (error) {
console.error("Error getting system prompt:", error)
vscode.window.showErrorMessage("Failed to get system prompt")
}
break
case "searchCommits": {
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
if (cwd) {
@@ -811,10 +956,123 @@ export class ClineProvider implements vscode.WebviewViewProvider {
}
break
}
case "experimentalDiffStrategy":
case "upsertApiConfiguration":
if (message.text && message.apiConfiguration) {
try {
await this.configManager.SaveConfig(message.text, message.apiConfiguration);
let listApiConfig = await this.configManager.ListConfig();
// Update listApiConfigMeta first to ensure UI has latest data
await this.updateGlobalState("listApiConfigMeta", listApiConfig);
await Promise.all([
this.updateApiConfiguration(message.apiConfiguration),
this.updateGlobalState("currentApiConfigName", message.text),
])
await this.postStateToWebview()
} catch (error) {
console.error("Error create new api configuration:", error)
vscode.window.showErrorMessage("Failed to create api configuration")
}
}
break
case "renameApiConfiguration":
if (message.values && message.apiConfiguration) {
try {
const { oldName, newName } = message.values
await this.configManager.SaveConfig(newName, message.apiConfiguration);
await this.configManager.DeleteConfig(oldName)
let listApiConfig = await this.configManager.ListConfig();
const config = listApiConfig?.find(c => c.name === newName);
// Update listApiConfigMeta first to ensure UI has latest data
await this.updateGlobalState("listApiConfigMeta", listApiConfig);
await Promise.all([
this.updateGlobalState("currentApiConfigName", newName),
])
await this.postStateToWebview()
} catch (error) {
console.error("Error create new api configuration:", error)
vscode.window.showErrorMessage("Failed to create api configuration")
}
}
break
case "loadApiConfiguration":
if (message.text) {
try {
const apiConfig = await this.configManager.LoadConfig(message.text);
const listApiConfig = await this.configManager.ListConfig();
const config = listApiConfig?.find(c => c.name === message.text);
// Update listApiConfigMeta first to ensure UI has latest data
await this.updateGlobalState("listApiConfigMeta", listApiConfig);
await Promise.all([
this.updateGlobalState("currentApiConfigName", message.text),
this.updateApiConfiguration(apiConfig),
])
await this.postStateToWebview()
} catch (error) {
console.error("Error load api configuration:", error)
vscode.window.showErrorMessage("Failed to load api configuration")
}
}
break
case "deleteApiConfiguration":
if (message.text) {
const answer = await vscode.window.showInformationMessage(
"Are you sure you want to delete this configuration profile?",
{ modal: true },
"Yes",
)
if (answer !== "Yes") {
break
}
try {
await this.configManager.DeleteConfig(message.text);
const listApiConfig = await this.configManager.ListConfig();
// Update listApiConfigMeta first to ensure UI has latest data
await this.updateGlobalState("listApiConfigMeta", listApiConfig);
// If this was the current config, switch to first available
let currentApiConfigName = await this.getGlobalState("currentApiConfigName")
if (message.text === currentApiConfigName && listApiConfig?.[0]?.name) {
const apiConfig = await this.configManager.LoadConfig(listApiConfig[0].name);
await Promise.all([
this.updateGlobalState("currentApiConfigName", listApiConfig[0].name),
this.updateApiConfiguration(apiConfig),
])
}
await this.postStateToWebview()
} catch (error) {
console.error("Error delete api configuration:", error)
vscode.window.showErrorMessage("Failed to delete api configuration")
}
}
break
case "getListApiConfiguration":
try {
let listApiConfig = await this.configManager.ListConfig();
await this.updateGlobalState("listApiConfigMeta", listApiConfig)
this.postMessageToWebview({ type: "listApiConfig", listApiConfig })
} catch (error) {
console.error("Error get list api configuration:", error)
vscode.window.showErrorMessage("Failed to get list api configuration")
}
break
case "experimentalDiffStrategy":
await this.updateGlobalState("experimentalDiffStrategy", message.bool ?? false)
await this.postStateToWebview()
break
}
},
null,
@@ -822,6 +1080,85 @@ export class ClineProvider implements vscode.WebviewViewProvider {
)
}
private async updateApiConfiguration(apiConfiguration: ApiConfiguration) {
// Update mode's default config
const { mode } = await this.getState();
if (mode) {
const currentApiConfigName = await this.getGlobalState("currentApiConfigName");
const listApiConfig = await this.configManager.ListConfig();
const config = listApiConfig?.find(c => c.name === currentApiConfigName);
if (config?.id) {
await this.configManager.SetModeConfig(mode, config.id);
}
}
const {
apiProvider,
apiModelId,
apiKey,
glamaModelId,
glamaModelInfo,
glamaApiKey,
openRouterApiKey,
awsAccessKey,
awsSecretKey,
awsSessionToken,
awsRegion,
awsUseCrossRegionInference,
vertexProjectId,
vertexRegion,
openAiBaseUrl,
openAiApiKey,
openAiModelId,
ollamaModelId,
ollamaBaseUrl,
lmStudioModelId,
lmStudioBaseUrl,
anthropicBaseUrl,
geminiApiKey,
openAiNativeApiKey,
deepSeekApiKey,
azureApiVersion,
openAiStreamingEnabled,
openRouterModelId,
openRouterModelInfo,
openRouterUseMiddleOutTransform,
} = apiConfiguration
await this.updateGlobalState("apiProvider", apiProvider)
await this.updateGlobalState("apiModelId", apiModelId)
await this.storeSecret("apiKey", apiKey)
await this.updateGlobalState("glamaModelId", glamaModelId)
await this.updateGlobalState("glamaModelInfo", glamaModelInfo)
await this.storeSecret("glamaApiKey", glamaApiKey)
await this.storeSecret("openRouterApiKey", openRouterApiKey)
await this.storeSecret("awsAccessKey", awsAccessKey)
await this.storeSecret("awsSecretKey", awsSecretKey)
await this.storeSecret("awsSessionToken", awsSessionToken)
await this.updateGlobalState("awsRegion", awsRegion)
await this.updateGlobalState("awsUseCrossRegionInference", awsUseCrossRegionInference)
await this.updateGlobalState("vertexProjectId", vertexProjectId)
await this.updateGlobalState("vertexRegion", vertexRegion)
await this.updateGlobalState("openAiBaseUrl", openAiBaseUrl)
await this.storeSecret("openAiApiKey", openAiApiKey)
await this.updateGlobalState("openAiModelId", openAiModelId)
await this.updateGlobalState("ollamaModelId", ollamaModelId)
await this.updateGlobalState("ollamaBaseUrl", ollamaBaseUrl)
await this.updateGlobalState("lmStudioModelId", lmStudioModelId)
await this.updateGlobalState("lmStudioBaseUrl", lmStudioBaseUrl)
await this.updateGlobalState("anthropicBaseUrl", anthropicBaseUrl)
await this.storeSecret("geminiApiKey", geminiApiKey)
await this.storeSecret("openAiNativeApiKey", openAiNativeApiKey)
await this.storeSecret("deepSeekApiKey", deepSeekApiKey)
await this.updateGlobalState("azureApiVersion", azureApiVersion)
await this.updateGlobalState("openAiStreamingEnabled", openAiStreamingEnabled)
await this.updateGlobalState("openRouterModelId", openRouterModelId)
await this.updateGlobalState("openRouterModelInfo", openRouterModelInfo)
await this.updateGlobalState("openRouterUseMiddleOutTransform", openRouterUseMiddleOutTransform)
if (this.cline) {
this.cline.api = buildApiHandler(apiConfiguration)
}
}
async updateCustomInstructions(instructions?: string) {
// User may be clearing the field
await this.updateGlobalState("customInstructions", instructions || undefined)
@@ -1266,9 +1603,14 @@ export class ClineProvider implements vscode.WebviewViewProvider {
mcpEnabled,
alwaysApproveResubmit,
requestDelaySeconds,
experimentalDiffStrategy,
currentApiConfigName,
listApiConfigMeta,
mode,
customPrompts,
enhancementApiConfigId,
experimentalDiffStrategy,
} = await this.getState()
const allowedCommands = vscode.workspace
.getConfiguration('roo-cline')
.get<string[]>('allowedCommands') || []
@@ -1285,8 +1627,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
uriScheme: vscode.env.uriScheme,
clineMessages: this.cline?.clineMessages || [],
taskHistory: (taskHistory || [])
.filter((item) => item.ts && item.task)
.sort((a, b) => b.ts - a.ts),
.filter((item: HistoryItem) => item.ts && item.task)
.sort((a: HistoryItem, b: HistoryItem) => b.ts - a.ts),
soundEnabled: soundEnabled ?? false,
diffEnabled: diffEnabled ?? true,
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
@@ -1301,7 +1643,12 @@ export class ClineProvider implements vscode.WebviewViewProvider {
mcpEnabled: mcpEnabled ?? true,
alwaysApproveResubmit: alwaysApproveResubmit ?? false,
requestDelaySeconds: requestDelaySeconds ?? 5,
experimentalDiffStrategy: experimentalDiffStrategy ?? false,
currentApiConfigName: currentApiConfigName ?? "default",
listApiConfigMeta: listApiConfigMeta ?? [],
mode: mode ?? codeMode,
customPrompts: customPrompts ?? {},
enhancementApiConfigId,
experimentalDiffStrategy: experimentalDiffStrategy ?? false,
}
}
@@ -1409,7 +1756,13 @@ export class ClineProvider implements vscode.WebviewViewProvider {
mcpEnabled,
alwaysApproveResubmit,
requestDelaySeconds,
experimentalDiffStrategy,
currentApiConfigName,
listApiConfigMeta,
mode,
modeApiConfigs,
customPrompts,
enhancementApiConfigId,
experimentalDiffStrategy,
] = await Promise.all([
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
this.getGlobalState("apiModelId") as Promise<string | undefined>,
@@ -1462,7 +1815,13 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getGlobalState("mcpEnabled") as Promise<boolean | undefined>,
this.getGlobalState("alwaysApproveResubmit") as Promise<boolean | undefined>,
this.getGlobalState("requestDelaySeconds") as Promise<number | undefined>,
this.getGlobalState("experimentalDiffStrategy") as Promise<boolean | undefined>,
this.getGlobalState("currentApiConfigName") as Promise<string | undefined>,
this.getGlobalState("listApiConfigMeta") as Promise<ApiConfigMeta[] | undefined>,
this.getGlobalState("mode") as Promise<Mode | undefined>,
this.getGlobalState("modeApiConfigs") as Promise<Record<Mode, string> | undefined>,
this.getGlobalState("customPrompts") as Promise<CustomPrompts | undefined>,
this.getGlobalState("enhancementApiConfigId") as Promise<string | undefined>,
this.getGlobalState("experimentalDiffStrategy") as Promise<boolean | undefined>,
])
let apiProvider: ApiProvider
@@ -1529,6 +1888,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0,
writeDelayMs: writeDelayMs ?? 1000,
terminalOutputLineLimit: terminalOutputLineLimit ?? 500,
mode: mode ?? codeMode,
preferredLanguage: preferredLanguage ?? (() => {
// Get VSCode's locale setting
const vscodeLang = vscode.env.language;
@@ -1559,7 +1919,12 @@ export class ClineProvider implements vscode.WebviewViewProvider {
mcpEnabled: mcpEnabled ?? true,
alwaysApproveResubmit: alwaysApproveResubmit ?? false,
requestDelaySeconds: requestDelaySeconds ?? 5,
experimentalDiffStrategy: experimentalDiffStrategy ?? false,
currentApiConfigName: currentApiConfigName ?? "default",
listApiConfigMeta: listApiConfigMeta ?? [],
modeApiConfigs: modeApiConfigs ?? {} as Record<Mode, string>,
customPrompts: customPrompts ?? {},
enhancementApiConfigId,
experimentalDiffStrategy: experimentalDiffStrategy ?? false,
}
}