Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
8af869c
Fix memory leaks when clearing test results
SimonSiefke Aug 28, 2026
eec850e
Fix disposal of immediately evicted test results
SimonSiefke Aug 28, 2026
17eb88d
Merge branch 'main' into fix/memory-leak-testResultsViewContent
connor4312 Aug 31, 2026
9f87857
Use DisposableMap for test result resources
SimonSiefke Sep 1, 2026
0f7c904
Merge branch 'main' into fix/memory-leak-testResultsViewContent
SimonSiefke Sep 1, 2026
867755f
agentHost: preserve remote sessions across reload (#333251)
roblourens Sep 1, 2026
ac25654
sessions: Refresh chat status after background completion (#333804)
sandy081 Sep 1, 2026
35e20f9
chat: remove duplicate spawned session links (#333645)
meganrogge Sep 1, 2026
1546311
Fix agents window attachment chips to match chat attachment behavior …
justschen Sep 1, 2026
39767fd
chat: remove deprecated agent debug log setting (#333650)
dmitrivMS Sep 1, 2026
1f02758
fix: cancel pending single-pane docked-tab reconciles on dispose (fix…
vscodebot-pr Sep 1, 2026
0352a61
dictation: Fix provider fallback and connection failures (#333811)
meganrogge Sep 1, 2026
ca4627a
npm: remove deprecated script explorer setting (#333653)
dmitrivMS Sep 1, 2026
2fede32
html: remove deprecated mirror cursor setting (#333651)
dmitrivMS Sep 1, 2026
daae5a5
chore: bump @github/copilot-sdk to 1.0.13-preview.4 and @github/copil…
vs-code-engineering[bot] Sep 1, 2026
3a5a3e7
chat: gate Voice Mode by account and policy (#333810)
meganrogge Sep 1, 2026
2bb7d70
remove flaky pet test for now (#333826)
justschen Sep 1, 2026
0667825
sessions: Allow experiment to control cloud sandbox default (#333825)
osortega Sep 1, 2026
670698e
refactor handling of enabled/disabled state of built-in skills in the…
aeschli Sep 1, 2026
aea8984
Browser: agent-scoped storage (#333694)
kycutler Sep 1, 2026
fde1a8d
sessions: stabilize Codicon background resizing (#333831)
TylerLeonhardt Sep 1, 2026
b176e3d
chat: use Copilot icon for Agent Host sessions (#333813)
roblourens Sep 1, 2026
91b0cac
Bound fetcher telemetry error cardinality (#333796)
chrmarti Sep 1, 2026
46f6e09
Fix sign-in dead-end in installed web apps (PWA) (#333084)
osortega Sep 1, 2026
bf1d0a2
chat: Improve uninstalled MCP server details (#333664)
houghj16 Sep 1, 2026
9fefe24
Address Auto routing profile review feedback (#333803)
lramos15 Sep 1, 2026
c2d05a9
agentHost: Hide merge actions after successful merge (#333829)
benibenj Sep 1, 2026
5a60c4a
POC: Chat session state frames (#332799)
bryanchen-d Sep 1, 2026
c89cda6
Improved visual clarity in multi-file diffs in Agents window (#333775)
hediet Sep 1, 2026
6abb039
sessions: show workspace before harness picker (#333844)
meganrogge Sep 1, 2026
33913cb
sessions: Sync GitHub context from new session input (#333847)
meganrogge Sep 1, 2026
9e62c0b
dictation: Cap recording and finalization duration (#333846)
meganrogge Sep 1, 2026
d7a951c
Agents - add commit operation into the Changes view (#333842)
lszomoru Sep 1, 2026
fef8ec3
Agents - move the view as list/tree actions one level lower (#333857)
lszomoru Sep 1, 2026
b8b400c
accounts: avoid more redundant managed settings requests (#333823)
joshspicer Sep 1, 2026
ebbbc9e
Handle warning and info messages froom SDK (#333851)
lramos15 Sep 1, 2026
418372b
fix: missing note on N/A (#333862)
rzhao271 Sep 1, 2026
0582939
Fix dark theme scrollbar colors customizations pages (#333634)
houghj16 Sep 1, 2026
ba0c096
sessions: refine Agent Merge PR actions (#333835)
benibenj Sep 1, 2026
4fdf440
Merge pull request #333244 from SimonSiefke/fix/memory-leak-testResul…
connor4312 Sep 1, 2026
54157b1
Fix Automation skill completion interactions (#333834)
pwang347 Sep 1, 2026
3a7538f
sessions: Move Clear Background Into Set Background (#333871)
TylerLeonhardt Sep 1, 2026
22652e6
agentHost: classify Copilot SKU telemetry context (#333853)
amunger Sep 1, 2026
0b0fbc4
Make browser ownership mutable (#332038)
kycutler Sep 1, 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
1 change: 1 addition & 0 deletions .eslint-plugin-local/code-no-telemetry-common-property.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const commonTelemetryProperties = new Set([
'common.copilottrackingid',
'common.copilotsdkversion',
'common.copilotruntimeversion',
'copilotsku',
'common.isagentswindow',
]);

Expand Down
2 changes: 1 addition & 1 deletion build/azure-pipelines/product-build-ado-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ extends:

if [ "${#trailer_values[@]}" = "0" ]; then
echo "##vso[task.logissue type=error]Commit $head_sha is missing the required 'Msrc-Case-Id' trailer."
printf "Add a trailer to the commit message, for example:\n\n Msrc-Case-Id: 12345\n\n"
printf "Add a trailer to the commit message, for example:\n\n Msrc-Case-Id: 12345\n\nIf there is no associated case ID, use N/A:\n\n Msrc-Case-Id: N/A\n\n"
exit 1
fi

Expand Down
15 changes: 15 additions & 0 deletions build/lib/policies/policyData.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@
}
],
"policies": [
{
"key": "agents.voice.enabled",
"name": "AgentsVoice",
"category": "InteractiveSession",
"minimumVersion": "1.137",
"localization": {
"description": {
"key": "agents.voice.enabled",
"value": "Enable the Voice Mode panel in the chat view for voice-driven coding conversations."
}
},
"type": "boolean",
"default": false,
"included": true
},
{
"key": "chat.agent.allowedNetworkDomains",
"name": "ChatAgentAllowedNetworkDomains",
Expand Down
10 changes: 0 additions & 10 deletions extensions/copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5242,16 +5242,6 @@
"onExp"
]
},
"github.copilot.chat.agentDebugLog.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "%github.copilot.config.chat.agentDebugLog.enabled%",
"deprecationMessage": "%github.copilot.config.chat.agentDebugLog.enabled.deprecated%",
"tags": [
"advanced",
"experimental"
]
},
"github.copilot.chat.agentDebugLog.fileLogging.enabled": {
"type": "boolean",
"default": false,
Expand Down
2 changes: 0 additions & 2 deletions extensions/copilot/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,6 @@
"github.copilot.config.inlineChat.reasoningEffort": "Controls the reasoning effort level for inline chat requests. Lower values result in faster responses with fewer reasoning tokens. Supported values depend on the model.",
"github.copilot.config.inlineChat.enableThinking": "Controls whether thinking/reasoning is enabled for inline chat requests. When disabled, reasoning summaries are suppressed for faster responses.",
"github.copilot.config.debug.requestLogger.maxEntries": "Maximum number of entries to keep in the request logger for debugging purposes.",
"github.copilot.config.chat.agentDebugLog.enabled": "Deprecated: use `github.copilot.chat.agentDebugLog.fileLogging.enabled` instead.",
"github.copilot.config.chat.agentDebugLog.enabled.deprecated": "This setting has been merged into `github.copilot.chat.agentDebugLog.fileLogging.enabled`. Please use this setting instead.",
"github.copilot.config.chat.agentDebugLog.fileLogging.enabled": "Enable agent debug logging: write chat debug events (tool calls, LLM requests, token usage, errors) to JSONL files for the debug panel and troubleshoot skill. Requires window reload to take effect.",
"github.copilot.config.chat.agentDebugLog.fileLogging.flushIntervalMs": "How often (in milliseconds) buffered debug log entries are flushed to disk. Lower values provide more up-to-date logs at the cost of more frequent disk writes.",
"github.copilot.config.chat.agentDebugLog.fileLogging.maxRetainedSessionLogs": "Maximum number of chat debug session log directories to retain on disk. Each chat session produces one directory. Older session logs are automatically deleted when this limit is exceeded.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -846,9 +846,7 @@ export namespace ConfigKey {
/** Simulate GitHub authentication failures for testing. Can't be TeamInternal because we lose these flags as part of testing. */
export const DebugGitHubAuthFailWith = defineSetting<'NotAuthorized' | 'RequestFailed' | 'ParseFailed' | 'HTTP401' | 'RateLimited' | 'GitHubLoginFailed' | null>('chat.debug.githubAuthFailWith', ConfigType.Simple, null);

// Agent debug logging settings — fileLogging.enabled is the canonical toggle
/** @deprecated Use ChatDebugFileLogging instead. Kept during experiment transition. */
export const AgentDebugLogEnabled = defineAndMigrateExpSetting<boolean>('agentDebugLog.enabled', 'chat.agentDebugLog.enabled', false);
// Agent debug logging settings
export const ChatDebugFileLogging = defineAndMigrateExpSetting<boolean>('chat.chatDebug.fileLogging.enabled', 'chat.agentDebugLog.fileLogging.enabled', false);
export const ChatDebugFileLoggingFlushInterval = defineAndMigrateSetting<number>('chat.chatDebug.fileLogging.flushIntervalMs', 'chat.agentDebugLog.fileLogging.flushIntervalMs', 4000);
export const ChatDebugFileLoggingMaxRetainedSessionLogs = defineSetting<number>('chat.agentDebugLog.fileLogging.maxRetainedSessionLogs', ConfigType.ExperimentBased, 50);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ const retiredAutoModeTiers: Readonly<Record<string, AutoModeTier>> = {

/**
* Maps a retired tier name to its current one, leaving anything else untouched.
* Only raw inputs that bypass the picker schema need this, notably the override setting.
* Needed for raw inputs that predate the rename, such as the override setting or a persisted picker
* value restored before its model's schema has loaded.
*/
export function normalizeAutoModeTier(value: string): string {
return retiredAutoModeTiers[value] ?? value;
export function normalizeAutoModeTier(value: unknown): unknown {
return typeof value === 'string' ? retiredAutoModeTiers[value] ?? value : value;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,19 +404,17 @@ export class AutomodeService extends Disposable implements IAutomodeService {
private _resolveTier(chatRequest: IAutoModeRoutingRequest | undefined): AutoModeTier | undefined {
const override = this._configurationService.getConfig(ConfigKey.Advanced.AutoModeTierOverride);
if (override) {
// Normalized because the override is a raw string setting, so unlike a picker value it never
// passes through the schema filter that drops retired names.
const normalized = normalizeAutoModeTier(override);
// The override is internal, so unlike the picker it may select `fast`.
if ((autoModeTiers as readonly string[]).includes(normalized)) {
if (autoModeTiers.some(tier => tier === normalized)) {
return normalized as AutoModeTier;
}
this._logService.warn(`[AutomodeService] Ignoring auto tier override '${override}' — not one of [${autoModeTiers.join(', ')}].`);
}
if (!this.areAutoModeTiersSupported()) {
return undefined;
}
const configured = chatRequest?.modelConfiguration?.[AUTO_MODE_TIER_PROPERTY];
const configured = normalizeAutoModeTier(chatRequest?.modelConfiguration?.[AUTO_MODE_TIER_PROPERTY]);
if (isSelectableAutoModeTier(configured) && configured !== defaultAutoModeTier) {
return configured;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,32 @@ describe('AutomodeService', () => {

expect(autoRequestBodies()).toEqual([{ prompt: 'panel turn', tier: 'efficiency' }]);
});

// A picker value stored before the rename can be restored unfiltered while its model's
// schema is still loading, so it must upgrade rather than silently fall back.
it('maps a retired tier name in a persisted picker value to its current one', async () => {
enableTiers();
const gpt4oEndpoint = createEndpoint('gpt-4o', 'OpenAI');
mockAuto(autoResponse('gpt-4o'));

automodeService = createService();
await automodeService.resolveAutoModeEndpoint({
location: ChatLocation.Panel,
prompt: 'panel turn',
sessionId: 'session-persisted-retired',
modelConfiguration: { tier: 'max' },
} as unknown as ChatRequest, [mockChatEndpoint, gpt4oEndpoint]);
// `balanced` upgrades to the current default, which reads as "never picked", so the inline
// pin applies instead of being treated as an explicit selection.
await automodeService.resolveAutoModeEndpoint({
location: ChatLocation.Editor,
prompt: 'inline turn',
sessionId: 'session-persisted-retired-default',
modelConfiguration: { tier: 'balanced' },
} as unknown as ChatRequest, [mockChatEndpoint, gpt4oEndpoint]);

expect(autoRequestBodies().map(b => b.tier)).toEqual(['intelligence', 'fast']);
});
});

describe('session cache', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const fetcherConfigKeys: Partial<Record<FetcherId, Config<boolean>>> = {
const terminalResponseStatusCodes = new Set([429, 502, 503]);
const allFetchersFailedTelemetryIntervalMs = 15 * 60 * 1000;
const maxAggregatedErrorLength = 1024;
const maxAggregatedErrorCount = 100;
const maxAggregatedErrorsSerializedLength = 8192;
const aggregatedErrorsOverflowKey = '<other>';
const allFetchersFailedErrors = new Map<string, number>();
Expand Down Expand Up @@ -85,7 +86,12 @@ function recordErrors(target: Map<string, number>, errors: readonly string[]): v
target.set(truncatedError, Math.min(count + 1, Number.MAX_SAFE_INTEGER));
continue;
}
target.set(truncatedError, 1);
if (target.size < maxAggregatedErrorCount) {
target.set(truncatedError, 1);
continue;
}
const overflowCount = target.get(aggregatedErrorsOverflowKey) ?? 0;
target.set(aggregatedErrorsOverflowKey, Math.min(overflowCount + 1, Number.MAX_SAFE_INTEGER));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,66 @@ suite('FetcherFallback Test Suite', function () {
}
});

test('caps terminal response error cardinality while preserving counts', async function () {
vi.useFakeTimers();
cleanupTime += 24 * 60 * 60 * 1000;
vi.setSystemTime(cleanupTime);
const primingTelemetryService = new SpyingTelemetryService();
const primingFetchers = createTestFetchers([
{ name: 'fetcher1', response: createFakeResponse(503, someJSON) },
{ name: 'fetcher2', response: createFakeResponse(200, someJSON) },
]);
await fetchWithFallbacks(primingFetchers.fetchers, 'https://example.com', { callSite: 'test', expectJSON: true, retryFallbacks: true }, knownBadFetchers, configurationService, logService, primingTelemetryService, experimentationService);
vi.advanceTimersByTime(16 * 60 * 1000);
const createSuccessfulFetchers = () => createTestFetchers([
{ name: 'fetcher1', response: createFakeResponse(200, someJSON) },
{ name: 'fetcher2', response: createFakeResponse(200, someJSON) },
]);
await fetchWithFallbacks(createSuccessfulFetchers().fetchers, 'https://example.com', { callSite: 'test', expectJSON: true, retryFallbacks: true }, knownBadFetchers, configurationService, logService, primingTelemetryService, experimentationService);
const spyingTelemetryService = new SpyingTelemetryService();
try {
for (let i = 0; i < 200; i++) {
const testFetchers = createTestFetchers([
{ name: 'fetcher1', response: createFakeResponse(503, someJSON, `unique status ${i}`) },
{ name: 'fetcher2', response: createFakeResponse(200, someJSON) },
]);
await fetchWithFallbacks(testFetchers.fetchers, 'https://example.com', { callSite: 'test', expectJSON: true, retryFallbacks: true }, knownBadFetchers, configurationService, logService, spyingTelemetryService, experimentationService);
}
const repeatedOverflowFetchers = createTestFetchers([
{ name: 'fetcher1', response: createFakeResponse(503, someJSON, 'unique status 199') },
{ name: 'fetcher2', response: createFakeResponse(200, someJSON) },
]);
await fetchWithFallbacks(repeatedOverflowFetchers.fetchers, 'https://example.com', { callSite: 'test', expectJSON: true, retryFallbacks: true }, knownBadFetchers, configurationService, logService, spyingTelemetryService, experimentationService);

vi.advanceTimersByTime(15 * 60 * 1000 + 1);
await fetchWithFallbacks(createSuccessfulFetchers().fetchers, 'https://example.com', { callSite: 'test', expectJSON: true, retryFallbacks: true }, knownBadFetchers, configurationService, logService, spyingTelemetryService, experimentationService);
const telemetryEvents = spyingTelemetryService.getEvents().telemetryServiceEvents;
const properties = telemetryEvents[0].properties;
if (!properties || !('errors' in properties) || typeof properties.errors !== 'string') {
assert.fail('Expected an errors telemetry property');
}
const errors: Record<string, number> = JSON.parse(properties.errors);

assert.deepStrictEqual({
eventCount: telemetryEvents.length,
eventName: telemetryEvents[0].eventName,
errorKeyCount: Object.keys(errors).length,
overflowCount: errors['<other>'],
repeatedOverflowErrorCount: errors['fetcher1: 503 unique status 199'],
allFailuresAccountedFor: Object.values(errors).reduce((total, count) => total + count, 0),
}, {
eventCount: 1,
eventName: 'fetcherTerminalResponse',
errorKeyCount: 101,
overflowCount: 101,
repeatedOverflowErrorCount: undefined,
allFailuresAccountedFor: 201,
});
} finally {
vi.useRealTimers();
}
});

test('no fetcher succeeds', async function () {
const fetcherSpec = [
{ name: 'fetcher1', response: createFakeResponse(407, someHTML) },
Expand Down Expand Up @@ -407,7 +467,7 @@ function createTestFetchers(fetcherSpecs: Array<{ name: string; response: Respon
}
return next;
},
fetchWithPagination: async <T>(baseUrl: string, options: PaginationOptions<T>): Promise<T[]> => {
fetchWithPagination: async <T>(_baseUrl: string, _options: PaginationOptions<T>): Promise<T[]> => {
throw new Error('Method not implemented.');
},
disconnectAll: async () => { },
Expand All @@ -422,10 +482,10 @@ function createTestFetchers(fetcherSpecs: Array<{ name: string; response: Respon
return { fetchers, calls };
}

function createFakeResponse(statusCode: number, content: string) {
function createFakeResponse(statusCode: number, content: string, statusText = 'status text') {
return Response.fromText(
statusCode,
'status text',
statusText,
new FakeHeaders(),
content,
'test-stub'
Expand Down
7 changes: 0 additions & 7 deletions extensions/html-language-features/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,6 @@
"default": true,
"description": "%html.hover.references%"
},
"html.mirrorCursorOnMatchingTag": {
"type": "boolean",
"scope": "resource",
"default": false,
"description": "%html.mirrorCursorOnMatchingTag%",
"deprecationMessage": "%html.mirrorCursorOnMatchingTagDeprecationMessage%"
},
"html.trace.server": {
"type": "string",
"scope": "window",
Expand Down
2 changes: 0 additions & 2 deletions extensions/html-language-features/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
"html.completion.attributeDefaultValue.doublequotes": "Attribute value is set to \"\".",
"html.completion.attributeDefaultValue.singlequotes": "Attribute value is set to ''.",
"html.completion.attributeDefaultValue.empty": "Attribute value is not set.",
"html.mirrorCursorOnMatchingTag": "Enable/disable mirroring cursor on matching HTML tag.",
"html.mirrorCursorOnMatchingTagDeprecationMessage": "Deprecated in favor of `editor.linkedEditing`",
"html.hover.documentation": "Show tag and attribute documentation in hover.",
"html.hover.references": "Show references to MDN in hover."
}
3 changes: 1 addition & 2 deletions extensions/npm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ For more information about auto detection of Tasks, see the [documentation](http

### Script Explorer

The Npm Script Explorer shows the npm scripts found in your workspace. The explorer view is enabled by the setting `npm.enableScriptExplorer`. A script can be opened, run, or debug from the explorer.
The Npm Script Explorer shows the npm scripts found in your workspace. A script can be opened, run, or debug from the explorer.

### Run Scripts from the Editor

Expand All @@ -37,7 +37,6 @@ The extension fetches data from <https://registry.npmjs.org> and <https://regist
- `npm.packageManager` - The package manager used to install dependencies: `auto`, `npm`, `yarn`, `pnpm` or `bun`. The default is `auto`, which detects your package manager based on files in your workspace.
- `npm.scriptRunner` - The script runner used to run the scripts: `auto`, `npm`, `yarn`, `pnpm`, `bun` or `node`. The default is `auto`, which detects your script runner based on files in your workspace.
- `npm.exclude` - Glob patterns for folders that should be excluded from automatic script detection. The pattern is matched against the **absolute path** of the package.json. For example, to exclude all test folders use '&ast;&ast;/test/&ast;&ast;'.
- `npm.enableScriptExplorer` - Enable an explorer view for npm scripts.
- `npm.scriptExplorerAction` - The default click action: `open` or `run`, the default is `open`.
- `npm.enableRunFromFolder` - Enable running npm scripts from the context menu of folders in Explorer, the default is `false`.
- `npm.scriptCodeLens.enable` - Enable/disable the code lenses to run a script, the default is `false`.
7 changes: 0 additions & 7 deletions extensions/npm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -292,13 +292,6 @@
"description": "%config.npm.exclude%",
"scope": "resource"
},
"npm.enableScriptExplorer": {
"type": "boolean",
"default": false,
"scope": "resource",
"deprecationMessage": "The NPM Script Explorer is now available in 'Views' menu in the Explorer in all folders.",
"markdownDescription": "%config.npm.enableScriptExplorer%"
},
"npm.enableRunFromFolder": {
"type": "boolean",
"default": false,
Expand Down
1 change: 0 additions & 1 deletion extensions/npm/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"config.npm.scriptRunner.vp": "Use Vite+ (vp) as the script runner.",
"config.npm.scriptRunner.auto": "Auto-detect which script runner to use based on lock files and installed package managers.",
"config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.",
"config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts when there is no top-level `package.json` file.",
"config.npm.scriptExplorerAction": "The default click action used in the NPM Scripts Explorer: `open` or `run`, the default is `open`.",
"config.npm.scriptExplorerExclude": "An array of regular expressions that indicate which scripts should be excluded from the NPM Scripts view.",
"config.npm.enableRunFromFolder": "Enable running npm scripts contained in a folder from the Explorer context menu.",
Expand Down
Loading
Loading