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
4 changes: 3 additions & 1 deletion .github/instructions/api-version.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Read this when changing proposed API in vscode.proposed.*.d.ts file
applyTo: 'src/vscode-dts/**/vscode.proposed.*.d.ts'
---

The following is only required for proposed API related to chat and languageModel proposals. It's optional for other proposed API, but recommended.
The following is only useful for proposed API related to chat and languageModel proposals. It's optional for other proposed API, and not recommended.

When a proposed API is changed in a non-backwards-compatible way, the version number at the top of the file must be incremented. If it doesn't have a version number, we must add one. The format of the number like this:

Expand All @@ -16,3 +16,5 @@ No semver, just a basic incrementing integer. See existing examples in `vscode.p
An example of a non-backwards-compatible change is removing a non-optional property or changing the type to one that is incompatible with the previous type.

An example of a backwards-compatible change is an additive change or deleting a property that was already optional.

Whenever possible, make a backwards-compatible change!
2 changes: 1 addition & 1 deletion extensions/copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6404,7 +6404,7 @@
"node-gyp": "npm:node-gyp@10.3.1",
"zod": "3.25.76"
},
"vscodeCommit": "afba0a4a1fc1e34dae9073d6787b6b541bda23eb",
"vscodeCommit": "94c8e2adc50e26ef70af85a0de3a9efed757acaa",
"__metadata": {
"id": "7ec7d6e6-b89e-4cc5-a59b-d6c4d238246f",
"publisherId": {
Expand Down
45 changes: 22 additions & 23 deletions extensions/copilot/script/build/vscodeDtsCheck.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,47 @@
*--------------------------------------------------------------------------------------------*/

// Usage: node script/build/vscodeDtsCheck.js
// Reads vscodeCommit from package.json, re-downloads proposed d.ts files
// at that commit, checks if any differ from what's committed, then restores
// the originals. Exits with code 1 if files are out of date.
// Compares proposed d.ts files in src/extension/ against the repo's
// src/vscode-dts/ directory. Exits with code 1 if files are out of date.

const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const vscodeDtsDir = path.resolve('..', '..', 'src', 'vscode-dts');
const targetDir = path.resolve('src', 'extension');

function main() {
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
const sha = pkg.vscodeCommit;
if (!sha) {
console.error('No vscodeCommit found in package.json. Run "npm run vscode-dts:update" first.');
const proposals = pkg.enabledApiProposals;
if (!proposals || proposals.length === 0) {
console.error('No enabledApiProposals found in package.json.');
process.exit(1);
}

console.log(`Checking proposed d.ts files against vscodeCommit: ${sha}`);
console.log('Checking proposed d.ts files against src/vscode-dts/...');

// Download proposed d.ts files using the commit SHA
execSync(`node node_modules/@vscode/dts/index.js dev ${sha}`, { stdio: 'inherit' });

// Compare downloaded files with committed ones
const downloaded = fs.readdirSync('.').filter(f => f.startsWith('vscode.') && f.endsWith('.ts'));
const mismatched = [];

for (const f of downloaded) {
const committedPath = path.join(targetDir, f);
const newContent = fs.readFileSync(f, 'utf-8');
for (const proposal of proposals) {
const fileName = `vscode.proposed.${proposal}.d.ts`;
const sourcePath = path.join(vscodeDtsDir, fileName);
const committedPath = path.join(targetDir, fileName);

if (!fs.existsSync(sourcePath)) {
console.warn(`Warning: ${fileName} not found in src/vscode-dts/, skipping`);
continue;
}

const sourceContent = fs.readFileSync(sourcePath, 'utf-8');

if (!fs.existsSync(committedPath)) {
mismatched.push(f + ' (missing)');
mismatched.push(fileName + ' (missing)');
} else {
const oldContent = fs.readFileSync(committedPath, 'utf-8');
if (oldContent !== newContent) {
mismatched.push(f);
const committedContent = fs.readFileSync(committedPath, 'utf-8');
if (sourceContent !== committedContent) {
mismatched.push(fileName);
}
}

// Clean up the downloaded file
fs.unlinkSync(f);
}

if (mismatched.length > 0) {
Expand Down
66 changes: 26 additions & 40 deletions extensions/copilot/script/build/vscodeDtsUpdate.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,44 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

// Usage: node script/build/vscodeDtsUpdate.js [branch]
// Downloads proposed API d.ts files from the given branch (default: main)
// of microsoft/vscode and writes the resolved commit SHA to package.json.
// Usage: node script/build/vscodeDtsUpdate.js
// Copies proposed API d.ts files from the repo's src/vscode-dts/ directory
// into this extension's src/extension/ folder based on enabledApiProposals.

const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const https = require('https');

const branch = process.argv[2] || 'main';
const vscodeDtsDir = path.resolve('..', '..', 'src', 'vscode-dts');
const targetDir = path.resolve('src', 'extension');

function resolveCommitSha(branch) {
return new Promise((resolve, reject) => {
const options = {
hostname: 'api.github.com',
path: `/repos/microsoft/vscode/commits/${encodeURIComponent(branch)}`,
headers: { 'User-Agent': 'vscode-copilot-chat', 'Accept': 'application/vnd.github.sha' }
};
https.get(options, res => {
if (res.statusCode !== 200) {
reject(new Error(`Failed to resolve commit for branch "${branch}": HTTP ${res.statusCode}`));
return;
}
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => resolve(data.trim()));
}).on('error', reject);
});
}

async function main() {
const sha = await resolveCommitSha(branch);
console.log(`Resolved branch "${branch}" to commit ${sha}`);

// Download proposed d.ts files using the commit SHA
execSync(`node node_modules/@vscode/dts/index.js dev ${sha}`, { stdio: 'inherit' });
function main() {
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
const proposals = pkg.enabledApiProposals;
if (!proposals || proposals.length === 0) {
console.error('No enabledApiProposals found in package.json.');
process.exit(1);
}

// Move downloaded files to src/extension/
const files = fs.readdirSync('.').filter(f => f.startsWith('vscode.') && f.endsWith('.ts'));
for (const f of files) {
fs.renameSync(f, path.join('src', 'extension', f));
let copied = 0;
for (const proposal of proposals) {
const fileName = `vscode.proposed.${proposal}.d.ts`;
const sourcePath = path.join(vscodeDtsDir, fileName);
if (!fs.existsSync(sourcePath)) {
console.warn(`Warning: ${fileName} not found in src/vscode-dts/`);
continue;
}
fs.copyFileSync(sourcePath, path.join(targetDir, fileName));
copied++;
}
console.log(`Copied ${copied} proposed API type definitions from src/vscode-dts/.`);

// Write the commit SHA to package.json
// Write the current commit SHA to package.json for reference
const sha = execSync('git rev-parse HEAD', { encoding: 'utf-8' }).trim();
const pkgPath = path.resolve('package.json');
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
pkg.vscodeCommit = sha;
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, '\t') + '\n');
console.log(`Wrote vscodeCommit: ${sha} to package.json`);
}

main().catch(err => {
console.error(err);
process.exit(1);
});
main();
15 changes: 13 additions & 2 deletions extensions/copilot/src/extension/tools/node/vscodeCmdTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,25 @@ class VSCodeCmdTool implements vscode.LanguageModelTool<IVSCodeCmdToolToolInput>
// Populate the Quick Open box with command ID rather than command name to avoid issues where Copilot didn't use the precise name,
// or when the Copilot response language (Spanish, French, etc.) might be different here than the UI one.
const commandStr = commandUri(quickOpenCommand, ['>' + commandId]);
const markdownString = new MarkdownString(l10n.t(`Copilot will execute the [{0}]({1}) (\`{2}\`) command.`, options.input.name, commandStr, options.input.commandId));
const hasArguments = !!options.input.args?.length;
const markdownString = new MarkdownString();
markdownString.appendMarkdown(l10n.t(`Copilot will execute the [{0}]({1}) (\`{2}\`) command.`, options.input.name, commandStr, options.input.commandId));
if (hasArguments) {
markdownString.appendMarkdown(`\n\n${l10n.t('Arguments')}:\n\n`);
markdownString.appendCodeblock(JSON.stringify(options.input.args, undefined, 2), 'json');
}
markdownString.isTrusted = { enabledCommands: [quickOpenCommand] };
return {
invocationMessage,
confirmationMessages: {
title: l10n.t`Run Command \`${options.input.name}\` (\`${options.input.commandId}\`)?`,
message: markdownString,
approveCombination: options.input.args ? l10n.t`Allow running command \`${options.input.commandId}\` with specific arguments` : l10n.t`Allow running command \`${options.input.commandId}\` without arguments`,
approveCombination: {
message: hasArguments
? l10n.t`Allow running command \`${options.input.commandId}\` with specific arguments`
: l10n.t`Allow running command \`${options.input.commandId}\` without arguments`,
arguments: hasArguments ? JSON.stringify(options.input.args) : undefined,
},
},
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,22 @@ declare module 'vscode' {
export interface LanguageModelToolConfirmationMessages {
/**
* When set, a button will be shown allowing the user to approve this particular
* combination of tool and arguments. The value is shown as the label for the
* approval option.
* combination of tool and arguments.
*
* For example, a tool that reads files could set this to `"Allow reading 'foo.txt'"`,
* For example, a tool that reads files could set this to
* `{ message: "Allow reading 'foo.txt'", arguments: JSON.stringify({ file: 'foo.txt' }) }`,
* so that the user can approve that specific file without approving all invocations
* of the tool.
*/
approveCombination?: string | MarkdownString;
approveCombination?: {
/**
* The label shown for the approval option.
*/
message: string | MarkdownString;
/**
* A string representation of the arguments that can be shown to the user.
*/
arguments?: string;
};
}
}
7 changes: 4 additions & 3 deletions src/vs/workbench/api/common/extHostLanguageModelTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,9 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape
checkProposedApiEnabled(item.extension, 'toolInvocationApproveCombination');
}

const approveCombinationLabel = result.confirmationMessages?.approveCombination
? typeConvert.MarkdownString.fromStrict(result.confirmationMessages.approveCombination)
const approveCombination = result.confirmationMessages?.approveCombination;
const approveCombinationLabel = approveCombination
? typeConvert.MarkdownString.fromStrict(approveCombination.message)
: undefined;
const approveCombinationKey = approveCombinationLabel
? await computeCombinationKey(toolId, context.parameters)
Expand All @@ -322,7 +323,7 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape
confirmationMessages: result.confirmationMessages ? {
title: typeof result.confirmationMessages.title === 'string' ? result.confirmationMessages.title : typeConvert.MarkdownString.from(result.confirmationMessages.title),
message: typeof result.confirmationMessages.message === 'string' ? result.confirmationMessages.message : typeConvert.MarkdownString.from(result.confirmationMessages.message),
approveCombination: approveCombinationLabel && approveCombinationKey ? { label: approveCombinationLabel, key: approveCombinationKey } : undefined,
approveCombination: approveCombinationLabel && approveCombinationKey ? { label: approveCombinationLabel, key: approveCombinationKey, arguments: approveCombination!.arguments } : undefined,
} : undefined,
invocationMessage: typeConvert.MarkdownString.fromStrict(result.invocationMessage),
pastTenseMessage: typeConvert.MarkdownString.fromStrict(result.pastTenseMessage),
Expand Down
Loading
Loading