Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bad7035
Add onDidDispose event for chat sessions input states
mjbvz Apr 22, 2026
29178d2
sessions: update Open in VS Code titlebar button with hover animation…
hawkticehurst Apr 23, 2026
607f2de
Use distro code-icon.svg in prod, shield fallback in dev
hawkticehurst Apr 23, 2026
9f29c28
Address Copilot PR review feedback
hawkticehurst Apr 23, 2026
2289e09
agentHost: finish implementing host-level settings
connor4312 Apr 24, 2026
2e9a405
Remove grayscale filter: always show icon in full color
hawkticehurst Apr 24, 2026
a4fed9c
Merge branch 'main' into agents/update-titlebar-open-vscode-button
Copilot Apr 24, 2026
0748f7f
pr comments
connor4312 Apr 24, 2026
a25b7d1
Merge remote-tracking branch 'origin/main' into connor4312/agent-host…
connor4312 Apr 24, 2026
19d48a9
Use service assessor
mjbvz Apr 24, 2026
113125f
Remove now unused props
mjbvz Apr 24, 2026
2d0f70c
Move agentPluginsHome to IUserDataProfile and IEnvironmentService (#3…
sandy081 Apr 24, 2026
64e0919
fix: schedule promptFallbackScheduler immediately in trackIdleOnPromp…
meganrogge Apr 24, 2026
d444d45
agentHost: finish implementing host-level settings (#312268)
connor4312 Apr 24, 2026
4b5b21a
Terminal agent: improve send_to_terminal output capture and add waitF…
meganrogge Apr 24, 2026
4b6403a
Merge pull request #312393 from mjbvz/dev/mjbvz/adverse-tyrannosaurus
mjbvz Apr 24, 2026
559c217
Fix sessions pickers/quick access (#312389)
roblourens Apr 24, 2026
f2b51f3
Fix browser view being always hidden (#312391)
kycutler Apr 24, 2026
4651bee
fix: use setupDelayedHover for settings indicator hovers (#304990)
yogeshwaran-c Apr 24, 2026
439c85a
sessions: enable customizations UI for all session types with deep-li…
TylerLeonhardt Apr 24, 2026
5bd1da4
feedback
sandy081 Apr 24, 2026
a080227
Improve screenshot diff logic for pull requests and pushes; add job s…
hediet Apr 24, 2026
172a0fd
Merge branch 'main' into agents/update-titlebar-open-vscode-button
lostintangent Apr 24, 2026
bb09e73
terminal: Disable read all by default (#311850)
dileepyavan Apr 24, 2026
f6a18ce
tools: fix run task tool not finding tools consistently
connor4312 Apr 24, 2026
10a3d98
Merge pull request #312250 from microsoft/agents/update-titlebar-open…
lostintangent Apr 24, 2026
10e4db6
Merge pull request #312411 from microsoft/connor4312/task-fix
connor4312 Apr 24, 2026
0e1f836
Make localIndex setting public (#312402)
vijayupadya Apr 24, 2026
5407371
Chat: Show provider instance name for duplicate BYOK models in model …
fishcharlie Apr 24, 2026
a9c0f03
fix: prevent tool deferral when tool_search is not in request (#312406)
bhavyaus Apr 24, 2026
96d2420
Mobile titlebar: live session title + web host widget on welcome (#31…
osortega Apr 24, 2026
f18d459
Update skill parsing behaviour (#311797)
pwang347 Apr 24, 2026
faf8592
Improve browser experience in Agents app (#312420)
kycutler Apr 24, 2026
ee29e9a
Agents web: drop [hostname] from remote agent host session labels (#3…
osortega Apr 24, 2026
2100f01
Merge branch 'main' into dev/mjbvz/complete-gibbon
mjbvz Apr 24, 2026
6aa1e80
Accessibility: Add verbosity setting, keybinding, and aria hints for …
meganrogge Apr 24, 2026
8f938b4
Fix mocks
mjbvz Apr 24, 2026
fda5c96
fix: post-install script doesn't check for existing sources (#311795)
rzhao271 Apr 24, 2026
337fb19
agents: persist agent-host workspace selection across reload (#312037)
roblourens Apr 24, 2026
2723584
Dismiss welcome dialog again. (#312421)
dmitrivMS Apr 24, 2026
8f547df
Bump openssl from 0.10.75 to 0.10.78 in /cli (#312161)
dependabot[bot] Apr 24, 2026
fe9a973
Merge pull request #312433 from mjbvz/dev/mjbvz/complete-gibbon
mjbvz Apr 24, 2026
52368ad
Rework titlebar item to use View > Browser command (#312440)
kycutler Apr 24, 2026
ff13067
/remote fixes for chat state, disablement, and session titles (#312428)
pierceboggan Apr 24, 2026
c36e7fb
Plan agent: 3-tier graceful degradation for Explore and search subage…
digitarald Apr 24, 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
70 changes: 58 additions & 12 deletions .github/workflows/screenshot-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,23 @@ jobs:

- name: Diff screenshots against merge base
id: diff
if: github.event_name == 'pull_request' && steps.oidc.outputs.token
if: steps.oidc.outputs.token
run: |
# We diff screenshots(checked-out commit) vs screenshots(merge-base of
# that commit with the target branch). This isolates the visual effect
# of just this PR's divergence from target. Using pull_request.base.sha
# would be wrong: it's the target-branch tip at PR creation time and can
# be stale, causing unrelated target-branch commits to show up as diffs.
TARGET_REF="origin/${{ github.event.pull_request.base.ref }}"
git fetch --no-tags --depth=1 origin "${{ github.event.pull_request.base.ref }}"
BASE_SHA=$(git merge-base "${{ github.sha }}" "$TARGET_REF")
echo "Using base SHA: $BASE_SHA (merge-base of ${{ github.sha }} and $TARGET_REF)"
if [ "${{ github.event_name }}" = "pull_request" ]; then
# For PRs, diff against the merge-base with the target branch.
# This isolates the visual effect of just this PR's divergence
# from target. Using pull_request.base.sha would be wrong: it's
# the target-branch tip at PR creation time and can be stale,
# causing unrelated target-branch commits to show up as diffs.
TARGET_REF="origin/${{ github.event.pull_request.base.ref }}"
git fetch --no-tags --depth=1 origin "${{ github.event.pull_request.base.ref }}"
BASE_SHA=$(git merge-base "${{ github.sha }}" "$TARGET_REF")
else
# For push events, diff against the parent commit.
BASE_SHA=$(git rev-parse "${{ github.sha }}^")
fi
echo "base_sha=$BASE_SHA" >> "$GITHUB_OUTPUT"
echo "Using base SHA: $BASE_SHA (base for ${{ github.sha }})"
BODY=$(node build/lib/screenshotDiffReport.ts \
https://hediet-screenshots.azurewebsites.net \
${{ github.repository_owner }} \
Expand All @@ -172,8 +178,25 @@ jobs:
env:
SCREENSHOT_SERVICE_TOKEN: ${{ steps.oidc.outputs.token }}

- name: Write job summary
if: steps.diff.outputs.has_changes == 'true' || steps.blocks-ci.outputs.match == 'false'
run: |
BODY="${COMMENT_BODY}"
if [ -n "$BLOCKS_CI_CONTENT" ]; then
if [ -n "$BODY" ]; then BODY+=$'\n\n---\n\n'; fi
BODY+="### blocks-ci screenshots changed"$'\n\n'
BODY+="Replace the contents of \`test/componentFixtures/blocks-ci-screenshots.md\` with:"$'\n\n'
BODY+="<details>"$'\n'"<summary>Updated blocks-ci-screenshots.md</summary>"$'\n\n'
BODY+="\`\`\`md"$'\n'"${BLOCKS_CI_CONTENT}"$'\n'"\`\`\`"$'\n\n'
BODY+="</details>"
fi
echo "$BODY" >> "$GITHUB_STEP_SUMMARY"
env:
COMMENT_BODY: ${{ steps.diff.outputs.body }}
BLOCKS_CI_CONTENT: ${{ steps.blocks-ci.outputs.content }}

- name: Post PR comment
if: github.event_name == 'pull_request' && (steps.diff.outputs.has_changes == 'true' || steps.blocks-ci.outputs.match == 'false')
if: github.event_name == 'pull_request'
uses: actions/github-script@v9
with:
script: |
Expand All @@ -190,7 +213,7 @@ jobs:
body += '</details>';
}

body = marker + '\n' + body;
const hasContent = body || blocksCiContent;

const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
Expand All @@ -200,6 +223,27 @@ jobs:
});
const existing = comments.find(c => c.body?.startsWith(marker));

if (!hasContent) {
// No changes to report — update existing comment if present, otherwise do nothing
if (existing) {
const baseSha = (process.env.BASE_SHA || '').slice(0, 8);
const currentSha = (process.env.CURRENT_SHA || '').slice(0, 8);
let noChangesBody = '~No screenshot changes.~';
if (baseSha && currentSha) {
noChangesBody = `**Base:** \`${baseSha}\` **Current:** \`${currentSha}\`\n\n` + noChangesBody;
}
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body: marker + '\n' + noChangesBody,
});
}
return;
}

body = marker + '\n' + body;

if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
Expand All @@ -218,6 +262,8 @@ jobs:
env:
COMMENT_BODY: ${{ steps.diff.outputs.body }}
BLOCKS_CI_CONTENT: ${{ steps.blocks-ci.outputs.content }}
BASE_SHA: ${{ steps.diff.outputs.base_sha }}
CURRENT_SHA: ${{ github.sha }}

- name: Fail if blocks-ci hashes changed
if: steps.blocks-ci.outputs.match == 'false'
Expand Down
8 changes: 4 additions & 4 deletions cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions extensions/copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4018,6 +4018,15 @@
"tags": [
"experimental"
]
},
"github.copilot.chat.localIndex.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "%github.copilot.config.localIndex.enabled%",
"tags": [
"experimental",
"onExp"
]
}
}
},
Expand Down
5 changes: 3 additions & 2 deletions extensions/copilot/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,11 @@
"copilot.agent.description": "Edit files in your workspace in agent mode",
"copilot.agent.compact.description": "Free up context by compacting the conversation history. Optionally include extra instructions for compaction.",
"copilot.chronicle.description": "Session history tools and insights",
"copilot.chronicle.standup.description": "Generate a standup report from recent coding sessions",
"copilot.chronicle.tips.description": "Get personalized tips based on your Copilot usage patterns",
"copilot.chronicle.standup.description": "Generate a standup report from recent chat sessions",
"copilot.chronicle.tips.description": "Get personalized tips based on your chat session usage patterns",
"github.copilot.config.sessionSearch.enabled": "Enable session search and /chronicle commands. This is a team-internal setting.",
"github.copilot.config.sessionSearch.localIndex.enabled": "Enable local session tracking. When enabled, Copilot tracks session data locally for /chronicle commands.",
"github.copilot.config.localIndex.enabled": "Enable local session tracking. When enabled, session data is tracked locally for /chronicle commands.",
"github.copilot.config.sessionSearch.cloudSync.enabled": "Enable cloud sync for session data. When enabled, session data is synced to your Copilot account for cross-device access.",
"github.copilot.config.sessionSearch.cloudSync.excludeRepositories": "Repository patterns to exclude from cloud sync. Use exact `owner/repo` names or glob patterns like `my-org/*`. Sessions from matching repos will only be stored locally.",
"copilot.workspace.explain.description": "Explain how the code in your active editor works",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { AGENT_FILE_EXTENSION } from '../../../platform/customInstructions/commo
import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext';
import { IFileSystemService } from '../../../platform/filesystem/common/fileSystemService';
import { ILogService } from '../../../platform/log/common/logService';
import { IExperimentationService } from '../../../platform/telemetry/common/nullExperimentationService';
import { Disposable } from '../../../util/vs/base/common/lifecycle';
import { AgentConfig, AgentHandoff, buildAgentMarkdown, DEFAULT_READ_TOOLS } from './agentTypes';

Expand All @@ -22,10 +23,8 @@ const BASE_PLAN_AGENT_CONFIG: AgentConfig = {
argumentHint: 'Outline the goal or problem to research',
target: 'vscode',
disableModelInvocation: true,
agents: ['Explore'],
tools: [
...DEFAULT_READ_TOOLS,
'agent',
],
handoffs: [], // Handoffs are generated dynamically in buildCustomizedConfig
body: '' // Body is generated dynamically in buildCustomizedConfig
Expand All @@ -52,6 +51,7 @@ export class PlanAgentProvider extends Disposable implements vscode.ChatCustomAg
@IVSCodeExtensionContext private readonly extensionContext: IVSCodeExtensionContext,
@IFileSystemService private readonly fileSystemService: IFileSystemService,
@ILogService private readonly logService: ILogService,
@IExperimentationService private readonly experimentationService: IExperimentationService,
) {
super();

Expand All @@ -63,7 +63,9 @@ export class PlanAgentProvider extends Disposable implements vscode.ChatCustomAg
if (e.affectsConfiguration(ConfigKey.PlanAgentAdditionalTools.fullyQualifiedId) ||
e.affectsConfiguration(ConfigKey.Deprecated.PlanAgentModel.fullyQualifiedId) ||
e.affectsConfiguration('chat.planAgent.defaultModel') ||
e.affectsConfiguration(ConfigKey.ImplementAgentModel.fullyQualifiedId)) {
e.affectsConfiguration(ConfigKey.ImplementAgentModel.fullyQualifiedId) ||
e.affectsConfiguration(ConfigKey.ExploreAgentEnabled.fullyQualifiedId) ||
e.affectsConfiguration(ConfigKey.Advanced.SearchSubagentToolEnabled.fullyQualifiedId)) {
this._onDidChangeCustomAgents.fire();
}
}));
Expand Down Expand Up @@ -103,12 +105,27 @@ export class PlanAgentProvider extends Disposable implements vscode.ChatCustomAg
return fileUri;
}

static buildAgentBody(): string {
const discoverySection = `## 1. Discovery
static buildAgentBody(exploreEnabled: boolean, searchSubagentEnabled: boolean): string {
let discoverySection: string;
if (exploreEnabled) {
discoverySection = `## 1. Discovery

Run the *Explore* subagent to gather context, analogous existing features to use as implementation templates, and potential blockers or ambiguities. When the task spans multiple independent areas (e.g., frontend + backend, different features, separate repos), launch **2-3 *Explore* subagents in parallel** — one per area — to speed up discovery.

Update the plan with your findings.`;
} else if (searchSubagentEnabled) {
discoverySection = `## 1. Discovery

Use #tool:searchSubagent to gather context, analogous existing features to use as implementation templates, and potential blockers or ambiguities. When the task spans multiple independent areas (e.g., frontend + backend, different features, separate repos), launch **2-3 search subagents in parallel** — one per area — to speed up discovery.

Update the plan with your findings.`;
} else {
discoverySection = `## 1. Discovery

Search the codebase to gather context, analogous existing features to use as implementation templates, and potential blockers or ambiguities.

Update the plan with your findings.`;
}

return `You are a PLANNING AGENT, pairing with the user to create a detailed, actionable plan.

Expand Down Expand Up @@ -197,6 +214,8 @@ Rules:

private buildCustomizedConfig(): AgentConfig {
const additionalTools = this.configurationService.getConfig(ConfigKey.PlanAgentAdditionalTools);
const isExploreEnabled = this.configurationService.getExperimentBasedConfig(ConfigKey.ExploreAgentEnabled, this.experimentationService);
const isSearchSubagentEnabled = this.configurationService.getExperimentBasedConfig(ConfigKey.Advanced.SearchSubagentToolEnabled, this.experimentationService);
const coreDefaultModel = this.configurationService.getNonExtensionConfig<string>('chat.planAgent.defaultModel');
const modelOverride = coreDefaultModel || this.configurationService.getConfig(ConfigKey.Deprecated.PlanAgentModel);

Expand Down Expand Up @@ -225,6 +244,11 @@ Rules:
// Always include askQuestions tool (now provided by core)
toolsToAdd.push('vscode/askQuestions');

// When explore agent is enabled, include the 'agent' tool to allow sub-agent calls
if (isExploreEnabled) {
toolsToAdd.push('agent');
}

// Merge additional tools (deduplicated)
const tools = toolsToAdd.length > 0
? [...new Set([...BASE_PLAN_AGENT_CONFIG.tools, ...toolsToAdd])]
Expand All @@ -233,9 +257,11 @@ Rules:
// Start with base config
return {
...BASE_PLAN_AGENT_CONFIG,
// When explore agent is enabled, allow the Explore subagent
...(isExploreEnabled ? { agents: ['Explore'] } : {}),
tools,
handoffs: [startImplementationHandoff, openInEditorHandoff, ...(BASE_PLAN_AGENT_CONFIG.handoffs ?? [])],
body: PlanAgentProvider.buildAgentBody(),
body: PlanAgentProvider.buildAgentBody(isExploreEnabled, isSearchSubagentEnabled),
...(modelOverride ? { model: modelOverride } : {}),
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,70 @@ suite('PlanAgentProvider', () => {

assert.equal(eventFired, true);
});

test('fires onDidChangeCustomAgents when SearchSubagentToolEnabled setting changes', async () => {
const provider = createProvider();

let eventFired = false;
provider.onDidChangeCustomAgents(() => {
eventFired = true;
});

await mockConfigurationService.setConfig(ConfigKey.Advanced.SearchSubagentToolEnabled, true);

assert.equal(eventFired, true);
});

test('buildAgentBody uses Explore discovery when explore is enabled', () => {
const body = PlanAgentProvider.buildAgentBody(true, true);
assert.ok(body.includes('Run the *Explore* subagent'));
assert.ok(!body.includes('#tool:searchSubagent'));
});

test('buildAgentBody uses search subagent discovery when explore is disabled but search is enabled', () => {
const body = PlanAgentProvider.buildAgentBody(false, true);
assert.ok(body.includes('#tool:searchSubagent'));
assert.ok(!body.includes('Run the *Explore* subagent'));
});

test('buildAgentBody uses generic discovery when both explore and search are disabled', () => {
const body = PlanAgentProvider.buildAgentBody(false, false);
assert.ok(body.includes('Search the codebase to gather context'));
assert.ok(!body.includes('Run the *Explore* subagent'));
assert.ok(!body.includes('#tool:searchSubagent'));
});

test('excludes agent tool and Explore subagent when explore is disabled', async () => {
await mockConfigurationService.setConfig(ConfigKey.ExploreAgentEnabled, false);

const provider = createProvider();
const agents = await provider.provideCustomAgents({}, {} as any);
const content = await getAgentContent(agents[0]);

// Should not have the 'agent' tool
const toolsMatch = content.match(/tools: \[([^\]]+)\]/);
assert.ok(toolsMatch);
assert.ok(!toolsMatch[1].includes('\'agent\''), 'Should not include agent tool when explore is disabled');

// Should not have agents field
assert.ok(!content.includes('agents:'), 'Should not include agents field when explore is disabled');
});

test('includes agent tool and Explore subagent when explore is enabled', async () => {
await mockConfigurationService.setConfig(ConfigKey.ExploreAgentEnabled, true);

const provider = createProvider();
const agents = await provider.provideCustomAgents({}, {} as any);
const content = await getAgentContent(agents[0]);

// Should have the 'agent' tool
const toolsMatch = content.match(/tools: \[([^\]]+)\]/);
assert.ok(toolsMatch);
assert.ok(toolsMatch[1].includes('\'agent\''), 'Should include agent tool when explore is enabled');

// Should have agents field with Explore
assert.ok(content.includes('agents:'), 'Should include agents field when explore is enabled');
});
});

suite('buildAgentMarkdown', () => {
Expand Down
6 changes: 5 additions & 1 deletion extensions/copilot/src/extension/byok/common/byokProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,11 @@ export function byokKnownModelToAPIInfo(providerName: string, id: string, capabi
version: '1.0.0',
maxOutputTokens: capabilities.maxOutputTokens,
maxInputTokens: capabilities.maxInputTokens,
detail: providerName,
// `detail` is intentionally omitted: when this model is resolved
// via a configured provider group, `LanguageModelsService` will
// fall back to the group name so multiple instances of the same
// vendor (e.g. multiple Ollama servers) are distinguishable in
// the model picker.
family: id,
tooltip: `${capabilities.name} is contributed via the ${providerName} provider.`,
multiplierNumeric: 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import type { Attachment, SendOptions } from '@github/copilot/sdk';

export interface ICopilotCLIPendingRequestContext {
readonly prompt: string;
readonly attachments: Attachment[];
readonly source?: SendOptions['source'];
}

const pendingRequestContextBySessionId = new Map<string, ICopilotCLIPendingRequestContext>();

export function setPendingCopilotCLIRequestContext(sessionId: string, context: ICopilotCLIPendingRequestContext): void {
pendingRequestContextBySessionId.set(sessionId, context);
}

export function takePendingCopilotCLIRequestContext(sessionId: string): ICopilotCLIPendingRequestContext | undefined {
const context = pendingRequestContextBySessionId.get(sessionId);
if (context) {
pendingRequestContextBySessionId.delete(sessionId);
}
return context;
}

export function clearPendingCopilotCLIRequestContext(sessionId: string): void {
pendingRequestContextBySessionId.delete(sessionId);
}
Loading
Loading