Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
785924f
sessions: remove unused contributions and add null debug service
jrieken Apr 21, 2026
c7023e5
make sure find in files doesn't show in explorer of agents app
jrieken Apr 21, 2026
2e19f63
Better fix for tsgo problem matcher
mjbvz Apr 21, 2026
7d8504b
Merge branch 'main' into dev/mjbvz/hidden-platypus
mjbvz Apr 21, 2026
b41c998
Display last used model info for Copilot CLI on chat panel (#311769)
anthonykim1 Apr 22, 2026
625023a
Delete tsbuildinfo for extensions on rebuild
mjbvz Apr 22, 2026
42fa383
move contributed dropdowns to secondary inputbar (#310958)
justschen Apr 22, 2026
a8458cf
feat(copilotcli): Add auto model configuration (#311780)
DonJayamanne Apr 22, 2026
bbae0cb
fix confirmation editor-background (#311276)
justschen Apr 22, 2026
f7e87a2
Chronicle: Surface agent_name, fix SQL truncation, and improve query…
vijayupadya Apr 22, 2026
2a50460
Merge branch 'main' into dev/mjbvz/hidden-platypus
mjbvz Apr 22, 2026
1bca40e
Fix enableThinking mismatch causing inline summarization cache misses…
bhavyaus Apr 22, 2026
b337ec5
Merge pull request #311766 from mjbvz/dev/mjbvz/hidden-platypus
mjbvz Apr 22, 2026
b65233e
Agents - register the "Changes" view in the "Views" menu (#311763)
lszomoru Apr 22, 2026
07be70e
Merge branch 'main' into dev/mjbvz/raspy-mosquito
mjbvz Apr 22, 2026
f7526a2
Merge pull request #311842 from mjbvz/dev/mjbvz/raspy-mosquito
mjbvz Apr 22, 2026
7247ba0
chore - change sessions.common so that the extensions view is dragged…
jrieken Apr 22, 2026
ad19771
Merge branch 'main' into joh/sessions-cleanup
jrieken Apr 22, 2026
32ccba7
update distro (#311854)
alexdima Apr 22, 2026
9d19001
Merge pull request #311704 from microsoft/joh/sessions-cleanup
jrieken Apr 22, 2026
200fb2e
fix session title alignment (#311865)
sandy081 Apr 22, 2026
9240766
updating distro (#311871)
aiday-mar Apr 22, 2026
e82fa25
Agents - add open command actions for files/changes (#311868)
lszomoru Apr 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,17 @@
"owner": "typescript",
"applyTo": "closedDocuments",
"fileLocation": [
"absolute"
"relative",
"${workspaceFolder}"
],
"pattern": "$tsc",
"source": "ts",
"pattern": {
"regexp": "\\] (.+)\\(([\\d,]+)\\): error TS(\\d+): (.*)$",
"file": 1,
"location": 2,
"code": 3,
"message": 4
},
"background": {
"beginsPattern": "Starting compilation\\.\\.\\.",
"endsPattern": "Finished compilation with"
Expand All @@ -71,7 +79,12 @@
"relative",
"${workspaceFolder}"
],
"pattern": "$tsc",
"pattern": {
"regexp": "\\] ([^(]+)\\((\\d+,\\d+)\\): (.*)$",
"file": 1,
"location": 2,
"message": 3
},
"background": {
"beginsPattern": "Starting compilation",
"endsPattern": "Finished compilation"
Expand Down
8 changes: 7 additions & 1 deletion build/gulpfile.extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ EventEmitter.defaultMaxListeners = 100;

import es from 'event-stream';
import fancyLog from 'fancy-log';
import * as fs from 'fs';
import glob from 'glob';
import gulp from 'gulp';
import filter from 'gulp-filter';
Expand Down Expand Up @@ -172,7 +173,12 @@ const tasks = compilations.map(function (tsconfigFile) {
return pipeline;
}

const cleanTask = task.define(`clean-extension-${name}`, util.rimraf(out));
const tsBuildInfoFile = path.join(path.dirname(absolutePath), path.basename(absolutePath, '.json') + '.tsbuildinfo');

const cleanTask = task.define(`clean-extension-${name}`, async () => {
await util.rimraf(out)();
fs.rmSync(tsBuildInfoFile, { force: true });
});

const transpileTask = task.define(`transpile-extension:${name}`, task.series(cleanTask, () => {
const pipeline = createPipeline(false, true, true);
Expand Down
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ export default tseslint.config(
'src/vs/workbench/contrib/search/browser/replace.ts',
'src/vs/workbench/contrib/search/browser/replaceService.ts',
'src/vs/workbench/contrib/search/browser/searchActionsCopy.ts',
'src/vs/workbench/contrib/search/browser/searchActionsBase.ts',
'src/vs/workbench/contrib/search/browser/searchActionsFind.ts',
'src/vs/workbench/contrib/search/browser/searchActionsNav.ts',
'src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts',
Expand Down
8 changes: 8 additions & 0 deletions extensions/copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4579,6 +4579,14 @@
"advanced"
]
},
"github.copilot.chat.cli.autoModel.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "%github.copilot.config.cli.autoModel.enabled%",
"tags": [
"advanced"
]
},
"github.copilot.chat.cli.planCommand.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 @@ -408,6 +408,7 @@
"github.copilot.config.cli.forkSessions.enabled": "Enable forking sessions in Copilot CLI.",
"github.copilot.config.cli.showExternalSessions": "Show sessions created by other applications.",
"github.copilot.config.cli.planExitMode.enabled": "Enable Plan Mode exit handling in Copilot CLI.",
"github.copilot.config.cli.autoModel.enabled": "Enable the Auto model option in Copilot CLI, which automatically selects the best model for each request. Requires VS Code reload.",
"github.copilot.config.cli.planCommand.enabled": "Enable the /plan command in Copilot CLI to create implementation plans before coding.",
"github.copilot.config.cli.aiGenerateBranchNames.enabled": "Enable AI-generated branch names in Copilot CLI.",
"github.copilot.config.cli.isolationOption.enabled": "Enable the isolation mode option for Copilot CLI. When enabled, users can choose between Worktree and Workspace modes.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ export interface RequestIdDetails {
* Build chat history from SDK events for VS Code chat session
* Converts SDKEvents into ChatRequestTurn2 and ChatResponseTurn2 objects
*/
export function buildChatHistoryFromEvents(sessionId: string, modelId: string | undefined, events: readonly SessionEvent[], getVSCodeRequestId: (sdkRequestId: string) => RequestIdDetails | undefined, delegationSummaryService: IChatDelegationSummaryService, logger: ILogger, workingDirectory?: URI, defaultModeInstructionsForLastRequest?: StoredModeInstructions): (ChatRequestTurn2 | ChatResponseTurn2)[] {
export function buildChatHistoryFromEvents(sessionId: string, modelId: string | undefined, events: readonly SessionEvent[], getVSCodeRequestId: (sdkRequestId: string) => RequestIdDetails | undefined, delegationSummaryService: IChatDelegationSummaryService, logger: ILogger, workingDirectory?: URI, defaultModeInstructionsForLastRequest?: StoredModeInstructions, lastResponseDetails?: string): (ChatRequestTurn2 | ChatResponseTurn2)[] {
const turns: (ChatRequestTurn2 | ChatResponseTurn2)[] = [];
let currentResponseParts: ExtendedChatResponsePart[] = [];
const pendingToolInvocations = new Map<string, [ChatToolInvocationPart | ChatResponseMarkdownPart | ChatResponseThinkingProgressPart, toolData: ToolCall, parentToolCallId: string | undefined]>();
Expand Down Expand Up @@ -744,7 +744,7 @@ export function buildChatHistoryFromEvents(sessionId: string, modelId: string |
flushPendingAssistantMessage();

if (currentResponseParts.length > 0) {
turns.push(new ChatResponseTurn2(currentResponseParts, {}, ''));
turns.push(new ChatResponseTurn2(currentResponseParts, lastResponseDetails ? { details: lastResponseDetails } : {}, ''));
}

return turns;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ describe('CopilotCLITools', () => {
expect((markdownParts[0] as any).value?.value || (markdownParts[0] as any).value).toContain('All tests are passing.');
});

it('preserves response details on the final rebuilt response turn', () => {
const events: any[] = [
{ type: 'user.message', data: { content: 'Hello', attachments: [] } },
{ type: 'assistant.message', data: { content: 'Hi there' } }
];
const turns = buildChatHistoryFromEvents('', 'base', events, getVSCodeRequestId, delegationSummary, logger, undefined, undefined, 'Base • 2x');
expect(turns).toHaveLength(2);
const responseTurn = turns[1] as ChatResponseTurn2;
expect(responseTurn.result).toEqual({ details: 'Base • 2x' });
});

it('converts file attachments to references on user messages', () => {
const events: any[] = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { IInstantiationService } from '../../../../util/vs/platform/instantiatio
import { getCopilotLogger } from './logger';
import { ensureRipgrepShim } from './ripgrepShim';
import { CancellationToken } from '../../../../util/vs/base/common/cancellation';
import { getModelCapabilitiesDescription } from '../../../conversation/common/languageModelAccess';

export const COPILOT_CLI_REASONING_EFFORT_PROPERTY = 'reasoningEffort';
const COPILOT_CLI_MODEL_MEMENTO_KEY = 'github.copilot.cli.sessionModel';
Expand Down Expand Up @@ -59,14 +60,21 @@ export interface ICopilotCLIModels {
registerLanguageModelChatProvider(lm: typeof vscode['lm']): void;
}

export function formatModelDetails(model: CopilotCLIModelInfo): string {
return `${model.name}${model.multiplier ? ` • ${model.multiplier}x` : ''}`;
}

export const ICopilotCLISDK = createServiceIdentifier<ICopilotCLISDK>('ICopilotCLISDK');

export const ICopilotCLIModels = createServiceIdentifier<ICopilotCLIModels>('ICopilotCLIModels');

export class CopilotCLIModels extends Disposable implements ICopilotCLIModels {
declare _serviceBrand: undefined;
private _availableModels?: Promise<CopilotCLIModelInfo[]>;
/** Synchronously available model infos (includes `auto`). Set once the eager fetch completes. */
private _resolvedModelInfos?: vscode.LanguageModelChatInformation[];
private readonly _onDidChange = this._register(new Emitter<void>());

constructor(
@ICopilotCLISDK private readonly copilotCLISDK: ICopilotCLISDK,
@IVSCodeExtensionContext private readonly extensionContext: IVSCodeExtensionContext,
Expand All @@ -75,20 +83,33 @@ export class CopilotCLIModels extends Disposable implements ICopilotCLIModels {
@IConfigurationService private readonly configurationService: IConfigurationService,
) {
super();
this._availableModels = this._getAvailableModels();
// Eagerly fetch available models so that they're ready when needed.
this._availableModels
.then(() => this._onDidChange.fire())
.catch((error) => {
this.logService.error('[CopilotCLIModels] Failed to fetch available models', error);
});
this._fetchAndCacheModels();
this._register(this._authenticationService.onDidAuthenticationChange(() => {
// Auth changed which means models could've changed. Fire the event
// Auth changed which means models could've changed. Clear caches and re-fetch.
this._availableModels = undefined;
this._resolvedModelInfos = undefined;
this._onDidChange.fire();
this._fetchAndCacheModels();
}));
}

private _fetchAndCacheModels(): void {
const availableModels = this._availableModels = this._getAvailableModels();
availableModels.then(models => {
// Bail out if a newer fetch has superseded this one (e.g. auth changed mid-flight).
if (this._availableModels !== availableModels) {
return;
}
this._resolvedModelInfos = this._buildModelInfos(models);
this._onDidChange.fire();
}).catch((error) => {
this.logService.error('[CopilotCLIModels] Failed to fetch available models', error);
});
}
async resolveModel(modelId: string): Promise<string | undefined> {
if (modelId.toLowerCase() === 'auto' && this.configurationService.getConfig(ConfigKey.Advanced.CLIAutoModelEnabled)) {
return modelId;
}
const models = await this.getModels();
modelId = modelId.trim().toLowerCase();
return models.find(m => m.id.toLowerCase() === modelId || m.name.toLowerCase() === modelId)?.id;
Expand Down Expand Up @@ -147,7 +168,11 @@ export class CopilotCLIModels extends Disposable implements ICopilotCLIModels {
const provider: vscode.LanguageModelChatProvider = {
onDidChangeLanguageModelChatInformation: this._onDidChange.event,
provideLanguageModelChatInformation: async (_options, _token) => {
return this._provideLanguageModelChatInfo();
const autoModelEnabled = this.configurationService.getConfig(ConfigKey.Advanced.CLIAutoModelEnabled);
if (!this._authenticationService.anyGitHubSession || !this._resolvedModelInfos) {
return autoModelEnabled ? [buildAutoModel()] : [];
}
return this._resolvedModelInfos;
},
provideLanguageModelChatResponse: async (_model, _messages, _options, _progress, _token) => {
// Implemented via chat participants.
Expand All @@ -158,14 +183,15 @@ export class CopilotCLIModels extends Disposable implements ICopilotCLIModels {
}
};
this._register(lm.registerLanguageModelChatProvider('copilotcli', provider));
this._onDidChange.fire();
}

private async _provideLanguageModelChatInfo(): Promise<vscode.LanguageModelChatInformation[]> {
const models = await this.getModels();
private _buildModelInfos(models: CopilotCLIModelInfo[]): vscode.LanguageModelChatInformation[] {
const isReasoningEffortEnabled = this.configurationService.getConfig(ConfigKey.Advanced.CLIThinkingEffortEnabled);
const modelsInfo = models.map((model, index) => {
const isAutoModelEnabled = this.configurationService.getConfig(ConfigKey.Advanced.CLIAutoModelEnabled);
const modelsInfo: vscode.LanguageModelChatInformation[] = models.map((model, index) => {
const multiplier = model.multiplier === undefined ? undefined : `${model.multiplier}x`;
return {
const modelInfo: vscode.LanguageModelChatInformation = {
id: model.id,
name: model.name,
family: model.id,
Expand All @@ -181,13 +207,40 @@ export class CopilotCLIModels extends Disposable implements ICopilotCLIModels {
toolCalling: true
},
targetChatSessionType: 'copilotcli',
isDefault: index === 0 // SDK guarantees the first item is the default model
isDefault: !isAutoModelEnabled && index === 0 ? true : undefined,
};
const tooltip = getModelCapabilitiesDescription(modelInfo) ?? '';
return {
...modelInfo,
tooltip
};
});
if (isAutoModelEnabled) {
modelsInfo.unshift(buildAutoModel(models[0]));
}
return modelsInfo;
}
}

function buildAutoModel(defaultModel?: CopilotCLIModelInfo): vscode.LanguageModelChatInformation {
return {
id: 'auto',
name: 'Auto',
tooltip: l10n.t('Auto selects the best model for your request based on capacity and performance.'),
family: defaultModel?.id ?? '',
version: '',
maxInputTokens: defaultModel?.maxInputTokens ?? defaultModel?.maxContextWindowTokens ?? 0,
maxOutputTokens: defaultModel?.maxOutputTokens ?? 0,
isUserSelectable: true,
capabilities: {
imageInput: defaultModel?.supportsVision,
toolCalling: true,
},
targetChatSessionType: 'copilotcli',
isDefault: true,
};
}

function buildConfigurationSchema(modelInfo: CopilotCLIModelInfo): vscode.LanguageModelConfigurationSchema | undefined {
const effortLevels = modelInfo.supportedReasoningEfforts ?? [];
if (effortLevels.length === 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { ICustomSessionTitleService } from '../common/customSessionTitleService'
import { IChatDelegationSummaryService } from '../common/delegationSummaryService';
import { SessionIdForCLI } from '../common/utils';
import { getCopilotCLISessionDir } from './cliHelpers';
import { getAgentFileNameFromFilePath, ICopilotCLIAgents, ICopilotCLISDK, isEnabledForCopilotCLI } from './copilotCli';
import { formatModelDetails, getAgentFileNameFromFilePath, ICopilotCLIAgents, ICopilotCLIModels, ICopilotCLISDK, isEnabledForCopilotCLI } from './copilotCli';
import { CopilotCliBridgeSpanProcessor } from './copilotCliBridgeSpanProcessor';
import { CopilotCLISession, ICopilotCLISession } from './copilotcliSession';
import { ICopilotCLISkills } from './copilotCLISkills';
Expand Down Expand Up @@ -167,6 +167,7 @@ export class CopilotCLISessionService extends Disposable implements ICopilotCLIS
@IPromptVariablesService private readonly _promptVariablesService: IPromptVariablesService,
@IChatDebugFileLoggerService private readonly _debugFileLogger: IChatDebugFileLoggerService,
@IPromptsService private readonly _promptsService: IPromptsService,
@ICopilotCLIModels private readonly _copilotCLIModels: ICopilotCLIModels,
) {
super();
this.showExternalSessions = this.configurationService.getConfig(ConfigKey.Advanced.CLIShowExternalSessions);
Expand Down Expand Up @@ -844,7 +845,8 @@ export class CopilotCLISessionService extends Disposable implements ICopilotCLIS
return mapping;
};

const history = buildChatHistoryFromEvents(sessionId, modelId, events, getVSCodeRequestId, this._delegationSummaryService, this.logService, getWorkingDirectory(workspace), defaultModeInstructions);
const lastResponseDetails = await this.getModelDetailsString(modelId);
const history = buildChatHistoryFromEvents(sessionId, modelId, events, getVSCodeRequestId, this._delegationSummaryService, this.logService, getWorkingDirectory(workspace), defaultModeInstructions, lastResponseDetails);

if (legacyMappings.length > 0) {
void this._chatSessionMetadataStore.updateRequestDetails(sessionId, legacyMappings).catch(error => {
Expand Down Expand Up @@ -893,6 +895,18 @@ export class CopilotCLISessionService extends Disposable implements ICopilotCLIS
};
}

private async getModelDetailsString(modelId: string | undefined): Promise<string | undefined> {
if (!modelId) {
return undefined;
}
const models = await this._copilotCLIModels.getModels().catch(ex => {
this.logService.error(ex, 'Failed to get models');
return [];
});
const modelInfo = models.find(m => m.id === modelId);
return modelInfo ? formatModelDetails(modelInfo) : undefined;
}


/**
* Fork an existing session using the SDK's `forkSession` API.
Expand Down
Loading
Loading