Skip to content

Commit b597e90

Browse files
committed
Merge remote-tracking branch 'origin/main' into agents/configure-remote-agent-plugins
# Conflicts: # src/vs/platform/agentHost/common/agentService.ts # src/vs/platform/agentHost/common/state/protocol/.ahp-version # src/vs/platform/agentHost/common/state/protocol/action-origin.generated.ts # src/vs/platform/agentHost/common/state/protocol/state.ts # src/vs/platform/agentHost/node/agentHostMain.ts # src/vs/platform/agentHost/node/agentHostServerMain.ts # src/vs/platform/agentHost/node/agentService.ts # src/vs/platform/agentHost/test/node/agentService.test.ts # src/vs/sessions/contrib/agentHost/test/browser/localAgentHostSessionsProvider.test.ts # src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationListWidget.ts
2 parents 6451ccd + 6743fb4 commit b597e90

246 files changed

Lines changed: 9998 additions & 3757 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/skills/vscode-dev-workbench/SKILL.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,20 @@ If your paths differ, check `server/` in `vscode-dev` for the source root resolu
2121

2222
## Start the dev server
2323

24+
**Critical:** Run `npm run dev` from the **`vscode-dev`** folder, NOT from `vscode`. The `vscode` repo has no `dev` script and will fail with `npm error Missing script: "dev"`. Terminal tools that simplify/strip leading `cd` into separate commands will silently keep the cwd of a previous terminal — always use an absolute `pushd` or verify with `pwd` before `npm run dev`.
25+
2426
```bash
25-
cd vscode-dev
26-
npm run dev # runs watch + nodemon; serves https://127.0.0.1:3000
27+
cd /path/to/vscode-dev # NOT /path/to/vscode
28+
npm run dev # runs watch + nodemon; serves https://127.0.0.1:3000
2729
```
2830

29-
On first start you may see one crash like `Cannot find module './indexes'` — it's the watcher racing the first build. nodemon restarts automatically once `out/` finishes compiling.
31+
If you're driving this through an agent/terminal tool, prefer:
32+
33+
```bash
34+
pushd /absolute/path/to/vscode-dev >/dev/null && pwd && npm run dev
35+
```
36+
37+
On first start you may see one crash like `Cannot find module './indexes'` — it's the watcher racing the first build. nodemon restarts automatically once `out/` finishes compiling. The server is ready when `curl -sk -o /dev/null -w "%{http_code}" https://127.0.0.1:3000/` returns `200`.
3038

3139
## URLs
3240

@@ -97,15 +105,19 @@ For a true mobile viewport, drive a standalone Playwright script with `devices['
97105
98106
## Testing the Agents window against a local mock agent host
99107
108+
If the scenario touches the Agents window (`/agents` route), you almost always need the mock agent host running. Without it, the Agents window will sit on the sign-in / tunnel-discovery screen and block any real interaction. Start it **in addition to** the dev server — it's a second terminal, not a replacement.
109+
100110
`vscode-dev` supports a `?mock-agent-host=ws://…` URL parameter that short-circuits tunnel discovery and wires the Agents window to a raw WebSocket. Pair it with the mock agent host binary from `microsoft/vscode`:
101111

102112
```bash
103-
cd vscode
113+
cd /path/to/vscode
104114
node out/vs/platform/agentHost/node/agentHostServerMain.js \
105115
--enable-mock-agent --quiet --without-connection-token --port 8765
106116
# Listens on ws://localhost:8765
107117
```
108118

119+
Prerequisite: `out/` in the `vscode` repo must be populated by the `VS Code - Build` task (or `npm run watch`). If `out/vs/platform/agentHost/node/agentHostServerMain.js` is missing, start that task first.
120+
109121
`--enable-mock-agent` registers the `ScriptedMockAgent` from `src/vs/platform/agentHost/test/node/mockAgent.ts` with one pre-existing session. Seed additional sessions via the `VSCODE_AGENT_HOST_MOCK_SEED_SESSIONS` env var, using a comma-separated list of session URIs (for example, `VSCODE_AGENT_HOST_MOCK_SEED_SESSIONS=mock://pre-1,mock://pre-2`). Scripted prompts include `hello`, `use-tool`, `error`, `permission`, `write-file`, `run-safe-command`, `slow`, `client-tool`, `subagent`, etc. (see `mockAgent.ts` for the full list).
110122

111123
Then open:

build/.moduleignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ vsda/**
159159
@vscode/policy-watcher/index.d.ts
160160
!@vscode/policy-watcher/build/Release/vscode-policy-watcher.node
161161

162+
@vscode/macos-keychain/build/**
163+
@vscode/macos-keychain/src/**
164+
@vscode/macos-keychain/test/**
165+
@vscode/macos-keychain/binding.gyp
166+
@vscode/macos-keychain/README.md
167+
@vscode/macos-keychain/index.d.ts
168+
!@vscode/macos-keychain/build/Release/keychainNative.node
169+
162170
@vscode/windows-ca-certs/**/*
163171
!@vscode/windows-ca-certs/package.json
164172
!@vscode/windows-ca-certs/**/*.node

build/azure-pipelines/darwin/app-entitlements.plist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,9 @@
1010
<true/>
1111
<key>com.apple.security.automation.apple-events</key>
1212
<true/>
13+
<key>keychain-access-groups</key>
14+
<array>
15+
<string>$(TeamIdentifierPrefix)com.microsoft.vscode.shared-secrets</string>
16+
</array>
1317
</dict>
1418
</plist>

build/azure-pipelines/darwin/steps/product-build-darwin-compile.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,16 @@ steps:
332332
BUILD_SOURCESDIRECTORY: $(Build.SourcesDirectory)
333333
displayName: ✍️ Codesign & Notarize
334334

335+
# Re-sign the app without the provisioning profile for tests.
336+
# This strips the keychain-access-groups entitlement which requires a
337+
# provisioning profile and is not needed for running tests. The codesign
338+
# step reads from the archives packaged above which have the full entitlements.
339+
- script: |
340+
set -e
341+
export CODESIGN_IDENTITY=$(security find-identity -v -p codesigning $(agent.tempdirectory)/buildagent.keychain | grep -oEi "([0-9A-F]{40})" | head -n 1)
342+
DEBUG=electron-osx-sign* node build/darwin/sign.ts --skip-provisioning-profile $(agent.builddirectory)
343+
displayName: Set Hardened Entitlements (for tests)
344+
335345
- ${{ if or(eq(parameters.VSCODE_RUN_ELECTRON_TESTS, true), eq(parameters.VSCODE_RUN_BROWSER_TESTS, true), eq(parameters.VSCODE_RUN_REMOTE_TESTS, true)) }}:
336346
- template: product-build-darwin-test.yml@self
337347
parameters:

build/darwin/sign.ts

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,66 @@ function getElectronVersion(): string {
1818
return target;
1919
}
2020

21-
function getEntitlementsForFile(filePath: string): string {
21+
const mainProvisioningProfilePath = path.join(baseDir, 'darwin', 'main.provisionprofile');
22+
const agentsProvisioningProfilePath = path.join(baseDir, 'darwin', 'agents.provisionprofile');
23+
24+
function hasProvisioningProfile(): boolean {
25+
return fs.existsSync(mainProvisioningProfilePath);
26+
}
27+
28+
function getEntitlementsForFile(filePath: string, tempDir: string, useProvisioningProfile: boolean, teamId?: string): string {
2229
if (filePath.includes(' Helper (GPU).app')) {
2330
return path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist');
2431
} else if (filePath.includes(' Helper (Renderer).app')) {
2532
return path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist');
2633
} else if (filePath.includes(' Helper (Plugin).app')) {
2734
return path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist');
2835
}
29-
return path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist');
36+
const entitlementsPath = path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist');
37+
if (!useProvisioningProfile) {
38+
// Without a provisioning profile, keychain-access-groups entitlement
39+
// will cause signing failures. Strip it from the entitlements plist.
40+
return getStrippedEntitlements(entitlementsPath, tempDir);
41+
}
42+
if (teamId) {
43+
return getExpandedEntitlements(entitlementsPath, tempDir, teamId);
44+
}
45+
return entitlementsPath;
46+
}
47+
48+
let _strippedEntitlementsPath: string | undefined;
49+
50+
/**
51+
* Returns a path to a copy of the entitlements plist with the
52+
* keychain-access-groups key removed.
53+
*/
54+
function getStrippedEntitlements(entitlementsPath: string, tempDir: string): string {
55+
if (!_strippedEntitlementsPath) {
56+
const content = fs.readFileSync(entitlementsPath, 'utf8');
57+
const stripped = content.replace(
58+
/\s*<key>keychain-access-groups<\/key>\s*<array>[\s\S]*?<\/array>/,
59+
''
60+
);
61+
_strippedEntitlementsPath = path.join(tempDir, 'app-entitlements-stripped.plist');
62+
fs.writeFileSync(_strippedEntitlementsPath, stripped);
63+
}
64+
return _strippedEntitlementsPath;
65+
}
66+
67+
let expandedEntitlementsPath: string | undefined;
68+
69+
/**
70+
* Returns a path to a copy of the entitlements plist with
71+
* $(TeamIdentifierPrefix) expanded to the actual team identifier.
72+
*/
73+
function getExpandedEntitlements(entitlementsPath: string, tempDir: string, teamId: string): string {
74+
if (!expandedEntitlementsPath) {
75+
const content = fs.readFileSync(entitlementsPath, 'utf8');
76+
const expanded = content.replace(/\$\(TeamIdentifierPrefix\)/g, teamId + '.');
77+
expandedEntitlementsPath = path.join(tempDir, 'app-entitlements.plist');
78+
fs.writeFileSync(expandedEntitlementsPath, expanded);
79+
}
80+
return expandedEntitlementsPath;
3081
}
3182

3283
async function retrySignOnKeychainError<T>(fn: () => Promise<T>, maxRetries: number = 3): Promise<T> {
@@ -58,7 +109,7 @@ async function retrySignOnKeychainError<T>(fn: () => Promise<T>, maxRetries: num
58109
throw lastError;
59110
}
60111

61-
async function main(buildDir?: string): Promise<void> {
112+
async function main(buildDir?: string, skipProvisioningProfile?: boolean): Promise<void> {
62113
const tempDir = process.env['AGENT_TEMPDIRECTORY'];
63114
const arch = process.env['VSCODE_ARCH'];
64115
const identity = process.env['CODESIGN_IDENTITY'];
@@ -78,23 +129,51 @@ async function main(buildDir?: string): Promise<void> {
78129
? path.resolve(appRoot, appName, 'Contents', 'Applications', `${product.embedded.nameLong}.app`, 'Contents', 'Info.plist')
79130
: undefined;
80131

132+
const useProvisioningProfile = !skipProvisioningProfile && hasProvisioningProfile();
133+
const resolvedProvisioningProfile = useProvisioningProfile ? mainProvisioningProfilePath : undefined;
134+
135+
let teamId: string | undefined;
136+
if (resolvedProvisioningProfile) {
137+
const profilePlist = await spawn('security', ['cms', '-D', '-i', resolvedProvisioningProfile]);
138+
const teamIdMatch = /<key>TeamIdentifier<\/key>\s*<array>\s*<string>(.*?)<\/string>/s.exec(profilePlist);
139+
if (teamIdMatch) {
140+
teamId = teamIdMatch[1];
141+
console.log(`Extracted TeamIdentifier from provisioning profile: ${teamId}`);
142+
} else {
143+
console.warn('Could not extract TeamIdentifier from provisioning profile; $(TeamIdentifierPrefix) will not be expanded');
144+
}
145+
}
146+
147+
// Embed the agents provisioning profile into the embedded app bundle
148+
// before signing, since @electron/osx-sign only supports one top-level profile.
149+
if (useProvisioningProfile && product.embedded && fs.existsSync(agentsProvisioningProfilePath)) {
150+
const embeddedAppPath = path.join(appRoot, appName, 'Contents', 'Applications', `${product.embedded.nameLong}.app`);
151+
if (fs.existsSync(embeddedAppPath)) {
152+
const embeddedProfileDest = path.join(embeddedAppPath, 'Contents', 'embedded.provisionprofile');
153+
fs.copyFileSync(agentsProvisioningProfilePath, embeddedProfileDest);
154+
console.log(`Embedded agents provisioning profile into ${embeddedProfileDest}`);
155+
}
156+
}
157+
81158
const appOpts: SignOptions = {
82159
app: path.join(appRoot, appName),
83160
platform: 'darwin',
84161
optionsForFile: (filePath) => ({
85-
entitlements: getEntitlementsForFile(filePath),
162+
entitlements: getEntitlementsForFile(filePath, tempDir, useProvisioningProfile, teamId),
86163
hardenedRuntime: true,
87164
}),
88165
preAutoEntitlements: false,
89-
preEmbedProvisioningProfile: false,
166+
preEmbedProvisioningProfile: !!resolvedProvisioningProfile,
167+
provisioningProfile: resolvedProvisioningProfile,
90168
keychain: path.join(tempDir, 'buildagent.keychain'),
91169
version: getElectronVersion(),
92170
identity,
93171
};
94172

95173
// Only overwrite plist entries for x64 and arm64 builds,
96174
// universal will get its copy from the x64 build.
97-
if (arch !== 'universal') {
175+
// Skip when re-signing (skipProvisioningProfile) since entries already exist.
176+
if (arch !== 'universal' && !skipProvisioningProfile) {
98177
await spawn('plutil', [
99178
'-insert',
100179
'NSAppleEventsUsageDescription',
@@ -171,10 +250,19 @@ async function main(buildDir?: string): Promise<void> {
171250
}
172251

173252
await retrySignOnKeychainError(() => sign(appOpts));
253+
254+
// Dump entitlements from the signed binary for diagnostic purposes
255+
const mainBinary = path.join(appRoot, appName, 'Contents', 'MacOS', product.nameShort);
256+
console.log(`Dumping entitlements from signed binary: ${mainBinary}`);
257+
const entitlementsDump = await spawn('codesign', ['--display', '--entitlements', '-', '--xml', mainBinary]);
258+
console.log(`Signed entitlements:\n${entitlementsDump}`);
174259
}
175260

176261
if (import.meta.main) {
177-
main(process.argv[2]).catch(async err => {
262+
const args = process.argv.slice(2);
263+
const skipProvisioningProfile = args.includes('--skip-provisioning-profile');
264+
const buildDir = args.filter(a => !a.startsWith('--'))[0];
265+
main(buildDir, skipProvisioningProfile).catch(async err => {
178266
console.error(err);
179267
const tempDir = process.env['AGENT_TEMPDIRECTORY'];
180268
if (tempDir) {

cli/Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/copilot/package.json

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,27 @@
235235
]
236236
}
237237
},
238+
{
239+
"name": "skill",
240+
"toolReferenceName": "skill",
241+
"displayName": "%copilot.tools.skill.name%",
242+
"icon": "$(book)",
243+
"userDescription": "%copilot.tools.skill.description%",
244+
"modelDescription": "Invoke a skill to handle a user's request with specialized instructions and workflows.\n\nSkills are domain-specific capabilities discovered from SKILL.md files. When a user's task matches an available skill, call this tool to load and apply it. If the user types a slash command (e.g. \"/deploy\", \"/test\"), treat it as a skill invocation.\n\nUsage:\n- Pass the skill name only (no arguments).\n- Examples: skill: \"docx\", skill: \"deploy\", skill: \"fix-ci-failures\"\n\nRules:\n- Available skills appear in system-reminder messages earlier in the conversation.\n- BLOCKING: When a matching skill exists, you MUST call this tool before producing any other output about the task.\n- Never reference a skill without calling this tool.\n- Do not call this tool for a skill that is already active in the current turn (indicated by a <command-name> tag).\n- Do not use this tool for built-in commands such as /help or /clear.",
245+
"when": "config.github.copilot.chat.skillTool.enabled",
246+
"inputSchema": {
247+
"type": "object",
248+
"properties": {
249+
"skill": {
250+
"type": "string",
251+
"description": "The skill name. E.g., \"commit\", \"review-pr\", or \"pdf\""
252+
}
253+
},
254+
"required": [
255+
"skill"
256+
]
257+
}
258+
},
238259
{
239260
"name": "copilot_searchWorkspaceSymbols",
240261
"toolReferenceName": "symbols",
@@ -1265,7 +1286,8 @@
12651286
"problems",
12661287
"readFile",
12671288
"viewImage",
1268-
"readNotebookCellOutput"
1289+
"readNotebookCellOutput",
1290+
"skill"
12691291
]
12701292
},
12711293
{
@@ -1294,6 +1316,7 @@
12941316
"resolveMemoryFileUri",
12951317
"runCommand",
12961318
"switchAgent",
1319+
"toolSearch",
12971320
"vscodeAPI"
12981321
]
12991322
},
@@ -3792,6 +3815,15 @@
37923815
"clear-both"
37933816
]
37943817
},
3818+
"github.copilot.chat.anthropic.cacheBreakpoints.lastTwoMessages": {
3819+
"type": "boolean",
3820+
"default": false,
3821+
"markdownDescription": "%github.copilot.config.anthropic.cacheBreakpoints.lastTwoMessages%",
3822+
"tags": [
3823+
"experimental",
3824+
"onExp"
3825+
]
3826+
},
37953827
"github.copilot.chat.responsesApiReasoningSummary": {
37963828
"type": "string",
37973829
"default": "detailed",
@@ -4411,6 +4443,15 @@
44114443
"experimental"
44124444
]
44134445
},
4446+
"github.copilot.chat.skillTool.enabled": {
4447+
"type": "boolean",
4448+
"default": false,
4449+
"markdownDescription": "%github.copilot.config.skill.enabled%",
4450+
"tags": [
4451+
"advanced",
4452+
"experimental"
4453+
]
4454+
},
44144455
"github.copilot.chat.executionSubagent.enabled": {
44154456
"type": "boolean",
44164457
"default": false,
@@ -4475,7 +4516,7 @@
44754516
},
44764517
"github.copilot.chat.agentHistorySummarizationInline": {
44774518
"type": "boolean",
4478-
"default": false,
4519+
"default": true,
44794520
"markdownDescription": "%github.copilot.config.agentHistorySummarizationInline%",
44804521
"tags": [
44814522
"advanced",

extensions/copilot/package.nls.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@
342342
"copilot.toolSet.web.description": "Fetch information from the web",
343343
"github.copilot.config.useMessagesApi": "Use the Messages API instead of the Chat Completions API when supported.",
344344
"github.copilot.config.anthropic.contextEditing.mode": "Select the context editing mode for Anthropic models. Automatically manages conversation context as it grows, helping optimize costs and stay within context window limits.\n\n- `off`: Context editing is disabled.\n- `clear-thinking`: Clears thinking blocks while preserving tool uses.\n- `clear-tooluse`: Clears tool uses while preserving thinking blocks.\n- `clear-both`: Clears both thinking blocks and tool uses.\n\n**Note**: This is an experimental feature. Context editing may cause additional cache rewrites. Enable with caution.",
345+
"github.copilot.config.anthropic.cacheBreakpoints.lastTwoMessages": "Use the 'last two messages' cache breakpoint strategy instead of heuristic-based placement for Anthropic Messages API.",
345346
"github.copilot.config.useResponsesApi": "Use the Responses API instead of the Chat Completions API when supported. Enables reasoning and reasoning summaries.\n\n**Note**: This is an experimental feature that is not yet activated for all users.\n\n**Important**: URL API path resolution for custom OpenAI-compatible and Azure models is independent of this setting and fully determined by `url` property of `#github.copilot.chat.customOAIModels#` or `#github.copilot.chat.azureModels#` respectively.",
346347
"github.copilot.config.responsesApiReasoningSummary": "Sets the reasoning summary style used for the Responses API. Requires `#github.copilot.chat.useResponsesApi#`.",
347348
"github.copilot.config.responsesApiContextManagement.enabled": "Enables context management for the Responses API. Requires `#github.copilot.chat.useResponsesApi#`.",
@@ -490,6 +491,9 @@
490491
"copilot.tools.runSubagent.description": "Runs a task within an isolated subagent context. Enables efficient organization of tasks and context window management.",
491492
"copilot.tools.searchSubagent.name": "Search Subagent",
492493
"copilot.tools.searchSubagent.description": "Launch an iterative search-focused subagent to find relevant code in your workspace.",
494+
"copilot.tools.skill.name": "Skill",
495+
"copilot.tools.skill.description": "Execute a skill by name. Skills provide specialized capabilities, domain knowledge, and refined workflows.",
496+
"github.copilot.config.skill.enabled": "Enable the skill tool in Copilot Chat. When enabled, skills are invoked via a dedicated skill tool instead of readFile.",
493497
"github.copilot.config.searchSubagent.enabled": "Enable the search subagent tool for iterative code exploration in the workspace.",
494498
"github.copilot.config.searchSubagent.useAgenticProxy": "Use the agentic proxy for the search subagent tool.",
495499
"github.copilot.config.searchSubagent.model": "Model to use for the search subagent. When useAgenticProxy is enabled, defaults to 'vscode-agentic-search-router-a'. Otherwise defaults to the main agent model.",

0 commit comments

Comments
 (0)