diff --git a/package.json b/package.json index f02ab4b69b..2587711f79 100644 --- a/package.json +++ b/package.json @@ -3325,6 +3325,469 @@ ] } } + }, + { + "id": "advanced", + "properties": { + "github.copilot.chat.debug.overrideChatEngine": { + "type": [ + "string", + "null" + ], + "markdownDescription": "%github.copilot.config.debug.overrideChatEngine%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.projectLabels.expanded": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.projectLabels.expanded%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.projectLabels.chat": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.projectLabels.chat%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.projectLabels.inline": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.projectLabels.inline%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.workspace.maxLocalIndexSize": { + "type": "number", + "default": 100000, + "markdownDescription": "%github.copilot.config.workspace.maxLocalIndexSize%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.workspace.enableFullWorkspace": { + "type": "boolean", + "default": true, + "markdownDescription": "%github.copilot.config.workspace.enableFullWorkspace%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.workspace.enableCodeSearch": { + "type": "boolean", + "default": true, + "markdownDescription": "%github.copilot.config.workspace.enableCodeSearch%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.workspace.enableEmbeddingsSearch": { + "type": "boolean", + "default": true, + "markdownDescription": "%github.copilot.config.workspace.enableEmbeddingsSearch%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.workspace.preferredEmbeddingsModel": { + "type": "string", + "default": "", + "markdownDescription": "%github.copilot.config.workspace.preferredEmbeddingsModel%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.workspace.prototypeAdoCodeSearchEndpointOverride": { + "type": "string", + "default": "", + "markdownDescription": "%github.copilot.config.workspace.prototypeAdoCodeSearchEndpointOverride%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.feedback.onChange": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.feedback.onChange%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.review.intent": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.review.intent%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.notebook.summaryExperimentEnabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.notebook.summaryExperimentEnabled%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.notebook.variableFilteringEnabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.notebook.variableFilteringEnabled%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.notebook.alternativeFormat": { + "type": "string", + "default": "xml", + "enum": ["xml", "markdown"], + "markdownDescription": "%github.copilot.config.notebook.alternativeFormat%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.notebook.alternativeNESFormat.enabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.notebook.alternativeNESFormat.enabled%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.debugTerminalCommandPatterns": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "markdownDescription": "%github.copilot.config.debugTerminalCommandPatterns%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.editSourceTracking.showDecorations": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.editSourceTracking.showDecorations%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.editSourceTracking.showStatusBar": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.editSourceTracking.showStatusBar%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.localWorkspaceRecording.enabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.localWorkspaceRecording.enabled%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.editRecording.enabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.editRecording.enabled%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.temporalContext.maxAge": { + "type": "number", + "default": 100, + "markdownDescription": "%github.copilot.config.temporalContext.maxAge%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.temporalContext.preferSameLang": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.temporalContext.preferSameLang%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.codesearch.agent.enabled": { + "type": "boolean", + "default": true, + "markdownDescription": "%github.copilot.config.codesearch.agent.enabled%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.agent.temperature": { + "type": ["number", "null"], + "markdownDescription": "%github.copilot.config.agent.temperature%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.instantApply.shortContextModelName": { + "type": "string", + "default": "gpt-4o-instant-apply-full-ft-v66-short", + "markdownDescription": "%github.copilot.config.instantApply.shortContextModelName%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.instantApply.shortContextLimit": { + "type": "number", + "default": 8000, + "markdownDescription": "%github.copilot.config.instantApply.shortContextLimit%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.enableUserPreferences": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.enableUserPreferences%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.summarizeAgentConversationHistoryThreshold": { + "type": ["number", "null"], + "markdownDescription": "%github.copilot.config.summarizeAgentConversationHistoryThreshold%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.agentHistorySummarizationMode": { + "type": ["string", "null"], + "markdownDescription": "%github.copilot.config.agentHistorySummarizationMode%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.agentHistorySummarizationWithPromptCache": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.agentHistorySummarizationWithPromptCache%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.agentHistorySummarizationForceGpt41": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.agentHistorySummarizationForceGpt41%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.useResponsesApiTruncation": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.useResponsesApiTruncation%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.omitBaseAgentInstructions": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.omitBaseAgentInstructions%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.promptFileContextProvider.enabled": { + "type": "boolean", + "default": true, + "markdownDescription": "%github.copilot.config.promptFileContextProvider.enabled%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.tools.defaultToolsGrouped": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.tools.defaultToolsGrouped%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.virtualTools.embeddingRanking": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.virtualTools.embeddingRanking%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.multiReplaceStringGrok.enabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.multiReplaceStringGrok.enabled%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.claudeCode.enabled": { + "type": ["boolean", "string"], + "default": false, + "markdownDescription": "%github.copilot.config.claudeCode.enabled%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.claudeCode.debug": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.claudeCode.debug%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.copilotCLI.enabled": { + "type": "boolean", + "default": true, + "markdownDescription": "%github.copilot.config.copilotCLI.enabled%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.gpt5AlternativePatch": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.gpt5AlternativePatch%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.inlineEdits.triggerOnEditorChangeAfterSeconds": { + "type": ["number", "null"], + "markdownDescription": "%github.copilot.config.inlineEdits.triggerOnEditorChangeAfterSeconds%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.inlineEdits.nextCursorPrediction.displayLine": { + "type": "boolean", + "default": true, + "markdownDescription": "%github.copilot.config.inlineEdits.nextCursorPrediction.displayLine%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.inlineEdits.nextCursorPrediction.currentFileMaxTokens": { + "type": "number", + "default": 2000, + "markdownDescription": "%github.copilot.config.inlineEdits.nextCursorPrediction.currentFileMaxTokens%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, + "github.copilot.chat.suggestRelatedFilesFromGitHistory.useEmbeddings": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.suggestRelatedFilesFromGitHistory.useEmbeddings%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.cli.isolation.enabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.cli.isolation.enabled%", + "tags": [ + "advanced", + "experimental" + ] + }, + "github.copilot.chat.cli.mcp.enabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%github.copilot.config.cli.mcp.enabled%", + "tags": [ + "advanced", + "experimental" + ] + } + } } ], "submenus": [ diff --git a/package.nls.json b/package.nls.json index f7550abfd6..697ec1c750 100644 --- a/package.nls.json +++ b/package.nls.json @@ -160,6 +160,7 @@ "github.copilot.config.debugTerminalCommandPatterns": "A list of commands for which the \"Debug Command\" quick fix action should be shown in the debug terminal.", "github.copilot.config.edits.suggestRelatedFilesFromGitHistory": "Whether to suggest related files from git history for the Copilot Edits working set.", "github.copilot.chat.edits.suggestRelatedFilesForTests": "Whether to suggest source files from test files for the Copilot Edits working set.", + "github.copilot.config.suggestRelatedFilesFromGitHistory.useEmbeddings": "Use embeddings to suggest related files from git history.", "github.copilot.config.codeGeneration.instructions": "A set of instructions that will be added to Copilot requests that generate code.\nInstructions can come from: \n- a file in the workspace: `{ \"file\": \"fileName\" }`\n- text in natural language: `{ \"text\": \"Use underscore for field names.\" }`\n\nNote: Keep your instructions short and precise. Poor instructions can degrade Copilot's quality and performance.", "github.copilot.config.codeGeneration.instructions.deprecated": "Use instructions files instead. See https://aka.ms/vscode-ghcp-custom-instructions for more information.", "github.copilot.config.codeGeneration.useInstructionFiles": "Controls whether code instructions from `.github/copilot-instructions.md` are added to Copilot requests.\n\nNote: Keep your instructions short and precise. Poor instructions can degrade Copilot's quality and performance. [Learn more](https://aka.ms/github-copilot-custom-instructions) about customizing Copilot.", @@ -361,6 +362,54 @@ "github.copilot.config.tools.memory.enabled": "Enable memory tool to allow models to store and retrieve information across conversations. \n\n**Note**: This is an experimental feature.", "github.copilot.config.completionsFetcher": "Sets the fetcher used for the inline completions.", "github.copilot.config.nesFetcher": "Sets the fetcher used for the next edit suggestions.", + "github.copilot.config.debug.overrideChatEngine": "Override the chat model. This allows you to test with different models.\n\n**Note**: This is an advanced debugging setting and should not be used while self-hosting as it may lead to a different experience compared to end-users.", + "github.copilot.config.projectLabels.expanded": "Use the expanded format for project labels in prompts.", + "github.copilot.config.projectLabels.chat": "Add project labels in chat requests.", + "github.copilot.config.projectLabels.inline": "Add project labels in inline edit requests.", + "github.copilot.config.workspace.maxLocalIndexSize": "Maximum size of the local workspace index.", + "github.copilot.config.workspace.enableFullWorkspace": "Enable full workspace context analysis.", + "github.copilot.config.workspace.enableCodeSearch": "Enable code search in workspace context.", + "github.copilot.config.workspace.enableEmbeddingsSearch": "Enable embeddings-based search in workspace context.", + "github.copilot.config.workspace.preferredEmbeddingsModel": "Preferred embeddings model for semantic search.", + "github.copilot.config.workspace.prototypeAdoCodeSearchEndpointOverride": "Override endpoint for Azure DevOps code search prototype.", + "github.copilot.config.feedback.onChange": "Enable feedback collection on configuration changes.", + "github.copilot.config.review.intent": "Enable intent detection for code review.", + "github.copilot.config.notebook.summaryExperimentEnabled": "Enable the notebook summary experiment.", + "github.copilot.config.notebook.variableFilteringEnabled": "Enable filtering variables by cell document symbols.", + "github.copilot.config.notebook.alternativeFormat": "Alternative document format for notebooks.", + "github.copilot.config.notebook.alternativeNESFormat.enabled": "Enable alternative format for Next Edit Suggestions in notebooks.", + "github.copilot.config.editSourceTracking.showDecorations": "Show decorations for edit source tracking.", + "github.copilot.config.editSourceTracking.showStatusBar": "Show status bar item for edit source tracking.", + "github.copilot.config.localWorkspaceRecording.enabled": "Enable local workspace recording for analysis.", + "github.copilot.config.editRecording.enabled": "Enable edit recording for analysis.", + "github.copilot.config.temporalContext.maxAge": "Maximum age (in editor changes) for temporal context.", + "github.copilot.config.temporalContext.preferSameLang": "Prefer same language files in temporal context.", + "github.copilot.config.codesearch.agent.enabled": "Enable code search capabilities in agent mode.", + "github.copilot.config.agent.temperature": "Temperature setting for agent mode requests.", + "github.copilot.config.instantApply.shortContextModelName": "Model name for short context instant apply.", + "github.copilot.config.instantApply.shortContextLimit": "Token limit for short context instant apply.", + "github.copilot.config.summarizeAgentConversationHistoryThreshold": "Threshold for summarizing agent conversation history.", + "github.copilot.config.agentHistorySummarizationMode": "Mode for agent history summarization.", + "github.copilot.config.agentHistorySummarizationWithPromptCache": "Use prompt caching for agent history summarization.", + "github.copilot.config.agentHistorySummarizationForceGpt41": "Force GPT-4.1 for agent history summarization.", + "github.copilot.config.useResponsesApiTruncation": "Use Responses API for truncation.", + "github.copilot.config.enableReadFileV2": "Enable version 2 of the read file tool.", + "github.copilot.config.enableAskAgent": "Enable the Ask agent for answering questions.", + "github.copilot.config.omitBaseAgentInstructions": "Omit base agent instructions from prompts.", + "github.copilot.config.promptFileContextProvider.enabled": "Enable prompt file context provider.", + "github.copilot.config.tools.defaultToolsGrouped": "Group default tools in prompts.", + "github.copilot.config.virtualTools.embeddingRanking": "Use embedding-based ranking for virtual tools.", + "github.copilot.config.multiReplaceStringGrok.enabled": "Enable multi-replace string with Grok.", + "github.copilot.config.claudeCode.enabled": "Enable Claude Code agent.", + "github.copilot.config.claudeCode.debug": "Enable debug mode for Claude Code agent.", + "github.copilot.config.copilotCLI.enabled": "Enable Copilot CLI integration.", + "github.copilot.config.cli.isolation.enabled": "Enable CLI isolation for agent sessions.", + "github.copilot.config.cli.mcp.enabled": "Enable Model Context Protocol (MCP) server for CLI.", + "github.copilot.config.copilotCodingAgent.enabled": "Enable Copilot Coding Agent (cloud).", + "github.copilot.config.gpt5AlternativePatch": "Enable GPT-5 alternative patch format.", + "github.copilot.config.inlineEdits.triggerOnEditorChangeAfterSeconds": "Trigger inline edits after editor has been idle for this many seconds.", + "github.copilot.config.inlineEdits.nextCursorPrediction.displayLine": "Display predicted cursor line for next edit suggestions.", + "github.copilot.config.inlineEdits.nextCursorPrediction.currentFileMaxTokens": "Maximum tokens for current file in next cursor prediction.", "github.copilot.command.refreshAgentSessions": "Refresh Agent Sessions", "github.copilot.command.deleteAgentSession": "Delete Agent Session", "github.copilot.command.cli.sessions.resumeInTerminal": "Resume Agent Session in Terminal", diff --git a/src/extension/agents/claude/node/claudeCodeAgent.ts b/src/extension/agents/claude/node/claudeCodeAgent.ts index 0521c23589..6e3da60faf 100644 --- a/src/extension/agents/claude/node/claudeCodeAgent.ts +++ b/src/extension/agents/claude/node/claudeCodeAgent.ts @@ -238,7 +238,7 @@ export class ClaudeCodeSession extends Disposable { private async _startSession(token: vscode.CancellationToken): Promise { // Build options for the Claude Code SDK // process.env.DEBUG = '1'; // debug messages from sdk.mjs - const isDebugEnabled = this.configService.getConfig(ConfigKey.Internal.ClaudeCodeDebugEnabled); + const isDebugEnabled = this.configService.getConfig(ConfigKey.AdvancedExperimental.ClaudeCodeDebugEnabled); this.logService.trace(`appRoot: ${this.envService.appRoot}`); const pathSep = isWindows ? ';' : ':'; const options: Options = { diff --git a/src/extension/agents/copilotcli/node/mcpHandler.ts b/src/extension/agents/copilotcli/node/mcpHandler.ts index ace93f68d5..c28291908a 100644 --- a/src/extension/agents/copilotcli/node/mcpHandler.ts +++ b/src/extension/agents/copilotcli/node/mcpHandler.ts @@ -89,7 +89,7 @@ export class CopilotCLIMCPHandler implements ICopilotCLIMCPHandler { ) { } public async loadMcpConfig(workingDirectory: string | undefined): Promise | undefined> { - if (!this.configurationService.getConfig(ConfigKey.Internal.CLIMCPServerEnabled)) { + if (!this.configurationService.getConfig(ConfigKey.AdvancedExperimental.CLIMCPServerEnabled)) { return undefined; } diff --git a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts index e055ec6562..e3c2970c8e 100644 --- a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts +++ b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts @@ -247,7 +247,7 @@ export class CopilotCLIChatSessionContentProvider implements vscode.ChatSessionC [MODELS_OPTION_ID]: _sessionModel.get(copilotcliSessionId)?.id ?? defaultModel.id, }; - if (!existingSession && this.configurationService.getConfig(ConfigKey.Internal.CLIIsolationEnabled)) { + if (!existingSession && this.configurationService.getConfig(ConfigKey.AdvancedExperimental.CLIIsolationEnabled)) { options[ISOLATION_OPTION_ID] = isolationEnabled ? 'enabled' : 'disabled'; } const history = existingSession?.object?.getChatHistory() || []; diff --git a/src/extension/chatSessions/vscode-node/copilotCLITerminalIntegration.ts b/src/extension/chatSessions/vscode-node/copilotCLITerminalIntegration.ts index d344048ff0..524fbc9493 100644 --- a/src/extension/chatSessions/vscode-node/copilotCLITerminalIntegration.ts +++ b/src/extension/chatSessions/vscode-node/copilotCLITerminalIntegration.ts @@ -59,7 +59,7 @@ export class CopilotCLITerminalIntegration extends Disposable implements ICopilo } private async initialize(): Promise { - const enabled = this.configurationService.getConfig(ConfigKey.Internal.CopilotCLIEnabled); + const enabled = this.configurationService.getConfig(ConfigKey.AdvancedExperimental.CopilotCLIEnabled); if (!enabled) { return; } diff --git a/src/extension/configuration/vscode-node/configurationMigration.ts b/src/extension/configuration/vscode-node/configurationMigration.ts index f30421a727..2ddbca0372 100644 --- a/src/extension/configuration/vscode-node/configurationMigration.ts +++ b/src/extension/configuration/vscode-node/configurationMigration.ts @@ -10,7 +10,7 @@ import { ConfigurationTarget, Uri, window, workspace, WorkspaceFolder } from 'vscode'; -import { Emitter } from '../../../util/vs/base/common/event'; +import { ConfigurationKeyValuePairs, ConfigurationMigration, ConfigurationMigrationRegistry, ConfigurationValue } from '../../../platform/configuration/common/configurationService'; import { DisposableStore, IDisposable } from '../../../util/vs/base/common/lifecycle'; import { localize } from '../../../util/vs/nls'; import { IExtensionContribution } from '../../common/contributions'; @@ -35,28 +35,6 @@ export const Extensions = { ConfigurationMigration: 'base.contributions.configuration.migration' }; -export type ConfigurationValue = { value: any | undefined /* Remove */ }; -export type ConfigurationKeyValuePairs = [string, ConfigurationValue][]; -export type ConfigurationMigrationFn = (value: any) => ConfigurationValue | ConfigurationKeyValuePairs | Promise; -export type ConfigurationMigration = { key: string; migrateFn: ConfigurationMigrationFn }; - -export interface IConfigurationMigrationRegistry { - registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void; -} - -class ConfigurationMigrationRegistryImpl implements IConfigurationMigrationRegistry { - readonly migrations: ConfigurationMigration[] = []; - - private readonly _onDidRegisterConfigurationMigrations = new Emitter(); - readonly onDidRegisterConfigurationMigration = this._onDidRegisterConfigurationMigrations.event; - - registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void { - this.migrations.push(...configurationMigrations); - } -} - -export const ConfigurationMigrationRegistry = new ConfigurationMigrationRegistryImpl(); - export class ConfigurationMigrationContribution implements IExtensionContribution { private readonly _disposables = new DisposableStore(); diff --git a/src/extension/context/node/resolvers/promptWorkspaceLabels.ts b/src/extension/context/node/resolvers/promptWorkspaceLabels.ts index 3f5845f16f..f25da68a09 100644 --- a/src/extension/context/node/resolvers/promptWorkspaceLabels.ts +++ b/src/extension/context/node/resolvers/promptWorkspaceLabels.ts @@ -56,7 +56,7 @@ export class PromptWorkspaceLabels implements IPromptWorkspaceLabels { } public async collectContext(): Promise { - const expandedLabels = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.ProjectLabelsExpanded, this._experimentationService); + const expandedLabels = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.ProjectLabelsExpanded, this._experimentationService); this.strategy = expandedLabels ? PromptWorkspaceLabelsStrategy.Expanded : PromptWorkspaceLabelsStrategy.Basic; await this.workspaceLabels.collectContext(); diff --git a/src/extension/conversation/vscode-node/feedbackCollection.ts b/src/extension/conversation/vscode-node/feedbackCollection.ts index 32b1f2b3d4..ce8320a6e8 100644 --- a/src/extension/conversation/vscode-node/feedbackCollection.ts +++ b/src/extension/conversation/vscode-node/feedbackCollection.ts @@ -25,7 +25,7 @@ export function startFeedbackCollection(accessor: ServicesAccessor) { const instantiationService = accessor.get(IInstantiationService); const logService = accessor.get(ILogService); const disposables = new DisposableStore(); - const enabled = configurationService.getConfig(ConfigKey.Internal.FeedbackOnChange); + const enabled = configurationService.getConfig(ConfigKey.AdvancedExperimental.FeedbackOnChange); if (!enabled) { return disposables; } diff --git a/src/extension/inlineEdits/node/nextEditCache.ts b/src/extension/inlineEdits/node/nextEditCache.ts index 2e72a04c72..02ed75e8ed 100644 --- a/src/extension/inlineEdits/node/nextEditCache.ts +++ b/src/extension/inlineEdits/node/nextEditCache.ts @@ -67,7 +67,7 @@ export class NextEditCache extends Disposable { } // if editor-change triggering is allowed, // it means an edit in file A can result in a cached edit for file B to be less relevant than with the edits in file A included - if (configService.getExperimentBasedConfig(ConfigKey.Internal.InlineEditsTriggerOnEditorChangeAfterSeconds, expService) !== undefined) { + if (configService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.InlineEditsTriggerOnEditorChangeAfterSeconds, expService) !== undefined) { for (const [k, v] of this._sharedCache.entries()) { if (v.docId !== doc.id) { this._sharedCache.deleteKey(k); diff --git a/src/extension/inlineEdits/node/nextEditProvider.ts b/src/extension/inlineEdits/node/nextEditProvider.ts index c42b412b31..110b7a6323 100644 --- a/src/extension/inlineEdits/node/nextEditProvider.ts +++ b/src/extension/inlineEdits/node/nextEditProvider.ts @@ -323,7 +323,7 @@ export class NextEditProvider extends Disposable implements INextEditProvider { completionItem: T | null; diff --git a/src/extension/inlineEdits/vscode-node/inlineCompletionProvider.ts b/src/extension/inlineEdits/vscode-node/inlineCompletionProvider.ts index 0705b3f3f8..bc4a470a11 100644 --- a/src/extension/inlineEdits/vscode-node/inlineCompletionProvider.ts +++ b/src/extension/inlineEdits/vscode-node/inlineCompletionProvider.ts @@ -120,7 +120,7 @@ export class InlineCompletionProviderImpl implements InlineCompletionItemProvide @IWorkspaceService private readonly _workspaceService: IWorkspaceService, ) { this._tracer = createTracer(['NES', 'Provider'], (s) => this._logService.trace(s)); - this._displayNextEditorNES = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.UseAlternativeNESNotebookFormat, this._expService); + this._displayNextEditorNES = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.UseAlternativeNESNotebookFormat, this._expService); } // copied from `vscodeWorkspace.ts` `DocumentFilter#_enabledLanguages` diff --git a/src/extension/inlineEdits/vscode-node/inlineEditModel.ts b/src/extension/inlineEdits/vscode-node/inlineEditModel.ts index 413252d911..1699d93a30 100644 --- a/src/extension/inlineEdits/vscode-node/inlineEditModel.ts +++ b/src/extension/inlineEdits/vscode-node/inlineEditModel.ts @@ -217,7 +217,7 @@ export class InlineEditTriggerer extends Disposable { const selectionLine = range.start.line; - const triggerOnActiveEditorChange = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.InlineEditsTriggerOnEditorChangeAfterSeconds, this._expService); + const triggerOnActiveEditorChange = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.InlineEditsTriggerOnEditorChangeAfterSeconds, this._expService); // If we're in a notebook cell, // Its possible user made changes in one cell and now is moving to another cell // In such cases we should account for the possibility of the user wanting to edit the new cell and trigger suggestions. @@ -264,7 +264,7 @@ export class InlineEditTriggerer extends Disposable { private _maybeTriggerOnDocumentSwitch(e: vscode.TextEditorSelectionChangeEvent, isSameDoc: boolean, parentTracer: ITracer): boolean { const tracer = parentTracer.subNoEntry('editorSwitch'); - const triggerAfterSeconds = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.InlineEditsTriggerOnEditorChangeAfterSeconds, this._expService); + const triggerAfterSeconds = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.InlineEditsTriggerOnEditorChangeAfterSeconds, this._expService); if (triggerAfterSeconds === undefined) { tracer.trace('document switch disabled'); return false; diff --git a/src/extension/inlineEdits/vscode-node/inlineEditProviderFeature.ts b/src/extension/inlineEdits/vscode-node/inlineEditProviderFeature.ts index 1b01f3b5c2..cc16c3d00e 100644 --- a/src/extension/inlineEdits/vscode-node/inlineEditProviderFeature.ts +++ b/src/extension/inlineEdits/vscode-node/inlineEditProviderFeature.ts @@ -87,7 +87,7 @@ export class InlineEditProviderFeature extends Disposable implements IExtensionC const tracer = createTracer(['NES', 'Feature'], (s) => this._logService.trace(s)); const constructorTracer = tracer.sub('constructor'); const hasUpdatedNesSettingKey = 'copilot.chat.nextEdits.hasEnabledNesInSettings'; - const enableEnhancedNotebookNES = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.UseAlternativeNESNotebookFormat, _experimentationService) || this._configurationService.getExperimentBasedConfig(ConfigKey.UseAlternativeNESNotebookFormat, _experimentationService); + const enableEnhancedNotebookNES = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.UseAlternativeNESNotebookFormat, _experimentationService) || this._configurationService.getExperimentBasedConfig(ConfigKey.UseAlternativeNESNotebookFormat, _experimentationService); const unificationState = unificationStateObservable(this); commands.executeCommand('setContext', useEnhancedNotebookNESContextKey, enableEnhancedNotebookNES); diff --git a/src/extension/inlineEdits/vscode-node/parts/vscodeWorkspace.ts b/src/extension/inlineEdits/vscode-node/parts/vscodeWorkspace.ts index 5d21aa8054..a29bd32bfc 100644 --- a/src/extension/inlineEdits/vscode-node/parts/vscodeWorkspace.ts +++ b/src/extension/inlineEdits/vscode-node/parts/vscodeWorkspace.ts @@ -44,7 +44,7 @@ export class VSCodeWorkspace extends ObservableWorkspace implements IDisposable private readonly _store = new DisposableStore(); private readonly _filter = this._instaService.createInstance(DocumentFilter); private get _useAlternativeNotebookFormat(): boolean { - return this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.UseAlternativeNESNotebookFormat, this._experimentationService) + return this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.UseAlternativeNESNotebookFormat, this._experimentationService) || this._configurationService.getExperimentBasedConfig(ConfigKey.UseAlternativeNESNotebookFormat, this._experimentationService); } private readonly _markdownNotebookCells = new Lazy(() => { diff --git a/src/extension/intents/node/agentIntent.ts b/src/extension/intents/node/agentIntent.ts index c4206852d2..2519ab432a 100644 --- a/src/extension/intents/node/agentIntent.ts +++ b/src/extension/intents/node/agentIntent.ts @@ -114,7 +114,7 @@ export const getAgentTools = (instaService: IInstantiationService, request: vsco return undefined; }); - if (await modelSupportsSimplifiedApplyPatchInstructions(model) && configurationService.getExperimentBasedConfig(ConfigKey.Internal.Gpt5AlternativePatch, experimentationService)) { + if (await modelSupportsSimplifiedApplyPatchInstructions(model) && configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.Gpt5AlternativePatch, experimentationService)) { const ap = tools.findIndex(t => t.name === ToolName.ApplyPatch); if (ap !== -1) { tools[ap] = { ...tools[ap], description: applyPatch5Description }; @@ -186,7 +186,7 @@ export class AgentIntent extends EditCodeIntent { maxToolCallIterations: getRequestedToolCallIterationLimit(request) ?? this.configurationService.getNonExtensionConfig('chat.agent.maxRequests') ?? 200, // Fallback for simulation tests - temperature: this.configurationService.getConfig(ConfigKey.Internal.AgentTemperature) ?? 0, + temperature: this.configurationService.getConfig(ConfigKey.AdvancedExperimental.AgentTemperature) ?? 0, overrideRequestLocation: ChatLocation.Agent, hideRateLimitTimeEstimate: true }; @@ -248,10 +248,10 @@ export class AgentIntentInvocation extends EditCodeIntentInvocation implements I // Reserve extra space when tools are involved due to token counting issues const baseBudget = Math.min( - this.configurationService.getConfig(ConfigKey.Internal.SummarizeAgentConversationHistoryThreshold) ?? this.endpoint.modelMaxPromptTokens, + this.configurationService.getConfig(ConfigKey.AdvancedExperimental.SummarizeAgentConversationHistoryThreshold) ?? this.endpoint.modelMaxPromptTokens, this.endpoint.modelMaxPromptTokens ); - const useTruncation = this.configurationService.getConfig(ConfigKey.Internal.UseResponsesApiTruncation); + const useTruncation = this.configurationService.getConfig(ConfigKey.AdvancedExperimental.UseResponsesApiTruncation); const safeBudget = useTruncation ? Number.MAX_SAFE_INTEGER : Math.floor((baseBudget - toolTokens) * 0.85); diff --git a/src/extension/intents/node/askAgentIntent.ts b/src/extension/intents/node/askAgentIntent.ts index 08d19ecde0..cf6ca3f232 100644 --- a/src/extension/intents/node/askAgentIntent.ts +++ b/src/extension/intents/node/askAgentIntent.ts @@ -64,7 +64,7 @@ export class AskAgentIntent implements IIntent { private getIntentHandlerOptions(request: vscode.ChatRequest): IDefaultIntentRequestHandlerOptions | undefined { return { maxToolCallIterations: getRequestedToolCallIterationLimit(request) ?? this.configurationService.getNonExtensionConfig('chat.agent.maxRequests') ?? 15, - temperature: this.configurationService.getConfig(ConfigKey.Internal.AgentTemperature) ?? 0, + temperature: this.configurationService.getConfig(ConfigKey.AdvancedExperimental.AgentTemperature) ?? 0, overrideRequestLocation: ChatLocation.EditingSession, }; } diff --git a/src/extension/intents/node/editCodeIntent.ts b/src/extension/intents/node/editCodeIntent.ts index 4c9e75acd7..57b19704d2 100644 --- a/src/extension/intents/node/editCodeIntent.ts +++ b/src/extension/intents/node/editCodeIntent.ts @@ -101,7 +101,7 @@ export class EditCodeIntent implements IIntent { private async _handleCodesearch(conversation: Conversation, request: vscode.ChatRequest, location: ChatLocation, stream: vscode.ChatResponseStream, token: CancellationToken, documentContext: IDocumentContext | undefined, chatTelemetry: ChatTelemetryBuilder): Promise<{ request: vscode.ChatRequest; conversation: Conversation }> { const foundReferences: vscode.ChatPromptReference[] = []; - if ((this.configurationService.getConfig(ConfigKey.CodeSearchAgentEnabled) || this.configurationService.getConfig(ConfigKey.Internal.CodeSearchAgentEnabled)) && request.toolReferences.find((r) => r.name === CodebaseTool.toolName && !isDirectorySemanticSearch(r))) { + if ((this.configurationService.getConfig(ConfigKey.CodeSearchAgentEnabled) || this.configurationService.getConfig(ConfigKey.AdvancedExperimental.CodeSearchAgentEnabled)) && request.toolReferences.find((r) => r.name === CodebaseTool.toolName && !isDirectorySemanticSearch(r))) { const latestTurn = conversation.getLatestTurn(); diff --git a/src/extension/intents/node/editCodeIntent2.ts b/src/extension/intents/node/editCodeIntent2.ts index b3043f6153..385efa827f 100644 --- a/src/extension/intents/node/editCodeIntent2.ts +++ b/src/extension/intents/node/editCodeIntent2.ts @@ -81,7 +81,7 @@ export class EditCode2Intent extends EditCodeIntent { protected override getIntentHandlerOptions(request: vscode.ChatRequest): IDefaultIntentRequestHandlerOptions | undefined { return { maxToolCallIterations: getRequestedToolCallIterationLimit(request) ?? this.configurationService.getNonExtensionConfig('chat.agent.maxRequests') ?? 15, - temperature: this.configurationService.getConfig(ConfigKey.Internal.AgentTemperature) ?? 0, + temperature: this.configurationService.getConfig(ConfigKey.AdvancedExperimental.AgentTemperature) ?? 0, overrideRequestLocation: ChatLocation.EditingSession, }; } diff --git a/src/extension/intents/node/notebookEditorIntent.ts b/src/extension/intents/node/notebookEditorIntent.ts index 9965218612..05120d7cc5 100644 --- a/src/extension/intents/node/notebookEditorIntent.ts +++ b/src/extension/intents/node/notebookEditorIntent.ts @@ -72,7 +72,7 @@ export class NotebookEditorIntent extends EditCodeIntent { protected override getIntentHandlerOptions(request: vscode.ChatRequest): IDefaultIntentRequestHandlerOptions | undefined { return { maxToolCallIterations: getRequestedToolCallIterationLimit(request) ?? this.configurationService.getNonExtensionConfig('chat.agent.maxRequests') ?? 15, - temperature: this.configurationService.getConfig(ConfigKey.Internal.AgentTemperature) ?? 0, + temperature: this.configurationService.getConfig(ConfigKey.AdvancedExperimental.AgentTemperature) ?? 0, overrideRequestLocation: ChatLocation.Notebook, }; } diff --git a/src/extension/onboardDebug/node/debuggableCommandIdentifier.ts b/src/extension/onboardDebug/node/debuggableCommandIdentifier.ts index b098fe3d3e..951a7566a2 100644 --- a/src/extension/onboardDebug/node/debuggableCommandIdentifier.ts +++ b/src/extension/onboardDebug/node/debuggableCommandIdentifier.ts @@ -130,7 +130,7 @@ export class DebuggableCommandIdentifier extends Disposable implements IDebuggab } private getSpecificTreatment(command: string): boolean | undefined { - const patterns = this.configurationService.getConfig(ConfigKey.Internal.TerminalToDebuggerPatterns); + const patterns = this.configurationService.getConfig(ConfigKey.AdvancedExperimental.TerminalToDebuggerPatterns); for (const pattern of patterns) { if (pattern.startsWith('!') && this.commandIncludes(command, pattern)) { return false; diff --git a/src/extension/onboardDebug/test/node/debuggableCommandIdentifier.spec.ts b/src/extension/onboardDebug/test/node/debuggableCommandIdentifier.spec.ts index 50910d553b..26a6d880a8 100644 --- a/src/extension/onboardDebug/test/node/debuggableCommandIdentifier.spec.ts +++ b/src/extension/onboardDebug/test/node/debuggableCommandIdentifier.spec.ts @@ -103,13 +103,13 @@ describe('DebuggableCommandIdentifier', () => { }); it('returns treatment value 1', async () => { - accessor.get(IConfigurationService).setConfig(ConfigKey.Internal.TerminalToDebuggerPatterns, ['othert']); + accessor.get(IConfigurationService).setConfig(ConfigKey.AdvancedExperimental.TerminalToDebuggerPatterns, ['othert']); const result = await debuggableCommandIdentifier.isDebuggable(undefined, 'othert hello', CancellationToken.None); expect(result).to.be.true; }); it('return treatment value 2', async () => { - accessor.get(IConfigurationService).setConfig(ConfigKey.Internal.TerminalToDebuggerPatterns, ['!mytool']); + accessor.get(IConfigurationService).setConfig(ConfigKey.AdvancedExperimental.TerminalToDebuggerPatterns, ['!mytool']); const result = await debuggableCommandIdentifier.isDebuggable(undefined, 'mytool hello', CancellationToken.None); expect(result).to.be.false; }); diff --git a/src/extension/prompt/vscode-node/endpointProviderImpl.ts b/src/extension/prompt/vscode-node/endpointProviderImpl.ts index 4affffcb96..bc0b3f64da 100644 --- a/src/extension/prompt/vscode-node/endpointProviderImpl.ts +++ b/src/extension/prompt/vscode-node/endpointProviderImpl.ts @@ -70,7 +70,7 @@ export class ProductionEndpointProvider implements IEndpointProvider { } private get _overridenChatModel(): string | undefined { - return this._configService.getConfig(ConfigKey.Internal.DebugOverrideChatEngine); + return this._configService.getConfig(ConfigKey.AdvancedExperimental.DebugOverrideChatEngine); } private getOrCreateChatEndpointInstance(modelMetadata: IChatModelInformation): IChatEndpoint { diff --git a/src/extension/promptFileContext/vscode-node/promptFileContextService.ts b/src/extension/promptFileContext/vscode-node/promptFileContextService.ts index e8ff55f1f8..630d5a4f4a 100644 --- a/src/extension/promptFileContext/vscode-node/promptFileContextService.ts +++ b/src/extension/promptFileContext/vscode-node/promptFileContextService.ts @@ -32,7 +32,7 @@ export class PromptFileContextContribution extends Disposable { @ILanguageContextProviderService private readonly languageContextProviderService: ILanguageContextProviderService, ) { super(); - this._enableCompletionContext = configurationService.getExperimentBasedConfigObservable(ConfigKey.Internal.PromptFileContext, experimentationService); + this._enableCompletionContext = configurationService.getExperimentBasedConfigObservable(ConfigKey.AdvancedExperimentalExperiments.PromptFileContext, experimentationService); this._register(autorun(reader => { if (this._enableCompletionContext.read(reader)) { this.registration = this.register(); diff --git a/src/extension/prompts/node/agent/agentPrompt.tsx b/src/extension/prompts/node/agent/agentPrompt.tsx index b69683eb8c..478310afb5 100644 --- a/src/extension/prompts/node/agent/agentPrompt.tsx +++ b/src/extension/prompts/node/agent/agentPrompt.tsx @@ -87,7 +87,7 @@ export class AgentPrompt extends PromptElement { async render(state: void, sizing: PromptSizing) { const instructions = await this.getInstructions(); - const omitBaseAgentInstructions = this.configurationService.getConfig(ConfigKey.Internal.OmitBaseAgentInstructions); + const omitBaseAgentInstructions = this.configurationService.getConfig(ConfigKey.AdvancedExperimental.OmitBaseAgentInstructions); const baseAgentInstructions = <> You are an expert AI programming assistant, working with a user in the VS Code editor.
diff --git a/src/extension/prompts/node/agent/defaultAgentInstructions.tsx b/src/extension/prompts/node/agent/defaultAgentInstructions.tsx index 9da71ee6c4..31631a8a59 100644 --- a/src/extension/prompts/node/agent/defaultAgentInstructions.tsx +++ b/src/extension/prompts/node/agent/defaultAgentInstructions.tsx @@ -6,6 +6,7 @@ import { BasePromptElementProps, PromptElement, PromptSizing } from '@vscode/prompt-tsx'; import type { LanguageModelToolInformation } from 'vscode'; import { ConfigKey, IConfigurationService } from '../../../../platform/configuration/common/configurationService'; +import { isHiddenModelB, isHiddenModelC, isHiddenModelD } from '../../../../platform/endpoint/common/chatModelCapabilities'; import { IExperimentationService } from '../../../../platform/telemetry/common/nullExperimentationService'; import { LanguageModelToolMCPSource } from '../../../../vscodeTypes'; import { ToolName } from '../../../tools/common/toolNames'; @@ -16,7 +17,6 @@ import { Tag } from '../base/tag'; import { CodeBlockFormattingRules, EXISTING_CODE_MARKER } from '../panel/codeBlockFormattingRules'; import { MathIntegrationRules } from '../panel/editorIntegrationRules'; import { KeepGoingReminder } from './agentPrompt'; -import { isHiddenModelB, isHiddenModelC, isHiddenModelD } from '../../../../platform/endpoint/common/chatModelCapabilities'; // Types and interfaces for reusable components interface ToolCapabilities extends Partial> { @@ -402,7 +402,7 @@ export class ApplyPatchInstructions extends PromptElement To edit files in the workspace, use the {ToolName.ApplyPatch} tool. If you have issues with it, you should first try to fix your patch and continue using {ToolName.ApplyPatch}. {this.props.tools[ToolName.EditFile] && <>If you are stuck, you can fall back on the {ToolName.EditFile} tool, but {ToolName.ApplyPatch} is much faster and is the preferred tool.}
diff --git a/src/extension/prompts/node/agent/summarizedConversationHistory.tsx b/src/extension/prompts/node/agent/summarizedConversationHistory.tsx index 6105f373fe..9d3b63522f 100644 --- a/src/extension/prompts/node/agent/summarizedConversationHistory.tsx +++ b/src/extension/prompts/node/agent/summarizedConversationHistory.tsx @@ -416,7 +416,7 @@ class ConversationHistorySummarizer { } private async getSummaryWithFallback(propsInfo: ISummarizedConversationHistoryInfo): Promise> { - const forceMode = this.configurationService.getConfig(ConfigKey.Internal.AgentHistorySummarizationMode); + const forceMode = this.configurationService.getConfig(ConfigKey.AdvancedExperimental.AgentHistorySummarizationMode); if (forceMode === SummaryMode.Simple) { return await this.getSummary(SummaryMode.Simple, propsInfo); } else { @@ -438,7 +438,7 @@ class ConversationHistorySummarizer { private async getSummary(mode: SummaryMode, propsInfo: ISummarizedConversationHistoryInfo): Promise> { const stopwatch = new StopWatch(false); - const forceGpt41 = this.configurationService.getExperimentBasedConfig(ConfigKey.Internal.AgentHistorySummarizationForceGpt41, this.experimentationService); + const forceGpt41 = this.configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.AgentHistorySummarizationForceGpt41, this.experimentationService); const gpt41Endpoint = await this.endpointProvider.getChatEndpoint('gpt-4.1'); const endpoint = forceGpt41 && (gpt41Endpoint.modelMaxPromptTokens >= this.props.endpoint.modelMaxPromptTokens) ? gpt41Endpoint : @@ -446,7 +446,7 @@ class ConversationHistorySummarizer { let summarizationPrompt: ChatMessage[]; const associatedRequestId = this.props.promptContext.conversation?.getLatestTurn().id; - const promptCacheMode = this.configurationService.getExperimentBasedConfig(ConfigKey.Internal.AgentHistorySummarizationWithPromptCache, this.experimentationService); + const promptCacheMode = this.configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.AgentHistorySummarizationWithPromptCache, this.experimentationService); try { if (mode === SummaryMode.Full && promptCacheMode) { const props: AgentPromptProps = { diff --git a/src/extension/prompts/node/agent/test/agentPrompt.spec.tsx b/src/extension/prompts/node/agent/test/agentPrompt.spec.tsx index bff430d525..d66611a05d 100644 --- a/src/extension/prompts/node/agent/test/agentPrompt.spec.tsx +++ b/src/extension/prompts/node/agent/test/agentPrompt.spec.tsx @@ -260,7 +260,7 @@ import { AgentPrompt, AgentPromptProps } from '../agentPrompt'; }); test('omit base agent instructions', async () => { - accessor.get(IConfigurationService).setConfig(ConfigKey.Internal.OmitBaseAgentInstructions, true); + accessor.get(IConfigurationService).setConfig(ConfigKey.AdvancedExperimental.OmitBaseAgentInstructions, true); expect(await agentPromptToString(accessor, { chatVariables: new ChatVariablesCollection(), history: [], diff --git a/src/extension/prompts/node/codeMapper/codeMapper.ts b/src/extension/prompts/node/codeMapper/codeMapper.ts index 81b56065a0..1e266e7432 100644 --- a/src/extension/prompts/node/codeMapper/codeMapper.ts +++ b/src/extension/prompts/node/codeMapper/codeMapper.ts @@ -314,7 +314,7 @@ export class CodeMapper { this.gpt4oProxyEndpoint = this.experimentationService.hasTreatments().then(() => this.instantiationService.createInstance(Proxy4oEndpoint)); this.shortIAEndpoint = this.experimentationService.hasTreatments().then(() => this.instantiationService.createInstance(ProxyInstantApplyShortEndpoint)); - this.shortContextLimit = configurationService.getExperimentBasedConfig(ConfigKey.Internal.InstantApplyShortContextLimit, experimentationService) ?? 8000; + this.shortContextLimit = configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.InstantApplyShortContextLimit, experimentationService) ?? 8000; } public async mapCode(request: ICodeMapperRequestInput, resultStream: MappedEditsResponseStream, telemetryInfo: ICodeMapperTelemetryInfo | undefined, token: CancellationToken): Promise { diff --git a/src/extension/prompts/node/inline/inlineChatEditCodePrompt.tsx b/src/extension/prompts/node/inline/inlineChatEditCodePrompt.tsx index d7ad7f5ba6..6c5b5e19f0 100644 --- a/src/extension/prompts/node/inline/inlineChatEditCodePrompt.tsx +++ b/src/extension/prompts/node/inline/inlineChatEditCodePrompt.tsx @@ -59,7 +59,7 @@ export class InlineChatEditCodePrompt extends PromptElement { {hasFilesInWorkingSet ? <>The user has a request for modifying one or more files.
: <>If the user asks a question, then answer it.
- If you need to change existing files and it's not clear which files should be changed, then refuse and answer with "Please add the files to be modified to the working set{(this.configurationService.getConfig(ConfigKey.CodeSearchAgentEnabled) || this.configurationService.getConfig(ConfigKey.Internal.CodeSearchAgentEnabled)) ? ", or use `#codebase` in your request to automatically discover working set files." : ""}".
+ If you need to change existing files and it's not clear which files should be changed, then refuse and answer with "Please add the files to be modified to the working set{(this.configurationService.getConfig(ConfigKey.CodeSearchAgentEnabled) || this.configurationService.getConfig(ConfigKey.AdvancedExperimental.CodeSearchAgentEnabled)) ? ", or use `#codebase` in your request to automatically discover working set files." : ""}".
The only exception is if you need to create new files. In that case, follow the following instructions.
} 1. Please come up with a solution that you first describe step-by-step.
2. Group your changes by file. Use the file path as the header.
@@ -302,7 +302,7 @@ export class EditCodeUserMessage extends PromptElement { async render(state: void, sizing: PromptSizing) { const { query, chatVariables, workingSet } = this.props.promptContext; - const useProjectLabels = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.ProjectLabelsChat, this.experimentationService); + const useProjectLabels = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.ProjectLabelsChat, this.experimentationService); return ( <> diff --git a/src/extension/prompts/node/panel/editCodePrompt2.tsx b/src/extension/prompts/node/panel/editCodePrompt2.tsx index 47e73c530f..23239a3599 100644 --- a/src/extension/prompts/node/panel/editCodePrompt2.tsx +++ b/src/extension/prompts/node/panel/editCodePrompt2.tsx @@ -40,7 +40,7 @@ export class EditCodePrompt2 extends PromptElement { {hasFilesInWorkingSet ? <>The user has a request for modifying one or more files. : <>If the user asks a question, then answer it.
- If you need to change existing files and it's not clear which files should be changed, then refuse and answer with "Please add the files to be modified to the working set{(this.configurationService.getConfig(ConfigKey.CodeSearchAgentEnabled) || this.configurationService.getConfig(ConfigKey.Internal.CodeSearchAgentEnabled)) ? ", or use `#codebase` in your request to automatically discover working set files." : ""}".
+ If you need to change existing files and it's not clear which files should be changed, then refuse and answer with "Please add the files to be modified to the working set{(this.configurationService.getConfig(ConfigKey.CodeSearchAgentEnabled) || this.configurationService.getConfig(ConfigKey.AdvancedExperimental.CodeSearchAgentEnabled)) ? ", or use `#codebase` in your request to automatically discover working set files." : ""}".
The only exception is if you need to create new files. In that case, follow the following instructions.} ; const hasReplaceStringTool = this.toolsService.getTool(ToolName.ReplaceString) !== undefined; @@ -135,7 +135,7 @@ class EditCode2UserMessage extends PromptElement { async render(state: void, sizing: PromptSizing) { const { query, chatVariables } = this.props.promptContext; - const useProjectLabels = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.ProjectLabelsChat, this.experimentationService); + const useProjectLabels = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.ProjectLabelsChat, this.experimentationService); const hasReplaceStringTool = !!this.props.promptContext.tools?.availableTools.find(tool => tool.name === ToolName.ReplaceString); const hasEditFileTool = !!this.props.promptContext.tools?.availableTools.find(tool => tool.name === ToolName.EditFile); const hasMultiReplaceStringTool = !!this.props.promptContext.tools?.availableTools.find(tool => tool.name === ToolName.MultiReplaceString); diff --git a/src/extension/prompts/node/panel/notebookInlinePrompt.tsx b/src/extension/prompts/node/panel/notebookInlinePrompt.tsx index 5593a761c5..a9c748dca9 100644 --- a/src/extension/prompts/node/panel/notebookInlinePrompt.tsx +++ b/src/extension/prompts/node/panel/notebookInlinePrompt.tsx @@ -37,7 +37,7 @@ export class NotebookInlinePrompt extends PromptElement { {hasFilesInWorkingSet ? <>The user has a request for modifying one or more files. : <>If the user asks a question, then answer it.
- If you need to change existing files and it's not clear which files should be changed, then refuse and answer with "Please add the files to be modified to the working set{(this.configurationService.getConfig(ConfigKey.CodeSearchAgentEnabled) || this.configurationService.getConfig(ConfigKey.Internal.CodeSearchAgentEnabled)) ? ", or use `#codebase` in your request to automatically discover working set files." : ""}".
+ If you need to change existing files and it's not clear which files should be changed, then refuse and answer with "Please add the files to be modified to the working set{(this.configurationService.getConfig(ConfigKey.CodeSearchAgentEnabled) || this.configurationService.getConfig(ConfigKey.AdvancedExperimental.CodeSearchAgentEnabled)) ? ", or use `#codebase` in your request to automatically discover working set files." : ""}".
The only exception is if you need to create new files. In that case, follow the following instructions.} ; const instructions = @@ -90,7 +90,7 @@ class EditCode2UserMessage extends PromptElement { async render(state: void, sizing: PromptSizing) { const { query, chatVariables } = this.props.promptContext; - const useProjectLabels = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.ProjectLabelsChat, this.experimentationService); + const useProjectLabels = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.ProjectLabelsChat, this.experimentationService); const hasReplaceStringTool = !!this.props.promptContext.tools?.availableTools.find(tool => tool.name === ToolName.ReplaceString); const hasEditFileTool = !!this.props.promptContext.tools?.availableTools.find(tool => tool.name === ToolName.EditFile); const hasMultiReplaceStringTool = !!this.props.promptContext.tools?.availableTools.find(tool => tool.name === ToolName.MultiReplaceString); diff --git a/src/extension/prompts/node/panel/panelChatBasePrompt.tsx b/src/extension/prompts/node/panel/panelChatBasePrompt.tsx index 79c4559a4d..534b678d66 100644 --- a/src/extension/prompts/node/panel/panelChatBasePrompt.tsx +++ b/src/extension/prompts/node/panel/panelChatBasePrompt.tsx @@ -38,7 +38,7 @@ export class PanelChatBasePrompt extends PromptElement async render(state: void, sizing: PromptSizing) { const { query, history, chatVariables, } = this.props.promptContext; - const useProjectLabels = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.ProjectLabelsChat, this.experimentationService); + const useProjectLabels = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.ProjectLabelsChat, this.experimentationService); const operatingSystem = this.envService.OS; return ( diff --git a/src/extension/prompts/node/panel/preferences.tsx b/src/extension/prompts/node/panel/preferences.tsx index 0a8d8baea1..3cb2ab4bbf 100644 --- a/src/extension/prompts/node/panel/preferences.tsx +++ b/src/extension/prompts/node/panel/preferences.tsx @@ -22,7 +22,7 @@ export class UserPreferences extends PromptElement { super(props); } override async render(state: void, sizing: PromptSizing) { - if (!this.configurationService.getConfig(ConfigKey.Internal.EnableUserPreferences)) { + if (!this.configurationService.getConfig(ConfigKey.AdvancedExperimental.EnableUserPreferences)) { return undefined; } diff --git a/src/extension/relatedFiles/node/gitRelatedFilesProvider.ts b/src/extension/relatedFiles/node/gitRelatedFilesProvider.ts index 113cd759d6..ccb245a05e 100644 --- a/src/extension/relatedFiles/node/gitRelatedFilesProvider.ts +++ b/src/extension/relatedFiles/node/gitRelatedFilesProvider.ts @@ -39,7 +39,7 @@ export class GitRelatedFilesProvider extends Disposable implements vscode.ChatRe ) { super(); - if (this._configurationService.getConfig(ConfigKey.Internal.GitHistoryRelatedFilesUsingEmbeddings)) { + if (this._configurationService.getConfig(ConfigKey.AdvancedExperimental.GitHistoryRelatedFilesUsingEmbeddings)) { // Index the latest 200 commits in the background // TODO@joyceerhl reindex incrementally when repository state changes // TODO@joyceerhl use only the changes from main branch? @@ -69,7 +69,7 @@ export class GitRelatedFilesProvider extends Disposable implements vscode.ChatRe return this.getCommitsForPromptWithoutFiles(chatRequest, token); } - if (this._configurationService.getConfig(ConfigKey.Internal.GitHistoryRelatedFilesUsingEmbeddings)) { + if (this._configurationService.getConfig(ConfigKey.AdvancedExperimental.GitHistoryRelatedFilesUsingEmbeddings)) { return this.computeRelevantCommits(chatRequest, token); } diff --git a/src/extension/test/node/configurations.spec.ts b/src/extension/test/node/configurations.spec.ts index 9e09a7bb23..1845b9d770 100644 --- a/src/extension/test/node/configurations.spec.ts +++ b/src/extension/test/node/configurations.spec.ts @@ -9,58 +9,80 @@ import { Config, ConfigKey } from '../../../platform/configuration/common/config import { packageJson } from '../../../platform/env/common/packagejson'; describe('Configurations', () => { - it('package.json configuration contains stable, experimental, and preview sections', () => { + it('package.json configuration contains stable, experimental, preview, and advanced sections', () => { const configurationContributions = packageJson.contributes.configuration; - // Should have 3 sections - expect(configurationContributions, 'package.json should have exactly 3 sections').toHaveLength(3); + // Should have 4 sections + expect(configurationContributions, 'package.json should have exactly 4 sections').toHaveLength(4); // Should have a stable section const stableSection = configurationContributions.find(section => section.id === 'stable'); const preview = configurationContributions.find(section => section.id === 'preview'); const experimental = configurationContributions.find(section => section.id === 'experimental'); + const advanced = configurationContributions.find(section => section.id === 'advanced'); expect(stableSection, 'stable configuration section is missing').toBeDefined(); expect(preview, 'preview configuration section is missing').toBeDefined(); expect(experimental, 'experimental configuration section is missing').toBeDefined(); + expect(advanced, 'advanced configuration section is missing').toBeDefined(); }); it('package.json configuration tags are correct for each section', () => { const configurationContributions = packageJson.contributes.configuration; - // Should have a stable section const stableSection = configurationContributions.find(section => section.id === 'stable')!; - const preview = configurationContributions.find(section => section.id === 'preview')!; - const experimental = configurationContributions.find(section => section.id === 'experimental')!; - - for (const section of [stableSection, preview, experimental]) { - const sectionSettings = Object.keys(section?.properties); - - for (const settingId of sectionSettings) { - const setting = section.properties[settingId]; - if (section.id === 'stable') { - expect(setting.tags ?? [], settingId).not.toContain('preview'); - expect(setting.tags ?? [], settingId).not.toContain('experimental'); - } else { - expect(setting.tags ?? [], settingId).toContain(section.id); - } - } + for (const settingId of Object.keys(stableSection?.properties)) { + const setting = stableSection.properties[settingId]; + expect(setting.tags ?? [], settingId).not.toContain('preview'); + expect(setting.tags ?? [], settingId).not.toContain('experimental'); + expect(setting.tags ?? [], settingId).not.toContain('advanced'); + } + + const previewSection = configurationContributions.find(section => section.id === 'preview')!; + for (const settingId of Object.keys(previewSection?.properties)) { + const setting = previewSection.properties[settingId]; + expect(setting.tags ?? [], settingId).toContain('preview'); + expect(setting.tags ?? [], settingId).not.toContain('experimental'); + expect(setting.tags ?? [], settingId).not.toContain('advanced'); + } + + const experimentalSection = configurationContributions.find(section => section.id === 'experimental')!; + for (const settingId of Object.keys(experimentalSection?.properties)) { + const setting = experimentalSection.properties[settingId]; + expect(setting.tags ?? [], settingId).toContain('experimental'); + expect(setting.tags ?? [], settingId).not.toContain('preview'); + expect(setting.tags ?? [], settingId).not.toContain('advanced'); + } + + const advancedSection = configurationContributions.find(section => section.id === 'advanced')!; + for (const settingId of Object.keys(advancedSection?.properties)) { + const setting = advancedSection.properties[settingId]; + expect(setting.tags ?? [], settingId).toContain('advanced'); + expect(setting.tags ?? [], settingId).not.toContain('preview'); } }); it('settings in code should match package.json', () => { + const configurationsInPackageJson = packageJson.contributes.configuration.flatMap(section => Object.keys(section.properties)); + const advancedConfigurationsInPackageJson = packageJson.contributes.configuration.filter(section => section.id === 'advanced').flatMap(section => Object.keys(section.properties)); + const otherConfigurationsInPackageJson = packageJson.contributes.configuration.filter(section => section.id !== 'advanced').flatMap(section => Object.keys(section.properties)); // Get keys from code - const publicConfigs = Object.values(ConfigKey).filter(key => key !== ConfigKey.Internal && key !== ConfigKey.Shared) as Config[]; const internalKeys = Object.values(ConfigKey.Internal).map(setting => setting.fullyQualifiedId); const sharedKeys = Object.values(ConfigKey.Shared).map(setting => setting.fullyQualifiedId); - const publicKeys = publicConfigs.map(setting => setting.fullyQualifiedId); - - // Validate Internal and Shared settings are not in package.json - [...internalKeys, ...sharedKeys].forEach(key => { - expect(configurationsInPackageJson, 'Internal settings and those shared with the completions extension should not be defined in the package.json').not.toContain(key); + const advancedPublicKeys = [ + ...Object.values(ConfigKey.AdvancedExperimental).map(setting => setting.fullyQualifiedId), + ...Object.values(ConfigKey.AdvancedExperimentalExperiments).map(setting => setting.fullyQualifiedId) + ]; + const otherPublicKeys = (Object.values(ConfigKey).filter(key => key !== ConfigKey.Internal && key !== ConfigKey.Shared && key !== ConfigKey.AdvancedExperimental && key !== ConfigKey.AdvancedExperimentalExperiments) as Config[]).map(setting => setting.fullyQualifiedId); + const registered = [...otherPublicKeys, ...advancedPublicKeys]; + const unregistered = [...internalKeys, ...sharedKeys]; + + // Validate unregistered settings are not in package.json + unregistered.forEach(key => { + expect(configurationsInPackageJson, 'unregistered settings should not be defined in the package.json').not.toContain(key); }); // Validate Internal settings have the correct prefix @@ -69,13 +91,19 @@ describe('Configurations', () => { }); // Validate public settings in code are in package.json - publicKeys.forEach(key => { - expect(configurationsInPackageJson, 'Setting in code is not defined in the package.json').toContain(key); + otherPublicKeys.forEach(key => { + expect(otherConfigurationsInPackageJson, 'Setting in code is not defined in the package.json').toContain(key); + }); + + // Validate advanced settings in code are in the advanced section of package.json + advancedPublicKeys.forEach(key => { + expect(key, 'Advanced settings must not start wih github.copilot.chat.advanced.').not.toMatch(/^github\.copilot\.chat\.advanced\./); + expect(advancedConfigurationsInPackageJson, `Advanced setting ${key} should be defined in the advanced section of package.json`).toContain(key); }); // Validate settings in package.json are in code configurationsInPackageJson.forEach(key => { - expect(publicKeys, 'Setting in package.json is not defined in code').toContain(key); + expect(registered, 'Setting in package.json is not defined in code').toContain(key); }); }); diff --git a/src/extension/tools/common/virtualTools/virtualToolGrouper.ts b/src/extension/tools/common/virtualTools/virtualToolGrouper.ts index c7646173d4..776ce470ff 100644 --- a/src/extension/tools/common/virtualTools/virtualToolGrouper.ts +++ b/src/extension/tools/common/virtualTools/virtualToolGrouper.ts @@ -50,7 +50,7 @@ export class VirtualToolGrouper implements IToolCategorization { * Determines if built-in tool grouping should be triggered based on configuration and tool count */ private shouldTriggerBuiltInGrouping(tools: LanguageModelToolInformation[]): boolean { - const defaultToolGroupingEnabled = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.DefaultToolsGrouped, this._expService); + const defaultToolGroupingEnabled = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.DefaultToolsGrouped, this._expService); return tools.length > Constant.START_BUILTIN_GROUPING_AFTER_TOOL_COUNT && defaultToolGroupingEnabled; } diff --git a/src/extension/workspaceRecorder/vscode-node/workspaceRecorderFeature.ts b/src/extension/workspaceRecorder/vscode-node/workspaceRecorderFeature.ts index c57147f16f..a6576dfdd7 100644 --- a/src/extension/workspaceRecorder/vscode-node/workspaceRecorderFeature.ts +++ b/src/extension/workspaceRecorder/vscode-node/workspaceRecorderFeature.ts @@ -31,7 +31,7 @@ import { WorkspaceRecorder } from './workspaceRecorder'; export class WorkspaceRecorderFeature extends Disposable { private readonly _gitApi = observableFromEvent(this, (listener) => this._gitExtensionService.onDidChange(listener), () => this._gitExtensionService.getExtensionApi()); - private readonly _workspaceRecordingEnabled = this._configurationService.getConfigObservable(ConfigKey.Internal.WorkspaceRecordingEnabled); + private readonly _workspaceRecordingEnabled = this._configurationService.getConfigObservable(ConfigKey.AdvancedExperimental.WorkspaceRecordingEnabled); constructor( @IVSCodeExtensionContext private readonly _vscodeExtensionContext: IVSCodeExtensionContext, diff --git a/src/extension/xtab/node/xtabProvider.ts b/src/extension/xtab/node/xtabProvider.ts index 328ac35599..d3ab37467b 100644 --- a/src/extension/xtab/node/xtabProvider.ts +++ b/src/extension/xtab/node/xtabProvider.ts @@ -1138,7 +1138,7 @@ export class XtabProvider implements IStatelessNextEditProvider { const systemMessage = 'Your task is to predict the next line number in the current file where the developer is most likely to make their next edit, using the provided context.'; - const maxTokens = this.configService.getExperimentBasedConfig(ConfigKey.Internal.InlineEditsNextCursorPredictionCurrentFileMaxTokens, this.expService); + const maxTokens = this.configService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.InlineEditsNextCursorPredictionCurrentFileMaxTokens, this.expService); const currentFileContentR = this.constructTaggedFile( promptPieces.currentDocument, diff --git a/src/platform/configuration/common/configurationService.ts b/src/platform/configuration/common/configurationService.ts index 3877000946..7613fe8d32 100644 --- a/src/platform/configuration/common/configurationService.ts +++ b/src/platform/configuration/common/configurationService.ts @@ -363,6 +363,11 @@ export interface BaseConfig { */ readonly id: string; + /** + * The old key as it appears in settings.json minus the "github.copilot." prefix. + */ + readonly oldId?: string; + /** * This setting is present in package.json and is visible to the general public. */ @@ -374,6 +379,11 @@ export interface BaseConfig { */ readonly fullyQualifiedId: string; + /** + * The fully qualified old id, e.g. "github.copilot.advanced.debug.overrideProxyUrl". + */ + readonly fullyQualifiedOldId?: string | undefined; + /** * The `X` in `github.copilot.advanced.X` settings. */ @@ -398,17 +408,11 @@ export const enum ConfigType { } export interface ConfigOptions { + readonly oldKey?: string; readonly internal?: boolean; readonly valueIgnoredForExternals?: boolean; } -/** - * Indicates that a setting is hidden and not registered in package.json - */ -const INTERNAL: ConfigOptions = { - internal: true -}; - /** * Indicates that a setting can not be configured by external users */ @@ -444,6 +448,7 @@ function getPackageJsonDefaults(): Map { function toBaseConfig(key: string, defaultValue: T | DefaultValueWithTeamValue | DefaultValueWithTeamAndInternalValue, options: ConfigOptions | undefined): BaseConfig { const fullyQualifiedId = `${CopilotConfigPrefix}.${key}`; + const fullyQualifiedOldId = options?.oldKey ? `${CopilotConfigPrefix}.${options.oldKey}` : undefined; const packageJsonDefaults = getPackageJsonDefaults(); const isPublic = packageJsonDefaults.has(fullyQualifiedId); const packageJsonDefaultValue = packageJsonDefaults.get(fullyQualifiedId); @@ -476,7 +481,7 @@ function toBaseConfig(key: string, defaultValue: T | DefaultValueWithTeamValu } } const advancedSubKey = fullyQualifiedId.startsWith('github.copilot.advanced.') ? fullyQualifiedId.substring('github.copilot.advanced.'.length) : undefined; - return { id: key, isPublic, fullyQualifiedId, advancedSubKey, defaultValue, options }; + return { id: key, oldId: options?.oldKey, isPublic, fullyQualifiedId, fullyQualifiedOldId, advancedSubKey, defaultValue, options }; } class ConfigRegistry { @@ -492,6 +497,30 @@ class ConfigRegistry { export const globalConfigRegistry = new ConfigRegistry(); +// Configuration Migration Types and Registry +export type ConfigurationValue = { value: any | undefined /* Remove */ }; +export type ConfigurationKeyValuePairs = [string, ConfigurationValue][]; +export type ConfigurationMigrationFn = (value: any) => ConfigurationValue | ConfigurationKeyValuePairs | Promise; +export type ConfigurationMigration = { key: string; migrateFn: ConfigurationMigrationFn }; + +export interface IConfigurationMigrationRegistry { + registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void; +} + +class ConfigurationMigrationRegistryImpl implements IConfigurationMigrationRegistry { + readonly migrations: ConfigurationMigration[] = []; + + private readonly _onDidRegisterConfigurationMigrations = new Emitter(); + readonly onDidRegisterConfigurationMigration = this._onDidRegisterConfigurationMigrations.event; + + registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void { + this.migrations.push(...configurationMigrations); + this._onDidRegisterConfigurationMigrations.fire(configurationMigrations); + } +} + +export const ConfigurationMigrationRegistry = new ConfigurationMigrationRegistryImpl(); + function defineValidatedSetting(key: string, validator: IValidator, defaultValue: T | DefaultValueWithTeamValue | DefaultValueWithTeamAndInternalValue, options?: ConfigOptions): Config { const value: Config = { ...toBaseConfig(key, defaultValue, options), configType: ConfigType.Simple, validator }; globalConfigRegistry.registerConfig(value); @@ -513,7 +542,7 @@ function defineSetting(key: string, defaultValue: T | DefaultValueWithTeamVal * config.github.copilot.chat.advanced.inlineEdits.internalRollout * ``` */ -export function defineExpSetting(key: string, defaultValue: T | DefaultValueWithTeamValue | DefaultValueWithTeamAndInternalValue, options?: ConfigOptions, expOptions?: { experimentName?: string }): ExperimentBasedConfig { +function defineExpSetting(key: string, defaultValue: T | DefaultValueWithTeamValue | DefaultValueWithTeamAndInternalValue, options?: ConfigOptions, expOptions?: { experimentName?: string }): ExperimentBasedConfig { const value: ExperimentBasedConfig = { ...toBaseConfig(key, defaultValue, options), configType: ConfigType.ExperimentBased, experimentName: expOptions?.experimentName }; if (value.advancedSubKey) { // This is a `github.copilot.advanced.*` setting @@ -523,6 +552,28 @@ export function defineExpSetting(key: strin return value; } +function migrateSetting(newKey: string, oldKey: string): void { + ConfigurationMigrationRegistry.registerConfigurationMigrations([{ + key: `${CopilotConfigPrefix}.${oldKey}`, + migrateFn: async (migrationValue: any) => { + return [ + [`${CopilotConfigPrefix}.${newKey}`, { value: migrationValue }], + [`${CopilotConfigPrefix}.${oldKey}`, { value: undefined }] + ]; + } + }]); +} + +function defineAndMigrateSetting(oldKey: string, newKey: string, defaultValue: T | DefaultValueWithTeamValue | DefaultValueWithTeamAndInternalValue, options?: ConfigOptions): Config { + migrateSetting(newKey, oldKey); + return defineSetting(newKey, defaultValue, { ...options, oldKey }); +} + +function defineAndMigrateExpSetting(oldKey: string, newKey: string, defaultValue: T | DefaultValueWithTeamValue | DefaultValueWithTeamAndInternalValue, options?: ConfigOptions, expOptions?: { experimentName?: string }): ExperimentBasedConfig { + migrateSetting(newKey, oldKey); + return defineExpSetting(newKey, defaultValue, { ...options, oldKey }, expOptions); +} + // Max CAPI tool count limit export const HARD_TOOL_LIMIT = 128; @@ -579,8 +630,8 @@ export namespace ConfigKey { */ export namespace Shared { /** Allows for overriding the base domain we use for making requests to the CAPI. This helps CAPI devs develop against a local instance. */ - export const DebugOverrideProxyUrl = defineSetting('advanced.debug.overrideProxyUrl', undefined, INTERNAL); - export const DebugOverrideCAPIUrl = defineSetting('advanced.debug.overrideCapiUrl', undefined, INTERNAL); + export const DebugOverrideProxyUrl = defineSetting('advanced.debug.overrideProxyUrl', undefined); + export const DebugOverrideCAPIUrl = defineSetting('advanced.debug.overrideCapiUrl', undefined); export const DebugUseNodeFetchFetcher = defineSetting('advanced.debug.useNodeFetchFetcher', true); export const DebugUseNodeFetcher = defineSetting('advanced.debug.useNodeFetcher', false); export const DebugUseElectronFetcher = defineSetting('advanced.debug.useElectronFetcher', true); @@ -589,16 +640,86 @@ export namespace ConfigKey { } /** - * Internal and debugging settings that should be hidden from users. - * - * Features should only be in this list temporarily, moving on to experimental to be accessible to early adopters. - */ - export namespace Internal { + * Advanced experimental settings that are available for all users to configure. + * These should only be in this list temporarily. Either should be made stable or removed. + */ + export namespace AdvancedExperimental { /** Allows forcing a particular model. * Note: this should not be used while self-hosting because it might lead to * a fundamental different experience compared to our end-users. - */ - export const DebugOverrideChatEngine = defineSetting('chat.advanced.debug.overrideChatEngine', undefined, INTERNAL); + */ + export const DebugOverrideChatEngine = defineAndMigrateSetting('chat.advanced.debug.overrideChatEngine', 'chat.debug.overrideChatEngine', undefined); + export const WorkspacePrototypeAdoCodeSearchEndpointOverride = defineAndMigrateSetting('chat.advanced.workspace.prototypeAdoCodeSearchEndpointOverride', 'chat.workspace.prototypeAdoCodeSearchEndpointOverride', ''); + export const FeedbackOnChange = defineAndMigrateSetting('chat.advanced.feedback.onChange', 'chat.feedback.onChange', false); + export const ReviewIntent = defineAndMigrateSetting('chat.advanced.review.intent', 'chat.review.intent', false); + /** Enable the new notebook priorities experiment */ + export const NotebookSummaryExperimentEnabled = defineAndMigrateSetting('chat.advanced.notebook.summaryExperimentEnabled', 'chat.notebook.summaryExperimentEnabled', false); + /** Enable filtering variables by cell document symbols */ + export const NotebookVariableFilteringEnabled = defineAndMigrateSetting('chat.advanced.notebook.variableFilteringEnabled', 'chat.notebook.variableFilteringEnabled', false); + export const TerminalToDebuggerPatterns = defineAndMigrateSetting('chat.advanced.debugTerminalCommandPatterns', 'chat.debugTerminalCommandPatterns', []); + export const WorkspaceRecordingEnabled = defineAndMigrateSetting('chat.advanced.localWorkspaceRecording.enabled', 'chat.localWorkspaceRecording.enabled', false); + export const EditRecordingEnabled = defineAndMigrateSetting('chat.advanced.editRecording.enabled', 'chat.editRecording.enabled', false); + export const CodeSearchAgentEnabled = defineAndMigrateSetting('chat.advanced.codesearch.agent.enabled', 'chat.codesearch.agent.enabled', true); + export const AgentTemperature = defineAndMigrateSetting('chat.advanced.agent.temperature', 'chat.agent.temperature', undefined); + export const EnableUserPreferences = defineAndMigrateSetting('chat.advanced.enableUserPreferences', 'chat.enableUserPreferences', false); + export const SummarizeAgentConversationHistoryThreshold = defineAndMigrateSetting('chat.advanced.summarizeAgentConversationHistoryThreshold', 'chat.summarizeAgentConversationHistoryThreshold', undefined); + export const AgentHistorySummarizationMode = defineAndMigrateSetting('chat.advanced.agentHistorySummarizationMode', 'chat.agentHistorySummarizationMode', undefined); + export const UseResponsesApiTruncation = defineAndMigrateSetting('chat.advanced.useResponsesApiTruncation', 'chat.useResponsesApiTruncation', false); + export const OmitBaseAgentInstructions = defineAndMigrateSetting('chat.advanced.omitBaseAgentInstructions', 'chat.omitBaseAgentInstructions', false); + export const ClaudeCodeDebugEnabled = defineAndMigrateSetting('chat.advanced.claudeCode.debug', 'chat.claudeCode.debug', false); + export const CopilotCLIEnabled = defineAndMigrateSetting('chat.advanced.copilotCLI.enabled', 'chat.copilotCLI.enabled', true); + export const GitHistoryRelatedFilesUsingEmbeddings = defineAndMigrateSetting('chat.advanced.suggestRelatedFilesFromGitHistory.useEmbeddings', 'chat.suggestRelatedFilesFromGitHistory.useEmbeddings', false); + export const CLIIsolationEnabled = defineAndMigrateSetting('chat.advanced.cli.isolation.enabled', 'chat.cli.isolation.enabled', false); + export const CLIMCPServerEnabled = defineAndMigrateSetting('chat.advanced.cli.mcp.enabled', 'chat.cli.mcp.enabled', false); + + // Unused + export const EditSourceTrackingShowDecorations = defineAndMigrateSetting('chat.advanced.editSourceTracking.showDecorations', 'chat.editSourceTracking.showDecorations', false); + export const EditSourceTrackingShowStatusBar = defineAndMigrateSetting('chat.advanced.editSourceTracking.showStatusBar', 'chat.editSourceTracking.showStatusBar', false); + export const EnableClaudeCodeAgent = defineAndMigrateSetting('chat.advanced.claudeCode.enabled', 'chat.claudeCode.enabled', false); + } + + /** + * Advanced experimental settings that are available for all users to configure and controlled by experiments. + * These should only be in this list temporarily. Either should be made stable or removed. + */ + export namespace AdvancedExperimentalExperiments { + /** Uses new expanded project labels */ + export const ProjectLabelsExpanded = defineAndMigrateExpSetting('chat.advanced.projectLabels.expanded', 'chat.projectLabels.expanded', false); + /** Add project labels in default agent */ + export const ProjectLabelsChat = defineAndMigrateExpSetting('chat.advanced.projectLabels.chat', 'chat.projectLabels.chat', false); + /** Add project labels in default agent */ + export const ProjectLabelsInline = defineAndMigrateExpSetting('chat.advanced.projectLabels.inline', 'chat.projectLabels.inline', false); + export const WorkspaceMaxLocalIndexSize = defineAndMigrateExpSetting('chat.advanced.workspace.maxLocalIndexSize', 'chat.workspace.maxLocalIndexSize', 100_000); + export const WorkspaceEnableFullWorkspace = defineAndMigrateExpSetting('chat.advanced.workspace.enableFullWorkspace', 'chat.workspace.enableFullWorkspace', true); + export const WorkspaceEnableCodeSearch = defineAndMigrateExpSetting('chat.advanced.workspace.enableCodeSearch', 'chat.workspace.enableCodeSearch', true); + export const WorkspaceEnableEmbeddingsSearch = defineAndMigrateExpSetting('chat.advanced.workspace.enableEmbeddingsSearch', 'chat.workspace.enableEmbeddingsSearch', true); + export const WorkspacePreferredEmbeddingsModel = defineAndMigrateExpSetting('chat.advanced.workspace.preferredEmbeddingsModel', 'chat.workspace.preferredEmbeddingsModel', ''); + export const NotebookAlternativeDocumentFormat = defineAndMigrateExpSetting('chat.advanced.notebook.alternativeFormat', 'chat.notebook.alternativeFormat', AlternativeNotebookFormat.xml); + export const UseAlternativeNESNotebookFormat = defineAndMigrateExpSetting('chat.advanced.notebook.alternativeNESFormat.enabled', 'chat.notebook.alternativeNESFormat.enabled', false); + /** Configure temporal context max age */ + export const TemporalContextMaxAge = defineAndMigrateExpSetting('chat.advanced.temporalContext.maxAge', 'chat.temporalContext.maxAge', 100); + export const TemporalContextPreferSameLang = defineAndMigrateExpSetting('chat.advanced.temporalContext.preferSameLang', 'chat.temporalContext.preferSameLang', false); + export const InstantApplyShortModelName = defineAndMigrateExpSetting('chat.advanced.instantApply.shortContextModelName', 'chat.instantApply.shortContextModelName', CHAT_MODEL.SHORT_INSTANT_APPLY); + export const InstantApplyShortContextLimit = defineAndMigrateExpSetting('chat.advanced.instantApply.shortContextLimit', 'chat.instantApply.shortContextLimit', 8000); + export const AgentHistorySummarizationWithPromptCache = defineAndMigrateExpSetting('chat.advanced.agentHistorySummarizationWithPromptCache', 'chat.agentHistorySummarizationWithPromptCache', false); + export const AgentHistorySummarizationForceGpt41 = defineAndMigrateExpSetting('chat.advanced.agentHistorySummarizationForceGpt41', 'chat.agentHistorySummarizationForceGpt41', false); + export const PromptFileContext = defineAndMigrateExpSetting('chat.advanced.promptFileContextProvider.enabled', 'chat.promptFileContextProvider.enabled', true); + export const DefaultToolsGrouped = defineAndMigrateExpSetting('chat.advanced.tools.defaultToolsGrouped', 'chat.tools.defaultToolsGrouped', false); + export const Gpt5AlternativePatch = defineAndMigrateExpSetting('chat.advanced.gpt5AlternativePatch', 'chat.gpt5AlternativePatch', false); + export const InlineEditsTriggerOnEditorChangeAfterSeconds = defineAndMigrateExpSetting('chat.advanced.inlineEdits.triggerOnEditorChangeAfterSeconds', 'chat.inlineEdits.triggerOnEditorChangeAfterSeconds', { defaultValue: undefined, teamDefaultValue: 10 }); + export const InlineEditsNextCursorPredictionDisplayLine = defineAndMigrateExpSetting('chat.advanced.inlineEdits.nextCursorPrediction.displayLine', 'chat.inlineEdits.nextCursorPrediction.displayLine', true); + export const InlineEditsNextCursorPredictionCurrentFileMaxTokens = defineAndMigrateExpSetting('chat.advanced.inlineEdits.nextCursorPrediction.currentFileMaxTokens', 'chat.inlineEdits.nextCursorPrediction.currentFileMaxTokens', xtabPromptOptions.DEFAULT_OPTIONS.currentFile.maxTokens); + + // Unused + export const VirtualToolEmbeddingRanking = defineAndMigrateExpSetting('chat.advanced.virtualTools.embeddingRanking', 'chat.virtualTools.embeddingRanking', false); + export const MultiReplaceStringGrok = defineAndMigrateExpSetting('chat.advanced.multiReplaceStringGrok.enabled', 'chat.multiReplaceStringGrok.enabled', false); + } + + /** + * Internal settings those only team members can configure + * Features should only be in this list temporarily, moving on to experimental to be accessible to early adopters. + */ + export namespace Internal { /** Allows forcing a particular context window size. * This setting doesn't validate values so large windows may not be supported by the model. * Note: this should not be used while self-hosting because it might lead to @@ -613,29 +734,6 @@ export namespace ConfigKey { export const DebugExpUseNodeFetchFetcher = defineExpSetting('chat.advanced.debug.useNodeFetchFetcher', undefined, INTERNAL_RESTRICTED); export const DebugExpUseNodeFetcher = defineExpSetting('chat.advanced.debug.useNodeFetcher', undefined, INTERNAL_RESTRICTED); export const DebugExpUseElectronFetcher = defineExpSetting('chat.advanced.debug.useElectronFetcher', undefined, INTERNAL_RESTRICTED); - export const GitHistoryRelatedFilesUsingEmbeddings = defineSetting('chat.advanced.suggestRelatedFilesFromGitHistory.useEmbeddings', false); - - /** Uses new expanded project labels */ - export const ProjectLabelsExpanded = defineExpSetting('chat.advanced.projectLabels.expanded', false, INTERNAL); - /** Add project labels in default agent */ - export const ProjectLabelsChat = defineExpSetting('chat.advanced.projectLabels.chat', false, INTERNAL); - /** Add project labels in default agent */ - export const ProjectLabelsInline = defineExpSetting('chat.advanced.projectLabels.inline', false, INTERNAL); - export const WorkspaceMaxLocalIndexSize = defineExpSetting('chat.advanced.workspace.maxLocalIndexSize', 100_000, INTERNAL); - export const WorkspaceEnableFullWorkspace = defineExpSetting('chat.advanced.workspace.enableFullWorkspace', true, INTERNAL); - export const WorkspaceEnableCodeSearch = defineExpSetting('chat.advanced.workspace.enableCodeSearch', true, INTERNAL); - export const WorkspaceEnableEmbeddingsSearch = defineExpSetting('chat.advanced.workspace.enableEmbeddingsSearch', true, INTERNAL); - export const WorkspacePreferredEmbeddingsModel = defineExpSetting('chat.advanced.workspace.preferredEmbeddingsModel', '', INTERNAL); - export const WorkspacePrototypeAdoCodeSearchEndpointOverride = defineSetting('chat.advanced.workspace.prototypeAdoCodeSearchEndpointOverride', '', INTERNAL); - export const FeedbackOnChange = defineSetting('chat.advanced.feedback.onChange', false, INTERNAL); - export const ReviewIntent = defineSetting('chat.advanced.review.intent', false, INTERNAL); - /** Enable the new notebook priorities experiment */ - export const NotebookSummaryExperimentEnabled = defineSetting('chat.advanced.notebook.summaryExperimentEnabled', false, INTERNAL); - /** Enable filtering variables by cell document symbols */ - export const NotebookVariableFilteringEnabled = defineSetting('chat.advanced.notebook.variableFilteringEnabled', false, INTERNAL); - export const NotebookAlternativeDocumentFormat = defineExpSetting('chat.advanced.notebook.alternativeFormat', AlternativeNotebookFormat.xml, INTERNAL); - export const UseAlternativeNESNotebookFormat = defineExpSetting('chat.advanced.notebook.alternativeNESFormat.enabled', false, INTERNAL); - export const TerminalToDebuggerPatterns = defineSetting('chat.advanced.debugTerminalCommandPatterns', [], INTERNAL); export const InlineEditsIgnoreCompletionsDisablement = defineValidatedSetting('chat.advanced.inlineEdits.ignoreCompletionsDisablement', vBoolean(), false, INTERNAL_RESTRICTED); export const InlineEditsAsyncCompletions = defineExpSetting('chat.advanced.inlineEdits.asyncCompletions', true, INTERNAL_RESTRICTED); export const InlineEditsDebounceUseCoreRequestTime = defineExpSetting('chat.advanced.inlineEdits.debounceUseCoreRequestTime', false, INTERNAL_RESTRICTED); @@ -656,9 +754,6 @@ export namespace ConfigKey { export const InlineEditsHideInternalInterface = defineValidatedSetting('chat.advanced.inlineEdits.hideInternalInterface', vBoolean(), false, INTERNAL_RESTRICTED); export const InlineEditsLogCancelledRequests = defineValidatedSetting('chat.advanced.inlineEdits.logCancelledRequests', vBoolean(), false, INTERNAL_RESTRICTED); export const InlineEditsUnification = defineExpSetting('chat.advanced.inlineEdits.unification', false, INTERNAL_RESTRICTED); - export const InlineEditsTriggerOnEditorChangeAfterSeconds = defineExpSetting('chat.advanced.inlineEdits.triggerOnEditorChangeAfterSeconds', { defaultValue: undefined, teamDefaultValue: 10 }, INTERNAL); - export const InlineEditsNextCursorPredictionDisplayLine = defineExpSetting('chat.advanced.inlineEdits.nextCursorPrediction.displayLine', true, INTERNAL); - export const InlineEditsNextCursorPredictionCurrentFileMaxTokens = defineExpSetting('chat.advanced.inlineEdits.nextCursorPrediction.currentFileMaxTokens', xtabPromptOptions.DEFAULT_OPTIONS.currentFile.maxTokens, INTERNAL); export const InlineEditsNextCursorPredictionEnabled = defineExpSetting('chat.advanced.inlineEdits.nextCursorPrediction.enabled', { defaultValue: undefined, teamDefaultValue: NextCursorLinePrediction.OnlyWithEdit }, INTERNAL_RESTRICTED); export const InlineEditsNextCursorPredictionModelName = defineExpSetting('chat.advanced.inlineEdits.nextCursorPrediction.modelName', { defaultValue: undefined, teamDefaultValue: "xtab-cursor-jump-1104" }, INTERNAL_RESTRICTED); export const InlineEditsNextCursorPredictionUrl = defineValidatedSetting('chat.advanced.inlineEdits.nextCursorPrediction.url', vString(), undefined, INTERNAL_RESTRICTED); @@ -697,47 +792,15 @@ export namespace ConfigKey { export const InlineEditsXtabOnlyMergeConflictLines = defineExpSetting('chat.advanced.inlineEdits.xtabProvider.onlyMergeConflictLines', false, INTERNAL_RESTRICTED); export const InlineEditsUndoInsertionFiltering = defineExpSetting<'v1' | 'v2' | undefined>('chat.advanced.inlineEdits.undoInsertionFiltering', 'v1', INTERNAL_RESTRICTED); export const InlineEditsDiagnosticsExplorationEnabled = defineSetting('chat.advanced.inlineEdits.inlineEditsDiagnosticsExplorationEnabled', false, INTERNAL_RESTRICTED); - export const EditSourceTrackingShowDecorations = defineSetting('chat.advanced.editSourceTracking.showDecorations', false, INTERNAL); - export const EditSourceTrackingShowStatusBar = defineSetting('chat.advanced.editSourceTracking.showStatusBar', false, INTERNAL); - export const WorkspaceRecordingEnabled = defineSetting('chat.advanced.localWorkspaceRecording.enabled', false, INTERNAL); - export const EditRecordingEnabled = defineSetting('chat.advanced.editRecording.enabled', false, INTERNAL); export const InternalWelcomeHintEnabled = defineSetting('chat.advanced.welcomePageHint.enabled', { defaultValue: false, internalDefaultValue: true, teamDefaultValue: true }, INTERNAL_RESTRICTED); - /** Configure temporal context max age */ - export const TemporalContextMaxAge = defineExpSetting('chat.advanced.temporalContext.maxAge', 100, INTERNAL); - export const TemporalContextPreferSameLang = defineExpSetting('chat.advanced.temporalContext.preferSameLang', false, INTERNAL); - export const CodeSearchAgentEnabled = defineSetting('chat.advanced.codesearch.agent.enabled', true, INTERNAL); - export const AgentTemperature = defineSetting('chat.advanced.agent.temperature', undefined, INTERNAL); - export const InlineChatUseCodeMapper = defineSetting('chat.advanced.inlineChat.useCodeMapper', false, INTERNAL_RESTRICTED); export const InstantApplyModelName = defineExpSetting('chat.advanced.instantApply.modelName', 'gpt-4o-instant-apply-full-ft-v66', INTERNAL_RESTRICTED); - export const InstantApplyShortModelName = defineExpSetting('chat.advanced.instantApply.shortContextModelName', CHAT_MODEL.SHORT_INSTANT_APPLY, INTERNAL); - export const InstantApplyShortContextLimit = defineExpSetting('chat.advanced.instantApply.shortContextLimit', 8000, INTERNAL); - - export const EnableUserPreferences = defineSetting('chat.advanced.enableUserPreferences', false, INTERNAL); - - export const SummarizeAgentConversationHistoryThreshold = defineSetting('chat.advanced.summarizeAgentConversationHistoryThreshold', undefined, INTERNAL); - export const AgentHistorySummarizationMode = defineSetting('chat.advanced.agentHistorySummarizationMode', undefined, INTERNAL); - export const AgentHistorySummarizationWithPromptCache = defineExpSetting('chat.advanced.agentHistorySummarizationWithPromptCache', false, INTERNAL); - export const AgentHistorySummarizationForceGpt41 = defineExpSetting('chat.advanced.agentHistorySummarizationForceGpt41', false, INTERNAL); - export const UseResponsesApiTruncation = defineSetting('chat.advanced.useResponsesApiTruncation', false); + export const VerifyTextDocumentChanges = defineExpSetting('chat.advanced.inlineEdits.verifyTextDocumentChanges', false, INTERNAL_RESTRICTED); + export const DiagnosticsContextProvider = defineExpSetting('chat.advanced.inlineEdits.diagnosticsContextProvider.enabled', true); + // TODO: @sandy081 - These should be moved away from this namespace export const EnableReadFileV2 = defineExpSetting('chat.advanced.enableReadFileV2', isPreRelease); export const AskAgent = defineExpSetting('chat.advanced.enableAskAgent', { defaultValue: false, teamDefaultValue: true, internalDefaultValue: true }); - export const VerifyTextDocumentChanges = defineExpSetting('chat.advanced.inlineEdits.verifyTextDocumentChanges', false, INTERNAL_RESTRICTED); - export const OmitBaseAgentInstructions = defineSetting('chat.advanced.omitBaseAgentInstructions', false, INTERNAL); - - export const PromptFileContext = defineExpSetting('chat.advanced.promptFileContextProvider.enabled', true); - export const DefaultToolsGrouped = defineExpSetting('chat.advanced.tools.defaultToolsGrouped', false, INTERNAL); - export const DiagnosticsContextProvider = defineExpSetting('chat.advanced.inlineEdits.diagnosticsContextProvider.enabled', true); - export const VirtualToolEmbeddingRanking = defineExpSetting('chat.advanced.virtualTools.embeddingRanking', false, INTERNAL); - export const MultiReplaceStringGrok = defineExpSetting('chat.advanced.multiReplaceStringGrok.enabled', false, INTERNAL); - - export const EnableClaudeCodeAgent = defineSetting('chat.advanced.claudeCode.enabled', false); - export const ClaudeCodeDebugEnabled = defineSetting('chat.advanced.claudeCode.debug', false); - export const CopilotCLIEnabled = defineSetting('chat.advanced.copilotCLI.enabled', true); - export const CLIIsolationEnabled = defineSetting('chat.advanced.cli.isolation.enabled', false); - export const CLIMCPServerEnabled = defineSetting('chat.advanced.cli.mcp.enabled', false); - export const Gpt5AlternativePatch = defineExpSetting('chat.advanced.gpt5AlternativePatch', false); } export const Enable = defineSetting<{ [key: string]: boolean }>('enable', { diff --git a/src/platform/configuration/test/common/configurationService.spec.ts b/src/platform/configuration/test/common/configurationService.spec.ts index 5e7b54c534..dabf93c73c 100644 --- a/src/platform/configuration/test/common/configurationService.spec.ts +++ b/src/platform/configuration/test/common/configurationService.spec.ts @@ -55,357 +55,275 @@ suite('AbstractConfigurationService', () => { suite('Internal Settings - Validation', () => { test('ProjectLabelsChat is correctly configured', () => { - const setting = ConfigKey.Internal.ProjectLabelsChat; - assert.strictEqual(setting.id, 'chat.advanced.projectLabels.chat'); + const setting = ConfigKey.AdvancedExperimentalExperiments.ProjectLabelsChat; + assert.strictEqual(setting.id, 'chat.projectLabels.chat'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('ProjectLabelsInline is correctly configured', () => { - const setting = ConfigKey.Internal.ProjectLabelsInline; - assert.strictEqual(setting.id, 'chat.advanced.projectLabels.inline'); + const setting = ConfigKey.AdvancedExperimentalExperiments.ProjectLabelsInline; + assert.strictEqual(setting.id, 'chat.projectLabels.inline'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('ProjectLabelsExpanded is correctly configured', () => { - const setting = ConfigKey.Internal.ProjectLabelsExpanded; - assert.strictEqual(setting.id, 'chat.advanced.projectLabels.expanded'); + const setting = ConfigKey.AdvancedExperimentalExperiments.ProjectLabelsExpanded; + assert.strictEqual(setting.id, 'chat.projectLabels.expanded'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('WorkspaceMaxLocalIndexSize is correctly configured', () => { - const setting = ConfigKey.Internal.WorkspaceMaxLocalIndexSize; - assert.strictEqual(setting.id, 'chat.advanced.workspace.maxLocalIndexSize'); + const setting = ConfigKey.AdvancedExperimentalExperiments.WorkspaceMaxLocalIndexSize; + assert.strictEqual(setting.id, 'chat.workspace.maxLocalIndexSize'); assert.strictEqual(setting.defaultValue, 100_000); - assert.strictEqual(setting.isPublic, false); - }); test('WorkspaceEnableFullWorkspace is correctly configured', () => { - const setting = ConfigKey.Internal.WorkspaceEnableFullWorkspace; - assert.strictEqual(setting.id, 'chat.advanced.workspace.enableFullWorkspace'); + const setting = ConfigKey.AdvancedExperimentalExperiments.WorkspaceEnableFullWorkspace; + assert.strictEqual(setting.id, 'chat.workspace.enableFullWorkspace'); assert.strictEqual(setting.defaultValue, true); - assert.strictEqual(setting.isPublic, false); - }); test('WorkspaceEnableCodeSearch is correctly configured', () => { - const setting = ConfigKey.Internal.WorkspaceEnableCodeSearch; - assert.strictEqual(setting.id, 'chat.advanced.workspace.enableCodeSearch'); + const setting = ConfigKey.AdvancedExperimentalExperiments.WorkspaceEnableCodeSearch; + assert.strictEqual(setting.id, 'chat.workspace.enableCodeSearch'); assert.strictEqual(setting.defaultValue, true); - assert.strictEqual(setting.isPublic, false); - }); test('WorkspaceEnableEmbeddingsSearch is correctly configured', () => { - const setting = ConfigKey.Internal.WorkspaceEnableEmbeddingsSearch; - assert.strictEqual(setting.id, 'chat.advanced.workspace.enableEmbeddingsSearch'); + const setting = ConfigKey.AdvancedExperimentalExperiments.WorkspaceEnableEmbeddingsSearch; + assert.strictEqual(setting.id, 'chat.workspace.enableEmbeddingsSearch'); assert.strictEqual(setting.defaultValue, true); - assert.strictEqual(setting.isPublic, false); - }); test('WorkspacePreferredEmbeddingsModel is correctly configured', () => { - const setting = ConfigKey.Internal.WorkspacePreferredEmbeddingsModel; - assert.strictEqual(setting.id, 'chat.advanced.workspace.preferredEmbeddingsModel'); + const setting = ConfigKey.AdvancedExperimentalExperiments.WorkspacePreferredEmbeddingsModel; + assert.strictEqual(setting.id, 'chat.workspace.preferredEmbeddingsModel'); assert.strictEqual(setting.defaultValue, ''); - assert.strictEqual(setting.isPublic, false); - }); test('WorkspacePrototypeAdoCodeSearchEndpointOverride is correctly configured', () => { - const setting = ConfigKey.Internal.WorkspacePrototypeAdoCodeSearchEndpointOverride; - assert.strictEqual(setting.id, 'chat.advanced.workspace.prototypeAdoCodeSearchEndpointOverride'); + const setting = ConfigKey.AdvancedExperimental.WorkspacePrototypeAdoCodeSearchEndpointOverride; + assert.strictEqual(setting.id, 'chat.workspace.prototypeAdoCodeSearchEndpointOverride'); assert.strictEqual(setting.defaultValue, ''); - assert.strictEqual(setting.isPublic, false); - }); test('FeedbackOnChange is correctly configured', () => { - const setting = ConfigKey.Internal.FeedbackOnChange; - assert.strictEqual(setting.id, 'chat.advanced.feedback.onChange'); + const setting = ConfigKey.AdvancedExperimental.FeedbackOnChange; + assert.strictEqual(setting.id, 'chat.feedback.onChange'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('ReviewIntent is correctly configured', () => { - const setting = ConfigKey.Internal.ReviewIntent; - assert.strictEqual(setting.id, 'chat.advanced.review.intent'); + const setting = ConfigKey.AdvancedExperimental.ReviewIntent; + assert.strictEqual(setting.id, 'chat.review.intent'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('NotebookSummaryExperimentEnabled is correctly configured', () => { - const setting = ConfigKey.Internal.NotebookSummaryExperimentEnabled; - assert.strictEqual(setting.id, 'chat.advanced.notebook.summaryExperimentEnabled'); + const setting = ConfigKey.AdvancedExperimental.NotebookSummaryExperimentEnabled; + assert.strictEqual(setting.id, 'chat.notebook.summaryExperimentEnabled'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('NotebookVariableFilteringEnabled is correctly configured', () => { - const setting = ConfigKey.Internal.NotebookVariableFilteringEnabled; - assert.strictEqual(setting.id, 'chat.advanced.notebook.variableFilteringEnabled'); + const setting = ConfigKey.AdvancedExperimental.NotebookVariableFilteringEnabled; + assert.strictEqual(setting.id, 'chat.notebook.variableFilteringEnabled'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('NotebookAlternativeDocumentFormat is correctly configured', () => { - const setting = ConfigKey.Internal.NotebookAlternativeDocumentFormat; - assert.strictEqual(setting.id, 'chat.advanced.notebook.alternativeFormat'); + const setting = ConfigKey.AdvancedExperimentalExperiments.NotebookAlternativeDocumentFormat; + assert.strictEqual(setting.id, 'chat.notebook.alternativeFormat'); assert.strictEqual(setting.defaultValue, AlternativeNotebookFormat.xml); - assert.strictEqual(setting.isPublic, false); - }); test('UseAlternativeNESNotebookFormat is correctly configured', () => { - const setting = ConfigKey.Internal.UseAlternativeNESNotebookFormat; - assert.strictEqual(setting.id, 'chat.advanced.notebook.alternativeNESFormat.enabled'); + const setting = ConfigKey.AdvancedExperimentalExperiments.UseAlternativeNESNotebookFormat; + assert.strictEqual(setting.id, 'chat.notebook.alternativeNESFormat.enabled'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('TerminalToDebuggerPatterns is correctly configured', () => { - const setting = ConfigKey.Internal.TerminalToDebuggerPatterns; - assert.strictEqual(setting.id, 'chat.advanced.debugTerminalCommandPatterns'); + const setting = ConfigKey.AdvancedExperimental.TerminalToDebuggerPatterns; + assert.strictEqual(setting.id, 'chat.debugTerminalCommandPatterns'); assert.deepStrictEqual(setting.defaultValue, []); - assert.strictEqual(setting.isPublic, false); - }); test('EditSourceTrackingShowDecorations is correctly configured', () => { - const setting = ConfigKey.Internal.EditSourceTrackingShowDecorations; - assert.strictEqual(setting.id, 'chat.advanced.editSourceTracking.showDecorations'); + const setting = ConfigKey.AdvancedExperimental.EditSourceTrackingShowDecorations; + assert.strictEqual(setting.id, 'chat.editSourceTracking.showDecorations'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('EditSourceTrackingShowStatusBar is correctly configured', () => { - const setting = ConfigKey.Internal.EditSourceTrackingShowStatusBar; - assert.strictEqual(setting.id, 'chat.advanced.editSourceTracking.showStatusBar'); + const setting = ConfigKey.AdvancedExperimental.EditSourceTrackingShowStatusBar; + assert.strictEqual(setting.id, 'chat.editSourceTracking.showStatusBar'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('WorkspaceRecordingEnabled is correctly configured', () => { - const setting = ConfigKey.Internal.WorkspaceRecordingEnabled; - assert.strictEqual(setting.id, 'chat.advanced.localWorkspaceRecording.enabled'); + const setting = ConfigKey.AdvancedExperimental.WorkspaceRecordingEnabled; + assert.strictEqual(setting.id, 'chat.localWorkspaceRecording.enabled'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('EditRecordingEnabled is correctly configured', () => { - const setting = ConfigKey.Internal.EditRecordingEnabled; - assert.strictEqual(setting.id, 'chat.advanced.editRecording.enabled'); + const setting = ConfigKey.AdvancedExperimental.EditRecordingEnabled; + assert.strictEqual(setting.id, 'chat.editRecording.enabled'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('TemporalContextMaxAge is correctly configured', () => { - const setting = ConfigKey.Internal.TemporalContextMaxAge; - assert.strictEqual(setting.id, 'chat.advanced.temporalContext.maxAge'); + const setting = ConfigKey.AdvancedExperimentalExperiments.TemporalContextMaxAge; + assert.strictEqual(setting.id, 'chat.temporalContext.maxAge'); assert.strictEqual(setting.defaultValue, 100); - assert.strictEqual(setting.isPublic, false); - }); test('TemporalContextPreferSameLang is correctly configured', () => { - const setting = ConfigKey.Internal.TemporalContextPreferSameLang; - assert.strictEqual(setting.id, 'chat.advanced.temporalContext.preferSameLang'); + const setting = ConfigKey.AdvancedExperimentalExperiments.TemporalContextPreferSameLang; + assert.strictEqual(setting.id, 'chat.temporalContext.preferSameLang'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('CodeSearchAgentEnabled is correctly configured', () => { - const setting = ConfigKey.Internal.CodeSearchAgentEnabled; - assert.strictEqual(setting.id, 'chat.advanced.codesearch.agent.enabled'); + const setting = ConfigKey.AdvancedExperimental.CodeSearchAgentEnabled; + assert.strictEqual(setting.id, 'chat.codesearch.agent.enabled'); assert.strictEqual(setting.defaultValue, true); - assert.strictEqual(setting.isPublic, false); - }); test('AgentTemperature is correctly configured', () => { - const setting = ConfigKey.Internal.AgentTemperature; - assert.strictEqual(setting.id, 'chat.advanced.agent.temperature'); + const setting = ConfigKey.AdvancedExperimental.AgentTemperature; + assert.strictEqual(setting.id, 'chat.agent.temperature'); assert.strictEqual(setting.defaultValue, undefined); - assert.strictEqual(setting.isPublic, false); - }); test('InstantApplyShortModelName is correctly configured', () => { - const setting = ConfigKey.Internal.InstantApplyShortModelName; - assert.strictEqual(setting.id, 'chat.advanced.instantApply.shortContextModelName'); + const setting = ConfigKey.AdvancedExperimentalExperiments.InstantApplyShortModelName; + assert.strictEqual(setting.id, 'chat.instantApply.shortContextModelName'); assert.strictEqual(setting.defaultValue, 'gpt-4o-instant-apply-full-ft-v66-short'); - assert.strictEqual(setting.isPublic, false); - }); test('InstantApplyShortContextLimit is correctly configured', () => { - const setting = ConfigKey.Internal.InstantApplyShortContextLimit; - assert.strictEqual(setting.id, 'chat.advanced.instantApply.shortContextLimit'); + const setting = ConfigKey.AdvancedExperimentalExperiments.InstantApplyShortContextLimit; + assert.strictEqual(setting.id, 'chat.instantApply.shortContextLimit'); assert.strictEqual(setting.defaultValue, 8000); - assert.strictEqual(setting.isPublic, false); - }); test('EnableUserPreferences is correctly configured', () => { - const setting = ConfigKey.Internal.EnableUserPreferences; - assert.strictEqual(setting.id, 'chat.advanced.enableUserPreferences'); + const setting = ConfigKey.AdvancedExperimental.EnableUserPreferences; + assert.strictEqual(setting.id, 'chat.enableUserPreferences'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('SummarizeAgentConversationHistoryThreshold is correctly configured', () => { - const setting = ConfigKey.Internal.SummarizeAgentConversationHistoryThreshold; - assert.strictEqual(setting.id, 'chat.advanced.summarizeAgentConversationHistoryThreshold'); + const setting = ConfigKey.AdvancedExperimental.SummarizeAgentConversationHistoryThreshold; + assert.strictEqual(setting.id, 'chat.summarizeAgentConversationHistoryThreshold'); assert.strictEqual(setting.defaultValue, undefined); - assert.strictEqual(setting.isPublic, false); - }); test('AgentHistorySummarizationMode is correctly configured', () => { - const setting = ConfigKey.Internal.AgentHistorySummarizationMode; - assert.strictEqual(setting.id, 'chat.advanced.agentHistorySummarizationMode'); + const setting = ConfigKey.AdvancedExperimental.AgentHistorySummarizationMode; + assert.strictEqual(setting.id, 'chat.agentHistorySummarizationMode'); assert.strictEqual(setting.defaultValue, undefined); - assert.strictEqual(setting.isPublic, false); - }); test('AgentHistorySummarizationWithPromptCache is correctly configured', () => { - const setting = ConfigKey.Internal.AgentHistorySummarizationWithPromptCache; - assert.strictEqual(setting.id, 'chat.advanced.agentHistorySummarizationWithPromptCache'); + const setting = ConfigKey.AdvancedExperimentalExperiments.AgentHistorySummarizationWithPromptCache; + assert.strictEqual(setting.id, 'chat.agentHistorySummarizationWithPromptCache'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('AgentHistorySummarizationForceGpt41 is correctly configured', () => { - const setting = ConfigKey.Internal.AgentHistorySummarizationForceGpt41; - assert.strictEqual(setting.id, 'chat.advanced.agentHistorySummarizationForceGpt41'); + const setting = ConfigKey.AdvancedExperimentalExperiments.AgentHistorySummarizationForceGpt41; + assert.strictEqual(setting.id, 'chat.agentHistorySummarizationForceGpt41'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('UseResponsesApiTruncation is correctly configured', () => { - const setting = ConfigKey.Internal.UseResponsesApiTruncation; - assert.strictEqual(setting.id, 'chat.advanced.useResponsesApiTruncation'); + const setting = ConfigKey.AdvancedExperimental.UseResponsesApiTruncation; + assert.strictEqual(setting.id, 'chat.useResponsesApiTruncation'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); }); test('OmitBaseAgentInstructions is correctly configured', () => { - const setting = ConfigKey.Internal.OmitBaseAgentInstructions; - assert.strictEqual(setting.id, 'chat.advanced.omitBaseAgentInstructions'); + const setting = ConfigKey.AdvancedExperimental.OmitBaseAgentInstructions; + assert.strictEqual(setting.id, 'chat.omitBaseAgentInstructions'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('PromptFileContext is correctly configured', () => { - const setting = ConfigKey.Internal.PromptFileContext; - assert.strictEqual(setting.id, 'chat.advanced.promptFileContextProvider.enabled'); + const setting = ConfigKey.AdvancedExperimentalExperiments.PromptFileContext; + assert.strictEqual(setting.id, 'chat.promptFileContextProvider.enabled'); assert.strictEqual(setting.defaultValue, true); - assert.strictEqual(setting.isPublic, false); }); test('DefaultToolsGrouped is correctly configured', () => { - const setting = ConfigKey.Internal.DefaultToolsGrouped; - assert.strictEqual(setting.id, 'chat.advanced.tools.defaultToolsGrouped'); + const setting = ConfigKey.AdvancedExperimentalExperiments.DefaultToolsGrouped; + assert.strictEqual(setting.id, 'chat.tools.defaultToolsGrouped'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('VirtualToolEmbeddingRanking is correctly configured', () => { - const setting = ConfigKey.Internal.VirtualToolEmbeddingRanking; - assert.strictEqual(setting.id, 'chat.advanced.virtualTools.embeddingRanking'); + const setting = ConfigKey.AdvancedExperimentalExperiments.VirtualToolEmbeddingRanking; + assert.strictEqual(setting.id, 'chat.virtualTools.embeddingRanking'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('MultiReplaceStringGrok is correctly configured', () => { - const setting = ConfigKey.Internal.MultiReplaceStringGrok; - assert.strictEqual(setting.id, 'chat.advanced.multiReplaceStringGrok.enabled'); + const setting = ConfigKey.AdvancedExperimentalExperiments.MultiReplaceStringGrok; + assert.strictEqual(setting.id, 'chat.multiReplaceStringGrok.enabled'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); - }); test('EnableClaudeCodeAgent is correctly configured', () => { - const setting = ConfigKey.Internal.EnableClaudeCodeAgent; - assert.strictEqual(setting.id, 'chat.advanced.claudeCode.enabled'); + const setting = ConfigKey.AdvancedExperimental.EnableClaudeCodeAgent; + assert.strictEqual(setting.id, 'chat.claudeCode.enabled'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); }); test('ClaudeCodeDebugEnabled is correctly configured', () => { - const setting = ConfigKey.Internal.ClaudeCodeDebugEnabled; - assert.strictEqual(setting.id, 'chat.advanced.claudeCode.debug'); + const setting = ConfigKey.AdvancedExperimental.ClaudeCodeDebugEnabled; + assert.strictEqual(setting.id, 'chat.claudeCode.debug'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); }); test('CopilotCLIEnabled is correctly configured', () => { - const setting = ConfigKey.Internal.CopilotCLIEnabled; - assert.strictEqual(setting.id, 'chat.advanced.copilotCLI.enabled'); + const setting = ConfigKey.AdvancedExperimental.CopilotCLIEnabled; + assert.strictEqual(setting.id, 'chat.copilotCLI.enabled'); assert.strictEqual(setting.defaultValue, true); - assert.strictEqual(setting.isPublic, false); }); test('Gpt5AlternativePatch is correctly configured', () => { - const setting = ConfigKey.Internal.Gpt5AlternativePatch; - assert.strictEqual(setting.id, 'chat.advanced.gpt5AlternativePatch'); + const setting = ConfigKey.AdvancedExperimentalExperiments.Gpt5AlternativePatch; + assert.strictEqual(setting.id, 'chat.gpt5AlternativePatch'); assert.strictEqual(setting.defaultValue, false); - assert.strictEqual(setting.isPublic, false); }); test('InlineEditsTriggerOnEditorChangeAfterSeconds is correctly configured', () => { - const setting = ConfigKey.Internal.InlineEditsTriggerOnEditorChangeAfterSeconds; - assert.strictEqual(setting.id, 'chat.advanced.inlineEdits.triggerOnEditorChangeAfterSeconds'); + const setting = ConfigKey.AdvancedExperimentalExperiments.InlineEditsTriggerOnEditorChangeAfterSeconds; + assert.strictEqual(setting.id, 'chat.inlineEdits.triggerOnEditorChangeAfterSeconds'); const defaultValue = setting.defaultValue as DefaultValueWithTeamValue; assert.strictEqual(defaultValue.defaultValue, undefined); assert.strictEqual(defaultValue.teamDefaultValue, 10); - assert.strictEqual(setting.isPublic, false); - }); test('InlineEditsNextCursorPredictionDisplayLine is correctly configured', () => { - const setting = ConfigKey.Internal.InlineEditsNextCursorPredictionDisplayLine; - assert.strictEqual(setting.id, 'chat.advanced.inlineEdits.nextCursorPrediction.displayLine'); + const setting = ConfigKey.AdvancedExperimentalExperiments.InlineEditsNextCursorPredictionDisplayLine; + assert.strictEqual(setting.id, 'chat.inlineEdits.nextCursorPrediction.displayLine'); assert.strictEqual(setting.defaultValue, true); - assert.strictEqual(setting.isPublic, false); - }); test('InlineEditsNextCursorPredictionCurrentFileMaxTokens is correctly configured', () => { - const setting = ConfigKey.Internal.InlineEditsNextCursorPredictionCurrentFileMaxTokens; - assert.strictEqual(setting.id, 'chat.advanced.inlineEdits.nextCursorPrediction.currentFileMaxTokens'); + const setting = ConfigKey.AdvancedExperimentalExperiments.InlineEditsNextCursorPredictionCurrentFileMaxTokens; + assert.strictEqual(setting.id, 'chat.inlineEdits.nextCursorPrediction.currentFileMaxTokens'); assert.strictEqual(setting.defaultValue, 2000); - assert.strictEqual(setting.isPublic, false); - }); }); diff --git a/src/platform/configuration/vscode/configurationServiceImpl.ts b/src/platform/configuration/vscode/configurationServiceImpl.ts index 0a082da5e1..96f44394a8 100644 --- a/src/platform/configuration/vscode/configurationServiceImpl.ts +++ b/src/platform/configuration/vscode/configurationServiceImpl.ts @@ -5,10 +5,11 @@ import type { WorkspaceConfiguration } from 'vscode'; import * as vscode from 'vscode'; +import { distinct } from '../../../util/vs/base/common/arrays'; import { ICopilotTokenStore } from '../../authentication/common/copilotTokenStore'; import { packageJson } from '../../env/common/packagejson'; import { IExperimentationService } from '../../telemetry/common/nullExperimentationService'; -import { AbstractConfigurationService, BaseConfig, Config, ConfigValueValidators, CopilotConfigPrefix, ExperimentBasedConfig, ExperimentBasedConfigType, InspectConfigResult } from '../common/configurationService'; +import { AbstractConfigurationService, BaseConfig, Config, ConfigValueValidators, CopilotConfigPrefix, ExperimentBasedConfig, ExperimentBasedConfigType, globalConfigRegistry, InspectConfigResult } from '../common/configurationService'; // Helper to avoid JSON.stringify quoting strings function stringOrStringify(value: any) { @@ -30,7 +31,18 @@ export class ConfigurationServiceImpl extends AbstractConfigurationService { if (changeEvent.affectsConfiguration(CopilotConfigPrefix)) { this.config = vscode.workspace.getConfiguration(CopilotConfigPrefix); } - this._onDidChangeConfiguration.fire(changeEvent); + this._onDidChangeConfiguration.fire({ + affectsConfiguration: (section: string, scope?: vscode.ConfigurationScope) => { + if (changeEvent.affectsConfiguration(section, scope)) { + return true; + } + const oldId = globalConfigRegistry.configs.get(section)?.fullyQualifiedOldId; + if (oldId && changeEvent.affectsConfiguration(oldId, scope)) { + return true; + } + return false; + } + }); }); } @@ -68,10 +80,10 @@ export class ConfigurationServiceImpl extends AbstractConfigurationService { // or internal users, so the (public) default value used by vscode is not the same. // We need to really check if the user or workspace configured the setting if (this.isConfigured(key, scope)) { - configuredValue = config.get(key.id); + configuredValue = config.get(key.id) ?? (key.oldId ? config.get(key.oldId) : undefined); } } else { - configuredValue = config.get(key.id); + configuredValue = config.get(key.id) ?? (key.oldId ? config.get(key.oldId) : undefined); } } @@ -98,7 +110,24 @@ export class ConfigurationServiceImpl extends AbstractConfigurationService { } const config = scope === undefined ? this.config : vscode.workspace.getConfiguration(CopilotConfigPrefix, scope); - return config.inspect(key.id); + const inspectResult = config.inspect(key.id); + if (!key.oldId) { + return inspectResult; + } + + const oldInspectResult = config.inspect(key.oldId); + const languageIds = distinct([...inspectResult?.languageIds ?? [], ...oldInspectResult?.languageIds ?? []]); + return { + defaultValue: inspectResult?.defaultValue ?? oldInspectResult?.defaultValue, + globalValue: inspectResult?.globalValue ?? oldInspectResult?.globalValue, + workspaceValue: inspectResult?.workspaceValue ?? oldInspectResult?.workspaceValue, + workspaceFolderValue: inspectResult?.workspaceFolderValue ?? oldInspectResult?.workspaceFolderValue, + defaultLanguageValue: inspectResult?.defaultLanguageValue ?? oldInspectResult?.defaultLanguageValue, + globalLanguageValue: inspectResult?.globalLanguageValue ?? oldInspectResult?.globalLanguageValue, + workspaceLanguageValue: inspectResult?.workspaceLanguageValue ?? oldInspectResult?.workspaceLanguageValue, + workspaceFolderLanguageValue: inspectResult?.workspaceFolderLanguageValue ?? oldInspectResult?.workspaceFolderLanguageValue, + languageIds: languageIds.length ? languageIds : undefined, + }; } override getNonExtensionConfig(configKey: string): T | undefined { @@ -190,6 +219,18 @@ export class ConfigurationServiceImpl extends AbstractConfigurationService { return expValue2; } + if (key.fullyQualifiedOldId) { + const oldExpValue = experimentationService.getTreatmentVariable>(`copilotchat.config.${key.oldId}`); + if (oldExpValue !== undefined) { + return oldExpValue; + } + + const oldExpValue2 = experimentationService.getTreatmentVariable>(`config.${key.fullyQualifiedOldId}`); + if (oldExpValue2 !== undefined) { + return oldExpValue2; + } + } + return this.getDefaultValue(key); } @@ -206,7 +247,7 @@ export class ConfigurationServiceImpl extends AbstractConfigurationService { return undefined; } - return config.get(key.id); + return config.get(key.id) ?? (key.oldId ? config.get(key.oldId) : undefined); } // Dumps config settings defined in the extension json @@ -247,8 +288,14 @@ export class ConfigurationServiceImpl extends AbstractConfigurationService { // Fire simulated event which checks if a configuration is affected in the treatments this._onDidChangeConfiguration.fire({ affectsConfiguration: (section: string, _scope?: vscode.ConfigurationScope) => { - const result = treatments.some(t => t.startsWith(`config.${section}`)); - return result; + if (treatments.some(t => t.startsWith(`config.${section}`))) { + return true; + } + const oldId = globalConfigRegistry.configs.get(section)?.fullyQualifiedOldId; + if (oldId && treatments.some(t => t.startsWith(`config.${oldId}`))) { + return true; + } + return false; } }); } diff --git a/src/platform/endpoint/node/proxyInstantApplyShortEndpoint.ts b/src/platform/endpoint/node/proxyInstantApplyShortEndpoint.ts index e74cdc0d2b..99f09f5f3a 100644 --- a/src/platform/endpoint/node/proxyInstantApplyShortEndpoint.ts +++ b/src/platform/endpoint/node/proxyInstantApplyShortEndpoint.ts @@ -34,7 +34,7 @@ export class ProxyInstantApplyShortEndpoint extends ChatEndpoint { @IExperimentationService experimentationService: IExperimentationService, @ILogService logService: ILogService, ) { - const model = configurationService.getExperimentBasedConfig(ConfigKey.Internal.InstantApplyShortModelName, experimentationService) ?? CHAT_MODEL.SHORT_INSTANT_APPLY; + const model = configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.InstantApplyShortModelName, experimentationService) ?? CHAT_MODEL.SHORT_INSTANT_APPLY; const modelInfo: IChatModelInformation = { id: model, name: model, diff --git a/src/platform/endpoint/node/responsesApi.ts b/src/platform/endpoint/node/responsesApi.ts index e3bde2b837..1b968665ba 100644 --- a/src/platform/endpoint/node/responsesApi.ts +++ b/src/platform/endpoint/node/responsesApi.ts @@ -53,7 +53,7 @@ export function createResponsesRequestBody(accessor: ServicesAccessor, options: text: verbosity ? { verbosity } : undefined, }; - body.truncation = configService.getConfig(ConfigKey.Internal.UseResponsesApiTruncation) ? + body.truncation = configService.getConfig(ConfigKey.AdvancedExperimental.UseResponsesApiTruncation) ? 'auto' : 'disabled'; const effortConfig = configService.getExperimentBasedConfig(ConfigKey.ResponsesApiReasoningEffort, expService); diff --git a/src/platform/multiFileEdit/common/editLogService.ts b/src/platform/multiFileEdit/common/editLogService.ts index 359b632db2..e55c2545d0 100644 --- a/src/platform/multiFileEdit/common/editLogService.ts +++ b/src/platform/multiFileEdit/common/editLogService.ts @@ -77,7 +77,7 @@ export class EditLogService implements IEditLogService { ) { } private _isEnabled() { - return this._configurationService.getConfig(ConfigKey.Internal.EditRecordingEnabled); + return this._configurationService.getConfig(ConfigKey.AdvancedExperimental.EditRecordingEnabled); } logEditChatRequest(turnId: string, prompt: ReadonlyArray, response: string): void { diff --git a/src/platform/notebook/common/alternativeContent.ts b/src/platform/notebook/common/alternativeContent.ts index 5bd4680cab..fcaa8e5b2a 100644 --- a/src/platform/notebook/common/alternativeContent.ts +++ b/src/platform/notebook/common/alternativeContent.ts @@ -67,7 +67,7 @@ export class AlternativeNotebookContentService implements IAlternativeNotebookCo return 'json'; } - return this.configurationService.getExperimentBasedConfig(ConfigKey.Internal.NotebookAlternativeDocumentFormat, this.experimentationService); + return this.configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.NotebookAlternativeDocumentFormat, this.experimentationService); } create(format: AlternativeContentFormat): BaseAlternativeNotebookContentProvider { diff --git a/src/platform/notebook/vscode/notebookServiceImpl.ts b/src/platform/notebook/vscode/notebookServiceImpl.ts index 21978964be..324997d940 100644 --- a/src/platform/notebook/vscode/notebookServiceImpl.ts +++ b/src/platform/notebook/vscode/notebookServiceImpl.ts @@ -46,7 +46,7 @@ export class NotebookService implements INotebookService { @ILogService private readonly _logger: ILogService, ) { this._isVariableFilteringEnabled = this._experimentationService.getTreatmentVariable('copilotchat.notebookVariableFiltering') - || this._configurationService.getConfig(ConfigKey.Internal.NotebookVariableFilteringEnabled); + || this._configurationService.getConfig(ConfigKey.AdvancedExperimental.NotebookVariableFilteringEnabled); this._registerExecutionListener(); } diff --git a/src/platform/remoteCodeSearch/common/adoCodeSearchService.ts b/src/platform/remoteCodeSearch/common/adoCodeSearchService.ts index 991553d3fe..182dc59910 100644 --- a/src/platform/remoteCodeSearch/common/adoCodeSearchService.ts +++ b/src/platform/remoteCodeSearch/common/adoCodeSearchService.ts @@ -264,7 +264,7 @@ export class AdoCodeSearchService extends Disposable implements IAdoCodeSearchSe throw new Error('No valid auth token'); } - let endpoint = this._configurationService.getConfig(ConfigKey.Internal.WorkspacePrototypeAdoCodeSearchEndpointOverride); + let endpoint = this._configurationService.getConfig(ConfigKey.AdvancedExperimental.WorkspacePrototypeAdoCodeSearchEndpointOverride); if (!endpoint) { endpoint = this.getAdoAlmSearchUrl(repo.adoRepoId); } diff --git a/src/platform/review/vscode/reviewServiceImpl.ts b/src/platform/review/vscode/reviewServiceImpl.ts index b169d97f34..550e590ec3 100644 --- a/src/platform/review/vscode/reviewServiceImpl.ts +++ b/src/platform/review/vscode/reviewServiceImpl.ts @@ -44,7 +44,7 @@ export class ReviewServiceImpl implements IReviewService { vscode.commands.executeCommand('setContext', ConfigKey.CodeFeedback.fullyQualifiedId, this.isCodeFeedbackEnabled()); } if (e.affectsConfiguration('github.copilot.advanced') || e.affectsConfiguration('github.copilot.advanced.review.intent')) { - vscode.commands.executeCommand('setContext', ConfigKey.Internal.ReviewIntent.fullyQualifiedId, this.isIntentEnabled()); + vscode.commands.executeCommand('setContext', ConfigKey.AdvancedExperimental.ReviewIntent.fullyQualifiedId, this.isIntentEnabled()); } })); this._disposables.add(this._authenticationService.onDidAuthenticationChange(() => { @@ -97,7 +97,7 @@ export class ReviewServiceImpl implements IReviewService { updateContextValues(): void { vscode.commands.executeCommand('setContext', ConfigKey.CodeFeedback.fullyQualifiedId, this.isCodeFeedbackEnabled()); vscode.commands.executeCommand('setContext', reviewDiffContextKey, this.isReviewDiffEnabled()); - vscode.commands.executeCommand('setContext', ConfigKey.Internal.ReviewIntent.fullyQualifiedId, this.isIntentEnabled()); + vscode.commands.executeCommand('setContext', ConfigKey.AdvancedExperimental.ReviewIntent.fullyQualifiedId, this.isIntentEnabled()); } isCodeFeedbackEnabled() { @@ -110,7 +110,7 @@ export class ReviewServiceImpl implements IReviewService { } isIntentEnabled(): boolean { - return this._configurationService.getConfig(ConfigKey.Internal.ReviewIntent); + return this._configurationService.getConfig(ConfigKey.AdvancedExperimental.ReviewIntent); } getDiagnosticCollection(): ReviewDiagnosticCollection { diff --git a/src/platform/test/node/simulationWorkspaceServices.ts b/src/platform/test/node/simulationWorkspaceServices.ts index be423964d2..d04e460483 100644 --- a/src/platform/test/node/simulationWorkspaceServices.ts +++ b/src/platform/test/node/simulationWorkspaceServices.ts @@ -274,10 +274,10 @@ export class SimulationReviewService implements IReviewService { } isIntentEnabled(): boolean { - if (ConfigValueValidators.isDefaultValueWithTeamValue(ConfigKey.Internal.ReviewIntent.defaultValue)) { - return ConfigKey.Internal.ReviewIntent.defaultValue.defaultValue; + if (ConfigValueValidators.isDefaultValueWithTeamValue(ConfigKey.AdvancedExperimental.ReviewIntent.defaultValue)) { + return ConfigKey.AdvancedExperimental.ReviewIntent.defaultValue.defaultValue; } - return ConfigKey.Internal.ReviewIntent.defaultValue; + return ConfigKey.AdvancedExperimental.ReviewIntent.defaultValue; } getDiagnosticCollection(): ReviewDiagnosticCollection { diff --git a/src/platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes.ts b/src/platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes.ts index d73cf1f9bc..550ef36ab1 100644 --- a/src/platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes.ts +++ b/src/platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes.ts @@ -218,7 +218,7 @@ export class GithubAvailableEmbeddingTypesService implements IGithubAvailableEmb const all = result.val; this._logService.info(`GithubAvailableEmbeddingTypesManager: Got embeddings. Primary: ${all.primary.join(',')}. Deprecated: ${all.deprecated.join(',')}`); - const preference = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.WorkspacePreferredEmbeddingsModel, this._experimentationService); + const preference = this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.WorkspacePreferredEmbeddingsModel, this._experimentationService); if (preference) { const preferred = [...all.primary, ...all.deprecated].find(type => type.id === preference); if (preferred) { diff --git a/src/platform/workspaceChunkSearch/node/codeSearchChunkSearch.ts b/src/platform/workspaceChunkSearch/node/codeSearchChunkSearch.ts index 1b4b7a7e69..235e23e02a 100644 --- a/src/platform/workspaceChunkSearch/node/codeSearchChunkSearch.ts +++ b/src/platform/workspaceChunkSearch/node/codeSearchChunkSearch.ts @@ -317,7 +317,7 @@ export class CodeSearchChunkSearch extends Disposable implements IWorkspaceChunk } private isCodeSearchEnabled() { - return this._configService.getExperimentBasedConfig(ConfigKey.Internal.WorkspaceEnableCodeSearch, this._experimentationService); + return this._configService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.WorkspaceEnableCodeSearch, this._experimentationService); } getRemoteIndexState(): CodeSearchRemoteIndexState { diff --git a/src/platform/workspaceChunkSearch/node/embeddingsChunkSearch.ts b/src/platform/workspaceChunkSearch/node/embeddingsChunkSearch.ts index ff6c4e4a0a..2c10543ca6 100644 --- a/src/platform/workspaceChunkSearch/node/embeddingsChunkSearch.ts +++ b/src/platform/workspaceChunkSearch/node/embeddingsChunkSearch.ts @@ -197,7 +197,7 @@ export class EmbeddingsChunkSearch extends Disposable implements IWorkspaceChunk } private isEmbeddingSearchEnabled() { - return this._configService.getExperimentBasedConfig(ConfigKey.Internal.WorkspaceEnableEmbeddingsSearch, this._experimentationService); + return this._configService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.WorkspaceEnableEmbeddingsSearch, this._experimentationService); } @LogExecTime(self => self._logService, 'EmbeddingsChunkSearch::searchSubsetOfFiles') diff --git a/src/platform/workspaceChunkSearch/node/fullWorkspaceChunkSearch.ts b/src/platform/workspaceChunkSearch/node/fullWorkspaceChunkSearch.ts index 349381a5a5..74126f3cae 100644 --- a/src/platform/workspaceChunkSearch/node/fullWorkspaceChunkSearch.ts +++ b/src/platform/workspaceChunkSearch/node/fullWorkspaceChunkSearch.ts @@ -188,6 +188,6 @@ export class FullWorkspaceChunkSearch extends Disposable implements IWorkspaceCh } private isEnabled(): boolean { - return this._configService.getExperimentBasedConfig(ConfigKey.Internal.WorkspaceEnableFullWorkspace, this._experimentationService); + return this._configService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.WorkspaceEnableFullWorkspace, this._experimentationService); } } diff --git a/src/platform/workspaceChunkSearch/node/workspaceFileIndex.ts b/src/platform/workspaceChunkSearch/node/workspaceFileIndex.ts index 80cb7c2497..3ae231efff 100644 --- a/src/platform/workspaceChunkSearch/node/workspaceFileIndex.ts +++ b/src/platform/workspaceChunkSearch/node/workspaceFileIndex.ts @@ -707,7 +707,7 @@ export class WorkspaceFileIndex extends Disposable implements IWorkspaceFileInde } private getMaxFilesToIndex(): number { - return this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.WorkspaceMaxLocalIndexSize, this._expService); + return this._configurationService.getExperimentBasedConfig(ConfigKey.AdvancedExperimentalExperiments.WorkspaceMaxLocalIndexSize, this._expService); } private async getWorkspaceFilesToIndex(maxResults: number, token: CancellationToken): Promise> {