Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion .agents/skills/launch/scripts/launch.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,8 @@ try {
if ($agents) {
$launchArgs.Add('--agents')
if (-not [string]::IsNullOrWhiteSpace($sessionTitle)) {
$launchArgs.Add("--session-title=$sessionTitle")
$sessionTitleBase64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($sessionTitle)).TrimEnd('=').Replace('+', '-').Replace('/', '_')
$launchArgs.Add("--session-title-base64=$sessionTitleBase64")
}
}
$launchArgs.Add("--user-data-dir=$destinationUdd")
Expand Down
3 changes: 2 additions & 1 deletion .agents/skills/launch/scripts/launch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ fi
if [[ "$AGENTS" == "1" ]]; then
ARGS=("--agents" "${ARGS[@]}")
if [[ -n "$SESSION_TITLE" ]]; then
ARGS+=("--session-title=$SESSION_TITLE")
SESSION_TITLE_BASE64=$(node -e 'process.stdout.write(Buffer.from(process.argv[1], "utf8").toString("base64url"))' -- "$SESSION_TITLE")
ARGS+=("--session-title-base64=$SESSION_TITLE_BASE64")
fi
fi
if (( ${#EXTRA_ARGS[@]} )); then
Expand Down
46 changes: 45 additions & 1 deletion build/lib/policies/policyData.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
}
},
"type": "boolean",
"default": false,
"default": true,
"included": true
},
{
Expand Down Expand Up @@ -220,6 +220,21 @@
"default": false,
"included": true
},
{
"key": "chat.agentHost.customTerminalTool.enabled",
"name": "ChatAgentHostCustomTerminalTool",
"category": "InteractiveSession",
"minimumVersion": "1.137",
"localization": {
"description": {
"key": "chat.agentHost.customTerminalTool.enabled.policy",
"value": "Enable the Agent Host custom terminal tool override for Copilot SDK sessions."
}
},
"type": "boolean",
"default": false,
"included": true
},
{
"key": "chat.agentHost.otel.captureContent",
"name": "CopilotOtelCaptureContent",
Expand Down Expand Up @@ -852,6 +867,35 @@
"default": 12,
"included": true
},
{
"key": "extensions.gallery.authProvider",
"name": "ExtensionGalleryAuthProvider",
"category": "Extensions",
"minimumVersion": "1.137",
"localization": {
"description": {
"key": "extensions.gallery.authProvider",
"value": "Configure the authentication provider for the Extensions Marketplace"
},
"enumDescriptions": [
{
"key": "extensions.gallery.authProvider.github",
"value": "Authenticate to the Extensions Marketplace using GitHub."
},
{
"key": "extensions.gallery.authProvider.microsoft",
"value": "Authenticate to the Extensions Marketplace using a Microsoft (Entra ID) account."
}
]
},
"type": "string",
"default": "github",
"enum": [
"github",
"microsoft"
],
"included": false
},
{
"key": "extensions.gallery.serviceUrl",
"name": "ExtensionGalleryServiceUrl",
Expand Down
2 changes: 1 addition & 1 deletion extensions/bat/language-configuration.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"comments": {
"lineComment": "@REM"
"lineComment": "@REM "
},
"brackets": [
["{", "}"],
Expand Down
16 changes: 16 additions & 0 deletions extensions/copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4144,6 +4144,22 @@
"onExp"
]
},
"github.copilot.chat.claudeOpusDefaultReasoningEffort": {
"type": "string",
"default": "",
"enum": [
"",
"low",
"medium",
"high",
"max"
],
"markdownDescription": "%github.copilot.config.claudeOpusDefaultReasoningEffort%",
"tags": [
"experimental",
"onExp"
]
},
"github.copilot.chat.gpt55ReadFileTool.enabled": {
"type": "boolean",
"default": true,
Expand Down
1 change: 1 addition & 0 deletions extensions/copilot/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@
"github.copilot.config.gpt56Verbosity.enabled": "Sets the response verbosity to low for gpt-5.6 models.",
"github.copilot.config.gemini3GetChangedFilesTool.enabled": "Enables the Get Changed Files tool for gemini-3 models.",
"github.copilot.config.gemini3LowReasoningEffort.enabled": "Sets the reasoning effort to low for gemini-3 models.",
"github.copilot.config.claudeOpusDefaultReasoningEffort": "Overrides the default thinking effort shown in the model picker for Claude Opus models. Leave empty to use the built-in default. Ignored if the model does not support the chosen level.",
"github.copilot.config.gpt55ReadFileTool.enabled": "Enables the Read File tool for gpt-5.5 models.",
"github.copilot.config.anthropic.tools.websearch.enabled": "Enable Anthropic's native web search tool for BYOK Claude models. When enabled, allows Claude to search the web for current information. \n\n**Note**: This is an experimental feature only available for BYOK Anthropic Claude models.",
"github.copilot.config.anthropic.tools.websearch.maxUses": "Maximum number of web searches allowed per request. Valid range is 1 to 20. Prevents excessive API calls within a single interaction. If Claude exceeds this limit, the response returns an error.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,17 @@ export function getReasoningEffortLabel(level: string): string {
* Builds the `reasoningEffort` property descriptor for a model's
* {@link LanguageModelConfigurationSchema}. Centralises the default-selection
* and localized descriptions so the picker stays consistent across the
* Copilot and BYOK code paths.
* Copilot and BYOK code paths. `defaultOverride` wins over the family default
* when it is one of the advertised levels.
*/
export function buildReasoningEffortSchemaProperty(effortLevels: readonly string[], family: string): NonNullable<LanguageModelConfigurationSchema['properties']>[string] {
export function buildReasoningEffortSchemaProperty(effortLevels: readonly string[], family: string, defaultOverride?: string): NonNullable<LanguageModelConfigurationSchema['properties']>[string] {
return {
type: 'string',
title: l10n.t('Thinking Effort'),
enum: effortLevels,
enumItemLabels: effortLevels.map(getReasoningEffortLabel),
enumDescriptions: effortLevels.map(getReasoningEffortDescription),
default: pickDefaultReasoningEffort(effortLevels, family),
default: defaultOverride && effortLevels.includes(defaultOverride) ? defaultOverride : pickDefaultReasoningEffort(effortLevels, family),
group: 'navigation',
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,12 @@ Learn more about [GitHub Copilot](https://docs.github.com/copilot/using-github-c
if (!baseLmModel) {
return request;
}
await vscode.commands.executeCommand('workbench.action.chat.changeModel', { vendor: baseLmModel.vendor, id: baseLmModel.id, family: baseLmModel.family });
// Switch to the base model and show a warning
request = { ...request, model: baseLmModel };
if (request.subAgentInvocationId === undefined) {
// A subagent runs inside the main request; changing the picker there would flip every widget mid-turn.
await vscode.commands.executeCommand('workbench.action.chat.changeModel', { vendor: baseLmModel.vendor, id: baseLmModel.id, family: baseLmModel.family });
}
// Switch to the base model and show a warning
let messageString: vscode.MarkdownString;
if (this.authenticationService.copilotToken?.isIndividual) {
messageString = new vscode.MarkdownString(vscode.l10n.t({
Expand All @@ -322,8 +325,10 @@ Learn more about [GitHub Copilot](https://docs.github.com/copilot/using-github-c
if (!autoModel) {
return request;
}
await vscode.commands.executeCommand('workbench.action.chat.changeModel', { vendor: autoModel.vendor, id: autoModel.id, family: autoModel.family });
request = { ...request, model: autoModel };
if (request.subAgentInvocationId === undefined) {
await vscode.commands.executeCommand('workbench.action.chat.changeModel', { vendor: autoModel.vendor, id: autoModel.id, family: autoModel.family });
}
if (alwaysSwitchToAuto) {
await vscode.workspace.getConfiguration('github.copilot').update('chat.rateLimitAutoSwitchToAuto', true, vscode.ConfigurationTarget.Global);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { IAuthenticationService } from '../../../platform/authentication/common/
import { CopilotToken } from '../../../platform/authentication/common/copilotToken';
import { IBlockedExtensionService } from '../../../platform/chat/common/blockedExtensionService';
import { ChatFetchResponseType, ChatLocation, getErrorDetailsFromChatFetchError } from '../../../platform/chat/common/commonTypes';
import { ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';
import { getTextPart } from '../../../platform/chat/common/globalStringUtils';
import { EmbeddingType, getWellKnownEmbeddingTypeInfo, IEmbeddingsComputer } from '../../../platform/embeddings/common/embeddingsComputer';
import { AUTO_MODE_TIER_PROPERTY, defaultAutoModeTier, selectableAutoModeTiers } from '../../../platform/endpoint/common/autoModeTiers';
Expand Down Expand Up @@ -119,7 +120,7 @@ function buildAutoRoutingContext(

// Auto model delegates to different backends, so the only picker it exposes is
// the routing tier; per-model options belong to the model it routes to.
function buildConfigurationSchema(endpoint: IChatEndpoint, autoTiersEnabled: boolean): { configurationSchema?: vscode.LanguageModelConfigurationSchema } {
function buildConfigurationSchema(endpoint: IChatEndpoint, autoTiersEnabled: boolean, opusDefaultEffort: string | undefined): { configurationSchema?: vscode.LanguageModelConfigurationSchema } {
if (endpoint instanceof AutoChatEndpoint) {
return autoTiersEnabled
? { configurationSchema: { properties: { [AUTO_MODE_TIER_PROPERTY]: buildAutoModeTierSchemaProperty(selectableAutoModeTiers, defaultAutoModeTier) } } }
Expand All @@ -131,7 +132,9 @@ function buildConfigurationSchema(endpoint: IChatEndpoint, autoTiersEnabled: boo
// Reasoning effort config
const effortLevels = endpoint.supportsReasoningEffort;
if (effortLevels && effortLevels.length > 1) {
properties.reasoningEffort = buildReasoningEffortSchemaProperty(effortLevels, endpoint.family.toLowerCase());
const family = endpoint.family.toLowerCase();
const defaultOverride = family.includes('opus') ? opusDefaultEffort : undefined;
properties.reasoningEffort = buildReasoningEffortSchemaProperty(effortLevels, family, defaultOverride);
}

// Context size config
Expand All @@ -158,6 +161,7 @@ function buildConfigurationSchema(endpoint: IChatEndpoint, autoTiersEnabled: boo

const DICTATION_CLEANUP_NANO_ALIAS = 'copilot-dictation-cleanup-nano';
const DICTATION_CLEANUP_LUNA_ALIAS = 'copilot-dictation-cleanup-luna';
const DICTATION_CLEANUP_LUNA_MODEL_ID = 'gpt-5.6-luna';
const dictationCleanupAliases: ReadonlySet<string> = new Set([DICTATION_CLEANUP_NANO_ALIAS, DICTATION_CLEANUP_LUNA_ALIAS]);
const utilityAliasFamilies: readonly ChatEndpointFamily[] = ['copilot-utility-small', 'copilot-utility', DICTATION_CLEANUP_NANO_ALIAS, DICTATION_CLEANUP_LUNA_ALIAS];

Expand All @@ -175,7 +179,7 @@ const utilityAliasFamilies: readonly ChatEndpointFamily[] = ['copilot-utility-sm
* normal copilot model entry.
*/
export function buildUtilityAliasModelInfo(
family: ChatEndpointFamily,
family: string,
endpoint: IChatEndpoint,
models: readonly vscode.LanguageModelChatInformation[],
baseCount: number,
Expand Down Expand Up @@ -245,6 +249,7 @@ export class LanguageModelAccess extends Disposable implements IExtensionContrib
@IVSCodeExtensionContext private readonly _vsCodeExtensionContext: IVSCodeExtensionContext,
@IAutomodeService private readonly _automodeService: IAutomodeService,
@IExperimentationService private readonly _expService: IExperimentationService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
) {
super();

Expand Down Expand Up @@ -298,6 +303,11 @@ export class LanguageModelAccess extends Disposable implements IExtensionContrib
// honored while routing goes through `POST /auto`.
this._onDidChange.fire();
}));
this._register(this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(ConfigKey.ClaudeOpusDefaultReasoningEffort.fullyQualifiedId)) {
this._onDidChange.fire();
}
}));
void this._refreshUtilityOverrides().catch(err => {
this._logService.warn(`[LanguageModelAccess] Failed to pre-resolve internal model aliases: ${err}`);
});
Expand Down Expand Up @@ -331,6 +341,7 @@ export class LanguageModelAccess extends Disposable implements IExtensionContrib

const seenFamilies = new Set<string>();
const autoTiersEnabled = this._automodeService.areAutoModeTiersSupported();
const opusDefaultEffort = this._configurationService.getExperimentBasedConfig(ConfigKey.ClaudeOpusDefaultReasoningEffort, this._expService) || undefined;

for (const endpoint of chatEndpoints) {
if (seenFamilies.has(endpoint.family) && !endpoint.showInModelPicker) {
Expand Down Expand Up @@ -409,7 +420,7 @@ export class LanguageModelAccess extends Disposable implements IExtensionContrib
imageInput: endpoint instanceof AutoChatEndpoint ? true : endpoint.supportsVision,
toolCalling: endpoint.supportsToolCalls,
},
...buildConfigurationSchema(endpoint, autoTiersEnabled),
...buildConfigurationSchema(endpoint, autoTiersEnabled, opusDefaultEffort),
};

models.push(model);
Expand Down Expand Up @@ -453,6 +464,14 @@ export class LanguageModelAccess extends Disposable implements IExtensionContrib
}
}

const luna = this._resolvedUtilityEndpoints.get(DICTATION_CLEANUP_LUNA_ALIAS);
if (luna && !models.some(model => model.id === DICTATION_CLEANUP_LUNA_MODEL_ID)) {
this._utilityAliasEndpoints.set(DICTATION_CLEANUP_LUNA_MODEL_ID, luna.endpoint);
const modelInfo = buildUtilityAliasModelInfo(DICTATION_CLEANUP_LUNA_MODEL_ID, luna.endpoint, models, luna.baseCount, requiresAuthorization);
this._logService.trace(`[LanguageModelAccess] Publishing core-only model '${DICTATION_CLEANUP_LUNA_MODEL_ID}' -> ${luna.endpoint.model}.`);
models.push(modelInfo.info);
}

// Resolution may hang (override lookups, base-count tokenization), so keep it off
// the model-info request path. Newly resolved endpoints are published on the next
// request once `_refreshUtilityOverrides` fires `_onDidChange`.
Expand Down Expand Up @@ -546,7 +565,8 @@ export class LanguageModelAccess extends Disposable implements IExtensionContrib
progress: vscode.Progress<vscode.LanguageModelResponsePart2>,
token: vscode.CancellationToken
): Promise<void> {
if (dictationCleanupAliases.has(model.id) && options.requestInitiator !== 'core') {
const isCoreOnlyModel = dictationCleanupAliases.has(model.id) || (model.id === DICTATION_CLEANUP_LUNA_MODEL_ID && this._utilityAliasEndpoints.has(model.id));
if (isCoreOnlyModel && options.requestInitiator !== 'core') {
throw new Error(`Model ${model.id} is only available to VS Code core.`);
}
let endpoint = await this._getEndpointForModel(model, buildAutoRoutingContext(messages, options));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ suite('LanguageModelAccess model info', () => {
}
});

test('publishes core-only aliases for dictation cleanup without publishing hidden models directly', async () => {
test('publishes core-only dictation cleanup models without exposing them in the picker', async () => {
const makeHiddenEndpoint = (model: string): IChatEndpoint => ({
model,
name: model,
Expand Down Expand Up @@ -510,27 +510,30 @@ suite('LanguageModelAccess model info', () => {
assert.ok(modelInfo, 'provideLanguageModelChatInfo did not resolve');
const nanoAlias = modelInfo.find(m => m.id === 'copilot-dictation-cleanup-nano');
const lunaAlias = modelInfo.find(m => m.id === 'copilot-dictation-cleanup-luna');
const lunaModel = modelInfo.find(m => m.id === 'gpt-5.6-luna');
assert.deepStrictEqual({
nanoAliasPublished: Boolean(nanoAlias),
nanoAliasUserSelectable: nanoAlias?.isUserSelectable,
lunaAliasPublished: Boolean(lunaAlias),
lunaAliasUserSelectable: lunaAlias?.isUserSelectable,
nanoPublishedDirectly: modelInfo.some(m => m.id === 'gpt-5.4-nano'),
lunaPublishedDirectly: modelInfo.some(m => m.id === 'gpt-5.6-luna'),
lunaPublishedDirectly: Boolean(lunaModel),
lunaDirectlyUserSelectable: lunaModel?.isUserSelectable,
otherPublished: modelInfo.some(m => m.id === 'some-hidden-model'),
}, {
nanoAliasPublished: true,
nanoAliasUserSelectable: false,
lunaAliasPublished: true,
lunaAliasUserSelectable: false,
nanoPublishedDirectly: false,
lunaPublishedDirectly: false,
lunaPublishedDirectly: true,
lunaDirectlyUserSelectable: false,
otherPublished: false,
});
for (const alias of [nanoAlias!, lunaAlias!]) {
for (const model of [nanoAlias!, lunaAlias!, lunaModel!]) {
await assert.rejects(
testAccess._provideLanguageModelChatResponse(
alias,
model,
[],
{ requestInitiator: 'publisher.extension' } as vscode.ProvideLanguageModelChatResponseOptions,
{ report: () => { } },
Expand Down Expand Up @@ -688,6 +691,12 @@ suite('reasoning effort schema', () => {
assert.deepStrictEqual(prop.enum, ['low', 'high']);
assert.strictEqual(prop.group, 'navigation');
});

test('buildReasoningEffortSchemaProperty honors a default override only when advertised', () => {
assert.strictEqual(buildReasoningEffortSchemaProperty(['low', 'medium', 'high'], 'claude-opus-4.5', 'medium').default, 'medium');
assert.strictEqual(buildReasoningEffortSchemaProperty(['low', 'high'], 'claude-opus-4.5', 'medium').default, 'high');
assert.strictEqual(buildReasoningEffortSchemaProperty(['low', 'medium', 'high'], 'claude-opus-4.5', undefined).default, 'high');
});
});

suite('auto mode tier schema', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,8 @@ export namespace ConfigKey {
export const EnableGemini3GetChangedFilesTool = defineSetting<boolean>('chat.gemini3GetChangedFilesTool.enabled', ConfigType.ExperimentBased, false);
/** When enabled, sends `reasoning_effort: 'low'` to Gemini 3 models. */
export const EnableGemini3LowReasoningEffort = defineSetting<boolean>('chat.gemini3LowReasoningEffort.enabled', ConfigType.ExperimentBased, false);
/** Default thinking effort for Claude Opus models in the model picker. Empty keeps the built-in default ('high'). */
export const ClaudeOpusDefaultReasoningEffort = defineSetting<string>('chat.claudeOpusDefaultReasoningEffort', ConfigType.ExperimentBased, '');
/** Enable read_file tool for GPT-5.5 models */
export const EnableGpt55ReadFileTool = defineSetting<boolean>('chat.gpt55ReadFileTool.enabled', ConfigType.ExperimentBased, true);
export const EnableChatImageUpload = defineSetting<boolean>('chat.imageUpload.enabled', ConfigType.Simple, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ export class BaseTelemetryService implements ITelemetryService {
...Object.fromEntries(props),
...this._sharedProperties
};
// Mark the queried-feature name trusted so the telemetry cleaner does not redact the
// `/vscode/`-scoped key as a `user-file-path`.
const queriedFeature = properties['ABExp.queriedFeature'];
if (typeof queriedFeature === 'string') {
properties['ABExp.queriedFeature'] = new TelemetryTrustedValue(queriedFeature);
}
this._microsoftTelemetrySender.sendInternalTelemetryEvent(eventName, properties);
this._microsoftTelemetrySender.sendTelemetryEvent(eventName, properties);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,16 @@ body {
width: 100%;
overflow: scroll;
}

.md-comment-widget {
background:
linear-gradient(var(--vscode-menu-background), var(--vscode-menu-background)),
var(--vscode-editor-background);
}

.md-comment-widget:hover,
.md-comment-widget--hover {
background:
linear-gradient(var(--vscode-list-hoverBackground), var(--vscode-list-hoverBackground)),
var(--vscode-editor-background);
}
Loading
Loading