Skip to content
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extensions/copilot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ To access GitHub Copilot, an active GitHub Copilot subscription is required. You

**Receive intelligent inline suggestions** as you type with [ghost text suggestions](https://aka.ms/vscode-completions) and [next edit suggestions](https://aka.ms/vscode-nes), helping you write code faster. Copilot predicts your next logical change, and you can accept suggestions with the Tab key.

![Video showing Copilot next edit suggestions.](https://github.com/microsoft/vscode-docs/raw/refs/heads/main/docs/copilot/images/inline-suggestions/nes-video.gif)
![Video showing Copilot next edit suggestions.](https://github.com/microsoft/vscode-docs/raw/refs/heads/main/docs/editing/images/inline-suggestions/nes-video.gif)

**Use inline chat for targeted edits** by pressing `Ctrl+I`/`Cmd+I` to open a chat prompt directly in the editor. Describe a change and Copilot suggests edits in place for refactoring methods, adding error handling, or explaining complex algorithms without leaving your editor.

Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/actions/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ export class MenuId {
static readonly BrowserChatActionsMenu = new MenuId('BrowserChatActionsMenu');
static readonly BrowserEmulationToolbar = new MenuId('BrowserEmulationToolbar');
static readonly AgentSessionsViewerFilterSubMenu = new MenuId('AgentSessionsViewerFilterSubMenu');
static readonly AgentSessionsExternalFilterSubMenu = new MenuId('AgentSessionsExternalFilterSubMenu');
static readonly AgentSessionsContext = new MenuId('AgentSessionsContext');
static readonly AgentSessionSectionContext = new MenuId('AgentSessionSectionContext');
static readonly AgentSessionsCreateSubMenu = new MenuId('AgentSessionsCreateSubMenu');
Expand Down
8 changes: 4 additions & 4 deletions src/vs/platform/agentHost/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,17 +211,17 @@ a chat URI. New provider code must consume the seams.

`register` takes the resolved provenance and whether to check tombstones. Explicit `AgentService.createSession` calls skip the tombstone check and clear any tombstone for that session URI; restore and discovery calls atomically decline to register if the session is or concurrently becomes tombstoned. An explicit row is never rewritten by catalog discovery. A migration-time host-owned marker can correct a previously discovered row back to internal provenance.

Providers own discovery lifecycle and push unknown chats with provider-classified provenance through `onDidDiscoverChats`. Claude and Codex classify their unknown native chats as external; Copilot classifies unknown extension-host chats as internal. Agent Service preserves that classification when it additively registers the event payload. Claude and Codex start their initial attempt when the first discovery-event listener is attached, then retry from their own SDK-readiness paths. Ordinary list refreshes never enumerate provider catalogs. Discovery has no migration marker or Copilot migrate-legacy gate. It never prunes a registry row when a provider later omits it and filters subagents and marked internal chat backings.
Providers own discovery lifecycle and push unknown chats with provider-classified provenance through `onDidDiscoverChats`. Claude, Codex, and Copilot classify their unknown native chats as external, except that Copilot keeps an unknown *legacy extension-host* chat internal because it is adoptable in place rather than someone else's session. Agent Service preserves that classification when it additively registers the event payload. Every provider starts one memoized initial attempt when the first discovery-event listener is attached; that attempt retries internally, but once it settles it is not re-armed by SDK readiness, so the only later trigger is an explicit one (for Copilot, the migrate-legacy toggle). Ordinary list refreshes never enumerate provider catalogs. External discovery has no migration marker or Copilot migrate-legacy gate; only the adoptable legacy extension-host half of Copilot's payload is withheld while migrate-legacy is off. Discovery never prunes a registry row when a provider later omits it and filters subagents and marked internal chat backings.

Claude and Codex each use one memoized initial path: resolve/download the SDK, enumerate once, classify the native catalog by stored session metadata, then emit only unknown chats as `external: true`. Provider session databases no longer persist a provider-local external property; legacy `claude.external` and `codex.external` values are recognized only as evidence that a chat was known. An empty or absent sidecar remains unknown.

If a provider cannot enumerate yet, its initial discovery attempt emits nothing; once ready, it emits the resulting chats through `onDidDiscoverChats`. Registry provenance is projected into `IAgentSessionMetadata._meta` with `readSessionExternal` / `withSessionExternal`, and the normal AHP listSessions round trip carries it to the Sessions provider. There is no external-specific UI behavior.

Legacy registry migration remains a separate `listChatsToMigrate()` contract. It returns only chats known from non-empty provider session metadata, without external provenance, and is gated by durable per-provider/global migration markers. Agent Host writes `agentHost.workspaceless` as either `true` or `false` into every session it creates. Agent Service classifies each migration candidate itself: marker presence means internal, while absence means a known external chat. Extension-host discovery is a separate Copilot flow and emits unknown extension-host chats as internal.
Legacy registry migration remains a separate `listChatsToMigrate()` contract. It returns only chats known from non-empty provider session metadata, without external provenance, and is gated by durable per-provider/global migration markers. Agent Host writes `agentHost.workspaceless` as either `true` or `false` into every session it creates. Agent Service classifies each migration candidate itself: marker presence means internal, while absence means a known external chat.

Provider-private discovery helpers name their concrete source: Claude uses `_listClaudeCodeChats()` / `_emitClaudeCodeChats()`, Codex uses `_listCodexChats()` / `_emitCodexChats()`, and Copilot uses the extension-host names above. Providers filter known session metadata before emitting; Agent Service still performs the authoritative additive registry write and atomic tombstone check.
Provider-private discovery helpers name their concrete source: Claude uses `_listClaudeCodeChats()` / `_emitClaudeCodeChats()`, Codex uses `_listCodexChats()` / `_emitCodexChats()`, and Copilot uses `_discoverCopilotChats()` / `_emitCopilotChats()`. Providers filter known session metadata before emitting; Agent Service still performs the authoritative additive registry write and atomic tombstone check. Copilot treats the existence of a per-session database (under `{userDataPath}/agentSessionData`, never the shared Copilot home) as "known", which also keeps peer-chat backings out of the payload; it additionally drops a chat whose SDK context carries no working directory, because `_doResumeSession` requires one and a discovered chat has no other source for it.

For Claude and Codex, migration and discovery partition the same native catalog: migration returns known entries as plain metadata, while discovery emits unknown entries as external. Central `agent-host.db` remains the durable provenance authority.
For every provider, migration and discovery partition the same native catalog: migration returns known entries as plain metadata, while discovery emits unknown entries with provider-classified provenance (external for Claude and Codex, and for Copilot everything except an unknown legacy extension-host chat, which is emitted as internal and adoptable). The partition is not quite exhaustive for Copilot: a chat whose session database exists but holds none of the metadata keys `listChatsToMigrate` requires is rejected by both halves. That is deliberate — an empty database is how Agent Host records a chat it already touched — and is asserted by `copilotAgent.test.ts`'s "does not discover an extension-host chat with an empty Agent Host database". Central `agent-host.db` remains the durable provenance authority.

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { encodeBase64 } from '../../../base/common/buffer.js';
import { ILoadEstimator, LoadEstimator } from '../../../base/parts/ipc/common/ipc.net.js';
import { ITelemetryService, TelemetryLevel, TELEMETRY_CRASH_REPORTER_SETTING_ID, TELEMETRY_OLD_SETTING_ID, TELEMETRY_SETTING_ID } from '../../telemetry/common/telemetry.js';
import { getTelemetryLevel } from '../../telemetry/common/telemetryUtils.js';
import { AgentHostTelemetryLevelConfigKey, AgentHostTerminalAutoApproveEnabledConfigKey, AgentHostTerminalAutoApproveRulesConfigKey, AgentHostDisableRepoInfoTelemetryConfigKey, getAgentHostTerminalAutoApproveRulesConfig, TERMINAL_AUTO_APPROVE_ENABLED_SETTING_ID, TERMINAL_AUTO_APPROVE_SETTING_ID, TERMINAL_IGNORE_DEFAULT_AUTO_APPROVE_RULES_SETTING_ID, DISABLE_REPO_INFO_TELEMETRY_SETTING_ID, telemetryLevelToAgentHostConfigValue } from '../common/agentHostSchema.js';
import { AgentHostAutoApprovePolicyRestrictedConfigKey, AgentHostTelemetryLevelConfigKey, AgentHostTerminalAutoApproveEnabledConfigKey, AgentHostTerminalAutoApproveRulesConfigKey, AgentHostDisableRepoInfoTelemetryConfigKey, getAgentHostTerminalAutoApproveRulesConfig, GLOBAL_AUTO_APPROVE_SETTING_ID, TERMINAL_AUTO_APPROVE_ENABLED_SETTING_ID, TERMINAL_AUTO_APPROVE_SETTING_ID, TERMINAL_IGNORE_DEFAULT_AUTO_APPROVE_RULES_SETTING_ID, DISABLE_REPO_INFO_TELEMETRY_SETTING_ID, telemetryLevelToAgentHostConfigValue } from '../common/agentHostSchema.js';
import { getAgentHostConfigurationSyncEntries, resolveAgentHostConfigurationSyncPatch, resolveAgentHostConfigurationSyncValue } from '../common/agentHostConfigurationSync.js';
import { managedPermissionsConfigurationIds, resolveManagedSettingsPermissions, type IAgentHostManagedSettingsPermissions } from '../common/agentHostManagedSettings.js';
import { AgentHostClientConnectionKind, toAgentHostClientMeta } from '../common/agentHostTelemetry.js';
Expand Down Expand Up @@ -366,6 +366,9 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
if (Object.keys(patch).length) {
this._dispatchRootConfig(patch);
}
if (e.affectsConfiguration(GLOBAL_AUTO_APPROVE_SETTING_ID)) {
this._updateAutoApprovePolicyRestriction();
}
if (e.affectsConfiguration(TELEMETRY_SETTING_ID) || e.affectsConfiguration(TELEMETRY_OLD_SETTING_ID) || e.affectsConfiguration(TELEMETRY_CRASH_REPORTER_SETTING_ID)) {
this._updateTelemetryLevel();
}
Expand Down Expand Up @@ -815,12 +818,18 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
this._updateTelemetryLevel();
this._updateTerminalAutoApproveEnabled();
this._updateTerminalAutoApproveRules();
this._updateAutoApprovePolicyRestriction();
this._updateDisableRepoInfoTelemetry();
if (includeManagedSettings) {
void this._updateManagedSettingsPermissions();
}
}

private _updateAutoApprovePolicyRestriction(): void {
const policyRestricted = this._configurationService.inspect<boolean>(GLOBAL_AUTO_APPROVE_SETTING_ID)?.policyValue === false;
this._dispatchRootConfig({ [AgentHostAutoApprovePolicyRestrictedConfigKey]: policyRestricted });
}

/**
* Apply a `reconnect` RPC result to the subscription manager. On `replay`
* we feed each missed envelope through the normal action path; on
Expand Down
3 changes: 3 additions & 0 deletions src/vs/platform/agentHost/common/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,9 @@ export interface IAgent {
/** Select provider-owned configuration inherited by a newly created chat. */
getInheritedChatConfig(config: Readonly<Record<string, unknown>>): Record<string, unknown> | undefined;

/** Select provider-owned configuration for an unattended autonomous turn. */
getAutonomousSessionConfig?(config: Readonly<Record<string, unknown>>): Record<string, unknown> | undefined;

/** Return dynamic completions for a provider-owned chat configuration property. */
chatConfigCompletions(params: IAgentChatConfigCompletionsParams): Promise<SessionConfigCompletionsResult>;

Expand Down
23 changes: 13 additions & 10 deletions src/vs/platform/agentHost/common/agentHostSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { localize } from '../../../nls.js';
import { structuralEquals } from '../../../base/common/equals.js';
import { ConfigurationTarget, type IConfigurationService, type IConfigurationValue } from '../../configuration/common/configuration.js';
import { DEFAULT_EDIT_AUTO_APPROVE_PATTERNS, type ChatEditAutoApprovePatterns } from '../../chat/common/chatSettings.js';
import { ChatExternalSessionsMode, DEFAULT_EDIT_AUTO_APPROVE_PATTERNS, type ChatEditAutoApprovePatterns } from '../../chat/common/chatSettings.js';
import type { IMcpServerConfiguration } from '../../mcp/common/mcpPlatformTypes.js';
import { TelemetryConfiguration, TelemetryLevel } from '../../telemetry/common/telemetry.js';
import { telemetryLevelToAgentHostValue } from './agentHostTelemetry.js';
Expand Down Expand Up @@ -440,6 +440,8 @@ export const GLOBAL_AUTO_APPROVE_SETTING_ID = 'chat.tools.global.autoApprove';
* with Allow all.
*/
export const AgentHostGlobalAutoApproveEnabledConfigKey = 'globalAutoApproveEnabled';
/** Whether managed policy forbids elevated session auto-approval modes. */
export const AgentHostAutoApprovePolicyRestrictedConfigKey = 'autoApprovePolicyRestricted';

/**
* Root config key forwarded from the renderer when VS Code's `chat.autoReply`
Expand Down Expand Up @@ -468,12 +470,7 @@ export const AgentHostMigrateLegacyCopilotCliEnabledConfigKey = 'migrateLegacyCo

export const AgentHostShowExternalSessionsConfigKey = 'showExternalSessions';

export const enum AgentHostExternalSessionsMode {
None = 'none',
All = 'all',
Last24Hours = 'last24Hours',
Last7Days = 'last7Days',
}
export { ChatExternalSessionsMode as AgentHostExternalSessionsMode };

/**
* Root config key forwarded from the renderer that gates multiple-working-directory
Expand Down Expand Up @@ -722,6 +719,12 @@ export const platformRootSchema = createSchema({
description: localize('agentHost.config.globalAutoApproveEnabled.description', "Whether VS Code's global auto-approve setting is enabled. When `true`, every tool call is auto-approved, equivalent to a session using Allow all."),
default: false,
}),
[AgentHostAutoApprovePolicyRestrictedConfigKey]: schemaProperty<boolean>({
type: 'boolean',
title: localize('agentHost.config.autoApprovePolicyRestricted', "Auto Approve Policy Restricted"),
default: false,
readOnly: true,
}),
[AgentHostAutoReplyEnabledConfigKey]: schemaProperty<boolean>({
type: 'boolean',
title: localize('agentHost.config.autoReplyEnabled.title', "Auto Reply"),
Expand Down Expand Up @@ -752,12 +755,12 @@ export const platformRootSchema = createSchema({
description: localize('agentHost.config.migrateLegacyCopilotCliEnabled.description', "Whether un-adopted extension-host Copilot CLI sessions are surfaced as adoptable agent-host sessions and migrated in place when opened."),
default: false,
}),
[AgentHostShowExternalSessionsConfigKey]: schemaProperty<AgentHostExternalSessionsMode>({
[AgentHostShowExternalSessionsConfigKey]: schemaProperty<ChatExternalSessionsMode>({
type: 'string',
title: localize('agentHost.config.showExternalSessions.title', "Show External Agent Sessions"),
description: localize('agentHost.config.showExternalSessions.description', "Controls whether sessions created outside the Agent Host are included in the session catalog."),
enum: [AgentHostExternalSessionsMode.None, AgentHostExternalSessionsMode.All, AgentHostExternalSessionsMode.Last24Hours, AgentHostExternalSessionsMode.Last7Days],
default: AgentHostExternalSessionsMode.Last7Days,
enum: [ChatExternalSessionsMode.None, ChatExternalSessionsMode.All, ChatExternalSessionsMode.Last24Hours, ChatExternalSessionsMode.Last7Days],
default: ChatExternalSessionsMode.Last7Days,
}),
[AgentHostCopilotMultiRootEnabledConfigKey]: schemaProperty<boolean>({
type: 'boolean',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
AgentHostMarkdownPlanRichLinksEnabledConfigKey,
AgentHostSystemProxyEnabledConfigKey,
} from './agentHostSchema.js';
import { AgentMergeConfigKey, AgentMergeSettingId } from './agentMerge.js';

// Settings consumed by the agent host starter (`electronAgentHostStarter.ts`
// and `nodeAgentHostStarter.ts`) to populate the spawned agent host process's
Expand Down Expand Up @@ -99,6 +100,69 @@ configurationRegistry.registerConfiguration({
title: nls.localize('chatAgentHostStarterConfigurationTitle', "Chat Agent Host Starter"),
type: 'object',
properties: {
[AgentMergeSettingId.Enabled]: {
type: 'boolean',
description: nls.localize('chat.agentHost.agentMerge.enabled', "Enables the experimental Agent Merge controller and its commands. Agent Merge can monitor an agent session's pull request, ask the agent to address selected blockers, and optionally merge the pull request when it is ready."),
default: product.quality !== 'stable',
scope: ConfigurationScope.APPLICATION,
tags: ['experimental'],
agentHost: { key: AgentMergeConfigKey.Enabled },
},
[AgentMergeSettingId.AddressReviews]: {
type: 'boolean',
description: nls.localize('chat.agentHost.agentMerge.addressReviews', "Controls whether enabled Agent Merge sessions address unresolved review threads, changes-requested reviews, and new pull request comments from repository maintainers or the Copilot pull request reviewer."),
default: true,
scope: ConfigurationScope.APPLICATION,
tags: ['experimental'],
agentHost: { key: AgentMergeConfigKey.AddressReviews },
},
[AgentMergeSettingId.FixCI]: {
type: 'boolean',
description: nls.localize('chat.agentHost.agentMerge.fixCI', "Controls whether enabled Agent Merge sessions ask the agent to fix failed required CI checks."),
default: true,
scope: ConfigurationScope.APPLICATION,
tags: ['experimental'],
agentHost: { key: AgentMergeConfigKey.FixCI },
},
[AgentMergeSettingId.ResolveConflicts]: {
type: 'boolean',
description: nls.localize('chat.agentHost.agentMerge.resolveConflicts', "Controls whether enabled Agent Merge sessions ask the agent to update branches that are behind or resolve merge conflicts."),
default: true,
scope: ConfigurationScope.APPLICATION,
tags: ['experimental'],
agentHost: { key: AgentMergeConfigKey.ResolveConflicts },
},
[AgentMergeSettingId.MergePullRequest]: {
type: 'boolean',
description: nls.localize('chat.agentHost.agentMerge.mergePullRequest', "Controls whether the Agent Host automatically merges or enqueues pull requests for enabled Agent Merge sessions after all selected maintenance work is complete."),
default: false,
scope: ConfigurationScope.APPLICATION,
tags: ['experimental'],
agentHost: { key: AgentMergeConfigKey.MergePullRequest },
},
[AgentMergeSettingId.MergeMethod]: {
type: 'string',
enum: ['auto', 'squash', 'merge', 'rebase'],
enumDescriptions: [
nls.localize('chat.agentHost.agentMerge.mergeMethod.auto', "Uses the first repository-compatible method in this order: squash, merge commit, rebase."),
nls.localize('chat.agentHost.agentMerge.mergeMethod.squash', "Uses squash merge when the repository permits it."),
nls.localize('chat.agentHost.agentMerge.mergeMethod.merge', "Uses a merge commit when the repository permits it."),
nls.localize('chat.agentHost.agentMerge.mergeMethod.rebase', "Uses rebase merge when the repository permits it."),
],
description: nls.localize('chat.agentHost.agentMerge.mergeMethod', "Controls the native merge method used by Agent Merge."),
default: 'auto',
scope: ConfigurationScope.APPLICATION,
tags: ['experimental'],
agentHost: { key: AgentMergeConfigKey.MergeMethod },
},
[AgentMergeSettingId.ReplyAttribution]: {
type: 'boolean',
description: nls.localize('chat.agentHost.agentMerge.replyAttribution', "Controls whether review-thread replies posted by Agent Merge include an automated-reply attribution note."),
default: true,
scope: ConfigurationScope.APPLICATION,
tags: ['experimental'],
agentHost: { key: AgentMergeConfigKey.ReplyAttribution },
},
[AgentHostActiveAgentTitleGenerationSettingId]: {
type: 'boolean',
description: nls.localize('chat.agentHost.experimental.activeAgentTitleGeneration', "When enabled, the active agent names new sessions and chats using rename tools. When disabled, a utility model generates titles. Changes apply to sessions and chats created afterward."),
Expand Down
Loading
Loading