From caebfcd98691f6b1d05d01b44e8b80da72a121b8 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Wed, 22 Apr 2026 01:54:37 +0200 Subject: [PATCH] feat(1.116): update patches (#2796) --- .nvmrc | 2 +- build.sh | 15 +- build/linux/package_bin.sh | 5 +- build/windows/package.sh | 2 +- patches/brand.patch | 318 +- patches/cli.patch | 25 +- patches/disable-copilot.patch | 171 +- patches/feat-experimental-font.patch | 240 +- patches/feat-gulp-build-tasks.patch | 55 + patches/feat-remove-copilot.patch | 5262 +++++++++++++++++ patches/fix-build-vsce.patch | 28 +- patches/fix-disable-esbuild.patch | 8 + patches/fix-keymap.patch | 38 +- patches/fix-policies.patch | 44 +- patches/insider/system-extensions.patch | 10 +- patches/linux/fix-build.patch | 283 +- ...lectron.patch => update-electron.patch.no} | 0 patches/use-github-pat.patch | 8 +- patches/version-1-update.patch | 70 +- patches/windows/appx.patch | 10 +- prepare_vscode.sh | 2 + product.json | 19 +- upstream/insider.json | 4 +- 23 files changed, 6019 insertions(+), 600 deletions(-) create mode 100644 patches/feat-gulp-build-tasks.patch create mode 100644 patches/feat-remove-copilot.patch create mode 100644 patches/fix-disable-esbuild.patch rename patches/{update-electron.patch => update-electron.patch.no} (100%) diff --git a/.nvmrc b/.nvmrc index 85e5027..32a2d7b 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.22.0 +22.22.1 diff --git a/build.sh b/build.sh index bd78e0f..84a75bc 100755 --- a/build.sh +++ b/build.sh @@ -13,14 +13,9 @@ if [[ "${SHOULD_BUILD}" == "yes" ]]; then cd vscode || { echo "'vscode' dir not found"; exit 1; } export NODE_OPTIONS="--max-old-space-size=8192" + export VSCODE_PUBLISH_COUNTER=1 - npm run monaco-compile-check - npm run valid-layers-check - - npm run gulp compile-build-without-mangling - npm run gulp compile-extension-media - npm run gulp compile-extensions-build - npm run gulp minify-vscode + npm run gulp vscode-min-prepack if [[ "${OS_NAME}" == "osx" ]]; then # remove win32 node modules @@ -30,7 +25,7 @@ if [[ "${SHOULD_BUILD}" == "yes" ]]; then npm run copy-policy-dto --prefix build node build/lib/policies/policyGenerator.ts build/lib/policies/policyData.jsonc darwin - npm run gulp "vscode-darwin-${VSCODE_ARCH}-min-ci" + npm run gulp "vscode-darwin-${VSCODE_ARCH}-min-packing" find "../VSCode-darwin-${VSCODE_ARCH}" -print0 | xargs -0 touch -c @@ -46,7 +41,7 @@ if [[ "${SHOULD_BUILD}" == "yes" ]]; then npm run copy-policy-dto --prefix build node build/lib/policies/policyGenerator.ts build/lib/policies/policyData.jsonc win32 - npm run gulp "vscode-win32-${VSCODE_ARCH}-min-ci" + npm run gulp "vscode-win32-${VSCODE_ARCH}-min-packing" if [[ "${VSCODE_ARCH}" != "x64" ]]; then SHOULD_BUILD_REH="no" @@ -67,7 +62,7 @@ if [[ "${SHOULD_BUILD}" == "yes" ]]; then npm run copy-policy-dto --prefix build node build/lib/policies/policyGenerator.ts build/lib/policies/policyData.jsonc linux - npm run gulp "vscode-linux-${VSCODE_ARCH}-min-ci" + npm run gulp "vscode-linux-${VSCODE_ARCH}-min-packing" find "../VSCode-linux-${VSCODE_ARCH}" -print0 | xargs -0 touch -c diff --git a/build/linux/package_bin.sh b/build/linux/package_bin.sh index 9258eae..8db1c93 100755 --- a/build/linux/package_bin.sh +++ b/build/linux/package_bin.sh @@ -7,6 +7,9 @@ if [[ "${CI_BUILD}" == "no" ]]; then exit 1 fi +npm -v +node -v + # include common functions . ./utils.sh @@ -136,7 +139,7 @@ find .build/extensions -type f -name '*.node' -print -delete npm run copy-policy-dto --prefix build node build/lib/policies/policyGenerator.ts build/lib/policies/policyData.jsonc linux -npm run gulp "vscode-linux-${VSCODE_ARCH}-min-ci" +npm run gulp "vscode-linux-${VSCODE_ARCH}-min-packing" if [[ -f "../build/linux/${VSCODE_ARCH}/ripgrep.sh" ]]; then bash "../build/linux/${VSCODE_ARCH}/ripgrep.sh" "../VSCode-linux-${VSCODE_ARCH}/resources/app/node_modules" diff --git a/build/windows/package.sh b/build/windows/package.sh index 4afa57f..a7204c1 100755 --- a/build/windows/package.sh +++ b/build/windows/package.sh @@ -31,7 +31,7 @@ find .build/extensions -type f -name '*.node' -print -delete npm run copy-policy-dto --prefix build node build/lib/policies/policyGenerator.ts build/lib/policies/policyData.jsonc win32 -npm run gulp "vscode-win32-${VSCODE_ARCH}-min-ci" +npm run gulp "vscode-win32-${VSCODE_ARCH}-min-packing" . ../build_cli.sh diff --git a/patches/brand.patch b/patches/brand.patch index 9417bf3..23adf9e 100644 --- a/patches/brand.patch +++ b/patches/brand.patch @@ -1,5 +1,5 @@ diff --git a/extensions/configuration-editing/src/configurationEditingMain.ts b/extensions/configuration-editing/src/configurationEditingMain.ts -index 2578270..99c8ca5 100644 +index 2578270c..99c8ca5b 100644 --- a/extensions/configuration-editing/src/configurationEditingMain.ts +++ b/extensions/configuration-editing/src/configurationEditingMain.ts @@ -54,4 +54,4 @@ function registerVariableCompletions(pattern: string): vscode.Disposable { @@ -10,7 +10,7 @@ index 2578270..99c8ca5 100644 + { label: 'workspaceFolderBasename', detail: vscode.l10n.t("The name of the folder opened in !!APP_NAME!! without any slashes (/)") }, { label: 'fileWorkspaceFolderBasename', detail: vscode.l10n.t("The current opened file workspace folder name without any slashes (/)") }, diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts -index 12b50f3..7cb0d1b 100644 +index 12b50f31..7cb0d1bd 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -123,3 +123,3 @@ export class SettingsDocument { @@ -19,7 +19,7 @@ index 12b50f3..7cb0d1b 100644 + completions.push(this.newSimpleCompletionItem(getText('appName'), range, vscode.l10n.t("e.g. !!APP_NAME!!"))); completions.push(this.newSimpleCompletionItem(getText('remoteName'), range, vscode.l10n.t("e.g. SSH"))); diff --git a/extensions/css-language-features/package.nls.json b/extensions/css-language-features/package.nls.json -index d3de224..94eea2f 100644 +index d3de2241..94eea2fb 100644 --- a/extensions/css-language-features/package.nls.json +++ b/extensions/css-language-features/package.nls.json @@ -4,4 +4,4 @@ @@ -45,7 +45,7 @@ index d3de224..94eea2f 100644 + "scss.completion.triggerPropertyValueCompletion.desc": "By default, !!APP_NAME!! triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.", "scss.completion.completePropertyWithSemicolon.desc": "Insert semicolon at end of line when completing CSS properties.", diff --git a/extensions/emmet/package.nls.json b/extensions/emmet/package.nls.json -index 683bcc7..312b4b2 100644 +index 683bcc7f..312b4b25 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -1,3 +1,3 @@ @@ -54,7 +54,7 @@ index 683bcc7..312b4b2 100644 + "description": "Emmet support for !!APP_NAME!!", "command.wrapWithAbbreviation": "Wrap with Abbreviation", diff --git a/extensions/extension-editing/src/constants.ts b/extensions/extension-editing/src/constants.ts -index 1be4d0e..647b147 100644 +index 1be4d0e1..647b1474 100644 --- a/extensions/extension-editing/src/constants.ts +++ b/extensions/extension-editing/src/constants.ts @@ -8,2 +8,2 @@ import { l10n } from 'vscode'; @@ -62,7 +62,7 @@ index 1be4d0e..647b147 100644 -export const redundantImplicitActivationEvent = l10n.t("This activation event can be removed as VS Code generates these automatically from your package.json contribution declarations."); +export const redundantImplicitActivationEvent = l10n.t("This activation event can be removed as !!APP_NAME!! generates these automatically from your package.json contribution declarations."); diff --git a/extensions/extension-editing/src/extensionLinter.ts b/extensions/extension-editing/src/extensionLinter.ts -index 6249500..6d89804 100644 +index 6249500e..6d89804e 100644 --- a/extensions/extension-editing/src/extensionLinter.ts +++ b/extensions/extension-editing/src/extensionLinter.ts @@ -34,3 +34,3 @@ const relativeUrlRequiresHttpsRepository = l10n.t("Relative image URLs require a @@ -71,7 +71,7 @@ index 6249500..6d89804 100644 +const apiProposalNotListed = l10n.t("This proposal cannot be used because for this extension the product defines a fixed set of API proposals. You can test your extension but before publishing you MUST reach out to the !!APP_NAME!! team."); diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json -index 147a75f..565109e 100644 +index 147a75f9..565109ec 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -259,3 +259,3 @@ @@ -195,7 +195,7 @@ index 147a75f..565109e 100644 + "view.workbench.learnMore": "To learn more about how to use Git and source control in !!APP_NAME!! [read our docs](https://aka.ms/vscode-scm)." } diff --git a/extensions/github/package.nls.json b/extensions/github/package.nls.json -index 0449759..70a7c7c 100644 +index 04497595..70a7c7ce 100644 --- a/extensions/github/package.nls.json +++ b/extensions/github/package.nls.json @@ -2,3 +2,3 @@ @@ -219,7 +219,7 @@ index 0449759..70a7c7c 100644 + "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for !!APP_NAME!!", "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" diff --git a/extensions/grunt/package.nls.json b/extensions/grunt/package.nls.json -index 789a579..12e230e 100644 +index 789a579c..12e230e8 100644 --- a/extensions/grunt/package.nls.json +++ b/extensions/grunt/package.nls.json @@ -1,4 +1,4 @@ @@ -230,7 +230,7 @@ index 789a579..12e230e 100644 + "displayName": "Grunt support for !!APP_NAME!!", "config.grunt.autoDetect": "Controls enablement of Grunt task detection. Grunt task detection can cause files in any open workspace to be executed.", diff --git a/extensions/html-language-features/client/src/htmlClient.ts b/extensions/html-language-features/client/src/htmlClient.ts -index 250b340..8f53898 100644 +index cfb91c1f..e19b59fe 100644 --- a/extensions/html-language-features/client/src/htmlClient.ts +++ b/extensions/html-language-features/client/src/htmlClient.ts @@ -109,3 +109,3 @@ export async function startClient(context: ExtensionContext, newLanguageClient: @@ -239,7 +239,7 @@ index 250b340..8f53898 100644 + const res = await window.showInformationMessage(l10n.t('!!APP_NAME!! now has built-in support for auto-renaming tags. Do you want to enable it?'), configure); if (res === configure) { diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json -index d839070..b3ea638 100644 +index d8390703..b3ea6389 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -3,3 +3,3 @@ @@ -253,7 +253,7 @@ index d839070..b3ea638 100644 + "html.trace.server.desc": "Traces the communication between !!APP_NAME!! and the HTML language server.", "html.validate.scripts": "Controls whether the built-in HTML language support validates embedded scripts.", diff --git a/extensions/html-language-features/schemas/package.schema.json b/extensions/html-language-features/schemas/package.schema.json -index 205143c..cc9f918 100644 +index 205143c3..cc9f918a 100644 --- a/extensions/html-language-features/schemas/package.schema.json +++ b/extensions/html-language-features/schemas/package.schema.json @@ -9,3 +9,3 @@ @@ -262,7 +262,7 @@ index 205143c..cc9f918 100644 + "markdownDescription": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-html-languageservice/blob/master/docs/customData.md).\n\n!!APP_NAME!! loads custom data on startup to enhance its HTML support for the custom HTML tags, attributes and attribute values you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.", "items": { diff --git a/extensions/jake/package.nls.json b/extensions/jake/package.nls.json -index e82030e..ac999f6 100644 +index e82030ef..ac999f61 100644 --- a/extensions/jake/package.nls.json +++ b/extensions/jake/package.nls.json @@ -1,4 +1,4 @@ @@ -273,7 +273,7 @@ index e82030e..ac999f6 100644 + "displayName": "Jake support for !!APP_NAME!!", "jake.taskDefinition.type.description": "The Jake task to customize.", diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json -index 30199b2..008aed7 100644 +index 30199b2b..008aed75 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -11,3 +11,3 @@ @@ -282,7 +282,7 @@ index 30199b2..008aed7 100644 + "json.tracing.desc": "Traces the communication between !!APP_NAME!! and the JSON language server.", "json.colorDecorators.enable.desc": "Enables or disables color decorators", diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json -index 45df470..0957c55 100644 +index 45df4704..0957c550 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -22,3 +22,3 @@ @@ -296,7 +296,7 @@ index 45df470..0957c55 100644 + "This setting is use the user drops or pastes image data into the editor. In this case, !!APP_NAME!! automatically creates a new image file in the workspace containing the dropped/pasted image.", "It's easier to explain this setting with an example. For example, let's say the setting value was:", diff --git a/extensions/media-preview/package.nls.json b/extensions/media-preview/package.nls.json -index 920ced7..755a166 100644 +index 920ced76..755a1669 100644 --- a/extensions/media-preview/package.nls.json +++ b/extensions/media-preview/package.nls.json @@ -2,3 +2,3 @@ @@ -305,7 +305,7 @@ index 920ced7..755a166 100644 + "description": "Provides !!APP_NAME!!'s built-in previews for images, audio, and video", "customEditor.audioPreview.displayName": "Audio Preview", diff --git a/extensions/media-preview/src/audioPreview.ts b/extensions/media-preview/src/audioPreview.ts -index 282d579..e3dfb4b 100644 +index 282d579b..e3dfb4be 100644 --- a/extensions/media-preview/src/audioPreview.ts +++ b/extensions/media-preview/src/audioPreview.ts @@ -83,3 +83,3 @@ class AudioPreview extends MediaPreview { @@ -314,7 +314,7 @@ index 282d579..e3dfb4b 100644 + ${vscode.l10n.t("Open file using !!APP_NAME!!'s standard text/binary editor?")} diff --git a/extensions/media-preview/src/imagePreview/index.ts b/extensions/media-preview/src/imagePreview/index.ts -index 6c2c8a7..064afc6 100644 +index 6c2c8a73..064afc6d 100644 --- a/extensions/media-preview/src/imagePreview/index.ts +++ b/extensions/media-preview/src/imagePreview/index.ts @@ -210,3 +210,3 @@ class ImagePreview extends MediaPreview { @@ -323,7 +323,7 @@ index 6c2c8a7..064afc6 100644 + ${vscode.l10n.t("Open file using !!APP_NAME!!'s standard text/binary editor?")} diff --git a/extensions/media-preview/src/videoPreview.ts b/extensions/media-preview/src/videoPreview.ts -index 1cb74c5..3f5f892 100644 +index 1cb74c58..3f5f8928 100644 --- a/extensions/media-preview/src/videoPreview.ts +++ b/extensions/media-preview/src/videoPreview.ts @@ -87,3 +87,3 @@ class VideoPreview extends MediaPreview { @@ -332,7 +332,7 @@ index 1cb74c5..3f5f892 100644 + ${vscode.l10n.t("Open file using !!APP_NAME!!'s standard text/binary editor?")} diff --git a/extensions/notebook-renderers/package.json b/extensions/notebook-renderers/package.json -index fad11bc..64e48ce 100644 +index fad11bc9..64e48ce4 100644 --- a/extensions/notebook-renderers/package.json +++ b/extensions/notebook-renderers/package.json @@ -22,3 +22,3 @@ @@ -341,7 +341,7 @@ index fad11bc..64e48ce 100644 + "displayName": "!!APP_NAME!! Builtin Notebook Output Renderer", "requiresMessaging": "never", diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json -index 1235a55..b647563 100644 +index 1235a551..b647563b 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -2,3 +2,3 @@ @@ -350,7 +350,7 @@ index 1235a55..b647563 100644 + "displayName": "NPM support for !!APP_NAME!!", "workspaceTrust": "This extension executes tasks, which require trust to run.", diff --git a/extensions/swift/syntaxes/swift.tmLanguage.json b/extensions/swift/syntaxes/swift.tmLanguage.json -index d52cabb..eb3a76c 100644 +index d52cabb8..eb3a76c1 100644 --- a/extensions/swift/syntaxes/swift.tmLanguage.json +++ b/extensions/swift/syntaxes/swift.tmLanguage.json @@ -260,3 +260,3 @@ @@ -359,7 +359,7 @@ index d52cabb..eb3a76c 100644 + "comment": "The simpler (?<=\\bProcess\\.|\\bCommandLine\\.) breaks !!APP_NAME!! / Atom, see https://github.com/textmate/swift.tmbundle/issues/29", "name": "support.variable.swift", diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json -index 48955c3..0ff7003 100644 +index 48955c38..0ff70032 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -136,5 +136,5 @@ @@ -406,7 +406,7 @@ index 48955c3..0ff7003 100644 + "walkthroughs.nodejsWelcome.learnMoreAboutJs.altText": "Learn more about JavaScript and Node.js in !!APP_NAME!!." } diff --git a/extensions/typescript-language-features/src/tsServer/versionManager.ts b/extensions/typescript-language-features/src/tsServer/versionManager.ts -index 8d99637..a8d9986 100644 +index 8d99637d..a8d99869 100644 --- a/extensions/typescript-language-features/src/tsServer/versionManager.ts +++ b/extensions/typescript-language-features/src/tsServer/versionManager.ts @@ -112,3 +112,3 @@ export class TypeScriptVersionManager extends Disposable { @@ -415,7 +415,7 @@ index 8d99637..a8d9986 100644 + : '') + vscode.l10n.t("Use !!APP_NAME!!'s Version"), description: bundledVersion.displayName, diff --git a/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts -index 12cb1cc..bfaa57e 100644 +index 12cb1cca..bfaa57ed 100644 --- a/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts +++ b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts @@ -70,3 +70,3 @@ export class DiskTypeScriptVersionProvider implements ITypeScriptVersionProvider @@ -424,16 +424,16 @@ index 12cb1cc..bfaa57e 100644 + vscode.window.showErrorMessage(vscode.l10n.t("!!APP_NAME!!\'s tsserver was deleted by another application such as a misbehaving virus detection tool. Please reinstall !!APP_NAME!!.")); throw new Error('Could not find bundled tsserver.js'); diff --git a/extensions/typescript-language-features/src/tsconfig.ts b/extensions/typescript-language-features/src/tsconfig.ts -index 9905fd5..62c2e3c 100644 +index 823985e9..f22a314e 100644 --- a/extensions/typescript-language-features/src/tsconfig.ts +++ b/extensions/typescript-language-features/src/tsconfig.ts -@@ -159,3 +159,3 @@ export async function openProjectConfigForFile( +@@ -146,3 +146,3 @@ export async function openProjectConfigForFile( vscode.window.showInformationMessage( - vscode.l10n.t("Please open a folder in VS Code to use a TypeScript or JavaScript project")); + vscode.l10n.t("Please open a folder in !!APP_NAME!! to use a TypeScript or JavaScript project")); return; diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts -index 507f9e6..067fb79 100644 +index ae18dff7..9d158d2b 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -660,3 +660,3 @@ export default class TypeScriptServiceClient extends Disposable implements IType @@ -452,7 +452,7 @@ index 507f9e6..067fb79 100644 + vscode.l10n.t("The JS/TS language service crashed.\nThis may be caused by a plugin contributed by one of these extensions: {0}.\nPlease try disabling these extensions before filing an issue against !!APP_NAME!!.", pluginExtensionList)); } else { diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json -index 0ba8a2d..db93abf 100644 +index 7403fed1..17c069a8 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -2,3 +2,3 @@ @@ -461,7 +461,7 @@ index 0ba8a2d..db93abf 100644 + "description": "API tests for !!APP_NAME!!", "version": "0.0.1", diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json -index 1abff3d..5d87461 100644 +index 1abff3d9..5d874614 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -2,3 +2,3 @@ @@ -470,7 +470,7 @@ index 1abff3d..5d87461 100644 + "description": "Colorize tests for !!APP_NAME!!", "version": "0.0.1", diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/14119.less b/extensions/vscode-colorize-tests/test/colorize-fixtures/14119.less -index a0006d8..132b67e 100644 +index a0006d85..132b67ea 100644 --- a/extensions/vscode-colorize-tests/test/colorize-fixtures/14119.less +++ b/extensions/vscode-colorize-tests/test/colorize-fixtures/14119.less @@ -1,2 +1,2 @@ @@ -478,16 +478,16 @@ index a0006d8..132b67e 100644 +#f(@hm: "broken highlighting in !!APP_NAME!!") { content: ""; diff --git a/extensions/vscode-colorize-tests/test/colorize-results/14119_less.json b/extensions/vscode-colorize-tests/test/colorize-results/14119_less.json -index 6680753..da1795a 100644 +index 313fe87c..24ba85c7 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/14119_less.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/14119_less.json -@@ -114,3 +114,3 @@ +@@ -130,3 +130,3 @@ { - "c": "broken highlighting in VS Code", + "c": "broken highlighting in !!APP_NAME!!", "t": "source.css.less meta.selector.less meta.group.less meta.property-value.less string.quoted.double.less", diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json -index 0990d7c..e34d7c4 100644 +index 0990d7c5..e34d7c4b 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -2,3 +2,3 @@ @@ -496,7 +496,7 @@ index 0990d7c..e34d7c4 100644 + "description": "Test resolver for !!APP_NAME!!", "version": "0.0.1", diff --git a/extensions/vscode-test-resolver/src/download.ts b/extensions/vscode-test-resolver/src/download.ts -index a351aa7..c32e3ef 100644 +index a351aa77..c32e3efd 100644 --- a/extensions/vscode-test-resolver/src/download.ts +++ b/extensions/vscode-test-resolver/src/download.ts @@ -32,3 +32,3 @@ async function downloadVSCodeServerArchive(updateUrl: string, commit: string, qu @@ -530,7 +530,7 @@ index a351aa7..c32e3ef 100644 + throw Error(`Failed to download and unzip !!APP_NAME!! ${quality} - ${commit}`); } diff --git a/extensions/vscode-test-resolver/src/extension.ts b/extensions/vscode-test-resolver/src/extension.ts -index c342647..17af7ae 100644 +index c342647e..17af7aea 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts @@ -180,3 +180,3 @@ export function activate(context: vscode.ExtensionContext) { @@ -539,7 +539,7 @@ index c342647..17af7ae 100644 + progress.report({ message: 'Installing !!APP_NAME!! Server' }); serverLocation = await downloadAndUnzipVSCodeServer(updateUrl, commit, quality, serverBin, m => outputChannel.appendLine(m)); diff --git a/src/main.ts b/src/main.ts -index 42f599c..1f90b59 100644 +index 42f599c9..1f90b59e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -413,3 +413,3 @@ function createDefaultArgvConfigSync(argvConfigPath: string): void { @@ -557,16 +557,16 @@ index 42f599c..1f90b59 100644 + ' // This can help in cases where you see rendering issues in !!APP_NAME!!.', ' // "disable-hardware-acceleration": true', diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts -index c2c70d3..44941e9 100644 +index e77eda91..e33f22ba 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts -@@ -550,3 +550,3 @@ export class CodeApplication extends Disposable { +@@ -552,3 +552,3 @@ export class CodeApplication extends Disposable { async startup(): Promise { - this.logService.debug('Starting VS Code'); + this.logService.debug('Starting !!APP_NAME!!'); this.logService.debug(`from: ${this.environmentMainService.appRoot}`); diff --git a/src/vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.ts b/src/vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.ts -index a200bf8..425936a 100644 +index a200bf81..425936a8 100644 --- a/src/vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.ts +++ b/src/vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.ts @@ -19,3 +19,3 @@ export class ToggleTabFocusModeAction extends Action2 { @@ -575,7 +575,7 @@ index a200bf8..425936a 100644 + title: nls.localize2({ key: 'toggle.tabMovesFocus', comment: ['Turn on/off use of tab key for moving focus around !!APP_NAME!!'] }, 'Toggle Tab Key Moves Focus'), precondition: undefined, diff --git a/src/vs/platform/contextkey/common/contextkeys.ts b/src/vs/platform/contextkey/common/contextkeys.ts -index c256dba..10a79c8 100644 +index c256dba0..10a79c82 100644 --- a/src/vs/platform/contextkey/common/contextkeys.ts +++ b/src/vs/platform/contextkey/common/contextkeys.ts @@ -19,3 +19,3 @@ export const IsMobileContext = new RawContextKey('isMobile', isMobile, @@ -584,7 +584,7 @@ index c256dba..10a79c8 100644 +export const ProductQualityContext = new RawContextKey('productQualityType', '', localize('productQualityType', "Quality type of !!APP_NAME!!")); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts -index cef0d3c..e6016ae 100644 +index 6eefd716..e6cafc5d 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -153,3 +153,3 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi @@ -592,18 +592,18 @@ index cef0d3c..e6016ae 100644 - throw new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extensionId, this.productService.version)); + throw new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with !!APP_NAME!! '{1}'.", extensionId, this.productService.version)); } -@@ -1065,3 +1065,3 @@ class InstallExtensionInProfileTask extends AbstractExtensionTask `'${p}'`).join(', '), diff --git a/src/vs/platform/externalTerminal/node/externalTerminalService.ts b/src/vs/platform/externalTerminal/node/externalTerminalService.ts -index e7cf3f5..4424cc7 100644 +index e7cf3f54..4424cc7f 100644 --- a/src/vs/platform/externalTerminal/node/externalTerminalService.ts +++ b/src/vs/platform/externalTerminal/node/externalTerminalService.ts @@ -17,3 +17,3 @@ import { ITerminalEnvironment } from '../../terminal/common/terminal.js'; @@ -624,7 +624,7 @@ index e7cf3f5..4424cc7 100644 +const TERMINAL_TITLE = nls.localize('console.title', "!!APP_NAME!! Console"); diff --git a/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts b/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts -index 27fd88b..ad97d7b 100644 +index 27fd88b4..ad97d7b4 100644 --- a/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts +++ b/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts @@ -339,3 +339,3 @@ const terminalPlatformConfiguration: IConfigurationNode = { @@ -633,25 +633,25 @@ index 27fd88b..ad97d7b 100644 + description: localize('terminal.integrated.inheritEnv', "Whether new shells should inherit their environment from !!APP_NAME!!, which may source a login shell to ensure $PATH and other development variables are initialized. This has no effect on Windows."), type: 'boolean', diff --git a/src/vs/platform/update/common/update.config.contribution.ts b/src/vs/platform/update/common/update.config.contribution.ts -index 53e3a78..888a549 100644 +index 6061e15b..69ca3707 100644 --- a/src/vs/platform/update/common/update.config.contribution.ts +++ b/src/vs/platform/update/common/update.config.contribution.ts -@@ -71,3 +71,3 @@ configurationRegistry.registerConfiguration({ +@@ -70,3 +70,3 @@ configurationRegistry.registerConfiguration({ title: localize('enableWindowsBackgroundUpdatesTitle', "Enable Background Updates"), - description: localize('enableWindowsBackgroundUpdates', "Enable to download and install new VS Code versions in the background."), + description: localize('enableWindowsBackgroundUpdates', "Enable to download and install new !!APP_NAME!! versions in the background."), included: isWindows && !isWeb diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts -index c943bca..d5c8506 100644 +index e13d1ba5..7c411624 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts -@@ -69,3 +69,3 @@ export type UpdateErrorClassification = { +@@ -74,3 +74,3 @@ export type UpdateErrorClassification = { messageHash: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The hash of the error message.' }; - comment: 'This is used to know how often VS Code updates have failed.'; + comment: 'This is used to know how often !!APP_NAME!! updates have failed.'; }; diff --git a/src/vs/server/node/server.cli.ts b/src/vs/server/node/server.cli.ts -index 58ff362..9bf7ed9 100644 +index 58ff362b..9bf7ed90 100644 --- a/src/vs/server/node/server.cli.ts +++ b/src/vs/server/node/server.cli.ts @@ -475,3 +475,3 @@ function asExtensionIdOrVSIX(inputs: string[] | undefined) { @@ -660,7 +660,7 @@ index 58ff362..9bf7ed9 100644 + console.error('Unable to connect to !!APP_NAME!! server: ' + message); console.error(err); diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts -index bfd8397..6181895 100644 +index 2028933f..01e243be 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -529,3 +529,3 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu @@ -669,7 +669,7 @@ index bfd8397..6181895 100644 + comment: 'Used to see which extensions are using the !!APP_NAME!! client id override'; extensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The extension id.' }; diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts -index 8337870..fdf02ac 100644 +index 83378707..fdf02ac9 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -43,3 +43,3 @@ const viewsContainerSchema: IJSONSchema = { @@ -678,7 +678,7 @@ index 8337870..fdf02ac 100644 + description: localize({ key: 'vscode.extension.contributes.views.containers.id', comment: ['Contribution refers to those that an extension contributes to !!APP_NAME!! through an extension/contribution point. '] }, "Unique id used to identify the container in which views can be contributed using 'views' contribution point"), type: 'string', diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts -index a04bbd0..202f130 100644 +index 2785bbd7..38e6fc5a 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -445,3 +445,3 @@ const newCommands: ApiCommand[] = [ @@ -687,7 +687,7 @@ index a04bbd0..202f130 100644 + ApiCommandArgument.String.with('viewId', 'Custom editor view id. This should be the viewType string for custom editors or the notebookType string for notebooks. Use \'default\' to use !!APP_NAME!!\'s default text editor'), new ApiCommandArgument('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts -index 92e874d..d59e726 100644 +index 92e874dc..d59e726e 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -464,4 +464,4 @@ export class ApiCommandArgument { @@ -698,7 +698,7 @@ index 92e874d..d59e726 100644 + static readonly TestProfile = new ApiCommandArgument('testProfile', 'A !!APP_NAME!! test profile', v => v instanceof extHostTypes.TestRunProfileBase, extHostTypeConverter.TestRunProfile.from); diff --git a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts -index 0d71384..ae8d169 100644 +index 0d713847..ae8d169b 100644 --- a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts +++ b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts @@ -364,3 +364,3 @@ suite('NotebookCell#Document', function () { @@ -707,7 +707,7 @@ index 0d71384..ae8d169 100644 + test('Opening a notebook results in !!APP_NAME!! firing the event onDidChangeActiveNotebookEditor twice #118470', function () { let count = 0; diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts -index 91f52f5..dbb0ad4 100644 +index 91f52f5d..dbb0ad40 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -688,3 +688,3 @@ class PolicyDiagnosticsAction extends Action2 { @@ -716,16 +716,16 @@ index 91f52f5..dbb0ad4 100644 + let content = '# !!APP_NAME!! Policy Diagnostics\n\n'; content += '*WARNING: This file may contain sensitive information.*\n\n'; diff --git a/src/vs/workbench/browser/actions/helpActions.ts b/src/vs/workbench/browser/actions/helpActions.ts -index 7e4fb7b..b459421 100644 +index 8251c4a4..b236d547 100644 --- a/src/vs/workbench/browser/actions/helpActions.ts +++ b/src/vs/workbench/browser/actions/helpActions.ts -@@ -163,3 +163,3 @@ class OpenNewsletterSignupUrlAction extends Action2 { +@@ -164,3 +164,3 @@ class OpenNewsletterSignupUrlAction extends Action2 { id: OpenNewsletterSignupUrlAction.ID, - title: localize2('newsletterSignup', 'Signup for the VS Code Newsletter'), + title: localize2('newsletterSignup', 'Signup for the !!APP_NAME!! Newsletter'), category: Categories.Help, diff --git a/src/vs/workbench/browser/web.factory.ts b/src/vs/workbench/browser/web.factory.ts -index e342f83..7c314e6 100644 +index e342f838..7c314e6b 100644 --- a/src/vs/workbench/browser/web.factory.ts +++ b/src/vs/workbench/browser/web.factory.ts @@ -35,3 +35,3 @@ export function create(domElement: HTMLElement, options: IWorkbenchConstructionO @@ -734,16 +734,16 @@ index e342f83..7c314e6 100644 + throw new Error('Unable to create the !!APP_NAME!! workbench more than once.'); } else { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts -index 058693c..9c3afe3 100644 +index bdc603ff..fbb18cfe 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts -@@ -806,3 +806,3 @@ const registry = Registry.as(ConfigurationExtensions.Con +@@ -807,3 +807,3 @@ const registry = Registry.as(ConfigurationExtensions.Con localize('profileName', "`${profileName}`: name of the profile in which the workspace is opened (e.g. Data Science (Profile)). Ignored if default profile is used."), - localize('appName', "`${appName}`: e.g. VS Code."), + localize('appName', "`${appName}`: e.g. !!APP_NAME!!."), localize('remoteName', "`${remoteName}`: e.g. SSH"), diff --git a/src/vs/workbench/common/contextkeys.ts b/src/vs/workbench/common/contextkeys.ts -index c034874..b0bb4a0 100644 +index 73e207b8..7019013c 100644 --- a/src/vs/workbench/common/contextkeys.ts +++ b/src/vs/workbench/common/contextkeys.ts @@ -41,3 +41,3 @@ export const EmbedderIdentifierContext = new RawContextKey(' @@ -752,29 +752,29 @@ index c034874..b0bb4a0 100644 +export const InAutomationContext = new RawContextKey('inAutomation', false, localize('inAutomation', "Whether !!APP_NAME!! is running under automation/smoke test")); diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts -index ae0171f..060350f 100644 +index 7e9c73f6..d576466a 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts -@@ -558,3 +558,3 @@ configurationRegistry.registerConfiguration({ +@@ -636,3 +636,3 @@ configurationRegistry.registerConfiguration({ nls.localize('chat.mcp.access.none', "No access to MCP servers."), - nls.localize('chat.mcp.access.registry', "Allows access to MCP servers installed from the registry that VS Code is connected to."), + nls.localize('chat.mcp.access.registry', "Allows access to MCP servers installed from the registry that !!APP_NAME!! is connected to."), nls.localize('chat.mcp.access.any', "Allow access to any installed MCP server.") -@@ -585,3 +585,3 @@ configurationRegistry.registerConfiguration({ +@@ -663,3 +663,3 @@ configurationRegistry.registerConfiguration({ { - key: 'chat.mcp.access.registry', value: nls.localize('chat.mcp.access.registry', "Allows access to MCP servers installed from the registry that VS Code is connected to."), + key: 'chat.mcp.access.registry', value: nls.localize('chat.mcp.access.registry', "Allows access to MCP servers installed from the registry that !!APP_NAME!! is connected to."), }, diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts -index 1936819..8f3bd30 100644 +index ffc5b0ec..e441a041 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts -@@ -119,3 +119,3 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation { +@@ -120,3 +120,3 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation { // Register VSCode agent - const { disposable: vscodeDisposable } = SetupAgent.doRegisterAgent(instantiationService, chatAgentService, 'setup.vscode', 'vscode', false, localize2('vscodeAgentDescription', "Ask questions about VS Code").value, ChatAgentLocation.Chat, ChatModeKind.Agent, context, controller); + const { disposable: vscodeDisposable } = SetupAgent.doRegisterAgent(instantiationService, chatAgentService, 'setup.vscode', 'vscode', false, localize2('vscodeAgentDescription', "Ask questions about !!APP_NAME!!").value, ChatAgentLocation.Chat, ChatModeKind.Agent, context, controller); disposables.add(vscodeDisposable); -@@ -136,4 +136,4 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation { +@@ -137,4 +137,4 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation { displayName: localize('setupToolDisplayName', "New Workspace"), - modelDescription: 'Scaffold a new workspace in VS Code', - userDescription: localize('setupToolsDescription', "Scaffold a new workspace in VS Code"), @@ -782,19 +782,34 @@ index 1936819..8f3bd30 100644 + userDescription: localize('setupToolsDescription', "Scaffold a new workspace in !!APP_NAME!!"), canBeReferencedInPrompt: true, diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptValidator.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptValidator.ts -index d814c0b..66237e0 100644 +index f351c7f8..8cf4cab5 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptValidator.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptValidator.ts -@@ -248,5 +248,5 @@ export class PromptValidator { +@@ -263,5 +263,5 @@ export class PromptValidator { if (validGithubCopilotAttributeNames.value.has(attribute.key)) { -- report(toMarker(localize('promptValidator.ignoredAttribute.vscode-agent', "Attribute '{0}' is ignored when running locally in VS Code.", attribute.key), attribute.range, MarkerSeverity.Info)); -+ report(toMarker(localize('promptValidator.ignoredAttribute.vscode-agent', "Attribute '{0}' is ignored when running locally in !!APP_NAME!!.", attribute.key), attribute.range, MarkerSeverity.Info)); +- report(toMarker(localize('promptValidator.ignoredAttribute.vscode-agent', "Attribute '{0}' is ignored when running locally in VS Code.", attribute.key), attribute.range, MarkerSeverity.Hint, [MarkerTag.Unnecessary])); ++ report(toMarker(localize('promptValidator.ignoredAttribute.vscode-agent', "Attribute '{0}' is ignored when running locally in !!APP_NAME!!.", attribute.key), attribute.range, MarkerSeverity.Hint, [MarkerTag.Unnecessary])); } else { -- report(toMarker(localize('promptValidator.unknownAttribute.vscode-agent', "Attribute '{0}' is not supported in VS Code agent files. Supported: {1}.", attribute.key, supportedNames.value), attribute.range, MarkerSeverity.Warning)); -+ report(toMarker(localize('promptValidator.unknownAttribute.vscode-agent', "Attribute '{0}' is not supported in !!APP_NAME!! agent files. Supported: {1}.", attribute.key, supportedNames.value), attribute.range, MarkerSeverity.Warning)); +- report(toMarker(localize('promptValidator.unknownAttribute.vscode-agent', "Attribute '{0}' is not supported in VS Code agent files. Supported: {1}.", attribute.key, supportedNames.value), attribute.range, MarkerSeverity.Hint, [MarkerTag.Unnecessary])); ++ report(toMarker(localize('promptValidator.unknownAttribute.vscode-agent', "Attribute '{0}' is not supported in !!APP_NAME!! agent files. Supported: {1}.", attribute.key, supportedNames.value), attribute.range, MarkerSeverity.Hint, [MarkerTag.Unnecessary])); } +@@ -271,3 +271,3 @@ export class PromptValidator { + if (target === Target.Claude) { +- report(toMarker(localize('promptValidator.unknownAttribute.rules', "Attribute '{0}' is not supported in rules files by VS Code agents. Supported: {1}.", attribute.key, supportedNames.value), attribute.range, MarkerSeverity.Hint, [MarkerTag.Unnecessary])); ++ report(toMarker(localize('promptValidator.unknownAttribute.rules', "Attribute '{0}' is not supported in rules files by !!APP_NAME!! agents. Supported: {1}.", attribute.key, supportedNames.value), attribute.range, MarkerSeverity.Hint, [MarkerTag.Unnecessary])); + } else { +@@ -277,3 +277,3 @@ export class PromptValidator { + case PromptsType.skill: +- report(toMarker(localize('promptValidator.unknownAttribute.skill', "Attribute '{0}' is not supported by VS Code agents. Supported: {1}.", attribute.key, supportedNames.value), attribute.range, MarkerSeverity.Hint, [MarkerTag.Unnecessary])); ++ report(toMarker(localize('promptValidator.unknownAttribute.skill', "Attribute '{0}' is not supported by !!APP_NAME!! agents. Supported: {1}.", attribute.key, supportedNames.value), attribute.range, MarkerSeverity.Hint, [MarkerTag.Unnecessary])); + break; +@@ -1095,3 +1095,3 @@ export function mapClaudeModels(claudeModelNames: readonly string[]): readonly s + /** +- * Maps Claude tool names to their VS Code tool equivalents. ++ * Maps Claude tool names to their !!APP_NAME!! tool equivalents. + */ diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts -index 0b9ced3..731d952 100644 +index 0b9ced32..731d9526 100644 --- a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -177,3 +177,3 @@ export class AdapterManager extends Disposable implements IAdapterManager { @@ -803,7 +818,7 @@ index 0b9ced3..731d952 100644 + description: nls.localize('debugServer', "For debug extension development only: if a port is specified !!APP_NAME!! tries to connect to a debug adapter running in server mode"), default: 4711 diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts -index 6e2b340..dfcfccb 100644 +index 69bec710..f83a14cc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -363,3 +363,3 @@ CommandsRegistry.registerCommand({ @@ -826,7 +841,7 @@ index 6e2b340..dfcfccb 100644 - 'description': localize('workbench.extensions.installExtension.option.donotSync', "When enabled, VS Code do not sync this extension when Settings Sync is on."), + 'description': localize('workbench.extensions.installExtension.option.donotSync', "When enabled, !!APP_NAME!! do not sync this extension when Settings Sync is on."), default: false -@@ -909,4 +909,4 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi +@@ -910,4 +910,4 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi Severity.Info, - vsixs.length > 1 ? localize('InstallVSIXs.successReload', "Completed installing extensions. Please reload Visual Studio Code to enable them.") - : localize('InstallVSIXAction.successReload', "Completed installing extension. Please reload Visual Studio Code to enable it."), @@ -834,7 +849,7 @@ index 6e2b340..dfcfccb 100644 + : localize('InstallVSIXAction.successReload', "Completed installing extension. Please reload VSCodium to enable it."), [{ diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts -index d1dacd0..035239a 100644 +index 75896f1c..debf353a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -109,3 +109,3 @@ export class PromptExtensionInstallFailureAction extends Action { @@ -852,13 +867,13 @@ index d1dacd0..035239a 100644 - alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension.displayName)); + alert(localize('uninstallExtensionComplete', "Please reload !!APP_NAME!! to complete the uninstallation of the extension {0}.", this.extension.displayName)); } catch (error) { -@@ -2598,3 +2598,3 @@ export class ExtensionStatusAction extends ExtensionAction { +@@ -2768,3 +2768,3 @@ export class ExtensionStatusAction extends ExtensionAction { const link = `[${localize('settings', "settings")}](${createCommandUri('workbench.action.openSettings', this.extension.deprecationInfo.settings.map(setting => `@id:${setting}`).join(' '))}})`; - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate settings tooltip', "This extension is deprecated as this functionality is now built-in to VS Code. Configure these {0} to use this functionality.", link)) }, true); + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate settings tooltip', "This extension is deprecated as this functionality is now built-in to !!APP_NAME!!. Configure these {0} to use this functionality.", link)) }, true); } else { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts -index de71064..d0035cc 100644 +index 58422886..fa26dc8a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -478,3 +478,3 @@ export class Extension implements IExtension { @@ -872,7 +887,7 @@ index de71064..d0035cc 100644 + return Promise.resolve(`Please check the [!!APP_NAME!! Release Notes](command:${ShowCurrentReleaseNotesActionId}) for changes to the built-in extensions.`); } diff --git a/src/vs/workbench/contrib/extensions/common/extensionsFileTemplate.ts b/src/vs/workbench/contrib/extensions/common/extensionsFileTemplate.ts -index 818e662..2d2ead7 100644 +index 818e6628..2d2ead7a 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionsFileTemplate.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsFileTemplate.ts @@ -29,3 +29,3 @@ export const ExtensionsConfigurationSchema: IJSONSchema = { @@ -886,7 +901,7 @@ index 818e662..2d2ead7 100644 + '\t// List of extensions recommended by !!APP_NAME!! that should not be recommended for users of this workspace.', '\t"unwantedRecommendations": [', diff --git a/src/vs/workbench/contrib/extensions/common/searchExtensionsTool.ts b/src/vs/workbench/contrib/extensions/common/searchExtensionsTool.ts -index 91541b6..ccb9414 100644 +index 91541b6a..ccb94143 100644 --- a/src/vs/workbench/contrib/extensions/common/searchExtensionsTool.ts +++ b/src/vs/workbench/contrib/extensions/common/searchExtensionsTool.ts @@ -23,3 +23,3 @@ export const SearchExtensionsToolData: IToolData = { @@ -895,7 +910,7 @@ index 91541b6..ccb9414 100644 + userDescription: localize('searchExtensionsTool.userDescription', 'Search for !!APP_NAME!! extensions'), source: ToolDataSource.Internal, diff --git a/src/vs/workbench/contrib/externalUriOpener/common/configuration.ts b/src/vs/workbench/contrib/externalUriOpener/common/configuration.ts -index f54ddfe..946de6b 100644 +index f54ddfe2..946de6be 100644 --- a/src/vs/workbench/contrib/externalUriOpener/common/configuration.ts +++ b/src/vs/workbench/contrib/externalUriOpener/common/configuration.ts @@ -57,3 +57,3 @@ export const externalUriOpenersConfigurationNode: IConfigurationNode = { @@ -904,7 +919,7 @@ index f54ddfe..946de6b 100644 + enumDescriptions: [nls.localize('externalUriOpeners.defaultId', "Open using !!APP_NAME!!'s standard opener.")], }, diff --git a/src/vs/workbench/contrib/localization/common/localization.contribution.ts b/src/vs/workbench/contrib/localization/common/localization.contribution.ts -index bd73995..61b7d12 100644 +index bd739953..61b7d124 100644 --- a/src/vs/workbench/contrib/localization/common/localization.contribution.ts +++ b/src/vs/workbench/contrib/localization/common/localization.contribution.ts @@ -58,5 +58,5 @@ export class BaseLocalizationWorkbenchContribution extends Disposable implements @@ -916,7 +931,7 @@ index bd73995..61b7d12 100644 + patternErrorMessage: localize('vscode.extension.contributes.localizations.translations.id.pattern', "Id should be `vscode` or in format `publisherId.extensionName` for translating !!APP_NAME!! or an extension respectively.") }, diff --git a/src/vs/workbench/contrib/localization/common/localizationsActions.ts b/src/vs/workbench/contrib/localization/common/localizationsActions.ts -index 050dde4..a8a61bd 100644 +index 050dde41..a8a61bd2 100644 --- a/src/vs/workbench/contrib/localization/common/localizationsActions.ts +++ b/src/vs/workbench/contrib/localization/common/localizationsActions.ts @@ -25,3 +25,3 @@ export class ConfigureDisplayLanguageAction extends Action2 { @@ -925,7 +940,7 @@ index 050dde4..a8a61bd 100644 + description: localize2('configureLocaleDescription', "Changes the locale of !!APP_NAME!! based on installed language packs. Common languages include French, Chinese, Spanish, Japanese, German, Korean, and more.") } diff --git a/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts b/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts -index 864cc4f..b0b431a 100644 +index dc231e44..c47eeb39 100644 --- a/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts +++ b/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts @@ -276,3 +276,3 @@ export class McpServersListView extends AbstractExtensionsListView = { @@ -1067,16 +1082,16 @@ index 132db6d..0dc7abe 100644 type: 'object', @@ -478,3 +478,3 @@ const terminalConfiguration: IStringDictionary = { restricted: true, -- markdownDescription: localize('terminal.integrated.windowsUseConptyDll', "Whether to use the experimental conpty.dll (v1.23.251008001) shipped with VS Code, instead of the one bundled with Windows."), -+ markdownDescription: localize('terminal.integrated.windowsUseConptyDll', "Whether to use the experimental conpty.dll (v1.23.251008001) shipped with !!APP_NAME!!, instead of the one bundled with Windows."), +- markdownDescription: localize('terminal.integrated.windowsUseConptyDll', "Whether to use the experimental conpty.dll (v1.25.260303002) shipped with VS Code, instead of the one bundled with Windows."), ++ markdownDescription: localize('terminal.integrated.windowsUseConptyDll', "Whether to use the experimental conpty.dll (v1.25.260303002) shipped with !!APP_NAME!!, instead of the one bundled with Windows."), type: 'boolean', -@@ -617,3 +617,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -608,3 +608,3 @@ const terminalConfiguration: IStringDictionary = { restricted: true, - markdownDescription: localize('terminal.integrated.shellIntegration.enabled', "Determines whether or not shell integration is auto-injected to support features like enhanced command tracking and current working directory detection. \n\nShell integration works by injecting the shell with a startup script. The script gives VS Code insight into what is happening within the terminal.\n\nSupported shells:\n\n- Linux/macOS: bash, fish, pwsh, zsh\n - Windows: pwsh, git bash\n\nThis setting applies only when terminals are created, so you will need to restart your terminals for it to take effect.\n\n Note that the script injection may not work if you have custom arguments defined in the terminal profile, have enabled {1}, have a [complex bash `PROMPT_COMMAND`](https://code.visualstudio.com/docs/editor/integrated-terminal#_complex-bash-promptcommand), or other unsupported setup. To disable decorations, see {0}", '`#terminal.integrated.shellIntegration.decorationsEnabled#`', '`#editor.accessibilitySupport#`'), + markdownDescription: localize('terminal.integrated.shellIntegration.enabled', "Determines whether or not shell integration is auto-injected to support features like enhanced command tracking and current working directory detection. \n\nShell integration works by injecting the shell with a startup script. The script gives !!APP_NAME!! insight into what is happening within the terminal.\n\nSupported shells:\n\n- Linux/macOS: bash, fish, pwsh, zsh\n - Windows: pwsh, git bash\n\nThis setting applies only when terminals are created, so you will need to restart your terminals for it to take effect.\n\n Note that the script injection may not work if you have custom arguments defined in the terminal profile, have enabled {1}, have a [complex bash `PROMPT_COMMAND`](https://code.visualstudio.com/docs/editor/integrated-terminal#_complex-bash-promptcommand), or other unsupported setup. To disable decorations, see {0}", '`#terminal.integrated.shellIntegration.decorationsEnabled#`', '`#editor.accessibilitySupport#`'), type: 'boolean', diff --git a/src/vs/workbench/contrib/terminalContrib/autoReplies/common/terminalAutoRepliesConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/autoReplies/common/terminalAutoRepliesConfiguration.ts -index dc20533..2d2d488 100644 +index dc20533b..2d2d4886 100644 --- a/src/vs/workbench/contrib/terminalContrib/autoReplies/common/terminalAutoRepliesConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/autoReplies/common/terminalAutoRepliesConfiguration.ts @@ -19,3 +19,3 @@ export const terminalAutoRepliesConfiguration: IStringDictionary commandService.executeCommand('workbench.extensions.installExtension', 'ms-vscode.vscode-speech'); diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts -index 36f7d09..e6b14db 100644 +index 2d6b6764..db7b70f5 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts -@@ -639,4 +639,4 @@ export class SwitchProductQualityContribution extends Disposable implements IWor +@@ -396,4 +396,4 @@ export class SwitchProductQualityContribution extends Disposable implements IWor detail: newQuality === 'insider' ? - nls.localize('relaunchDetailInsiders', "Press the reload button to switch to the Insiders version of VS Code.") : - nls.localize('relaunchDetailStable', "Press the reload button to switch to the Stable version of VS Code."), + nls.localize('relaunchDetailInsiders', "Press the reload button to switch to the Insiders version of !!APP_NAME!!.") : + nls.localize('relaunchDetailStable', "Press the reload button to switch to the Stable version of !!APP_NAME!!."), primaryButton: nls.localize({ key: 'reload', comment: ['&& denotes a mnemonic'] }, "&&Reload") -@@ -675,3 +675,3 @@ export class SwitchProductQualityContribution extends Disposable implements IWor +@@ -432,3 +432,3 @@ export class SwitchProductQualityContribution extends Disposable implements IWor message: nls.localize('selectSyncService.message', "Choose the settings sync service to use after changing the version"), - detail: nls.localize('selectSyncService.detail', "The Insiders version of VS Code will synchronize your settings, keybindings, extensions, snippets and UI State using separate insiders settings sync service by default."), + detail: nls.localize('selectSyncService.detail', "The Insiders version of !!APP_NAME!! will synchronize your settings, keybindings, extensions, snippets and UI State using separate insiders settings sync service by default."), buttons: [ diff --git a/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts b/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts -index 393c8c3..9268a6c 100644 +index 393c8c36..9268a6c2 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts @@ -55,3 +55,3 @@ function computeTrustedDomainContent(defaultTrustedDomains: string[], trustedDom @@ -1142,7 +1157,7 @@ index 393c8c3..9268a6c 100644 + content += `// By default, !!APP_NAME!! trusts "localhost".\n`; } diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts -index f6e28df..86b8e78 100644 +index 5e452010..9402651b 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts @@ -52,3 +52,3 @@ registerAction2(class extends Action2 { @@ -1156,7 +1171,7 @@ index f6e28df..86b8e78 100644 + localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the Welcome page, with content to aid in getting started with !!APP_NAME!! and extensions."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise. Note: This is only observed as a global configuration, it will be ignored if set in a workspace or folder configuration."), diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExtensionPoint.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExtensionPoint.ts -index 297598e..1fc5b45 100644 +index 297598ef..1fc5b45c 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExtensionPoint.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExtensionPoint.ts @@ -161,3 +161,3 @@ export const walkthroughsExtensionPoint = ExtensionsRegistry.registerExtensionPo @@ -1165,24 +1180,35 @@ index 297598e..1fc5b45 100644 + description: localize('walkthroughs.steps.completionEvents.onCommand', 'Check off step when a given command is executed anywhere in !!APP_NAME!!.'), body: 'onCommand:${1:commandId}' diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts -index 60f47c9..eb2c4b8 100644 +index 40e38cd8..7c0ff9dd 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts -@@ -211,13 +211,2 @@ export const startEntries: GettingStartedStartEntryContent = [ +@@ -200,24 +200,2 @@ export const startEntries: GettingStartedStartEntryContent = [ }, - { +- id: 'topLevelOpenTunnel', +- title: localize('gettingStarted.topLevelOpenTunnel.title', "Open Tunnel..."), +- description: localize('gettingStarted.topLevelOpenTunnel.description', "Connect to a remote machine through a Tunnel"), +- when: 'isWeb && showRemoteStartEntryInWeb', +- icon: Codicon.remote, +- content: { +- type: 'startEntry', +- command: 'command:workbench.action.remote.showWebStartEntryActions', +- } +- }, +- { - id: 'topLevelNewWorkspaceChat', - title: localize('gettingStarted.newWorkspaceChat.title', "Generate New Workspace..."), - description: localize('gettingStarted.newWorkspaceChat.description', "Chat to create a new workspace"), - icon: Codicon.chatSparkle, -- when: '!isWeb && !chatSetupHidden', +- when: '!isWeb && !chatSetupHidden && !chatSetupDisabledInWorkspace', - content: { - type: 'startEntry', - command: 'command:welcome.newWorkspaceChat', - } - }, ]; -@@ -226,26 +215,2 @@ const Button = (title: string, href: string) => `[${title}](${href})`; +@@ -226,26 +204,2 @@ const Button = (title: string, href: string) => `[${title}](${href})`; -const CopilotStepTitle = localize('gettingStarted.copilotSetup.title', "Use AI features with Copilot for free"); -const CopilotDescription = localize({ key: 'gettingStarted.copilotSetup.description', comment: ['{Locked="["}', '{Locked="]({0})"}'] }, "You can use [Copilot]({0}) to generate code across multiple files, fix errors, ask questions about your code, and much more using natural language.", defaultChat.documentationUrl ?? ''); @@ -1201,7 +1227,7 @@ index 60f47c9..eb2c4b8 100644 - id, - title: CopilotStepTitle, - description, -- when: `${when} && !chatSetupHidden`, +- when: `${when} && !chatSetupHidden && !chatSetupDisabledInWorkspace`, - media: { - type: 'svg', altText: 'VS Code Copilot multi file edits', path: 'multi-file-edits.svg' - }, @@ -1209,41 +1235,41 @@ index 60f47c9..eb2c4b8 100644 -} - export const walkthroughs: GettingStartedWalkthroughContent = [ -@@ -253,3 +218,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -253,3 +207,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ id: 'Setup', - title: localize('gettingStarted.setup.title', "Get started with VS Code"), + title: localize('gettingStarted.setup.title', "Get started with !!APP_NAME!!"), description: localize('gettingStarted.setup.description', "Customize your editor, learn the basics, and start coding"), -@@ -258,3 +223,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -258,3 +212,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ when: '!isWeb', - walkthroughPageTitle: localize('gettingStarted.setup.walkthroughPageTitle', 'Setup VS Code'), + walkthroughPageTitle: localize('gettingStarted.setup.walkthroughPageTitle', 'Setup !!APP_NAME!!'), next: 'Beginner', -@@ -263,6 +228,2 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -263,6 +217,2 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ steps: [ -- createCopilotSetupStep('CopilotSetupAnonymous', CopilotAnonymousButton, 'chatAnonymous && !chatSetupInstalled', true), +- createCopilotSetupStep('CopilotSetupAnonymous', CopilotAnonymousButton, 'chatAnonymous && !chatSetupCompleted', true), - createCopilotSetupStep('CopilotSetupSignedOut', CopilotSignedOutButton, 'chatEntitlementSignedOut && !chatAnonymous', false), -- createCopilotSetupStep('CopilotSetupComplete', CopilotCompleteButton, 'chatSetupInstalled && !chatSetupDisabled && (chatAnonymous || chatPlanPro || chatPlanProPlus || chatPlanBusiness || chatPlanEnterprise || chatPlanFree)', false), -- createCopilotSetupStep('CopilotSetupSignedIn', CopilotSignedInButton, '!chatEntitlementSignedOut && (!chatSetupInstalled || chatSetupDisabled || chatPlanCanSignUp)', false), +- createCopilotSetupStep('CopilotSetupComplete', CopilotCompleteButton, 'chatSetupCompleted && !chatSetupDisabled && (chatAnonymous || chatPlanPro || chatPlanProPlus || chatPlanBusiness || chatPlanEnterprise || chatPlanFree)', false), +- createCopilotSetupStep('CopilotSetupSignedIn', CopilotSignedInButton, '!chatEntitlementSignedOut && (!chatSetupCompleted || chatSetupDisabled || chatPlanCanSignUp)', false), { -@@ -280,4 +241,4 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -280,4 +230,4 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ title: localize('gettingStarted.videoTutorial.title', "Watch video tutorials"), - description: localize('gettingStarted.videoTutorial.description.interpolated', "Watch the first in a series of short & practical video tutorials for VS Code's key features.\n{0}", Button(localize('watch', "Watch Tutorial"), 'https://aka.ms/vscode-getting-started-video')), - media: { type: 'svg', altText: 'VS Code Settings', path: 'learn.svg' }, + description: localize('gettingStarted.videoTutorial.description.interpolated', "Watch the first in a series of short & practical video tutorials for !!APP_NAME!!'s key features.\n{0}", Button(localize('watch', "Watch Tutorial"), 'https://aka.ms/vscode-getting-started-video')), + media: { type: 'svg', altText: '!!APP_NAME!! Settings', path: 'learn.svg' }, } -@@ -289,3 +250,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -289,3 +239,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ id: 'SetupWeb', - title: localize('gettingStarted.setupWeb.title', "Get Started with VS Code for the Web"), + title: localize('gettingStarted.setupWeb.title', "Get Started with !!APP_NAME!! for the Web"), description: localize('gettingStarted.setupWeb.description', "Customize your editor, learn the basics, and start coding"), -@@ -295,3 +256,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -295,3 +245,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ next: 'Beginner', - walkthroughPageTitle: localize('gettingStarted.setupWeb.walkthroughPageTitle', 'Setup VS Code Web'), + walkthroughPageTitle: localize('gettingStarted.setupWeb.walkthroughPageTitle', 'Setup !!APP_NAME!! Web'), content: { -@@ -321,6 +282,6 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -321,6 +271,6 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ title: localize('gettingStarted.extensions.title', "Code with extensions"), - description: localize('gettingStarted.extensionsWeb.description.interpolated', "Extensions are VS Code's power-ups. A growing number are becoming available in the web.\n{0}", Button(localize('browsePopularWeb', "Browse Popular Web Extensions"), 'command:workbench.extensions.action.showPopularExtensions')), + description: localize('gettingStarted.extensionsWeb.description.interpolated', "Extensions are !!APP_NAME!!'s power-ups. A growing number are becoming available in the web.\n{0}", Button(localize('browsePopularWeb', "Browse Popular Web Extensions"), 'command:workbench.extensions.action.showPopularExtensions')), @@ -1252,7 +1278,7 @@ index 60f47c9..eb2c4b8 100644 - type: 'svg', altText: 'VS Code extension marketplace with featured language extensions', path: 'extensions-web.svg' + type: 'svg', altText: '!!APP_NAME!! extension marketplace with featured language extensions', path: 'extensions-web.svg' }, -@@ -336,12 +297,2 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -336,12 +286,2 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ }, - { - id: 'settingsSyncWeb', @@ -1265,42 +1291,42 @@ index 60f47c9..eb2c4b8 100644 - }, - }, { -@@ -349,3 +300,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -349,3 +289,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ title: localize('gettingStarted.commandPalette.title', "Unlock productivity with the Command Palette "), - description: localize('gettingStarted.commandPalette.description.interpolated', "Run commands without reaching for your mouse to accomplish any task in VS Code.\n{0}", Button(localize('commandPalette', "Open Command Palette"), 'command:workbench.action.showCommands')), + description: localize('gettingStarted.commandPalette.description.interpolated', "Run commands without reaching for your mouse to accomplish any task in !!APP_NAME!!.\n{0}", Button(localize('commandPalette', "Open Command Palette"), 'command:workbench.action.showCommands')), media: { type: 'svg', altText: 'Command Palette overlay for searching and executing commands.', path: 'commandPalette.svg' }, -@@ -355,3 +306,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -355,3 +295,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"), - description: localize('gettingStarted.setup.OpenFolderWeb.description.interpolated', "You're all set to start coding. You can open a local project or a remote repository to get your files into VS Code.\n{0}\n{1}", Button(localize('openFolder', "Open Folder"), 'command:workbench.action.addRootFolder'), Button(localize('openRepository', "Open Repository"), 'command:remoteHub.openRepository')), + description: localize('gettingStarted.setup.OpenFolderWeb.description.interpolated', "You're all set to start coding. You can open a local project or a remote repository to get your files into !!APP_NAME!!.\n{0}\n{1}", Button(localize('openFolder', "Open Folder"), 'command:workbench.action.addRootFolder'), Button(localize('openRepository', "Open Repository"), 'command:remoteHub.openRepository')), when: 'workspaceFolderCount == 0', -@@ -376,3 +327,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -376,3 +316,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ title: localize('gettingStarted.setupAccessibility.title', "Get Started with Accessibility Features"), - description: localize('gettingStarted.setupAccessibility.description', "Learn the tools and shortcuts that make VS Code accessible. Note that some actions are not actionable from within the context of the walkthrough."), + description: localize('gettingStarted.setupAccessibility.description', "Learn the tools and shortcuts that make !!APP_NAME!! accessible. Note that some actions are not actionable from within the context of the walkthrough."), isFeatured: true, -@@ -381,3 +332,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -381,3 +321,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ next: 'Setup', - walkthroughPageTitle: localize('gettingStarted.setupAccessibility.walkthroughPageTitle', 'Setup VS Code Accessibility'), + walkthroughPageTitle: localize('gettingStarted.setupAccessibility.walkthroughPageTitle', 'Setup !!APP_NAME!! Accessibility'), content: { -@@ -412,3 +363,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -412,3 +352,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ title: localize('gettingStarted.commandPaletteAccessibility.title', "Unlock productivity with the Command Palette "), - description: localize('gettingStarted.commandPaletteAccessibility.description.interpolated', "Run commands without reaching for your mouse to accomplish any task in VS Code.\n{0}", Button(localize('commandPalette', "Open Command Palette"), 'command:workbench.action.showCommands')), + description: localize('gettingStarted.commandPaletteAccessibility.description.interpolated', "Run commands without reaching for your mouse to accomplish any task in !!APP_NAME!!.\n{0}", Button(localize('commandPalette', "Open Command Palette"), 'command:workbench.action.showCommands')), media: { type: 'markdown', path: 'empty' }, -@@ -492,3 +443,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -492,3 +432,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ title: localize('gettingStarted.settings.title', "Tune your settings"), - description: localize('gettingStarted.settingsAndSync.description.interpolated', "Customize every aspect of VS Code and [sync](command:workbench.userDataSync.actions.turnOn) customizations across devices.\n{0}", Button(localize('tweakSettings', "Open Settings"), 'command:toSide:workbench.action.openSettings')), + description: localize('gettingStarted.settingsAndSync.description.interpolated', "Customize every aspect of !!APP_NAME!! and [sync](command:workbench.userDataSync.actions.turnOn) customizations across devices.\n{0}", Button(localize('tweakSettings', "Open Settings"), 'command:toSide:workbench.action.openSettings')), when: 'workspacePlatform != \'webworker\' && syncStatus != uninitialized', -@@ -496,3 +447,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -496,3 +436,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ media: { - type: 'svg', altText: 'VS Code Settings', path: 'settings.svg' + type: 'svg', altText: '!!APP_NAME!! Settings', path: 'settings.svg' }, -@@ -502,6 +453,6 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -502,6 +442,6 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ title: localize('gettingStarted.extensions.title', "Code with extensions"), - description: localize('gettingStarted.extensions.description.interpolated', "Extensions are VS Code's power-ups. They range from handy productivity hacks, expanding out-of-the-box features, to adding completely new capabilities.\n{0}", Button(localize('browsePopular', "Browse Popular Extensions"), 'command:workbench.extensions.action.showPopularExtensions')), + description: localize('gettingStarted.extensions.description.interpolated', "Extensions are !!APP_NAME!!'s power-ups. They range from handy productivity hacks, expanding out-of-the-box features, to adding completely new capabilities.\n{0}", Button(localize('browsePopular', "Browse Popular Extensions"), 'command:workbench.extensions.action.showPopularExtensions')), @@ -1310,7 +1336,7 @@ index 60f47c9..eb2c4b8 100644 + type: 'svg', altText: '!!APP_NAME!! extension marketplace with featured language extensions', path: 'extensions.svg' }, diff --git a/src/vs/workbench/contrib/welcomeWalkthrough/browser/editor/vs_code_editor_walkthrough.ts b/src/vs/workbench/contrib/welcomeWalkthrough/browser/editor/vs_code_editor_walkthrough.ts -index bdd30bf..317d11c 100644 +index bdd30bf2..317d11c8 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/editor/vs_code_editor_walkthrough.ts +++ b/src/vs/workbench/contrib/welcomeWalkthrough/browser/editor/vs_code_editor_walkthrough.ts @@ -13,3 +13,3 @@ export default function content(accessor: ServicesAccessor) { @@ -1334,7 +1360,7 @@ index bdd30bf..317d11c 100644 +Well if you have got this far then you will have touched on some of the editing features in !!APP_NAME!!. But don't stop now :) We have lots of additional [documentation](https://code.visualstudio.com/docs), [introductory videos](https://code.visualstudio.com/docs/getstarted/introvideos) and [tips and tricks](https://go.microsoft.com/fwlink/?linkid=852118) for the product that will help you learn how to use it. And while you are here, here are a few additional things you can try: - Open the Integrated Terminal by pressing kb(workbench.action.terminal.toggleTerminal), then see what's possible by [reviewing the terminal documentation](https://code.visualstudio.com/docs/editor/integrated-terminal) diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts -index 1159e4c..d8ebb47 100644 +index d1345355..93cae5fd 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -765,3 +765,3 @@ Registry.as(ConfigurationExtensions.Configuration) @@ -1348,21 +1374,21 @@ index 1159e4c..d8ebb47 100644 + markdownDescription: localize('workspace.trust.emptyWindow.description', "Controls whether or not the empty window is trusted by default within !!APP_NAME!!. When used with `#{0}#`, you can enable the full functionality of !!APP_NAME!! without prompting in an empty window.", WORKSPACE_TRUST_UNTRUSTED_FILES), tags: [WORKSPACE_TRUST_SETTING_TAG], diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts -index 4c3893a..9c3267a 100644 +index 928e7ea2..2b474fc4 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts -@@ -448,3 +448,3 @@ import product from '../../platform/product/common/product.js'; +@@ -449,3 +449,3 @@ import product from '../../platform/product/common/product.js'; type: 'boolean', - description: localize('argv.disableChromiumSandbox', "Disables the Chromium sandbox. This is useful when running VS Code as elevated on Linux and running under Applocker on Windows.") + description: localize('argv.disableChromiumSandbox', "Disables the Chromium sandbox. This is useful when running !!APP_NAME!! as elevated on Linux and running under Applocker on Windows.") }, -@@ -452,3 +452,3 @@ import product from '../../platform/product/common/product.js'; +@@ -453,3 +453,3 @@ import product from '../../platform/product/common/product.js'; type: 'boolean', - description: localize('argv.useInMemorySecretStorage', "Ensures that an in-memory store will be used for secret storage instead of using the OS's credential store. This is often used when running VS Code extension tests or when you're experiencing difficulties with the credential store.") + description: localize('argv.useInMemorySecretStorage', "Ensures that an in-memory store will be used for secret storage instead of using the OS's credential store. This is often used when running !!APP_NAME!! extension tests or when you're experiencing difficulties with the credential store.") }, diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts -index ea7f364..25c4fda 100644 +index e60991aa..bbea4f92 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -1044,3 +1044,3 @@ export class ExtensionManagementService extends CommontExtensionManagementServic @@ -1371,7 +1397,7 @@ index ea7f364..25c4fda 100644 + const productName = localize('!!APP_NAME!! for Web', "{0} for the Web", this.productService.nameLong); const virtualWorkspaceSupport = this.extensionManifestPropertiesService.getExtensionVirtualWorkspaceSupportType(manifest); diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts -index f9d57d5..73d4c66 100644 +index 09dd590e..c4877b48 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -181,3 +181,3 @@ export const schema: IJSONSchema = { @@ -1429,7 +1455,7 @@ index f9d57d5..73d4c66 100644 + description: nls.localize('vscode.extension.scripts.uninstall', 'Uninstall hook for !!APP_NAME!! extension. Script that gets executed when the extension is completely uninstalled from !!APP_NAME!! which is when !!APP_NAME!! is restarted (shutdown and start) after the extension is uninstalled. Only Node scripts are supported.'), type: 'string' diff --git a/src/vs/workbench/services/extensions/electron-browser/nativeExtensionService.ts b/src/vs/workbench/services/extensions/electron-browser/nativeExtensionService.ts -index 2b6104a..9d2dffd 100644 +index 2b6104a7..9d2dffd4 100644 --- a/src/vs/workbench/services/extensions/electron-browser/nativeExtensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/nativeExtensionService.ts @@ -167,3 +167,3 @@ export class NativeExtensionService extends AbstractExtensionService implements @@ -1438,7 +1464,7 @@ index 2b6104a..9d2dffd 100644 + label: nls.localize('relaunch', "Relaunch !!APP_NAME!!"), run: () => { diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts -index 43da461..c7d4149 100644 +index 43da4619..c7d4149e 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts @@ -199,3 +199,3 @@ export class UserDataProfileManagementService extends Disposable implements IUse diff --git a/patches/cli.patch b/patches/cli.patch index 7e81ed8..4e2d4ca 100644 --- a/patches/cli.patch +++ b/patches/cli.patch @@ -1,5 +1,5 @@ diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs -index d3f7db8..988024b 100644 +index d3a9a88a..7cf60f0b 100644 --- a/cli/src/commands/serve_web.rs +++ b/cli/src/commands/serve_web.rs @@ -756,3 +756,3 @@ impl ConnectionManager { @@ -13,10 +13,10 @@ index d3f7db8..988024b 100644 + .join(args.release.quality.server_entrypoint().unwrap()); diff --git a/cli/src/constants.rs b/cli/src/constants.rs -index 1e277a8..97f17d3 100644 +index 9e2b066d..974ee57c 100644 --- a/cli/src/constants.rs +++ b/cli/src/constants.rs -@@ -35,3 +35,6 @@ pub const DOCUMENTATION_URL: Option<&'static str> = option_env!("VSCODE_CLI_DOCU +@@ -37,3 +37,6 @@ pub const DOCUMENTATION_URL: Option<&'static str> = option_env!("VSCODE_CLI_DOCU pub const VSCODE_CLI_COMMIT: Option<&'static str> = option_env!("VSCODE_CLI_COMMIT"); -pub const VSCODE_CLI_UPDATE_ENDPOINT: Option<&'static str> = option_env!("VSCODE_CLI_UPDATE_URL"); +pub const VSCODE_CLI_UPDATE_ENDPOINT: Option<&'static str> = option_env!("VSCODE_CLI_UPDATE_ENDPOINT"); @@ -25,7 +25,7 @@ index 1e277a8..97f17d3 100644 +pub const VSCODE_CLI_BINARY_NAME: Option<&'static str> = option_env!("VSCODE_CLI_BINARY_NAME"); diff --git a/cli/src/options.rs b/cli/src/options.rs -index 7d152c0..c0f2fb2 100644 +index 7d152c0e..c0f2fb2e 100644 --- a/cli/src/options.rs +++ b/cli/src/options.rs @@ -9,3 +9,3 @@ use serde::{Deserialize, Serialize}; @@ -57,8 +57,17 @@ index 7d152c0..c0f2fb2 100644 - server_name + Ok(server_name) } +diff --git a/cli/src/tunnels/agent_host.rs b/cli/src/tunnels/agent_host.rs +index 9d1f240c..2e67da43 100644 +--- a/cli/src/tunnels/agent_host.rs ++++ b/cli/src/tunnels/agent_host.rs +@@ -162,3 +162,3 @@ impl AgentHostManager { + .join("bin") +- .join(release.quality.server_entrypoint()) ++ .join(release.quality.server_entrypoint().unwrap()) + }; diff --git a/cli/src/tunnels/code_server.rs b/cli/src/tunnels/code_server.rs -index bbabadc..b454d0e 100644 +index bbabadcf..b454d0ea 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs @@ -462,3 +462,3 @@ impl<'a> ServerBuilder<'a> { @@ -67,7 +76,7 @@ index bbabadc..b454d0e 100644 + .join(self.server_params.release.quality.server_entrypoint().unwrap()), &["--version"], diff --git a/cli/src/tunnels/paths.rs b/cli/src/tunnels/paths.rs -index 3d7d718..98529bc 100644 +index 3d7d718a..98529bc6 100644 --- a/cli/src/tunnels/paths.rs +++ b/cli/src/tunnels/paths.rs @@ -100,3 +100,3 @@ impl InstalledServer { @@ -76,7 +85,7 @@ index 3d7d718..98529bc 100644 + .join(self.quality.server_entrypoint().unwrap()) }, diff --git a/cli/src/update_service.rs b/cli/src/update_service.rs -index 55f1dad..3b7ef5c 100644 +index 55f1dadc..3b7ef5c4 100644 --- a/cli/src/update_service.rs +++ b/cli/src/update_service.rs @@ -10,3 +10,3 @@ use serde::{Deserialize, Serialize}; @@ -303,7 +312,7 @@ index 55f1dad..3b7ef5c 100644 - pub fn env_default() -> Option { diff --git a/extensions/tunnel-forwarding/src/extension.ts b/extensions/tunnel-forwarding/src/extension.ts -index 2f71999..e689f62 100644 +index 2f71999b..e689f628 100644 --- a/extensions/tunnel-forwarding/src/extension.ts +++ b/extensions/tunnel-forwarding/src/extension.ts @@ -37,3 +37,3 @@ if (process.env.VSCODE_FORWARDING_IS_DEV) { diff --git a/patches/disable-copilot.patch b/patches/disable-copilot.patch index 9211b7f..293bbfc 100644 --- a/patches/disable-copilot.patch +++ b/patches/disable-copilot.patch @@ -1,133 +1,140 @@ diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts -index 1998414..cdc533b 100644 +index f7d4917d..b62a8a43 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts -@@ -206,3 +206,4 @@ abstract class OpenChatGlobalAction extends Action2 { - ChatContextKeys.Setup.hidden.negate(), -- ChatContextKeys.Setup.disabled.negate() -+ ChatContextKeys.Setup.disabled.negate(), -+ ContextKeyExpr.has('config.chat.disableAIFeatures').negate() - ) -@@ -1142,3 +1143,3 @@ export function registerChatActions() { +@@ -1144,3 +1144,3 @@ export function registerChatActions() { precondition: ContextKeyExpr.and( - ChatContextKeys.Setup.installed, + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.disabled.negate(), -@@ -1715,3 +1716,4 @@ MenuRegistry.appendMenuItem(MenuId.EditorContext, { - ChatContextKeys.Setup.hidden.negate(), -- ChatContextKeys.Setup.disabled.negate() -+ ChatContextKeys.Setup.disabled.negate(), -+ ContextKeyExpr.has('config.chat.disableAIFeatures').negate() - ) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts -index b8c8e03..512e40f 100644 +index 04243ca9..5241910f 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts -@@ -314,3 +314,4 @@ class AttachSelectionToChatAction extends Action2 { +@@ -316,3 +316,4 @@ class AttachSelectionToChatAction extends Action2 { ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeUserData) - ) + ), + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ) diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts -index be62dda..7b5f1ed 100644 +index 7e9c73f6..03da6f20 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts -@@ -1237,3 +1237,3 @@ configurationRegistry.registerConfiguration({ +@@ -1420,3 +1420,3 @@ configurationRegistry.registerConfiguration({ description: nls.localize('chat.disableAIFeatures', "Disable and hide built-in AI features provided by GitHub Copilot, including chat and inline suggestions."), - default: false, + default: true, scope: ConfigurationScope.WINDOW diff --git a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts -index ddb5df4..7831288 100644 +index 8ac8f531..f56d3e96 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts -@@ -70,10 +70,9 @@ const chatViewDescriptor: IViewDescriptor = { - ctorDescriptor: new SyncDescriptor(ChatViewPane), -- when: ContextKeyExpr.or( -- ContextKeyExpr.or( -- ChatContextKeys.Setup.hidden, -- ChatContextKeys.Setup.disabled -- )?.negate(), -- ChatContextKeys.panelParticipantRegistered, -- ChatContextKeys.extensionInvalid -- ) -+ when: ContextKeyExpr.and( -+ ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), -+ ChatContextKeys.Setup.disabled.negate(), -+ ChatContextKeys.Setup.hidden.negate(), -+ ChatContextKeys.panelParticipantRegistered, -+ ChatContextKeys.extensionInvalid.negate() -+ ) - }; +@@ -72,2 +72,3 @@ const chatViewDescriptor: IViewDescriptor = { + ContextKeyExpr.and( ++ ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), + ChatContextKeys.Setup.hidden.negate(), diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupContributions.ts b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupContributions.ts -index 4a71579..f8b3e83 100644 +index 3fa75da3..b1832fcb 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupContributions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupContributions.ts -@@ -228,2 +228,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr - ChatContextKeys.Setup.untrusted, +@@ -232,8 +232,9 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr + f1: true, +- precondition: ContextKeyExpr.or( +- ChatContextKeys.Setup.hidden, +- ChatContextKeys.Setup.disabledInWorkspace, +- ChatContextKeys.Setup.untrusted, +- ChatContextKeys.Setup.completed.negate(), +- ChatContextKeys.Entitlement.canSignUp ++ precondition: ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), - ChatContextKeys.Setup.installed.negate(), -@@ -346,2 +347,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr - ChatContextKeys.Setup.hidden.negate(), ++ ChatContextKeys.Setup.hidden.negate(), ++ ChatContextKeys.Setup.disabledInWorkspace.negate(), ++ ChatContextKeys.Setup.untrusted.negate(), ++ ChatContextKeys.Setup.completed, ++ ChatContextKeys.Entitlement.canSignUp.negate() + ) +@@ -353,2 +354,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr + when: ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), - ChatContextKeys.Setup.installed.negate(), -@@ -518,2 +520,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr - ChatContextKeys.Setup.disabled.negate(), + ChatContextKeys.Setup.hidden.negate(), +@@ -385,2 +387,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr + when: ContextKeyExpr.and( ++ ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), + IsWebContext.negate(), +@@ -415,2 +418,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr + precondition: ContextKeyExpr.and( ++ ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), + ChatContextKeys.Setup.hidden.negate(), +@@ -472,2 +476,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr + precondition: ContextKeyExpr.and( ++ ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), + ChatContextKeys.Setup.hidden.negate(), +@@ -564,2 +569,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr + const internalGenerateCodeContext = ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), - ChatContextKeys.Setup.installed.negate(), -diff --git a/src/vs/workbench/contrib/chat/common/actions/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/actions/chatContextKeys.ts -index c8fc17b..fbd2afd 100644 ---- a/src/vs/workbench/contrib/chat/common/actions/chatContextKeys.ts -+++ b/src/vs/workbench/contrib/chat/common/actions/chatContextKeys.ts -@@ -163,3 +163,3 @@ export namespace ChatContextKeyExprs { - export const chatSetupTriggerContext = ContextKeyExpr.or( -- ChatContextKeys.Setup.installed.negate(), -+ ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), - ChatContextKeys.Entitlement.canSignUp + ChatContextKeys.Setup.hidden.negate(), +@@ -801,3 +807,7 @@ export class ChatTeardownContribution extends Disposable implements IWorkbenchCo + category: CHAT_CATEGORY, +- precondition: ContextKeyExpr.and(ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate()), ++ precondition: ContextKeyExpr.and( ++ ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ++ ChatContextKeys.Setup.hidden.negate(), ++ ChatContextKeys.Setup.disabledInWorkspace.negate() ++ ), + menu: { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts -index e9b4077..b33d6f2 100644 +index 2c244736..2f5023d8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts -@@ -133,3 +133,9 @@ MenuRegistry.appendMenuItem(MenuId.InlineChatEditorAffordance, { - order: 1, -- when: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasNonEmptySelection, CTX_INLINE_CHAT_FILE_BELONGS_TO_CHAT.negate(), ChatEntitlementContextKeys.Setup.hidden.negate()), -+ when: ContextKeyExpr.and( -+ EditorContextKeys.writable, -+ EditorContextKeys.hasNonEmptySelection, -+ CTX_INLINE_CHAT_FILE_BELONGS_TO_CHAT.negate(), -+ ChatEntitlementContextKeys.Setup.hidden.negate(), -+ ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), -+ ), - command: { +@@ -43,3 +43,4 @@ const inlineChatContextKey = ContextKeyExpr.and( + EditorContextKeys.writable, +- EditorContextKeys.editorSimpleInput.negate() ++ EditorContextKeys.editorSimpleInput.negate(), ++ ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), + ); diff --git a/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts b/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts -index 864cc4f..b877a8e 100644 +index dc231e44..633de730 100644 --- a/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts +++ b/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts +@@ -545,3 +545,3 @@ export class McpServersViewsContribution extends Disposable implements IWorkbenc + ctorDescriptor: new SyncDescriptor(McpServersListView, [{}]), +- when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext, ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate()), ++ when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext, ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate(), ContextKeyExpr.has('config.chat.disableAIFeatures').negate()), + weight: 40, @@ -554,3 +554,3 @@ export class McpServersViewsContribution extends Disposable implements IWorkbenc ctorDescriptor: new SyncDescriptor(DefaultBrowseMcpServersView, [{}]), -- when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext.toNegated(), ChatContextKeys.Setup.hidden.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyExpr.or(ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`), ProductQualityContext.notEqualsTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`))), -+ when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext.toNegated(), ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.hidden.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyExpr.or(ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`), ProductQualityContext.notEqualsTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`))), +- when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext.toNegated(), ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyExpr.or(ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`), ProductQualityContext.notEqualsTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`))), ++ when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext.toNegated(), ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyExpr.or(ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`), ProductQualityContext.notEqualsTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`)), ContextKeyExpr.has('config.chat.disableAIFeatures').negate()), weight: 40, +@@ -563,3 +563,3 @@ export class McpServersViewsContribution extends Disposable implements IWorkbenc + ctorDescriptor: new SyncDescriptor(McpServersListView, [{}]), +- when: ContextKeyExpr.and(SearchMcpServersContext, ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyExpr.or(ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`), ProductQualityContext.notEqualsTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`))), ++ when: ContextKeyExpr.and(SearchMcpServersContext, ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyExpr.or(ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`), ProductQualityContext.notEqualsTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`)), ContextKeyExpr.has('config.chat.disableAIFeatures').negate()), + }, @@ -569,3 +569,3 @@ export class McpServersViewsContribution extends Disposable implements IWorkbenc ctorDescriptor: new SyncDescriptor(DefaultBrowseMcpServersView, [{ showWelcome: true }]), -- when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext.toNegated(), ChatContextKeys.Setup.hidden.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`).negate(), ProductQualityContext.isEqualTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`).negate()), -+ when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext.toNegated(), ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.hidden.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`).negate(), ProductQualityContext.isEqualTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`).negate()), +- when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext.toNegated(), ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`).negate(), ProductQualityContext.isEqualTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`).negate()), ++ when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext.toNegated(), ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`).negate(), ProductQualityContext.isEqualTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`).negate(), ContextKeyExpr.has('config.chat.disableAIFeatures').negate()), weight: 40, +@@ -578,3 +578,3 @@ export class McpServersViewsContribution extends Disposable implements IWorkbenc + ctorDescriptor: new SyncDescriptor(McpServersListView, [{ showWelcome: true }]), +- when: ContextKeyExpr.and(SearchMcpServersContext, ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`).negate(), ProductQualityContext.isEqualTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`).negate()), ++ when: ContextKeyExpr.and(SearchMcpServersContext, ChatContextKeys.Setup.hidden.negate(), ChatContextKeys.Setup.disabledInWorkspace.negate(), McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`).negate(), ProductQualityContext.isEqualTo('stable'), ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`).negate(), ContextKeyExpr.has('config.chat.disableAIFeatures').negate()), + } diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts -index 8f2ea73..429e28f 100644 +index 6bdf7f36..3bfc7d19 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts -@@ -705,3 +705,3 @@ registerAction2(class extends Action2 { - ChatContextKeys.Setup.disabled.negate(), -- ChatContextKeys.Setup.installed.negate(), +@@ -703,2 +703,3 @@ registerAction2(class extends Action2 { + when: ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), - ContextKeyExpr.in(ResourceContextKey.Resource.key, 'git.mergeChanges'), + ChatContextKeys.Setup.hidden.negate(), diff --git a/src/vs/workbench/contrib/scm/browser/scmInput.ts b/src/vs/workbench/contrib/scm/browser/scmInput.ts -index a35d479..da5a449 100644 +index 6adf03c6..38a6d42c 100644 --- a/src/vs/workbench/contrib/scm/browser/scmInput.ts +++ b/src/vs/workbench/contrib/scm/browser/scmInput.ts -@@ -850,2 +850,3 @@ registerAction2(class extends Action2 { - ChatContextKeys.Setup.disabled.negate(), +@@ -848,2 +848,3 @@ registerAction2(class extends Action2 { + when: ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), - ChatContextKeys.Setup.installed.negate(), + ChatContextKeys.Setup.hidden.negate(), diff --git a/patches/feat-experimental-font.patch b/patches/feat-experimental-font.patch index 2352c04..2b68450 100644 --- a/patches/feat-experimental-font.patch +++ b/patches/feat-experimental-font.patch @@ -1,5 +1,5 @@ diff --git a/src/vs/base/browser/ui/actionbar/actionbar.css b/src/vs/base/browser/ui/actionbar/actionbar.css -index e9e55ad..0ce9147 100644 +index e9e55ad9..0ce9147c 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.css +++ b/src/vs/base/browser/ui/actionbar/actionbar.css @@ -127 +127,72 @@ @@ -77,10 +77,10 @@ index e9e55ad..0ce9147 100644 +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/button/button.css b/src/vs/base/browser/ui/button/button.css -index 8496f1b..964455a 100644 +index 0ec4cbb4..0fc77fae 100644 --- a/src/vs/base/browser/ui/button/button.css +++ b/src/vs/base/browser/ui/button/button.css -@@ -183 +183,43 @@ +@@ -184 +184,43 @@ } + + @@ -126,7 +126,7 @@ index 8496f1b..964455a 100644 +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.css b/src/vs/base/browser/ui/codicons/codicon/codicon.css -index d7f257d..af55bdd 100644 +index d7f257db..af55bddb 100644 --- a/src/vs/base/browser/ui/codicons/codicon/codicon.css +++ b/src/vs/base/browser/ui/codicons/codicon/codicon.css @@ -25 +25,7 @@ @@ -139,7 +139,7 @@ index d7f257d..af55bdd 100644 +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css -index d3dfd9a..cf59627 100644 +index d3dfd9a5..cf596272 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -119 +119,21 @@ @@ -166,7 +166,7 @@ index d3dfd9a..cf59627 100644 +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/inputbox/inputBox.css b/src/vs/base/browser/ui/inputbox/inputBox.css -index dc5e637..b580762 100644 +index dc5e637f..b580762b 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.css +++ b/src/vs/base/browser/ui/inputbox/inputBox.css @@ -107 +107,28 @@ @@ -200,7 +200,7 @@ index dc5e637..b580762 100644 +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/selectBox/selectBox.css b/src/vs/base/browser/ui/selectBox/selectBox.css -index fd4d00e..92ddc99 100644 +index fd4d00ea..92ddc996 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.css +++ b/src/vs/base/browser/ui/selectBox/selectBox.css @@ -35 +35,16 @@ @@ -222,7 +222,7 @@ index fd4d00e..92ddc99 100644 +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts -index b7cbca1..16f531c 100644 +index b7cbca15..16f531cb 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -8,2 +8,3 @@ import * as arrays from '../../../common/arrays.js'; @@ -235,7 +235,7 @@ index b7cbca1..16f531c 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/base/browser/ui/splitview/paneview.css b/src/vs/base/browser/ui/splitview/paneview.css -index 7bb4282..92c027a 100644 +index 7bb4282a..92c027a2 100644 --- a/src/vs/base/browser/ui/splitview/paneview.css +++ b/src/vs/base/browser/ui/splitview/paneview.css @@ -153 +153,38 @@ @@ -279,7 +279,7 @@ index 7bb4282..92c027a 100644 +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts -index fb2e1f4..3b1e23f 100644 +index fb2e1f40..3b1e23f2 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -21,2 +21,3 @@ import { IView, Sizing, SplitView } from './splitview.js'; @@ -302,7 +302,7 @@ index fb2e1f4..3b1e23f 100644 + const headerSize = this.headerSize; diff --git a/src/vs/base/browser/ui/toggle/toggle.css b/src/vs/base/browser/ui/toggle/toggle.css -index e2d206d..e69352c 100644 +index e2d206d3..e69352c8 100644 --- a/src/vs/base/browser/ui/toggle/toggle.css +++ b/src/vs/base/browser/ui/toggle/toggle.css @@ -69 +69,27 @@ @@ -335,7 +335,7 @@ index e2d206d..e69352c 100644 +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/toggle/toggle.ts b/src/vs/base/browser/ui/toggle/toggle.ts -index 0b2fcbb..6370e6f 100644 +index 0b2fcbbb..6370e6f7 100644 --- a/src/vs/base/browser/ui/toggle/toggle.ts +++ b/src/vs/base/browser/ui/toggle/toggle.ts @@ -5,2 +5,3 @@ @@ -350,7 +350,7 @@ index 0b2fcbb..6370e6f 100644 } diff --git a/src/vs/base/common/font.ts b/src/vs/base/common/font.ts new file mode 100644 -index 0000000..8b9689c +index 00000000..8b9689c7 --- /dev/null +++ b/src/vs/base/common/font.ts @@ -0,0 +1,187 @@ @@ -544,7 +544,7 @@ index 0000000..8b9689c \ No newline at end of file diff --git a/src/vs/base/test/common/font.test.ts b/src/vs/base/test/common/font.test.ts new file mode 100644 -index 0000000..62a49d5 +index 00000000..62a49d53 --- /dev/null +++ b/src/vs/base/test/common/font.test.ts @@ -0,0 +1,482 @@ @@ -1031,7 +1031,7 @@ index 0000000..62a49d5 + }); +}); diff --git a/src/vs/platform/quickinput/browser/tree/quickInputDelegate.ts b/src/vs/platform/quickinput/browser/tree/quickInputDelegate.ts -index 328285f..0735dfa 100644 +index 328285f0..0735dfa2 100644 --- a/src/vs/platform/quickinput/browser/tree/quickInputDelegate.ts +++ b/src/vs/platform/quickinput/browser/tree/quickInputDelegate.ts @@ -6,2 +6,3 @@ @@ -1044,7 +1044,7 @@ index 328285f..0735dfa 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css -index 0d6a2da..6127a14 100644 +index 1f6e583f..79401713 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -11,20 +11,20 @@ @@ -1089,7 +1089,7 @@ index 0d6a2da..6127a14 100644 + font-family: var(--vscode-workbench-font-family, var(--monaco-font)); + font-size: var(--vscode-workbench-font-size, 13px); -@@ -335 +337,41 @@ body { +@@ -356 +358,41 @@ body { } + + @@ -1133,33 +1133,33 @@ index 0d6a2da..6127a14 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts -index 0307cab..5e5f6f3 100644 +index 86c8b184..bfd30e64 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts -@@ -40,2 +40,3 @@ import { IViewsService } from '../../../services/views/common/viewsService.js'; +@@ -41,2 +41,3 @@ import { IViewsService } from '../../../services/views/common/viewsService.js'; import { SwitchCompositeViewAction } from '../compositeBarActions.js'; +import { FONT, getFontSize, updateActivityBarSize } from '../../../../base/common/font.js'; -@@ -45,2 +46,3 @@ export class ActivitybarPart extends Part { +@@ -46,2 +47,3 @@ export class ActivitybarPart extends Part { static readonly COMPACT_ACTION_HEIGHT = 32; + static readonly COMPACT_ACTION_HEIGHT_RATIO = 32/48; -@@ -48,2 +50,3 @@ export class ActivitybarPart extends Part { +@@ -49,2 +51,3 @@ export class ActivitybarPart extends Part { static readonly COMPACT_ACTIVITYBAR_WIDTH = 36; + static readonly COMPACT_ACTIVITYBAR_WIDTH_RATIO = 36/48; -@@ -51,2 +54,3 @@ export class ActivitybarPart extends Part { +@@ -52,2 +55,3 @@ export class ActivitybarPart extends Part { static readonly COMPACT_ICON_SIZE = 16; + static readonly COMPACT_ICON_SIZE_RATIO = 16/24; -@@ -58,4 +62,4 @@ export class ActivitybarPart extends Part { +@@ -59,4 +63,4 @@ export class ActivitybarPart extends Part { - get minimumWidth(): number { return this._isCompact ? ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH : ActivitybarPart.ACTIVITYBAR_WIDTH; } - get maximumWidth(): number { return this._isCompact ? ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH : ActivitybarPart.ACTIVITYBAR_WIDTH; } + get minimumWidth(): number { return this._isCompact ? FONT.activityBarSize48 * ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH_RATIO : FONT.activityBarSize48; } + get maximumWidth(): number { return this._isCompact ? FONT.activityBarSize48 * ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH_RATIO : FONT.activityBarSize48; } readonly minimumHeight: number = 0; -@@ -90,2 +94,11 @@ export class ActivitybarPart extends Part { +@@ -91,2 +95,11 @@ export class ActivitybarPart extends Part { })); + + this._register(configurationService.onDidChangeConfiguration(e => { @@ -1171,21 +1171,21 @@ index 0307cab..5e5f6f3 100644 + } + })); } -@@ -96,4 +109,4 @@ export class ActivitybarPart extends Part { +@@ -97,4 +110,4 @@ export class ActivitybarPart extends Part { this.element.style.setProperty('--activity-bar-width', `${this.minimumWidth}px`); - this.element.style.setProperty('--activity-bar-action-height', `${this._isCompact ? ActivitybarPart.COMPACT_ACTION_HEIGHT : ActivitybarPart.ACTION_HEIGHT}px`); - this.element.style.setProperty('--activity-bar-icon-size', `${this._isCompact ? ActivitybarPart.COMPACT_ICON_SIZE : ActivitybarPart.ICON_SIZE}px`); + this.element.style.setProperty('--activity-bar-action-height', `${this._isCompact ? FONT.activityBarSize32 : FONT.activityBarSize48}px`); + this.element.style.setProperty('--activity-bar-icon-size', `${this._isCompact ? FONT.activityBarSize : FONT.activityBarSize24}px`); } -@@ -153,2 +166,6 @@ export class ActivitybarPart extends Part { +@@ -154,2 +167,6 @@ export class ActivitybarPart extends Part { + // Apply font settings before show() so composite bar uses correct sizes + this.applyActivityBarFontFamily(parent); + this.applyActivityBarFontSize(parent); + this.updateCompactStyle(); -@@ -162,2 +179,34 @@ export class ActivitybarPart extends Part { +@@ -163,2 +180,34 @@ export class ActivitybarPart extends Part { + private applyActivityBarFontFamily(container?: HTMLElement): void { + const target = container ?? this.getContainer(); @@ -1221,7 +1221,7 @@ index 0307cab..5e5f6f3 100644 + getPinnedPaneCompositeIds(): string[] { diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css -index a40a351..51eb067 100644 +index a40a3515..51eb067d 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -230 +230,60 @@ @@ -1287,7 +1287,7 @@ index a40a351..51eb067 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css -index 568a721..b3d7e50 100644 +index 568a7212..b3d7e506 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css @@ -8,2 +8,4 @@ @@ -1306,7 +1306,7 @@ index 568a721..b3d7e50 100644 + height: calc(var(--vscode-workbench-activitybar-font-size) * 2.1875); } diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts -index d32082b..ad7e524 100644 +index d32082b4..ad7e524c 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts @@ -36,2 +36,4 @@ import { VisibleViewContainersTracker } from '../visibleViewContainersTracker.js @@ -1362,7 +1362,7 @@ index d32082b..ad7e524 100644 + this._onDidChange.fire(undefined); // Signal grid that size constraints changed } diff --git a/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css b/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css -index aec3de2..b0e1fd8 100644 +index aec3de2d..b0e1fd8f 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css +++ b/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css @@ -28,2 +28,8 @@ @@ -1375,7 +1375,7 @@ index aec3de2..b0e1fd8 100644 + .monaco-workbench .part.auxiliarybar > .title > .title-label { diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts -index b0a44e2..e711a80 100644 +index b0a44e2c..e711a809 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -48,2 +48,4 @@ import { MarkdownString } from '../../../../base/common/htmlContent.js'; @@ -1451,7 +1451,7 @@ index b0a44e2..e711a80 100644 + private get editorActionsEnabled(): boolean { diff --git a/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css -index 57ab8ca..72a328f 100644 +index 57ab8ca5..72a328fc 100644 --- a/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css @@ -9,2 +9,3 @@ @@ -1498,7 +1498,7 @@ index 57ab8ca..72a328f 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/media/editortitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/editortitlecontrol.css -index a24f761..6b15b9c 100644 +index a24f7613..6b15b9c2 100644 --- a/src/vs/workbench/browser/parts/editor/media/editortitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/editortitlecontrol.css @@ -47 +47,28 @@ @@ -1532,7 +1532,7 @@ index a24f761..6b15b9c 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css -index 4f9477d..b47fd85 100644 +index 4f9477d0..b47fd85b 100644 --- a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css @@ -176,4 +176,4 @@ @@ -1663,7 +1663,7 @@ index 4f9477d..b47fd85 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts -index b0befd9..7c25771 100644 +index b56047d6..d6e6c7ed 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -6,2 +6,3 @@ @@ -1697,7 +1697,7 @@ index b0befd9..7c25771 100644 + super(parent, editorPartsView, groupsView, groupView, tabsModel, contextMenuService, instantiationService, contextKeyService, keybindingService, notificationService, quickInputService, themeService, editorResolverService, hostService, configurationService); diff --git a/src/vs/workbench/browser/parts/media/paneCompositePart.css b/src/vs/workbench/browser/parts/media/paneCompositePart.css -index fe0f2ad..195267c 100644 +index fe0f2adf..195267cf 100644 --- a/src/vs/workbench/browser/parts/media/paneCompositePart.css +++ b/src/vs/workbench/browser/parts/media/paneCompositePart.css @@ -369 +369,119 @@ @@ -1822,7 +1822,7 @@ index fe0f2ad..195267c 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css -index e1c147d..63d0e10 100644 +index e1c147d8..63d0e109 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -10,2 +10,7 @@ @@ -1887,7 +1887,7 @@ index e1c147d..63d0e10 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts -index 9afcf59..a8d4c2e 100644 +index 9afcf596..a8d4c2e6 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -34,2 +34,3 @@ import { IConfigurationService } from '../../../../platform/configuration/common @@ -1942,7 +1942,7 @@ index 9afcf59..a8d4c2e 100644 + this._onDidChange.fire(undefined); // Signal grid that size constraints changed } diff --git a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css -index decb51a..d0db436 100644 +index decb51ab..d0db4363 100644 --- a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css +++ b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css @@ -15,3 +15,3 @@ @@ -1999,7 +1999,7 @@ index decb51a..d0db436 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts -index 101b9c6..970cdaa 100644 +index 101b9c6c..970cdaae 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -36,2 +36,3 @@ import { VisibleViewContainersTracker } from '../visibleViewContainersTracker.js @@ -2061,7 +2061,7 @@ index 101b9c6..970cdaa 100644 + private registerActions(): void { diff --git a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css -index 1f3b102..42ad22c 100644 +index 1f3b102e..42ad22c0 100644 --- a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css +++ b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css @@ -11,2 +11,3 @@ @@ -2141,7 +2141,7 @@ index 1f3b102..42ad22c 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts -index 18340a8..0a33ce0 100644 +index 18340a8a..0a33ce06 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -38,2 +38,4 @@ import { IView } from '../../../../base/browser/ui/grid/grid.js'; @@ -2237,7 +2237,7 @@ index 18340a8..0a33ce0 100644 + super(`workbench.parts.auxiliaryStatus.${id}`, instantiationService, themeService, contextService, storageService, layoutService, contextMenuService, contextKeyService, configurationService); } diff --git a/src/vs/workbench/browser/parts/views/media/paneviewlet.css b/src/vs/workbench/browser/parts/views/media/paneviewlet.css -index aca98de..5bf9bf7 100644 +index aca98deb..5bf9bf71 100644 --- a/src/vs/workbench/browser/parts/views/media/paneviewlet.css +++ b/src/vs/workbench/browser/parts/views/media/paneviewlet.css @@ -87 +87,30 @@ @@ -2273,7 +2273,7 @@ index aca98de..5bf9bf7 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts -index 1c9305b..6471a0d 100644 +index 1c9305bd..6471a0d3 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -79,2 +79,3 @@ import { IAccessibleViewInformationService } from '../../../services/accessibili @@ -2290,10 +2290,10 @@ index 1c9305b..6471a0d 100644 - static readonly ITEM_HEIGHT = 22; static readonly TREE_TEMPLATE_ID = 'treeExplorer'; diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts -index 058693c..ee81a59 100644 +index bdc603ff..4bc6d153 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts -@@ -702,2 +702,85 @@ const registry = Registry.as(ConfigurationExtensions.Con +@@ -698,2 +698,85 @@ const registry = Registry.as(ConfigurationExtensions.Con }, + 'workbench.experimental.fontFamily': { + type: 'string', @@ -2380,7 +2380,7 @@ index 058693c..ee81a59 100644 + }, 'workbench.settings.editor': { diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts -index 10e2c3e..74e3bbe 100644 +index 10e2c3ed..74e3bbe9 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -9,2 +9,3 @@ import { Event, Emitter, setGlobalLeakWarningThreshold } from '../../base/common @@ -2460,7 +2460,7 @@ index 10e2c3e..74e3bbe 100644 + this.updateFontSize(configurationService); diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts -index 6f58865..3ab0ee4 100644 +index 6f588651..3ab0ee4e 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts @@ -17,2 +17,3 @@ import { localize } from '../../../../nls.js'; @@ -2473,43 +2473,31 @@ index 6f58865..3ab0ee4 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts -index bcb46c6..0aef82b 100644 +index d4f277de..4f386424 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts -@@ -52,2 +52,3 @@ import { BugIndicatingError } from '../../../../../base/common/errors.js'; - import { ILogService } from '../../../../../platform/log/common/log.js'; +@@ -53,2 +53,3 @@ import { BugIndicatingError } from '../../../../../base/common/errors.js'; + import { compareIgnoreCase } from '../../../../../base/common/strings.js'; +import { FONT } from '../../../../../base/common/font.js'; -@@ -638,5 +639,2 @@ export class AgentSessionsListDelegate implements IListVirtualDelegate { - getHeight(element: SectionItem) { -- return 22; -+ return FONT.sidebarSize22; - } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatChangesSummaryPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatChangesSummaryPart.ts -index cc7bb15..f260d0e 100644 +index cc7bb156..f260d0e3 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatChangesSummaryPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatChangesSummaryPart.ts @@ -33,2 +33,3 @@ import { ResourcePool } from './chatCollections.js'; @@ -2522,7 +2510,7 @@ index cc7bb15..f260d0e 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMultiDiffContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMultiDiffContentPart.ts -index 15ae799..06a95d5 100644 +index 15ae7996..06a95d59 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMultiDiffContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatMultiDiffContentPart.ts @@ -36,2 +36,3 @@ import { ChatTreeItem } from '../../chat.js'; @@ -2535,7 +2523,7 @@ index 15ae799..06a95d5 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatReferencesContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatReferencesContentPart.ts -index 8493938..50a19e4 100644 +index 84939387..50a19e4f 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatReferencesContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatReferencesContentPart.ts @@ -50,2 +50,3 @@ import { IHoverService } from '../../../../../../platform/hover/browser/hover.js @@ -2548,20 +2536,20 @@ index 8493938..50a19e4 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTodoListWidget.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTodoListWidget.ts -index 619d00a..9edbd1b 100644 +index 8f0f2c48..52818d1d 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTodoListWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTodoListWidget.ts @@ -11,2 +11,3 @@ import { IListRenderer, IListVirtualDelegate } from '../../../../../../base/brow import { Codicon } from '../../../../../../base/common/codicons.js'; +import { FONT } from '../../../../../../base/common/font.js'; import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; -@@ -23,3 +24,3 @@ class TodoListDelegate implements IListVirtualDelegate { +@@ -24,3 +25,3 @@ class TodoListDelegate implements IListVirtualDelegate { getHeight(element: IChatTodo): number { - return 22; + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTreeContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTreeContentPart.ts -index 703940e..e0fa9eb 100644 +index 703940ed..e0fa9eb5 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTreeContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatTreeContentPart.ts @@ -12,2 +12,3 @@ import { IAsyncDataSource, ITreeNode } from '../../../../../../base/browser/ui/t @@ -2577,7 +2565,7 @@ index 703940e..e0fa9eb 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/media/chatViewPane.css b/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/media/chatViewPane.css -index 83799c1..b2d3db2 100644 +index 83799c15..b2d3db2c 100644 --- a/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/media/chatViewPane.css +++ b/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/media/chatViewPane.css @@ -169 +169,68 @@ @@ -2651,7 +2639,7 @@ index 83799c1..b2d3db2 100644 +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts -index c6298b3..8fcab5f 100644 +index c6298b30..8fcab5f3 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts @@ -15,2 +15,3 @@ import { safeIntl } from '../../../../../base/common/date.js'; @@ -2664,7 +2652,7 @@ index c6298b3..8fcab5f 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts -index cacba31..7785734 100644 +index cacba319..77857347 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -40,2 +40,3 @@ import { SelectionClipboardContributionID } from '../selectionClipboard.js'; @@ -2679,14 +2667,14 @@ index cacba31..7785734 100644 + lineHeight: FONT.sidebarSize20, wordWrap: 'off', diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts -index b5234b6..b36e465 100644 +index 5162553b..5adbed8f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -43,2 +43,3 @@ import { MarshalledCommentThread, MarshalledCommentThreadInternal } from '../../ import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { FONT } from '../../../../base/common/font.js'; -@@ -83,5 +84,5 @@ class CommentsModelVirtualDelegate implements IListVirtualDelegate { +@@ -1431,3 +1432,3 @@ class ListDelegate implements IListVirtualDelegate { getHeight(element: TestExplorerTreeElement) { - return element instanceof TestTreeErrorMessage ? 17 + 10 : 22; + return element instanceof TestTreeErrorMessage ? FONT.sidebarSize17 + 10 : FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts -index c7c9cc7..84510c2 100644 +index 7219c099..7c6bc96c 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -59,4 +59,3 @@ import { IHoverService, WorkbenchHoverDelegate } from '../../../../platform/hove @@ -3948,13 +3936,13 @@ index c7c9cc7..84510c2 100644 - pageSize = Math.max(20, Math.floor((this.tree?.renderHeight ?? 0 / ItemHeight) + (this.pageOnScroll ? 1 : -1))); + pageSize = Math.max(20, Math.floor((this.tree?.renderHeight ?? 0 / FONT.sidebarSize22) + (this.pageOnScroll ? 1 : -1))); } -@@ -1147,3 +1146,3 @@ export class TimelineListVirtualDelegate implements IListVirtualDelegate { + ); +- vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( ++ ++ const prepackTask = task.define(`vscode${dashed(minified)}-prepack`, task.series( + copyCodiconsTask, +@@ -763,2 +763,6 @@ BUILD_TARGETS.forEach(buildTarget => { + compileExtensionMediaBuildTask, ++ )); ++ gulp.task(prepackTask); ++ ++ const packingTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}-packing`, task.series( + writeISODate('out-build'), +@@ -767,5 +771,11 @@ BUILD_TARGETS.forEach(buildTarget => { + )); +- } else { ++ gulp.task(packingTask); ++ + vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( +- minified ? compileBuildWithManglingTask : compileBuildWithoutManglingTask, ++ prepackTask, ++ packingTask, ++ )); ++ } else { ++ const prepackTask = task.define(`vscode${dashed(minified)}-prepack`, task.series( ++ compileBuildWithoutManglingTask, + cleanExtensionsBuildTask, +@@ -775,4 +785,14 @@ BUILD_TARGETS.forEach(buildTarget => { + minified ? minifyVSCodeTask : bundleVSCodeTask, ++ )); ++ gulp.task(prepackTask); ++ ++ const packingTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}-packing`, task.series( + vscodeTaskCI + )); ++ gulp.task(packingTask); ++ ++ vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( ++ prepackTask, ++ packingTask, ++ )); + } diff --git a/patches/feat-remove-copilot.patch b/patches/feat-remove-copilot.patch new file mode 100644 index 0000000..62cf4a4 --- /dev/null +++ b/patches/feat-remove-copilot.patch @@ -0,0 +1,5262 @@ +diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts +index 2c44fc94..d85dadfe 100644 +--- a/build/gulpfile.reh.ts ++++ b/build/gulpfile.reh.ts +@@ -30,3 +30,3 @@ import rceditCallback from 'rcedit'; + import { compileBuildWithManglingTask } from './gulpfile.compile.ts'; +-import { cleanExtensionsBuildTask, compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileExtensionMediaBuildTask, compileCopilotExtensionBuildTask } from './gulpfile.extensions.ts'; ++import { cleanExtensionsBuildTask, compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileExtensionMediaBuildTask } from './gulpfile.extensions.ts'; + import { vscodeWebResourceIncludes, createVSCodeWebFileContentMapper } from './gulpfile.vscode.web.ts'; +@@ -36,3 +36,2 @@ import buildfile from './buildfile.ts'; + import { fetchUrls, fetchGithub } from './lib/fetch.ts'; +-import { getCopilotExcludeFilter, copyCopilotNativeDeps, prepareBuiltInCopilotExtensionShims } from './lib/copilot.ts'; + import jsonEditor from 'gulp-json-editor'; +@@ -346,3 +345,2 @@ function packageTask(type: string, platform: string, arch: string, sourceFolderN + .pipe(util.cleanNodeModules(path.join(import.meta.dirname, `.moduleignore.${process.platform}`))) +- .pipe(filter(getCopilotExcludeFilter(platform, arch))) + .pipe(jsFilter) +@@ -465,13 +463,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { + +-function copyCopilotNativeDepsTaskREH(platform: string, arch: string, destinationFolderName: string) { +- return async () => { +- const outputDir = path.join(BUILD_ROOT, destinationFolderName); +- const nodeModulesDir = path.join(outputDir, 'node_modules'); +- copyCopilotNativeDeps(platform, arch, nodeModulesDir); +- +- const builtInCopilotExtensionDir = path.join(outputDir, 'extensions', 'copilot'); +- prepareBuiltInCopilotExtensionShims(platform, arch, builtInCopilotExtensionDir, nodeModulesDir); +- }; +-} +- + /** +@@ -525,3 +512,2 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { + packageTask(type, platform, arch, sourceFolderName, destinationFolderName), +- copyCopilotNativeDepsTaskREH(platform, arch, destinationFolderName) + ]; +@@ -539,3 +525,2 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { + compileNonNativeExtensionsBuildTask, +- compileCopilotExtensionBuildTask, + compileExtensionMediaBuildTask, +diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts +index 93b5f711..d8084ab1 100644 +--- a/build/gulpfile.vscode.ts ++++ b/build/gulpfile.vscode.ts +@@ -31,5 +31,4 @@ import minimist from 'minimist'; + import { compileBuildWithoutManglingTask } from './gulpfile.compile.ts'; +-import { compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileAllExtensionsBuildTask, compileExtensionMediaBuildTask, cleanExtensionsBuildTask, compileCopilotExtensionBuildTask } from './gulpfile.extensions.ts'; ++import { compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileAllExtensionsBuildTask, compileExtensionMediaBuildTask, cleanExtensionsBuildTask } from './gulpfile.extensions.ts'; + import { copyCodiconsTask } from './lib/compilation.ts'; +-import { getCopilotExcludeFilter, copyCopilotNativeDeps, prepareBuiltInCopilotExtensionShims } from './lib/copilot.ts'; + import type { EmbeddedProductInfo } from './lib/embeddedType.ts'; +@@ -446,3 +445,2 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d + .pipe(util.cleanNodeModules(path.join(import.meta.dirname, `.moduleignore.${process.platform}`))) +- .pipe(filter(getCopilotExcludeFilter(platform, arch))) + .pipe(jsFilter) +@@ -453,3 +451,2 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d + '**/@vscode/ripgrep/bin/*', +- '**/@github/copilot-*/**', + '**/node-pty/build/Release/*', +@@ -691,20 +688,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { + +-function copyCopilotNativeDepsTask(platform: string, arch: string, destinationFolderName: string) { +- const outputDir = path.join(path.dirname(root), destinationFolderName); +- +- return async () => { +- // On Windows with win32VersionedUpdate, app resources live under a +- // commit-hash prefix: {output}/{commitHash}/resources/app/ +- const versionedResourcesFolder = util.getVersionedResourcesFolder(platform, commit!); +- const appBase = platform === 'darwin' +- ? path.join(outputDir, `${product.nameLong}.app`, 'Contents', 'Resources', 'app') +- : path.join(outputDir, versionedResourcesFolder, 'resources', 'app'); +- const appNodeModulesDir = path.join(appBase, 'node_modules'); +- copyCopilotNativeDeps(platform, arch, appNodeModulesDir); +- +- const builtInCopilotExtensionDir = path.join(appBase, 'extensions', 'copilot'); +- prepareBuiltInCopilotExtensionShims(platform, arch, builtInCopilotExtensionDir, appNodeModulesDir); +- }; +-} +- + const buildRoot = path.dirname(root); +@@ -734,3 +713,2 @@ BUILD_TARGETS.forEach(buildTarget => { + packageTask(platform, arch, sourceFolderName, destinationFolderName, opts), +- copyCopilotNativeDepsTask(platform, arch, destinationFolderName) + ]; +@@ -761,3 +739,2 @@ BUILD_TARGETS.forEach(buildTarget => { + compileNonNativeExtensionsBuildTask, +- compileCopilotExtensionBuildTask, + compileExtensionMediaBuildTask, +@@ -782,3 +759,2 @@ BUILD_TARGETS.forEach(buildTarget => { + compileNonNativeExtensionsBuildTask, +- compileCopilotExtensionBuildTask, + compileExtensionMediaBuildTask, +diff --git a/build/npm/dirs.ts b/build/npm/dirs.ts +index 74b5422b..db8df90a 100644 +--- a/build/npm/dirs.ts ++++ b/build/npm/dirs.ts +@@ -17,3 +17,2 @@ export const dirs = [ + 'extensions/configuration-editing', +- 'extensions/copilot', + 'extensions/css-language-features', +diff --git a/build/npm/postinstall.ts b/build/npm/postinstall.ts +index db659fa7..ee169a6f 100644 +--- a/build/npm/postinstall.ts ++++ b/build/npm/postinstall.ts +@@ -316,33 +316,2 @@ async function main() { + fs.writeFileSync(stateContentsFile, JSON.stringify(computeContents())); +- +- // Symlink .claude/ files to their canonical locations to test Claude agent harness +- const claudeDir = path.join(root, '.claude'); +- fs.mkdirSync(claudeDir, { recursive: true }); +- +- const claudeMdLink = path.join(claudeDir, 'CLAUDE.md'); +- const claudeMdLinkType = ensureAgentHarnessLink(path.join('..', '.github', 'copilot-instructions.md'), claudeMdLink); +- if (claudeMdLinkType !== 'existing') { +- log('.', `Created ${claudeMdLinkType} .claude/CLAUDE.md -> .github/copilot-instructions.md`); +- } +- +- const claudeSkillsLink = path.join(claudeDir, 'skills'); +- const claudeSkillsLinkType = ensureAgentHarnessLink(path.join('..', '.agents', 'skills'), claudeSkillsLink); +- if (claudeSkillsLinkType !== 'existing') { +- log('.', `Created ${claudeSkillsLinkType} .claude/skills -> .agents/skills`); +- } +- +- // Temporary: patch @github/copilot-sdk session.js to fix ESM import +- // (missing .js extension on vscode-jsonrpc/node). Fixed upstream in v0.1.32. +- // TODO: Remove once @github/copilot-sdk is updated to >=0.1.32 +- for (const dir of ['', 'remote']) { +- const sessionFile = path.join(root, dir, 'node_modules', '@github', 'copilot-sdk', 'dist', 'session.js'); +- if (fs.existsSync(sessionFile)) { +- const content = fs.readFileSync(sessionFile, 'utf8'); +- const patched = content.replace(/from "vscode-jsonrpc\/node"/g, 'from "vscode-jsonrpc/node.js"'); +- if (content !== patched) { +- fs.writeFileSync(sessionFile, patched); +- log(dir || '.', 'Patched @github/copilot-sdk session.js (vscode-jsonrpc ESM import fix)'); +- } +- } +- } + } +diff --git a/package-lock.json b/package-lock.json +index 75b98719..86572201 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -12,5 +12,2 @@ + "dependencies": { +- "@anthropic-ai/sandbox-runtime": "0.0.42", +- "@github/copilot": "^1.0.24", +- "@github/copilot-sdk": "^0.2.2", + "@microsoft/1ds-core-js": "^3.2.13", +@@ -188,31 +185,2 @@ + }, +- "node_modules/@anthropic-ai/sandbox-runtime": { +- "version": "0.0.42", +- "resolved": "https://registry.npmjs.org/@anthropic-ai/sandbox-runtime/-/sandbox-runtime-0.0.42.tgz", +- "integrity": "sha512-kJpuhU4hHMumeygIkKvNhscEsTtQK1sat1kZwhb6HLYBznwjMGOdnuBI/RM9HeFwxArn71/ciD2WJbxttXBMHw==", +- "license": "Apache-2.0", +- "dependencies": { +- "@pondwader/socks5-server": "^1.0.10", +- "@types/lodash-es": "^4.17.12", +- "commander": "^12.1.0", +- "lodash-es": "^4.17.23", +- "shell-quote": "^1.8.3", +- "zod": "^3.24.1" +- }, +- "bin": { +- "srt": "dist/cli.js" +- }, +- "engines": { +- "node": ">=18.0.0" +- } +- }, +- "node_modules/@anthropic-ai/sandbox-runtime/node_modules/commander": { +- "version": "12.1.0", +- "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", +- "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", +- "license": "MIT", +- "engines": { +- "node": ">=18" +- } +- }, + "node_modules/@appium/logger": { +@@ -1073,138 +1041,2 @@ + }, +- "node_modules/@github/copilot": { +- "version": "1.0.24", +- "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.24.tgz", +- "integrity": "sha512-/nZ2GwhaGq0HeI3W+6LE0JGw25/bipC6tYVa+oQ5tIvAafBazuNt10CXkeaor+u9oBWLZtPbdTyAzE2tjy9NpQ==", +- "license": "SEE LICENSE IN LICENSE.md", +- "bin": { +- "copilot": "npm-loader.js" +- }, +- "optionalDependencies": { +- "@github/copilot-darwin-arm64": "1.0.24", +- "@github/copilot-darwin-x64": "1.0.24", +- "@github/copilot-linux-arm64": "1.0.24", +- "@github/copilot-linux-x64": "1.0.24", +- "@github/copilot-win32-arm64": "1.0.24", +- "@github/copilot-win32-x64": "1.0.24" +- } +- }, +- "node_modules/@github/copilot-darwin-arm64": { +- "version": "1.0.24", +- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.24.tgz", +- "integrity": "sha512-lejn6KV+09rZEICX3nRx9a58DQFQ2kK3NJ3EICfVLngUCWIUmwn1BLezjeTQc9YNasHltA1hulvfsWqX+VjlMw==", +- "cpu": [ +- "arm64" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "darwin" +- ], +- "bin": { +- "copilot-darwin-arm64": "copilot" +- } +- }, +- "node_modules/@github/copilot-darwin-x64": { +- "version": "1.0.24", +- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.24.tgz", +- "integrity": "sha512-r2F3keTvr4Bunz3V+waRAvsHgqsVQGyIZFBebsNPWxBX1eh3IXgtBqxCR7vXTFyZonQ8VaiJH3YYEfAhyKsk9g==", +- "cpu": [ +- "x64" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "darwin" +- ], +- "bin": { +- "copilot-darwin-x64": "copilot" +- } +- }, +- "node_modules/@github/copilot-linux-arm64": { +- "version": "1.0.24", +- "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.24.tgz", +- "integrity": "sha512-B3oANXKKKLhnKYozXa/W+DxfCQAHJCs0QKR5rBwNrwJbf656twNgALSxWTSJk+1rEP6MrHCswUAcwjwZL7Q+FQ==", +- "cpu": [ +- "arm64" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linux-arm64": "copilot" +- } +- }, +- "node_modules/@github/copilot-linux-x64": { +- "version": "1.0.24", +- "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.24.tgz", +- "integrity": "sha512-NGTldizY54B+4Sfhu/GWoEQNMwqqUNgMwbSgBshFv+Hqy1EwuvNWKVov1Y0Vzhp4qAHc6ZxBk/OPIW8Ato9FUg==", +- "cpu": [ +- "x64" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linux-x64": "copilot" +- } +- }, +- "node_modules/@github/copilot-sdk": { +- "version": "0.2.2", +- "resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-0.2.2.tgz", +- "integrity": "sha512-VZCqS08YlUM90bUKJ7VLeIxgTTEHtfXBo84T1IUMNvXRREX2csjPH6Z+CPw3S2468RcCLvzBXcc9LtJJTLIWFw==", +- "license": "MIT", +- "dependencies": { +- "@github/copilot": "^1.0.21", +- "vscode-jsonrpc": "^8.2.1", +- "zod": "^4.3.6" +- }, +- "engines": { +- "node": ">=20.0.0" +- } +- }, +- "node_modules/@github/copilot-sdk/node_modules/zod": { +- "version": "4.3.6", +- "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", +- "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", +- "license": "MIT", +- "funding": { +- "url": "https://github.com/sponsors/colinhacks" +- } +- }, +- "node_modules/@github/copilot-win32-arm64": { +- "version": "1.0.24", +- "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.24.tgz", +- "integrity": "sha512-/pd/kgef7/HIIg1SQq4q8fext39pDSC44jHB10KkhfgG1WaDFhQbc/aSSMQfxeldkRbQh6VANp8WtGQdwtMCBA==", +- "cpu": [ +- "arm64" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "win32" +- ], +- "bin": { +- "copilot-win32-arm64": "copilot.exe" +- } +- }, +- "node_modules/@github/copilot-win32-x64": { +- "version": "1.0.24", +- "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.24.tgz", +- "integrity": "sha512-RDvOiSvyEJwELqErwANJTrdRuMIHkwPE4QK7Le7WsmaSKxiuS4H1Pa8Yxnd2FWrMsCHEbase23GJlymbnGYLXQ==", +- "cpu": [ +- "x64" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "win32" +- ], +- "bin": { +- "copilot-win32-x64": "copilot.exe" +- } +- }, + "node_modules/@gulp-sourcemaps/identity-map": { +@@ -2450,8 +2282,2 @@ + }, +- "node_modules/@pondwader/socks5-server": { +- "version": "1.0.10", +- "resolved": "https://registry.npmjs.org/@pondwader/socks5-server/-/socks5-server-1.0.10.tgz", +- "integrity": "sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg==", +- "license": "MIT" +- }, + "node_modules/@promptbook/utils": { +@@ -2778,17 +2604,2 @@ + }, +- "node_modules/@types/lodash": { +- "version": "4.17.23", +- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.23.tgz", +- "integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==", +- "license": "MIT" +- }, +- "node_modules/@types/lodash-es": { +- "version": "4.17.12", +- "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", +- "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", +- "license": "MIT", +- "dependencies": { +- "@types/lodash": "*" +- } +- }, + "node_modules/@types/minimatch": { +@@ -13284,8 +13095,2 @@ + }, +- "node_modules/lodash-es": { +- "version": "4.18.1", +- "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", +- "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", +- "license": "MIT" +- }, + "node_modules/lodash.camelcase": { +@@ -17223,2 +17028,3 @@ + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", ++ "dev": true, + "license": "MIT", +@@ -19610,11 +19416,2 @@ + }, +- "node_modules/vscode-jsonrpc": { +- "version": "8.2.1", +- "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", +- "integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==", +- "license": "MIT", +- "engines": { +- "node": ">=14.0.0" +- } +- }, + "node_modules/vscode-oniguruma": { +@@ -20328,2 +20125,3 @@ + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", ++ "dev": true, + "license": "MIT", +diff --git a/package.json b/package.json +index b925a690..86fdd1f6 100644 +--- a/package.json ++++ b/package.json +@@ -23,3 +23,3 @@ + "compile-check-ts-native": "tsgo --project ./src/tsconfig.json --noEmit --skipLibCheck", +- "watch": "npm-run-all2 -lp watch-client-transpile watch-client watch-extensions watch-copilot", ++ "watch": "npm-run-all2 -lp watch-client-transpile watch-client watch-extensions", + "watchd": "deemon npm run watch", +@@ -40,5 +40,2 @@ + "kill-watch-extensionsd": "deemon --kill npm run watch-extensions", +- "watch-copilot": "npm --prefix extensions/copilot run watch", +- "watch-copilotd": "deemon npm run watch-copilot", +- "kill-watch-copilotd": "deemon --kill npm run watch-copilot", + "precommit": "node --experimental-strip-types build/hygiene.ts", +@@ -81,4 +78,2 @@ + "perf": "node scripts/code-perf.js", +- "copilot:setup": "npm --prefix extensions/copilot run setup", +- "copilot:get_token": "npm --prefix extensions/copilot run get_token", + "update-build-ts-version": "npm install -D typescript@next && npm install -D @typescript/native-preview && (cd build && npm run typecheck)", +@@ -89,5 +84,2 @@ + "dependencies": { +- "@anthropic-ai/sandbox-runtime": "0.0.42", +- "@github/copilot": "^1.0.24", +- "@github/copilot-sdk": "^0.2.2", + "@microsoft/1ds-core-js": "^3.2.13", +diff --git a/src/vs/platform/agentHost/node/agentHostMain.ts b/src/vs/platform/agentHost/node/agentHostMain.ts +index d6b6d42f..c891d99c 100644 +--- a/src/vs/platform/agentHost/node/agentHostMain.ts ++++ b/src/vs/platform/agentHost/node/agentHostMain.ts +@@ -16,3 +16,2 @@ import { AgentService } from './agentService.js'; + import { IAgentHostTerminalManager } from './agentHostTerminalManager.js'; +-import { CopilotAgent } from './copilot/copilotAgent.js'; + import { ProtocolServerHandler } from './protocolServerHandler.js'; +@@ -33,3 +32,2 @@ import { DiskFileSystemProvider } from '../../files/node/diskFileSystemProvider. + import { Schemas } from '../../../base/common/network.js'; +-import { InstantiationService } from '../../instantiation/common/instantiationService.js'; + import { ServiceCollection } from '../../instantiation/common/serviceCollection.js'; +@@ -93,4 +91,2 @@ function startAgentHost(): void { + diServices.set(IAgentHostTerminalManager, agentService.terminalManager); +- const instantiationService = new InstantiationService(diServices); +- agentService.registerProvider(instantiationService.createInstance(CopilotAgent)); + } catch (err) { +diff --git a/src/vs/platform/agentHost/node/agentHostServerMain.ts b/src/vs/platform/agentHost/node/agentHostServerMain.ts +index 2234c1de..767b4267 100644 +--- a/src/vs/platform/agentHost/node/agentHostServerMain.ts ++++ b/src/vs/platform/agentHost/node/agentHostServerMain.ts +@@ -29,5 +29,3 @@ import product from '../../product/common/product.js'; + import { IProductService } from '../../product/common/productService.js'; +-import { InstantiationService } from '../../instantiation/common/instantiationService.js'; + import { ServiceCollection } from '../../instantiation/common/serviceCollection.js'; +-import { CopilotAgent } from './copilot/copilotAgent.js'; + import { AgentService } from './agentService.js'; +@@ -175,6 +173,2 @@ async function main(): Promise { + diServices.set(IAgentHostTerminalManager, agentService.terminalManager); +- const instantiationService = new InstantiationService(diServices); +- const copilotAgent = disposables.add(instantiationService.createInstance(CopilotAgent)); +- agentService.registerProvider(copilotAgent); +- log('CopilotAgent registered'); + } +diff --git a/src/vs/platform/agentHost/node/agentService.ts b/src/vs/platform/agentHost/node/agentService.ts +index 13561b5e..d5b5517e 100644 +--- a/src/vs/platform/agentHost/node/agentService.ts ++++ b/src/vs/platform/agentHost/node/agentService.ts +@@ -33,3 +33,3 @@ import { AgentHostStateManager } from './agentHostStateManager.js'; + */ +-function extractSubagentMeta(start: IAgentToolStartEvent | undefined): { subagentDescription?: string; subagentAgentName?: string } { ++function extractSubagentMeta(start: IAgentToolStartEvent | undefined): { subagentDescription?: string; subagentAgentName?: string; } { + if (!start?.toolKind || start.toolKind !== 'subagent' || !start.toolArguments) { +@@ -558,3 +558,3 @@ export class AgentService extends Disposable implements IAgentService { + id: string; +- userMessage: { text: string }; ++ userMessage: { text: string; }; + responseParts: IResponsePart[]; +diff --git a/src/vs/platform/agentHost/node/copilot/copilotAgent.ts b/src/vs/platform/agentHost/node/copilot/copilotAgent.ts +deleted file mode 100644 +index 565e4160..00000000 +--- a/src/vs/platform/agentHost/node/copilot/copilotAgent.ts ++++ /dev/null +@@ -1,696 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import { CopilotClient } from '@github/copilot-sdk'; +-import { rgPath } from '@vscode/ripgrep'; +-import { Limiter, SequencerByKey } from '../../../../base/common/async.js'; +-import { Emitter } from '../../../../base/common/event.js'; +-import { Disposable, DisposableMap } from '../../../../base/common/lifecycle.js'; +-import { FileAccess } from '../../../../base/common/network.js'; +-import { delimiter, dirname } from '../../../../base/common/path.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { generateUuid } from '../../../../base/common/uuid.js'; +-import { IParsedPlugin, parsePlugin } from '../../../agentPlugins/common/pluginParsers.js'; +-import { IFileService } from '../../../files/common/files.js'; +-import { IInstantiationService } from '../../../instantiation/common/instantiation.js'; +-import { ILogService } from '../../../log/common/log.js'; +-import { IAgentPluginManager, ISyncedCustomization } from '../../common/agentPluginManager.js'; +-import { AgentSession, IAgent, IAgentAttachment, IAgentCreateSessionConfig, IAgentCreateSessionResult, IAgentDescriptor, IAgentMessageEvent, IAgentModelInfo, IAgentProgressEvent, IAgentSessionMetadata, IAgentSessionProjectInfo, IAgentSubagentStartedEvent, IAgentToolCompleteEvent, IAgentToolStartEvent } from '../../common/agentService.js'; +-import { ISessionDataService } from '../../common/sessionDataService.js'; +-import { CustomizationStatus, ICustomizationRef, SessionInputResponseKind, type ISessionInputAnswer, type IPendingMessage, type PolicyState } from '../../common/state/sessionState.js'; +-import { CopilotAgentSession, SessionWrapperFactory } from './copilotAgentSession.js'; +-import { parsedPluginsEqual, toSdkCustomAgents, toSdkHooks, toSdkMcpServers, toSdkSkillDirectories } from './copilotPluginConverters.js'; +-import { CopilotSessionWrapper } from './copilotSessionWrapper.js'; +-import { forkCopilotSessionOnDisk, getCopilotDataDir, truncateCopilotSessionOnDisk } from './copilotAgentForking.js'; +-import { IProtectedResourceMetadata } from '../../common/state/protocol/state.js'; +-import { IAgentHostTerminalManager } from '../agentHostTerminalManager.js'; +-import { ICopilotSessionContext, projectFromCopilotContext } from './copilotGitProject.js'; +-import { createShellTools, ShellManager } from './copilotShellTools.js'; +- +-/** +- * Agent provider backed by the Copilot SDK {@link CopilotClient}. +- */ +-export class CopilotAgent extends Disposable implements IAgent { +- readonly id = 'copilot' as const; +- +- private readonly _onDidSessionProgress = this._register(new Emitter()); +- readonly onDidSessionProgress = this._onDidSessionProgress.event; +- +- private _client: CopilotClient | undefined; +- private _clientStarting: Promise | undefined; +- private _githubToken: string | undefined; +- private readonly _sessions = this._register(new DisposableMap()); +- private readonly _sessionSequencer = new SequencerByKey(); +- private readonly _plugins: PluginController; +- +- constructor( +- @ILogService private readonly _logService: ILogService, +- @IInstantiationService private readonly _instantiationService: IInstantiationService, +- @IFileService private readonly _fileService: IFileService, +- @ISessionDataService private readonly _sessionDataService: ISessionDataService, +- @IAgentHostTerminalManager private readonly _terminalManager: IAgentHostTerminalManager, +- ) { +- super(); +- this._plugins = this._instantiationService.createInstance(PluginController); +- } +- +- // ---- auth --------------------------------------------------------------- +- +- getDescriptor(): IAgentDescriptor { +- return { +- provider: 'copilot', +- displayName: 'Copilot', +- description: 'Copilot SDK agent running in a dedicated process', +- }; +- } +- +- getProtectedResources(): IProtectedResourceMetadata[] { +- return [{ +- resource: 'https://api.github.com', +- resource_name: 'GitHub Copilot', +- authorization_servers: ['https://github.com/login/oauth'], +- scopes_supported: ['read:user', 'user:email'], +- required: true, +- }]; +- } +- +- async authenticate(resource: string, token: string): Promise { +- if (resource !== 'https://api.github.com') { +- return false; +- } +- const tokenChanged = this._githubToken !== token; +- this._githubToken = token; +- this._logService.info(`[Copilot] Auth token ${tokenChanged ? 'updated' : 'unchanged'}`); +- if (tokenChanged && this._client && this._sessions.size === 0) { +- this._logService.info('[Copilot] Restarting CopilotClient with new token'); +- const client = this._client; +- this._client = undefined; +- this._clientStarting = undefined; +- await client.stop(); +- } +- return true; +- } +- +- // ---- client lifecycle --------------------------------------------------- +- +- private async _ensureClient(): Promise { +- if (this._client) { +- return this._client; +- } +- if (this._clientStarting) { +- return this._clientStarting; +- } +- this._clientStarting = (async () => { +- this._logService.info(`[Copilot] Starting CopilotClient... ${this._githubToken ? '(with token)' : '(no token)'}`); +- +- // Build a clean env for the CLI subprocess, stripping Electron/VS Code vars +- // that can interfere with the Node.js process the SDK spawns. +- const env: Record = Object.assign({}, process.env, { ELECTRON_RUN_AS_NODE: '1' }); +- delete env['NODE_OPTIONS']; +- delete env['VSCODE_INSPECTOR_OPTIONS']; +- delete env['VSCODE_ESM_ENTRYPOINT']; +- delete env['VSCODE_HANDLES_UNCAUGHT_ERRORS']; +- for (const key of Object.keys(env)) { +- if (key === 'ELECTRON_RUN_AS_NODE') { +- continue; +- } +- if (key.startsWith('VSCODE_') || key.startsWith('ELECTRON_')) { +- delete env[key]; +- } +- } +- env['COPILOT_CLI_RUN_AS_NODE'] = '1'; +- env['USE_BUILTIN_RIPGREP'] = 'false'; +- +- // Resolve the CLI entry point from node_modules. We can't use require.resolve() +- // because @github/copilot's exports map blocks direct subpath access. +- // FileAccess.asFileUri('') points to the `out/` directory; node_modules is one level up. +- const cliPath = URI.joinPath(FileAccess.asFileUri(''), '..', 'node_modules', '@github', 'copilot', 'index.js').fsPath; +- +- // Add VS Code's built-in ripgrep to PATH so the CLI subprocess can find it. +- // If @vscode/ripgrep is in an .asar file, the binary is unpacked. +- const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked'); +- const rgDir = dirname(rgDiskPath); +- // On Windows the env key is typically "Path" (not "PATH"). Since we copied +- // process.env into a plain (case-sensitive) object, we must find the actual key. +- const pathKey = Object.keys(env).find(k => k.toUpperCase() === 'PATH') ?? 'PATH'; +- const currentPath = env[pathKey]; +- env[pathKey] = currentPath ? `${currentPath}${delimiter}${rgDir}` : rgDir; +- this._logService.info(`[Copilot] Resolved CLI path: ${cliPath}`); +- +- const client = new CopilotClient({ +- githubToken: this._githubToken, +- useLoggedInUser: !this._githubToken, +- useStdio: true, +- autoStart: true, +- env, +- cliPath, +- }); +- await client.start(); +- this._logService.info('[Copilot] CopilotClient started successfully'); +- this._client = client; +- this._clientStarting = undefined; +- return client; +- })(); +- return this._clientStarting; +- } +- +- // ---- session management ------------------------------------------------- +- +- async listSessions(): Promise { +- this._logService.info('[Copilot] Listing sessions...'); +- const client = await this._ensureClient(); +- const sessions = await client.listSessions(); +- const projectLimiter = new Limiter(4); +- const projectByContext = new Map>(); +- const result: IAgentSessionMetadata[] = await Promise.all(sessions.map(async s => { +- const session = AgentSession.uri(this.id, s.sessionId); +- let { project, resolved } = await this._readSessionProject(session); +- if (!resolved) { +- project = await this._resolveSessionProject(s.context, projectLimiter, projectByContext); +- this._storeSessionProjectResolution(session, project); +- } +- return { +- session, +- startTime: s.startTime.getTime(), +- modifiedTime: s.modifiedTime.getTime(), +- ...(project ? { project } : {}), +- summary: s.summary, +- workingDirectory: typeof s.context?.cwd === 'string' ? URI.file(s.context.cwd) : undefined, +- }; +- })); +- this._logService.info(`[Copilot] Found ${result.length} sessions`); +- return result; +- } +- +- async listModels(): Promise { +- this._logService.info('[Copilot] Listing models...'); +- const client = await this._ensureClient(); +- const models = await client.listModels(); +- const result = models.map(m => ({ +- provider: this.id, +- id: m.id, +- name: m.name, +- maxContextWindow: m.capabilities.limits.max_context_window_tokens, +- supportsVision: m.capabilities.supports.vision, +- supportsReasoningEffort: m.capabilities.supports.reasoningEffort, +- supportedReasoningEfforts: m.supportedReasoningEfforts, +- defaultReasoningEffort: m.defaultReasoningEffort, +- policyState: m.policy?.state as PolicyState | undefined, +- billingMultiplier: m.billing?.multiplier, +- })); +- this._logService.info(`[Copilot] Found ${result.length} models`); +- return result; +- } +- +- async createSession(config?: IAgentCreateSessionConfig): Promise { +- this._logService.info(`[Copilot] Creating session... ${config?.model ? `model=${config.model}` : ''}`); +- const client = await this._ensureClient(); +- const parsedPlugins = await this._plugins.getAppliedPlugins(); +- +- // When forking, we manipulate the CLI's on-disk data and then resume +- // instead of creating a fresh session via the SDK. +- if (config?.fork) { +- const sourceSessionId = AgentSession.id(config.fork.session); +- const newSessionId = config.session ? AgentSession.id(config.session) : generateUuid(); +- +- // Serialize against the source session to prevent concurrent +- // modifications while we read its on-disk data. +- return this._sessionSequencer.queue(sourceSessionId, async () => { +- this._logService.info(`[Copilot] Forking session ${sourceSessionId} at index ${config.fork!.turnIndex} → ${newSessionId}`); +- +- // Ensure the source session is loaded so on-disk data is available +- if (!this._sessions.has(sourceSessionId)) { +- await this._resumeSession(sourceSessionId); +- } +- +- const copilotDataDir = getCopilotDataDir(); +- await forkCopilotSessionOnDisk(copilotDataDir, sourceSessionId, newSessionId, config.fork!.turnIndex); +- +- // Resume the forked session so the SDK loads the forked history +- const agentSession = await this._resumeSession(newSessionId); +- const session = agentSession.sessionUri; +- this._logService.info(`[Copilot] Forked session created: ${session.toString()}`); +- const project = await projectFromCopilotContext({ cwd: config.workingDirectory?.fsPath }); +- this._storeSessionMetadata(session, undefined, config.workingDirectory, project, true); +- return { session, ...(project ? { project } : {}) }; +- }); +- } +- +- const sessionId = config?.session ? AgentSession.id(config.session) : generateUuid(); +- const sessionUri = AgentSession.uri(this.id, sessionId); +- const shellManager = this._instantiationService.createInstance(ShellManager, sessionUri); +- const sessionConfig = this._buildSessionConfig(parsedPlugins, shellManager); +- +- const factory: SessionWrapperFactory = async callbacks => { +- const raw = await client.createSession({ +- model: config?.model, +- sessionId, +- streaming: true, +- workingDirectory: config?.workingDirectory?.fsPath, +- ...await sessionConfig(callbacks), +- }); +- return new CopilotSessionWrapper(raw); +- }; +- +- const agentSession = this._createAgentSession(factory, config?.workingDirectory, sessionId, shellManager); +- this._plugins.setAppliedPlugins(agentSession, parsedPlugins); +- await agentSession.initializeSession(); +- +- const session = agentSession.sessionUri; +- this._logService.info(`[Copilot] Session created: ${session.toString()}`); +- const project = await projectFromCopilotContext({ cwd: config?.workingDirectory?.fsPath }); +- // Persist model, working directory, and project so we can recreate the +- // session if the SDK loses it and avoid rediscovering git metadata. +- this._storeSessionMetadata(agentSession.sessionUri, config?.model, config?.workingDirectory, project, true); +- return { session, ...(project ? { project } : {}) }; +- } +- +- async setClientCustomizations(clientId: string, customizations: ICustomizationRef[], progress?: (results: ISyncedCustomization[]) => void): Promise { +- return this._plugins.sync(clientId, customizations, progress); +- } +- +- setCustomizationEnabled(uri: string, enabled: boolean): void { +- this._plugins.setEnabled(uri, enabled); +- } +- +- async sendMessage(session: URI, prompt: string, attachments?: IAgentAttachment[], turnId?: string): Promise { +- const sessionId = AgentSession.id(session); +- await this._sessionSequencer.queue(sessionId, async () => { +- +- // If plugin config changed, dispose this session so it gets resumed +- // with the updated plugin primitives. +- let entry = this._sessions.get(sessionId); +- if (entry && await this._plugins.needsSessionRefresh(entry)) { +- this._logService.info(`[Copilot:${sessionId}] Plugin config changed, refreshing session`); +- this._sessions.deleteAndDispose(sessionId); +- entry = undefined; +- } +- +- entry ??= await this._resumeSession(sessionId); +- await entry.send(prompt, attachments, turnId); +- }); +- } +- +- setPendingMessages(session: URI, steeringMessage: IPendingMessage | undefined, _queuedMessages: readonly IPendingMessage[]): void { +- const sessionId = AgentSession.id(session); +- const entry = this._sessions.get(sessionId); +- if (!entry) { +- this._logService.warn(`[Copilot:${sessionId}] setPendingMessages: session not found`); +- return; +- } +- +- // Steering: send with mode 'immediate' so the SDK injects it mid-turn +- if (steeringMessage) { +- entry.sendSteering(steeringMessage); +- } +- +- // Queued messages are consumed by the server (AgentSideEffects) +- // which dispatches SessionTurnStarted and calls sendMessage directly. +- // No SDK-level enqueue is needed. +- } +- +- async getSessionMessages(session: URI): Promise<(IAgentMessageEvent | IAgentToolStartEvent | IAgentToolCompleteEvent | IAgentSubagentStartedEvent)[]> { +- const sessionId = AgentSession.id(session); +- const entry = this._sessions.get(sessionId) ?? await this._resumeSession(sessionId).catch(() => undefined); +- if (!entry) { +- return []; +- } +- return entry.getMessages(); +- } +- +- async disposeSession(session: URI): Promise { +- const sessionId = AgentSession.id(session); +- await this._sessionSequencer.queue(sessionId, async () => { +- this._sessions.deleteAndDispose(sessionId); +- }); +- } +- +- async abortSession(session: URI): Promise { +- const sessionId = AgentSession.id(session); +- await this._sessionSequencer.queue(sessionId, async () => { +- const entry = this._sessions.get(sessionId); +- if (entry) { +- await entry.abort(); +- } +- }); +- } +- +- async truncateSession(session: URI, turnIndex?: number): Promise { +- const sessionId = AgentSession.id(session); +- await this._sessionSequencer.queue(sessionId, async () => { +- this._logService.info(`[Copilot:${sessionId}] Truncating session${turnIndex !== undefined ? ` at index ${turnIndex}` : ' (all turns)'}`); +- +- const keepUpToTurnIndex = turnIndex ?? -1; +- +- // Destroy the SDK session first and wait for cleanup to complete, +- // ensuring on-disk data (events.jsonl, locks) is released before +- // we modify it. Then dispose the wrapper. +- const entry = this._sessions.get(sessionId); +- if (entry) { +- await entry.destroySession(); +- } +- this._sessions.deleteAndDispose(sessionId); +- +- const copilotDataDir = getCopilotDataDir(); +- await truncateCopilotSessionOnDisk(copilotDataDir, sessionId, keepUpToTurnIndex); +- +- // Resume the session from the modified on-disk data +- await this._resumeSession(sessionId); +- this._logService.info(`[Copilot:${sessionId}] Session truncated and resumed`); +- }); +- } +- +- async forkSession(sourceSession: URI, newSessionId: string, turnIndex: number): Promise { +- const sourceSessionId = AgentSession.id(sourceSession); +- await this._sessionSequencer.queue(sourceSessionId, async () => { +- this._logService.info(`[Copilot] Forking session ${sourceSessionId} at index ${turnIndex} → ${newSessionId}`); +- +- const copilotDataDir = getCopilotDataDir(); +- await forkCopilotSessionOnDisk(copilotDataDir, sourceSessionId, newSessionId, turnIndex); +- this._logService.info(`[Copilot] Forked session ${newSessionId} created on disk`); +- }); +- } +- +- async changeModel(session: URI, model: string): Promise { +- const sessionId = AgentSession.id(session); +- const entry = this._sessions.get(sessionId); +- if (entry) { +- await entry.setModel(model); +- } +- this._storeSessionMetadata(session, model, undefined, undefined); +- } +- +- async shutdown(): Promise { +- this._logService.info('[Copilot] Shutting down...'); +- this._sessions.clearAndDisposeAll(); +- await this._client?.stop(); +- this._client = undefined; +- } +- +- respondToPermissionRequest(requestId: string, approved: boolean): void { +- for (const [, session] of this._sessions) { +- if (session.respondToPermissionRequest(requestId, approved)) { +- return; +- } +- } +- } +- +- respondToUserInputRequest(requestId: string, response: SessionInputResponseKind, answers?: Record): void { +- for (const [, session] of this._sessions) { +- if (session.respondToUserInputRequest(requestId, response, answers)) { +- return; +- } +- } +- } +- +- /** +- * Returns true if this provider owns the given session ID. +- */ +- hasSession(session: URI): boolean { +- return this._sessions.has(AgentSession.id(session)); +- } +- +- // ---- helpers ------------------------------------------------------------ +- +- /** +- * Creates a {@link CopilotAgentSession}, registers it in the sessions map, +- * and returns it. The caller must call {@link CopilotAgentSession.initializeSession} +- * to wire up the SDK session. +- */ +- private _createAgentSession(wrapperFactory: SessionWrapperFactory, workingDirectory: URI | undefined, sessionId: string, shellManager: ShellManager): CopilotAgentSession { +- const sessionUri = AgentSession.uri(this.id, sessionId); +- +- const agentSession = this._instantiationService.createInstance( +- CopilotAgentSession, +- sessionUri, +- sessionId, +- workingDirectory, +- this._onDidSessionProgress, +- wrapperFactory, +- shellManager, +- ); +- +- this._sessions.set(sessionId, agentSession); +- return agentSession; +- } +- +- /** +- * Builds the common session configuration (plugins + shell tools) shared +- * by both {@link createSession} and {@link _resumeSession}. +- * +- * Returns an async function that resolves the final config given the +- * session's permission/hook callbacks, so it can be called lazily +- * inside the {@link SessionWrapperFactory}. +- */ +- private _buildSessionConfig(parsedPlugins: readonly IParsedPlugin[], shellManager: ShellManager) { +- const shellTools = createShellTools(shellManager, this._terminalManager, this._logService); +- +- return async (callbacks: Parameters[0]) => { +- const customAgents = await toSdkCustomAgents(parsedPlugins.flatMap(p => p.agents), this._fileService); +- return { +- onPermissionRequest: callbacks.onPermissionRequest, +- onUserInputRequest: callbacks.onUserInputRequest, +- hooks: toSdkHooks(parsedPlugins.flatMap(p => p.hooks), callbacks.hooks), +- mcpServers: toSdkMcpServers(parsedPlugins.flatMap(p => p.mcpServers)), +- customAgents, +- skillDirectories: toSdkSkillDirectories(parsedPlugins.flatMap(p => p.skills)), +- tools: shellTools, +- }; +- }; +- } +- +- private async _resumeSession(sessionId: string): Promise { +- this._logService.info(`[Copilot:${sessionId}] Session not in memory, resuming...`); +- const client = await this._ensureClient(); +- const parsedPlugins = await this._plugins.getAppliedPlugins(); +- +- const sessionUri = AgentSession.uri(this.id, sessionId); +- const sessionMetadata = await client.getSessionMetadata(sessionId).catch(err => { +- this._logService.warn(`[Copilot:${sessionId}] getSessionMetadata failed`, err); +- return undefined; +- }); +- const workingDirectory = typeof sessionMetadata?.context?.cwd === 'string' ? URI.file(sessionMetadata.context.cwd) : undefined; +- const shellManager = this._instantiationService.createInstance(ShellManager, sessionUri); +- const sessionConfig = this._buildSessionConfig(parsedPlugins, shellManager); +- +- const factory: SessionWrapperFactory = async callbacks => { +- const config = await sessionConfig(callbacks); +- try { +- const raw = await client.resumeSession(sessionId, { +- ...config, +- workingDirectory: workingDirectory?.fsPath, +- }); +- return new CopilotSessionWrapper(raw); +- } catch (err) { +- // The SDK fails to resume sessions that have no messages. +- // Fall back to creating a new session with the same ID, +- // seeding model & working directory from stored metadata. +- if (!err || (err as { code?: number }).code !== -32603) { +- throw err; +- } +- +- this._logService.warn(`[Copilot:${sessionId}] Resume failed (session not found in SDK), recreating`); +- const metadata = await this._readSessionMetadata(sessionUri); +- const raw = await client.createSession({ +- ...config, +- sessionId, +- streaming: true, +- model: metadata.model, +- workingDirectory: metadata.workingDirectory?.fsPath, +- }); +- +- return new CopilotSessionWrapper(raw); +- } +- }; +- +- const agentSession = this._createAgentSession(factory, workingDirectory, sessionId, shellManager); +- this._plugins.setAppliedPlugins(agentSession, parsedPlugins); +- await agentSession.initializeSession(); +- +- return agentSession; +- } +- +- // ---- session metadata persistence -------------------------------------- +- +- private static readonly _META_MODEL = 'copilot.model'; +- private static readonly _META_CWD = 'copilot.workingDirectory'; +- private static readonly _META_PROJECT_RESOLVED = 'copilot.project.resolved'; +- private static readonly _META_PROJECT_URI = 'copilot.project.uri'; +- private static readonly _META_PROJECT_DISPLAY_NAME = 'copilot.project.displayName'; +- +- private _storeSessionMetadata(session: URI, model: string | undefined, workingDirectory: URI | undefined, project: IAgentSessionProjectInfo | undefined, projectResolved = project !== undefined): void { +- const dbRef = this._sessionDataService.tryOpenDatabase(session); +- dbRef?.then(ref => { +- if (!ref) { +- return; +- } +- const db = ref.object; +- const work: Promise[] = []; +- if (model) { +- work.push(db.setMetadata(CopilotAgent._META_MODEL, model)); +- } +- if (workingDirectory) { +- work.push(db.setMetadata(CopilotAgent._META_CWD, workingDirectory.toString())); +- } +- if (projectResolved) { +- work.push(db.setMetadata(CopilotAgent._META_PROJECT_RESOLVED, 'true')); +- } +- if (project) { +- work.push(db.setMetadata(CopilotAgent._META_PROJECT_URI, project.uri.toString())); +- work.push(db.setMetadata(CopilotAgent._META_PROJECT_DISPLAY_NAME, project.displayName)); +- } +- Promise.all(work).finally(() => ref.dispose()); +- }); +- } +- +- private async _readSessionMetadata(session: URI): Promise<{ model?: string; workingDirectory?: URI }> { +- const ref = await this._sessionDataService.tryOpenDatabase(session); +- if (!ref) { +- return {}; +- } +- try { +- const [model, cwd] = await Promise.all([ +- ref.object.getMetadata(CopilotAgent._META_MODEL), +- ref.object.getMetadata(CopilotAgent._META_CWD), +- ]); +- return { +- model, +- workingDirectory: cwd ? URI.parse(cwd) : undefined, +- }; +- } finally { +- ref.dispose(); +- } +- } +- +- private async _readSessionProject(session: URI): Promise<{ project?: IAgentSessionProjectInfo; resolved: boolean }> { +- const ref = await this._sessionDataService.tryOpenDatabase(session); +- if (!ref) { +- return { resolved: false }; +- } +- try { +- const [resolved, uri, displayName] = await Promise.all([ +- ref.object.getMetadata(CopilotAgent._META_PROJECT_RESOLVED), +- ref.object.getMetadata(CopilotAgent._META_PROJECT_URI), +- ref.object.getMetadata(CopilotAgent._META_PROJECT_DISPLAY_NAME), +- ]); +- const project = uri && displayName ? { uri: URI.parse(uri), displayName } : undefined; +- return { project, resolved: resolved === 'true' || project !== undefined }; +- } finally { +- ref.dispose(); +- } +- } +- +- private _storeSessionProjectResolution(session: URI, project: IAgentSessionProjectInfo | undefined): void { +- this._storeSessionMetadata(session, undefined, undefined, project, true); +- } +- +- private _resolveSessionProject(context: ICopilotSessionContext | undefined, limiter: Limiter, projectByContext: Map>): Promise { +- const key = this._projectContextKey(context); +- if (!key) { +- return Promise.resolve(undefined); +- } +- +- let project = projectByContext.get(key); +- if (!project) { +- project = limiter.queue(() => projectFromCopilotContext(context)); +- projectByContext.set(key, project); +- } +- return project; +- } +- +- private _projectContextKey(context: ICopilotSessionContext | undefined): string | undefined { +- if (context?.cwd) { +- return `cwd:${context.cwd}`; +- } +- if (context?.gitRoot) { +- return `gitRoot:${context.gitRoot}`; +- } +- if (context?.repository) { +- return `repository:${context.repository}`; +- } +- return undefined; +- } +- +- override dispose(): void { +- this._client?.stop().catch(() => { /* best-effort */ }); +- super.dispose(); +- } +-} +- +-class PluginController { +- private readonly _enablement = new Map(); +- private _lastSynced: Promise<{ synced: ISyncedCustomization[]; parsed: IParsedPlugin[] }> = Promise.resolve({ synced: [], parsed: [] }); +- +- /** Parsed plugin contents from the most recently applied sync. */ +- private _appliedParsed = new WeakMap(); +- +- constructor( +- @IAgentPluginManager private readonly _pluginManager: IAgentPluginManager, +- @ILogService private readonly _logService: ILogService, +- @IFileService private readonly _fileService: IFileService, +- ) { } +- +- /** +- * Returns true if the plugin configuration has changed since the last +- * time sessions were created/resumed. Used by {@link CopilotAgent.sendMessage} +- * to decide whether a session needs to be refreshed. +- */ +- public async needsSessionRefresh(session: CopilotAgentSession): Promise { +- const { parsed } = await this._lastSynced; +- return !parsedPluginsEqual(this._appliedParsed.get(session) || [], parsed); +- } +- +- /** +- * Returns the current parsed plugins filtered by enablement, +- * then marks them as applied so {@link needsSessionRefresh} returns +- * false until the next change. +- */ +- public async getAppliedPlugins(): Promise { +- const { parsed } = await this._lastSynced; +- return parsed; +- } +- +- public setAppliedPlugins(session: CopilotAgentSession, plugins: readonly IParsedPlugin[]) { +- this._appliedParsed.set(session, plugins); +- } +- +- public setEnabled(pluginProtocolUri: string, enabled: boolean) { +- this._enablement.set(pluginProtocolUri, enabled); +- } +- +- public sync(clientId: string, customizations: ICustomizationRef[], progress?: (results: ISyncedCustomization[]) => void) { +- const prev = this._lastSynced; +- const promise = this._lastSynced = prev.catch(() => []).then(async () => { +- const result = await this._pluginManager.syncCustomizations(clientId, customizations, status => { +- progress?.(status.map(c => ({ customization: c }))); +- }); +- +- +- const parsed: IParsedPlugin[] = []; +- const synced: ISyncedCustomization[] = []; +- for (const dir of result) { +- if (dir.pluginDir) { +- try { +- parsed.push(await parsePlugin(dir.pluginDir, this._fileService, undefined, this._getUserHome())); +- synced.push(dir); +- } catch (e) { +- this._logService.warn(`[Copilot:PluginController] Error parsing plugin: ${e}`); +- synced.push({ customization: { ...dir.customization, status: CustomizationStatus.Error, statusMessage: `Error parsing plugin: ${e}` } }); +- } +- } else { +- synced.push(dir); +- } +- } +- +- return { synced, parsed }; +- }); +- +- return promise.then(p => p.synced); +- } +- +- private _getUserHome(): string { +- return process.env['HOME'] ?? process.env['USERPROFILE'] ?? ''; +- } +-} +diff --git a/src/vs/platform/agentHost/node/copilot/copilotAgentForking.ts b/src/vs/platform/agentHost/node/copilot/copilotAgentForking.ts +deleted file mode 100644 +index 0631ba51..00000000 +--- a/src/vs/platform/agentHost/node/copilot/copilotAgentForking.ts ++++ /dev/null +@@ -1,599 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import * as fs from 'fs'; +-import * as os from 'os'; +-import type { Database } from '@vscode/sqlite3'; +-import { generateUuid } from '../../../../base/common/uuid.js'; +-import * as path from '../../../../base/common/path.js'; +- +-// ---- Types ------------------------------------------------------------------ +- +-/** +- * A single event entry from a Copilot CLI `events.jsonl` file. +- * The Copilot CLI stores session history as a newline-delimited JSON log +- * where events form a linked list via `parentId`. +- */ +-export interface ICopilotEventLogEntry { +- readonly type: string; +- readonly data: Record; +- readonly id: string; +- readonly timestamp: string; +- readonly parentId: string | null; +-} +- +-// ---- Promise wrappers around callback-based @vscode/sqlite3 API ----------- +- +-function dbExec(db: Database, sql: string): Promise { +- return new Promise((resolve, reject) => { +- db.exec(sql, err => err ? reject(err) : resolve()); +- }); +-} +- +-function dbRun(db: Database, sql: string, params: unknown[]): Promise { +- return new Promise((resolve, reject) => { +- db.run(sql, params, function (err: Error | null) { +- if (err) { +- return reject(err); +- } +- resolve(); +- }); +- }); +-} +- +-function dbAll(db: Database, sql: string, params: unknown[]): Promise[]> { +- return new Promise((resolve, reject) => { +- db.all(sql, params, (err: Error | null, rows: Record[]) => { +- if (err) { +- return reject(err); +- } +- resolve(rows); +- }); +- }); +-} +- +-function dbClose(db: Database): Promise { +- return new Promise((resolve, reject) => { +- db.close(err => err ? reject(err) : resolve()); +- }); +-} +- +-function dbOpen(dbPath: string): Promise { +- return new Promise((resolve, reject) => { +- import('@vscode/sqlite3').then(sqlite3 => { +- const db = new sqlite3.default.Database(dbPath, (err: Error | null) => { +- if (err) { +- return reject(err); +- } +- resolve(db); +- }); +- }, reject); +- }); +-} +- +-// ---- Pure functions (testable, no I/O) ------------------------------------ +- +-/** +- * Parses a JSONL string into an array of event log entries. +- */ +-export function parseEventLog(content: string): ICopilotEventLogEntry[] { +- const entries: ICopilotEventLogEntry[] = []; +- for (const line of content.split('\n')) { +- const trimmed = line.trim(); +- if (trimmed.length === 0) { +- continue; +- } +- entries.push(JSON.parse(trimmed)); +- } +- return entries; +-} +- +-/** +- * Serializes an array of event log entries back into a JSONL string. +- */ +-export function serializeEventLog(entries: readonly ICopilotEventLogEntry[]): string { +- return entries.map(e => JSON.stringify(e)).join('\n') + '\n'; +-} +- +-/** +- * Finds the index of the last event that belongs to the given turn (0-based). +- * +- * A "turn" corresponds to one `user.message` event and all subsequent events +- * up to (and including) the `assistant.turn_end` that closes that interaction, +- * or the `session.shutdown` that ends the session. +- * +- * @returns The inclusive index of the last event in the specified turn, +- * or `-1` if the turn is not found. +- */ +-export function findTurnBoundaryInEventLog(entries: readonly ICopilotEventLogEntry[], turnIndex: number): number { +- let userMessageCount = -1; +- let lastEventForTurn = -1; +- +- for (let i = 0; i < entries.length; i++) { +- const entry = entries[i]; +- +- if (entry.type === 'user.message') { +- userMessageCount++; +- if (userMessageCount > turnIndex) { +- // We've entered the next turn — stop +- return lastEventForTurn; +- } +- } +- +- if (userMessageCount === turnIndex) { +- lastEventForTurn = i; +- } +- } +- +- // If we scanned everything and the target turn was found, return its last event +- return lastEventForTurn; +-} +- +-/** +- * Builds a forked event log from the source session's events. +- * +- * - Keeps events up to and including the specified fork turn (0-based). +- * - Rewrites `session.start` with the new session ID. +- * - Generates fresh UUIDs for all events. +- * - Re-chains `parentId` links via an old→new ID map. +- * - Strips `session.shutdown` and `session.resume` lifecycle events. +- */ +-export function buildForkedEventLog( +- entries: readonly ICopilotEventLogEntry[], +- forkTurnIndex: number, +- newSessionId: string, +-): ICopilotEventLogEntry[] { +- const boundary = findTurnBoundaryInEventLog(entries, forkTurnIndex); +- if (boundary < 0) { +- throw new Error(`Fork turn index ${forkTurnIndex} not found in event log`); +- } +- +- // Keep events up to boundary, filtering out lifecycle events +- const kept = entries +- .slice(0, boundary + 1) +- .filter(e => e.type !== 'session.shutdown' && e.type !== 'session.resume'); +- +- // Build UUID remap and re-chain +- const idMap = new Map(); +- const result: ICopilotEventLogEntry[] = []; +- +- for (const entry of kept) { +- const newId = generateUuid(); +- idMap.set(entry.id, newId); +- +- let data = entry.data; +- if (entry.type === 'session.start') { +- data = { ...data, sessionId: newSessionId }; +- } +- +- const newParentId = entry.parentId !== null +- ? (idMap.get(entry.parentId) ?? result[result.length - 1]?.id ?? null) +- : null; +- +- result.push({ +- type: entry.type, +- data, +- id: newId, +- timestamp: entry.timestamp, +- parentId: newParentId, +- }); +- } +- +- return result; +-} +- +-/** +- * Builds a truncated event log from the source session's events. +- * +- * - Keeps events up to and including the specified turn (0-based). +- * - Prepends a new `session.start` event using the original start data. +- * - Re-chains `parentId` links for remaining events. +- */ +-export function buildTruncatedEventLog( +- entries: readonly ICopilotEventLogEntry[], +- keepUpToTurnIndex: number, +-): ICopilotEventLogEntry[] { +- const boundary = findTurnBoundaryInEventLog(entries, keepUpToTurnIndex); +- if (boundary < 0) { +- throw new Error(`Turn index ${keepUpToTurnIndex} not found in event log`); +- } +- +- // Find the original session.start for its metadata +- const originalStart = entries.find(e => e.type === 'session.start'); +- if (!originalStart) { +- throw new Error('No session.start event found in event log'); +- } +- +- // Keep events from after session start up to boundary, stripping lifecycle events +- const kept = entries +- .slice(0, boundary + 1) +- .filter(e => e.type !== 'session.start' && e.type !== 'session.shutdown' && e.type !== 'session.resume'); +- +- // Build new start event +- const newStartId = generateUuid(); +- const newStart: ICopilotEventLogEntry = { +- type: 'session.start', +- data: { ...originalStart.data, startTime: new Date().toISOString() }, +- id: newStartId, +- timestamp: new Date().toISOString(), +- parentId: null, +- }; +- +- // Re-chain: first remaining event points to the new start +- const idMap = new Map(); +- idMap.set(originalStart.id, newStartId); +- +- const result: ICopilotEventLogEntry[] = [newStart]; +- let lastId = newStartId; +- +- for (const entry of kept) { +- const newId = generateUuid(); +- idMap.set(entry.id, newId); +- +- const newParentId = entry.parentId !== null +- ? (idMap.get(entry.parentId) ?? lastId) +- : lastId; +- +- result.push({ +- type: entry.type, +- data: entry.data, +- id: newId, +- timestamp: entry.timestamp, +- parentId: newParentId, +- }); +- lastId = newId; +- } +- +- return result; +-} +- +-/** +- * Generates a `workspace.yaml` file content for a Copilot CLI session. +- */ +-export function buildWorkspaceYaml(sessionId: string, cwd: string, summary: string): string { +- const now = new Date().toISOString(); +- return [ +- `id: ${sessionId}`, +- `cwd: ${cwd}`, +- `summary_count: 0`, +- `created_at: ${now}`, +- `updated_at: ${now}`, +- `summary: ${summary}`, +- '', +- ].join('\n'); +-} +- +-// ---- SQLite operations (Copilot CLI session-store.db) --------------------- +- +-/** +- * Forks a session record in the Copilot CLI's `session-store.db`. +- * +- * Copies the source session's metadata, turns (up to `forkTurnIndex`), +- * session files, search index entries, and checkpoints into a new session. +- */ +-export async function forkSessionInDb( +- db: Database, +- sourceSessionId: string, +- newSessionId: string, +- forkTurnIndex: number, +-): Promise { +- await dbExec(db, 'PRAGMA foreign_keys = ON'); +- await dbExec(db, 'BEGIN TRANSACTION'); +- try { +- const now = new Date().toISOString(); +- +- // Copy session row +- await dbRun(db, +- `INSERT INTO sessions (id, cwd, repository, branch, summary, created_at, updated_at, host_type) +- SELECT ?, cwd, repository, branch, summary, ?, ?, host_type +- FROM sessions WHERE id = ?`, +- [newSessionId, now, now, sourceSessionId], +- ); +- +- // Copy turns up to fork point (turn_index is 0-based) +- await dbRun(db, +- `INSERT INTO turns (session_id, turn_index, user_message, assistant_response, timestamp) +- SELECT ?, turn_index, user_message, assistant_response, timestamp +- FROM turns +- WHERE session_id = ? AND turn_index <= ?`, +- [newSessionId, sourceSessionId, forkTurnIndex], +- ); +- +- // Copy session files that were first seen at or before the fork point +- await dbRun(db, +- `INSERT INTO session_files (session_id, file_path, tool_name, turn_index, first_seen_at) +- SELECT ?, file_path, tool_name, turn_index, first_seen_at +- FROM session_files +- WHERE session_id = ? AND turn_index <= ?`, +- [newSessionId, sourceSessionId, forkTurnIndex], +- ); +- +- // Copy search index entries for kept turns only. +- // source_id format is ":turn:"; filter by +- // parsing the turn index so we don't leak content from later turns. +- await dbAll(db, +- `SELECT content, source_type, source_id +- FROM search_index +- WHERE session_id = ? AND source_type = 'turn'`, +- [sourceSessionId], +- ).then(async rows => { +- const prefix = `${sourceSessionId}:turn:`; +- for (const row of rows) { +- const sourceId = row.source_id as string; +- if (sourceId.startsWith(prefix)) { +- const turnIdx = parseInt(sourceId.substring(prefix.length), 10); +- if (!isNaN(turnIdx) && turnIdx <= forkTurnIndex) { +- const newSourceId = sourceId.replace(sourceSessionId, newSessionId); +- await dbRun(db, +- `INSERT INTO search_index (content, session_id, source_type, source_id) +- VALUES (?, ?, ?, ?)`, +- [row.content, newSessionId, row.source_type, newSourceId], +- ); +- } +- } +- } +- }); +- +- // Copy checkpoints at or before the fork point. +- // checkpoint_number is 1-based and correlates to turns, so we keep +- // only those where checkpoint_number <= forkTurnIndex + 1. +- await dbRun(db, +- `INSERT INTO checkpoints (session_id, checkpoint_number, title, overview, history, work_done, technical_details, important_files, next_steps, created_at) +- SELECT ?, checkpoint_number, title, overview, history, work_done, technical_details, important_files, next_steps, created_at +- FROM checkpoints +- WHERE session_id = ? AND checkpoint_number <= ?`, +- [newSessionId, sourceSessionId, forkTurnIndex + 1], +- ); +- +- await dbExec(db, 'COMMIT'); +- } catch (err) { +- await dbExec(db, 'ROLLBACK'); +- throw err; +- } +-} +- +-/** +- * Truncates a session in the Copilot CLI's `session-store.db`. +- * +- * Removes all turns after `keepUpToTurnIndex` and updates session metadata. +- */ +-export async function truncateSessionInDb( +- db: Database, +- sessionId: string, +- keepUpToTurnIndex: number, +-): Promise { +- await dbExec(db, 'PRAGMA foreign_keys = ON'); +- await dbExec(db, 'BEGIN TRANSACTION'); +- try { +- const now = new Date().toISOString(); +- +- // Delete turns after the truncation point +- await dbRun(db, +- `DELETE FROM turns WHERE session_id = ? AND turn_index > ?`, +- [sessionId, keepUpToTurnIndex], +- ); +- +- // Update session timestamp +- await dbRun(db, +- `UPDATE sessions SET updated_at = ? WHERE id = ?`, +- [now, sessionId], +- ); +- +- // Remove search index entries for removed turns +- // source_id format is ":turn:" +- await dbAll(db, +- `SELECT source_id FROM search_index +- WHERE session_id = ? AND source_type = 'turn'`, +- [sessionId], +- ).then(async rows => { +- const prefix = `${sessionId}:turn:`; +- for (const row of rows) { +- const sourceId = row.source_id as string; +- if (sourceId.startsWith(prefix)) { +- const turnIdx = parseInt(sourceId.substring(prefix.length), 10); +- if (!isNaN(turnIdx) && turnIdx > keepUpToTurnIndex) { +- await dbRun(db, +- `DELETE FROM search_index WHERE source_id = ? AND session_id = ?`, +- [sourceId, sessionId], +- ); +- } +- } +- } +- }); +- +- await dbExec(db, 'COMMIT'); +- } catch (err) { +- await dbExec(db, 'ROLLBACK'); +- throw err; +- } +-} +- +-// ---- File system operations ----------------------------------------------- +- +-/** +- * Resolves the Copilot CLI data directory. +- * The Copilot CLI stores its data in `~/.copilot/` by default, or in the +- * directory specified by `COPILOT_CONFIG_DIR`. +- */ +-export function getCopilotDataDir(): string { +- return process.env['COPILOT_CONFIG_DIR'] ?? path.join(os.homedir(), '.copilot'); +-} +- +-/** +- * Forks a Copilot CLI session on disk. +- * +- * 1. Reads the source session's `events.jsonl` +- * 2. Builds a forked event log +- * 3. Creates the new session folder with all required files/directories +- * 4. Updates the `session-store.db` +- * +- * @param copilotDataDir Path to the `.copilot` directory +- * @param sourceSessionId UUID of the source session to fork from +- * @param newSessionId UUID for the new forked session +- * @param forkTurnIndex 0-based turn index to fork at (inclusive) +- */ +-export async function forkCopilotSessionOnDisk( +- copilotDataDir: string, +- sourceSessionId: string, +- newSessionId: string, +- forkTurnIndex: number, +-): Promise { +- const sessionStateDir = path.join(copilotDataDir, 'session-state'); +- +- // Read source events +- const sourceEventsPath = path.join(sessionStateDir, sourceSessionId, 'events.jsonl'); +- const sourceContent = await fs.promises.readFile(sourceEventsPath, 'utf-8'); +- const sourceEntries = parseEventLog(sourceContent); +- +- // Build forked event log +- const forkedEntries = buildForkedEventLog(sourceEntries, forkTurnIndex, newSessionId); +- +- // Read source workspace.yaml for cwd/summary +- let cwd = ''; +- let summary = ''; +- try { +- const workspaceYamlPath = path.join(sessionStateDir, sourceSessionId, 'workspace.yaml'); +- const yamlContent = await fs.promises.readFile(workspaceYamlPath, 'utf-8'); +- const cwdMatch = yamlContent.match(/^cwd:\s*(.+)$/m); +- const summaryMatch = yamlContent.match(/^summary:\s*(.+)$/m); +- if (cwdMatch) { +- cwd = cwdMatch[1].trim(); +- } +- if (summaryMatch) { +- summary = summaryMatch[1].trim(); +- } +- } catch { +- // Fall back to session.start data +- const startEvent = sourceEntries.find(e => e.type === 'session.start'); +- if (startEvent) { +- const ctx = startEvent.data.context as Record | undefined; +- cwd = ctx?.cwd ?? ''; +- } +- } +- +- // Create new session folder structure +- const newSessionDir = path.join(sessionStateDir, newSessionId); +- await fs.promises.mkdir(path.join(newSessionDir, 'checkpoints'), { recursive: true }); +- await fs.promises.mkdir(path.join(newSessionDir, 'files'), { recursive: true }); +- await fs.promises.mkdir(path.join(newSessionDir, 'research'), { recursive: true }); +- +- // Write events.jsonl +- await fs.promises.writeFile( +- path.join(newSessionDir, 'events.jsonl'), +- serializeEventLog(forkedEntries), +- 'utf-8', +- ); +- +- // Write workspace.yaml +- await fs.promises.writeFile( +- path.join(newSessionDir, 'workspace.yaml'), +- buildWorkspaceYaml(newSessionId, cwd, summary), +- 'utf-8', +- ); +- +- // Write empty vscode.metadata.json +- await fs.promises.writeFile( +- path.join(newSessionDir, 'vscode.metadata.json'), +- '{}', +- 'utf-8', +- ); +- +- // Write empty checkpoints index +- await fs.promises.writeFile( +- path.join(newSessionDir, 'checkpoints', 'index.md'), +- '', +- 'utf-8', +- ); +- +- // Update session-store.db +- const dbPath = path.join(copilotDataDir, 'session-store.db'); +- const db = await dbOpen(dbPath); +- try { +- await forkSessionInDb(db, sourceSessionId, newSessionId, forkTurnIndex); +- } finally { +- await dbClose(db); +- } +-} +- +-/** +- * Truncates a Copilot CLI session on disk. +- * +- * 1. Reads the session's `events.jsonl` +- * 2. Builds a truncated event log +- * 3. Overwrites `events.jsonl` and updates `workspace.yaml` +- * 4. Updates the `session-store.db` +- * +- * @param copilotDataDir Path to the `.copilot` directory +- * @param sessionId UUID of the session to truncate +- * @param keepUpToTurnIndex 0-based turn index to keep up to (inclusive) +- */ +-export async function truncateCopilotSessionOnDisk( +- copilotDataDir: string, +- sessionId: string, +- keepUpToTurnIndex: number, +-): Promise { +- const sessionStateDir = path.join(copilotDataDir, 'session-state'); +- const sessionDir = path.join(sessionStateDir, sessionId); +- +- // Read and truncate events +- const eventsPath = path.join(sessionDir, 'events.jsonl'); +- const content = await fs.promises.readFile(eventsPath, 'utf-8'); +- const entries = parseEventLog(content); +- +- let truncatedEntries: ICopilotEventLogEntry[]; +- if (keepUpToTurnIndex < 0) { +- // Truncate all turns: keep only a fresh session.start event +- const originalStart = entries.find(e => e.type === 'session.start'); +- if (!originalStart) { +- throw new Error('No session.start event found in event log'); +- } +- truncatedEntries = [{ +- type: 'session.start', +- data: { ...originalStart.data, startTime: new Date().toISOString() }, +- id: generateUuid(), +- timestamp: new Date().toISOString(), +- parentId: null, +- }]; +- } else { +- truncatedEntries = buildTruncatedEventLog(entries, keepUpToTurnIndex); +- } +- +- // Overwrite events.jsonl +- await fs.promises.writeFile(eventsPath, serializeEventLog(truncatedEntries), 'utf-8'); +- +- // Update workspace.yaml timestamp +- try { +- const yamlPath = path.join(sessionDir, 'workspace.yaml'); +- let yaml = await fs.promises.readFile(yamlPath, 'utf-8'); +- yaml = yaml.replace(/^updated_at:\s*.+$/m, `updated_at: ${new Date().toISOString()}`); +- await fs.promises.writeFile(yamlPath, yaml, 'utf-8'); +- } catch { +- // workspace.yaml may not exist (old format) +- } +- +- // Update session-store.db +- const dbPath = path.join(copilotDataDir, 'session-store.db'); +- const db = await dbOpen(dbPath); +- try { +- await truncateSessionInDb(db, sessionId, keepUpToTurnIndex); +- } finally { +- await dbClose(db); +- } +-} +- +-/** +- * Maps a protocol turn ID to a 0-based turn index by finding the turn's +- * position within the session's event log. +- * +- * The protocol state assigns arbitrary string IDs to turns, but the Copilot +- * CLI's `events.jsonl` uses sequential `user.message` events. To bridge the +- * two, we match turns by their position in the sequence. +- * +- * @returns The 0-based turn index, or `-1` if the turn ID is not found in the +- * `turnIds` array. +- */ +-export function turnIdToIndex(turnIds: readonly string[], turnId: string): number { +- return turnIds.indexOf(turnId); +-} +diff --git a/src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts b/src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts +deleted file mode 100644 +index e4e4203d..00000000 +--- a/src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts ++++ /dev/null +@@ -1,754 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import type { PermissionRequest, PermissionRequestResult } from '@github/copilot-sdk'; +-import { DeferredPromise } from '../../../../base/common/async.js'; +-import { Emitter } from '../../../../base/common/event.js'; +-import { Disposable, IReference, toDisposable } from '../../../../base/common/lifecycle.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { extUriBiasedIgnorePathCase, normalizePath } from '../../../../base/common/resources.js'; +-import { generateUuid } from '../../../../base/common/uuid.js'; +-import { IInstantiationService } from '../../../instantiation/common/instantiation.js'; +-import { ILogService } from '../../../log/common/log.js'; +-import { localize } from '../../../../nls.js'; +-import { IAgentAttachment, IAgentMessageEvent, IAgentProgressEvent, IAgentSubagentStartedEvent, IAgentToolCompleteEvent, IAgentToolStartEvent } from '../../common/agentService.js'; +-import { ISessionDatabase, ISessionDataService } from '../../common/sessionDataService.js'; +-import { SessionInputAnswerState, SessionInputAnswerValueKind, SessionInputQuestionKind, SessionInputResponseKind, ToolResultContentType, type ISessionInputAnswer, type ISessionInputRequest, type IPendingMessage, type IToolResultContent } from '../../common/state/sessionState.js'; +-import { CopilotSessionWrapper } from './copilotSessionWrapper.js'; +-import { getEditFilePath, getInvocationMessage, getPastTenseMessage, getShellLanguage, getToolDisplayName, getToolInputString, getToolKind, isEditTool, isHiddenTool, isShellTool } from './copilotToolDisplay.js'; +-import { FileEditTracker } from './fileEditTracker.js'; +-import { mapSessionEvents } from './mapSessionEvents.js'; +-import type { ShellManager } from './copilotShellTools.js'; +- +-/** +- * Factory function that produces a {@link CopilotSessionWrapper}. +- * Called by {@link CopilotAgentSession.initializeSession} with the +- * session's permission handler and edit-tracking hooks so the factory +- * can wire them into the SDK session it creates. +- * +- * In production, the factory calls `CopilotClient.createSession()` or +- * `resumeSession()`. In tests, it returns a mock wrapper directly. +- */ +-export type SessionWrapperFactory = (callbacks: { +- readonly onPermissionRequest: (request: PermissionRequest) => Promise; +- readonly onUserInputRequest: (request: IUserInputRequest, invocation: { sessionId: string }) => Promise; +- readonly hooks: { +- readonly onPreToolUse: (input: { toolName: string; toolArgs: unknown }) => Promise; +- readonly onPostToolUse: (input: { toolName: string; toolArgs: unknown }) => Promise; +- }; +-}) => Promise; +- +-/** Matches the SDK's `UserInputRequest` which is not re-exported from the package entry point. */ +-interface IUserInputRequest { +- question: string; +- choices?: string[]; +- allowFreeform?: boolean; +-} +- +-/** Matches the SDK's `UserInputResponse` which is not re-exported from the package entry point. */ +-interface IUserInputResponse { +- answer: string; +- wasFreeform: boolean; +-} +- +-function tryStringify(value: unknown): string | undefined { +- try { +- return JSON.stringify(value); +- } catch { +- return undefined; +- } +-} +- +-/** +- * Derives display fields from a permission request for the tool confirmation UI. +- */ +-function getPermissionDisplay(request: { kind: string;[key: string]: unknown }): { +- confirmationTitle: string; +- invocationMessage: string; +- toolInput?: string; +- /** Normalized permission kind for auto-approval routing. */ +- permissionKind: string; +-} { +- const path = typeof request.path === 'string' ? request.path : (typeof request.fileName === 'string' ? request.fileName : undefined); +- const fullCommandText = typeof request.fullCommandText === 'string' ? request.fullCommandText : undefined; +- const intention = typeof request.intention === 'string' ? request.intention : undefined; +- const serverName = typeof request.serverName === 'string' ? request.serverName : undefined; +- const toolName = typeof request.toolName === 'string' ? request.toolName : undefined; +- +- switch (request.kind) { +- case 'shell': +- return { +- confirmationTitle: localize('copilot.permission.shell.title', "Run in terminal"), +- invocationMessage: intention ?? localize('copilot.permission.shell.message', "Run command"), +- toolInput: fullCommandText, +- permissionKind: 'shell', +- }; +- case 'custom-tool': { +- // Custom tool overrides (e.g. our shell tool). Extract the actual +- // tool args from the SDK's wrapper envelope. +- const args = typeof request.args === 'object' && request.args !== null ? request.args as Record : undefined; +- const command = typeof args?.command === 'string' ? args.command : undefined; +- const sdkToolName = typeof request.toolName === 'string' ? request.toolName : undefined; +- if (command && sdkToolName && isShellTool(sdkToolName)) { +- return { +- confirmationTitle: localize('copilot.permission.shell.title', "Run in terminal"), +- invocationMessage: localize('copilot.permission.shell.message', "Run command"), +- toolInput: command, +- permissionKind: 'shell', +- }; +- } +- return { +- confirmationTitle: toolName ?? localize('copilot.permission.default.title', "Permission request"), +- invocationMessage: localize('copilot.permission.default.message', "Permission request"), +- toolInput: args ? tryStringify(args) : tryStringify(request), +- permissionKind: request.kind, +- }; +- } +- case 'write': +- return { +- confirmationTitle: localize('copilot.permission.write.title', "Write file"), +- invocationMessage: path ? localize('copilot.permission.write.message', "Edit {0}", path) : localize('copilot.permission.write.messageGeneric', "Edit file"), +- toolInput: tryStringify(path ? { path } : request) ?? undefined, +- permissionKind: 'write', +- }; +- case 'mcp': { +- const title = toolName ?? localize('copilot.permission.mcp.defaultTool', "MCP Tool"); +- return { +- confirmationTitle: serverName ? `${serverName}: ${title}` : title, +- invocationMessage: serverName ? `${serverName}: ${title}` : title, +- toolInput: tryStringify({ serverName, toolName }) ?? undefined, +- permissionKind: 'mcp', +- }; +- } +- case 'read': +- return { +- confirmationTitle: localize('copilot.permission.read.title', "Read file"), +- invocationMessage: intention ?? localize('copilot.permission.read.message', "Read file"), +- toolInput: tryStringify(path ? { path, intention } : request) ?? undefined, +- permissionKind: 'read', +- }; +- default: +- return { +- confirmationTitle: localize('copilot.permission.default.title', "Permission request"), +- invocationMessage: localize('copilot.permission.default.message', "Permission request"), +- toolInput: tryStringify(request) ?? undefined, +- permissionKind: request.kind, +- }; +- } +-} +- +-/** +- * Encapsulates a single Copilot SDK session and all its associated bookkeeping. +- * +- * Created by {@link CopilotAgent}, one instance per active session. Disposing +- * this class tears down all per-session resources (SDK wrapper, edit tracker, +- * database reference, pending permissions). +- */ +-export class CopilotAgentSession extends Disposable { +- readonly sessionId: string; +- readonly sessionUri: URI; +- +- /** Tracks active tool invocations so we can produce past-tense messages on completion. */ +- private readonly _activeToolCalls = new Map | undefined }>(); +- /** Pending permission requests awaiting a renderer-side decision. */ +- private readonly _pendingPermissions = new Map>(); +- /** Pending user input requests awaiting a renderer-side answer. */ +- private readonly _pendingUserInputs = new Map }>; questionId: string }>(); +- /** File edit tracker for this session. */ +- private readonly _editTracker: FileEditTracker; +- /** Session database reference. */ +- private readonly _databaseRef: IReference; +- /** Protocol turn ID set by {@link send}, used for file edit tracking. */ +- private _turnId = ''; +- /** SDK session wrapper, set by {@link initializeSession}. */ +- private _wrapper!: CopilotSessionWrapper; +- +- private readonly _workingDirectory: URI | undefined; +- +- constructor( +- sessionUri: URI, +- rawSessionId: string, +- workingDirectory: URI | undefined, +- private readonly _onDidSessionProgress: Emitter, +- private readonly _wrapperFactory: SessionWrapperFactory, +- private readonly _shellManager: ShellManager | undefined, +- @IInstantiationService private readonly _instantiationService: IInstantiationService, +- @ILogService private readonly _logService: ILogService, +- @ISessionDataService sessionDataService: ISessionDataService, +- ) { +- super(); +- this.sessionId = rawSessionId; +- this.sessionUri = sessionUri; +- this._workingDirectory = workingDirectory; +- +- this._databaseRef = sessionDataService.openDatabase(sessionUri); +- this._register(toDisposable(() => this._databaseRef.dispose())); +- +- this._editTracker = this._instantiationService.createInstance(FileEditTracker, sessionUri.toString(), this._databaseRef.object); +- +- this._register(toDisposable(() => this._denyPendingPermissions())); +- this._register(toDisposable(() => this._shellManager?.dispose())); +- this._register(toDisposable(() => this._cancelPendingUserInputs())); +- } +- +- /** +- * Creates (or resumes) the SDK session via the injected factory and +- * wires up all event listeners. Must be called exactly once after +- * construction before using the session. +- */ +- async initializeSession(): Promise { +- this._wrapper = this._register(await this._wrapperFactory({ +- onPermissionRequest: request => this.handlePermissionRequest(request), +- onUserInputRequest: (request, invocation) => this.handleUserInputRequest(request, invocation), +- hooks: { +- onPreToolUse: async input => { +- if (isEditTool(input.toolName)) { +- const filePath = getEditFilePath(input.toolArgs); +- if (filePath) { +- await this._editTracker.trackEditStart(filePath); +- } +- } +- }, +- onPostToolUse: async input => { +- if (isEditTool(input.toolName)) { +- const filePath = getEditFilePath(input.toolArgs); +- if (filePath) { +- await this._editTracker.completeEdit(filePath); +- } +- } +- }, +- }, +- })); +- this._subscribeToEvents(); +- this._subscribeForLogging(); +- } +- +- // ---- session operations ------------------------------------------------- +- +- async send(prompt: string, attachments?: IAgentAttachment[], turnId?: string): Promise { +- if (turnId) { +- this._turnId = turnId; +- } +- this._logService.info(`[Copilot:${this.sessionId}] sendMessage called: "${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}" (${attachments?.length ?? 0} attachments)`); +- +- const sdkAttachments = attachments?.map(a => { +- if (a.type === 'selection') { +- return { type: 'selection' as const, filePath: a.path, displayName: a.displayName ?? a.path, text: a.text, selection: a.selection }; +- } +- return { type: a.type, path: a.path, displayName: a.displayName }; +- }); +- if (sdkAttachments?.length) { +- this._logService.trace(`[Copilot:${this.sessionId}] Attachments: ${JSON.stringify(sdkAttachments.map(a => ({ type: a.type, path: a.type === 'selection' ? a.filePath : a.path })))}`); +- } +- +- await this._wrapper.session.send({ prompt, attachments: sdkAttachments }); +- this._logService.info(`[Copilot:${this.sessionId}] session.send() returned`); +- } +- +- async sendSteering(steeringMessage: IPendingMessage): Promise { +- this._logService.info(`[Copilot:${this.sessionId}] Sending steering message: "${steeringMessage.userMessage.text.substring(0, 100)}"`); +- try { +- await this._wrapper.session.send({ +- prompt: steeringMessage.userMessage.text, +- mode: 'immediate', +- }); +- this._onDidSessionProgress.fire({ +- session: this.sessionUri, +- type: 'steering_consumed', +- id: steeringMessage.id, +- }); +- } catch (err) { +- this._logService.error(`[Copilot:${this.sessionId}] Steering message failed`, err); +- } +- } +- +- async getMessages(): Promise<(IAgentMessageEvent | IAgentToolStartEvent | IAgentToolCompleteEvent | IAgentSubagentStartedEvent)[]> { +- const events = await this._wrapper.session.getMessages(); +- let db: ISessionDatabase | undefined; +- try { +- db = this._databaseRef.object; +- } catch { +- // Database may not exist yet — that's fine +- } +- return mapSessionEvents(this.sessionUri, db, events); +- } +- +- async abort(): Promise { +- this._logService.info(`[Copilot:${this.sessionId}] Aborting session...`); +- this._denyPendingPermissions(); +- await this._wrapper.session.abort(); +- } +- +- /** +- * Explicitly destroys the underlying SDK session and waits for cleanup +- * to complete. Call this before {@link dispose} when you need to ensure +- * the session's on-disk data is no longer locked (e.g. before +- * truncation or fork operations that modify the session files). +- */ +- async destroySession(): Promise { +- await this._wrapper.session.destroy(); +- } +- +- async setModel(model: string): Promise { +- this._logService.info(`[Copilot:${this.sessionId}] Changing model to: ${model}`); +- await this._wrapper.session.setModel(model); +- } +- +- // ---- permission handling ------------------------------------------------ +- +- /** +- * Handles a permission request from the SDK by firing a `tool_ready` event +- * (which transitions the tool to PendingConfirmation) and waiting for the +- * side-effects layer to respond via {@link respondToPermissionRequest}. +- */ +- async handlePermissionRequest( +- request: PermissionRequest, +- ): Promise { +- this._logService.info(`[Copilot:${this.sessionId}] Permission request: kind=${request.kind}`); +- +- // Auto-approve reads inside the working directory +- if (request.kind === 'read') { +- const requestPath = typeof request.path === 'string' ? request.path : undefined; +- if (requestPath && this._workingDirectory && extUriBiasedIgnorePathCase.isEqualOrParent(normalizePath(URI.file(requestPath)), this._workingDirectory)) { +- this._logService.trace(`[Copilot:${this.sessionId}] Auto-approving read inside working directory: ${requestPath}`); +- return { kind: 'approved' }; +- } +- } +- +- const toolCallId = request.toolCallId; +- if (!toolCallId) { +- // TODO: handle permission requests without a toolCallId by creating a synthetic tool call +- this._logService.warn(`[Copilot:${this.sessionId}] Permission request without toolCallId, auto-denying: kind=${request.kind}`); +- return { kind: 'denied-interactively-by-user' }; +- } +- +- this._logService.info(`[Copilot:${this.sessionId}] Requesting confirmation for tool call: ${toolCallId}`); +- +- const deferred = new DeferredPromise(); +- this._pendingPermissions.set(toolCallId, deferred); +- +- // Derive display information from the permission request kind +- const { confirmationTitle, invocationMessage, toolInput, permissionKind } = getPermissionDisplay(request); +- +- // Fire a tool_ready event to transition the tool to PendingConfirmation +- this._onDidSessionProgress.fire({ +- session: this.sessionUri, +- type: 'tool_ready', +- toolCallId, +- invocationMessage, +- toolInput, +- confirmationTitle, +- permissionKind, +- permissionPath: typeof request.path === 'string' ? request.path : (typeof request.fileName === 'string' ? request.fileName : undefined), +- }); +- +- const approved = await deferred.p; +- this._logService.info(`[Copilot:${this.sessionId}] Permission response: toolCallId=${toolCallId}, approved=${approved}`); +- return { kind: approved ? 'approved' : 'denied-interactively-by-user' }; +- } +- +- respondToPermissionRequest(requestId: string, approved: boolean): boolean { +- const deferred = this._pendingPermissions.get(requestId); +- if (deferred) { +- this._pendingPermissions.delete(requestId); +- deferred.complete(approved); +- return true; +- } +- return false; +- } +- +- // ---- user input handling ------------------------------------------------ +- +- /** +- * Handles a user input request from the SDK (ask_user tool) by firing a +- * `user_input_request` progress event and waiting for the renderer to +- * respond via {@link respondToUserInputRequest}. +- */ +- async handleUserInputRequest( +- request: IUserInputRequest, +- _invocation: { sessionId: string }, +- ): Promise { +- const requestId = generateUuid(); +- const questionId = generateUuid(); +- this._logService.info(`[Copilot:${this.sessionId}] User input request: requestId=${requestId}, question="${request.question.substring(0, 100)}"`); +- +- const deferred = new DeferredPromise<{ response: SessionInputResponseKind; answers?: Record }>(); +- this._pendingUserInputs.set(requestId, { deferred, questionId }); +- +- // Build the protocol ISessionInputRequest from the SDK's simple format +- const inputRequest: ISessionInputRequest = { +- id: requestId, +- message: request.question, +- questions: [request.choices && request.choices.length > 0 +- ? { +- kind: SessionInputQuestionKind.SingleSelect, +- id: questionId, +- message: request.question, +- required: true, +- options: request.choices.map(c => ({ id: c, label: c })), +- allowFreeformInput: request.allowFreeform ?? true, +- } +- : { +- kind: SessionInputQuestionKind.Text, +- id: questionId, +- message: request.question, +- required: true, +- }, +- ], +- }; +- +- this._onDidSessionProgress.fire({ +- session: this.sessionUri, +- type: 'user_input_request', +- request: inputRequest, +- }); +- +- const result = await deferred.p; +- this._logService.info(`[Copilot:${this.sessionId}] User input response: requestId=${requestId}, response=${result.response}`); +- +- if (result.response !== SessionInputResponseKind.Accept || !result.answers) { +- return { answer: '', wasFreeform: true }; +- } +- +- // Extract the answer for our single question +- const answer = result.answers[questionId]; +- if (!answer || answer.state === SessionInputAnswerState.Skipped) { +- return { answer: '', wasFreeform: true }; +- } +- +- const { value: val } = answer; +- if (val.kind === SessionInputAnswerValueKind.Text) { +- return { answer: val.value, wasFreeform: true }; +- } else if (val.kind === SessionInputAnswerValueKind.Selected) { +- const wasFreeform = !request.choices?.includes(val.value); +- return { answer: val.value, wasFreeform }; +- } +- +- return { answer: '', wasFreeform: true }; +- } +- +- respondToUserInputRequest(requestId: string, response: SessionInputResponseKind, answers?: Record): boolean { +- const pending = this._pendingUserInputs.get(requestId); +- if (pending) { +- this._pendingUserInputs.delete(requestId); +- pending.deferred.complete({ response, answers }); +- return true; +- } +- return false; +- } +- +- // ---- event wiring ------------------------------------------------------- +- +- private _subscribeToEvents(): void { +- const wrapper = this._wrapper; +- const sessionId = this.sessionId; +- const session = this.sessionUri; +- +- this._register(wrapper.onMessageDelta(e => { +- this._logService.trace(`[Copilot:${sessionId}] delta: ${e.data.deltaContent}`); +- this._onDidSessionProgress.fire({ +- session, +- type: 'delta', +- messageId: e.data.messageId, +- content: e.data.deltaContent, +- parentToolCallId: e.data.parentToolCallId, +- }); +- })); +- +- this._register(wrapper.onMessage(e => { +- this._logService.info(`[Copilot:${sessionId}] Full message received: ${e.data.content.length} chars`); +- this._onDidSessionProgress.fire({ +- session, +- type: 'message', +- role: 'assistant', +- messageId: e.data.messageId, +- content: e.data.content, +- toolRequests: e.data.toolRequests?.map(tr => ({ +- toolCallId: tr.toolCallId, +- name: tr.name, +- arguments: tr.arguments !== undefined ? tryStringify(tr.arguments) : undefined, +- type: tr.type, +- })), +- reasoningOpaque: e.data.reasoningOpaque, +- reasoningText: e.data.reasoningText, +- encryptedContent: e.data.encryptedContent, +- parentToolCallId: e.data.parentToolCallId, +- }); +- })); +- +- this._register(wrapper.onToolStart(e => { +- if (isHiddenTool(e.data.toolName)) { +- this._logService.trace(`[Copilot:${sessionId}] Tool started (hidden): ${e.data.toolName}`); +- return; +- } +- this._logService.info(`[Copilot:${sessionId}] Tool started: ${e.data.toolName}`); +- const toolArgs = e.data.arguments !== undefined ? tryStringify(e.data.arguments) : undefined; +- let parameters: Record | undefined; +- if (toolArgs) { +- try { parameters = JSON.parse(toolArgs) as Record; } catch { /* ignore */ } +- } +- const displayName = getToolDisplayName(e.data.toolName); +- this._activeToolCalls.set(e.data.toolCallId, { toolName: e.data.toolName, displayName, parameters }); +- const toolKind = getToolKind(e.data.toolName); +- +- this._onDidSessionProgress.fire({ +- session, +- type: 'tool_start', +- toolCallId: e.data.toolCallId, +- toolName: e.data.toolName, +- displayName, +- invocationMessage: getInvocationMessage(e.data.toolName, displayName, parameters), +- toolInput: getToolInputString(e.data.toolName, parameters, toolArgs), +- toolKind, +- language: toolKind === 'terminal' ? getShellLanguage(e.data.toolName) : undefined, +- toolArguments: toolArgs, +- mcpServerName: e.data.mcpServerName, +- mcpToolName: e.data.mcpToolName, +- parentToolCallId: e.data.parentToolCallId, +- }); +- })); +- +- this._register(wrapper.onToolComplete(async e => { +- const tracked = this._activeToolCalls.get(e.data.toolCallId); +- if (!tracked) { +- return; +- } +- this._logService.info(`[Copilot:${sessionId}] Tool completed: ${e.data.toolCallId}`); +- this._activeToolCalls.delete(e.data.toolCallId); +- const displayName = tracked.displayName; +- const toolOutput = e.data.error?.message ?? e.data.result?.content; +- +- const content: IToolResultContent[] = []; +- if (toolOutput !== undefined) { +- content.push({ type: ToolResultContentType.Text, text: toolOutput }); +- } +- +- const filePath = isEditTool(tracked.toolName) ? getEditFilePath(tracked.parameters) : undefined; +- if (filePath) { +- try { +- const fileEdit = await this._editTracker.takeCompletedEdit(this._turnId, e.data.toolCallId, filePath); +- if (fileEdit) { +- content.push(fileEdit); +- } +- } catch (err) { +- this._logService.warn(`[Copilot:${sessionId}] Failed to take completed edit`, err); +- } +- } +- +- // Add terminal content reference for shell tools +- if (isShellTool(tracked.toolName) && this._shellManager) { +- const terminalUri = this._shellManager.getTerminalUriForToolCall(e.data.toolCallId); +- if (terminalUri) { +- content.push({ +- type: ToolResultContentType.Terminal, +- resource: terminalUri, +- title: tracked.displayName, +- }); +- } +- } +- +- this._onDidSessionProgress.fire({ +- session, +- type: 'tool_complete', +- toolCallId: e.data.toolCallId, +- result: { +- success: e.data.success, +- pastTenseMessage: getPastTenseMessage(tracked.toolName, displayName, tracked.parameters, e.data.success), +- content: content.length > 0 ? content : undefined, +- error: e.data.error, +- }, +- isUserRequested: e.data.isUserRequested, +- toolTelemetry: e.data.toolTelemetry !== undefined ? tryStringify(e.data.toolTelemetry) : undefined, +- parentToolCallId: e.data.parentToolCallId, +- }); +- })); +- +- this._register(wrapper.onIdle(() => { +- this._logService.info(`[Copilot:${sessionId}] Session idle`); +- this._onDidSessionProgress.fire({ session, type: 'idle' }); +- })); +- +- this._register(wrapper.onSubagentStarted(e => { +- this._logService.info(`[Copilot:${sessionId}] Subagent started: toolCallId=${e.data.toolCallId}, agent=${e.data.agentName}`); +- this._onDidSessionProgress.fire({ +- session, +- type: 'subagent_started', +- toolCallId: e.data.toolCallId, +- agentName: e.data.agentName, +- agentDisplayName: e.data.agentDisplayName, +- agentDescription: e.data.agentDescription, +- }); +- })); +- +- this._register(wrapper.onSessionError(e => { +- this._logService.error(`[Copilot:${sessionId}] Session error: ${e.data.errorType} - ${e.data.message}`); +- this._onDidSessionProgress.fire({ +- session, +- type: 'error', +- errorType: e.data.errorType, +- message: e.data.message, +- stack: e.data.stack, +- }); +- })); +- +- this._register(wrapper.onUsage(e => { +- this._logService.trace(`[Copilot:${sessionId}] Usage: model=${e.data.model}, in=${e.data.inputTokens ?? '?'}, out=${e.data.outputTokens ?? '?'}, cacheRead=${e.data.cacheReadTokens ?? '?'}`); +- this._onDidSessionProgress.fire({ +- session, +- type: 'usage', +- inputTokens: e.data.inputTokens, +- outputTokens: e.data.outputTokens, +- model: e.data.model, +- cacheReadTokens: e.data.cacheReadTokens, +- }); +- })); +- +- this._register(wrapper.onReasoningDelta(e => { +- this._logService.trace(`[Copilot:${sessionId}] Reasoning delta: ${e.data.deltaContent.length} chars`); +- this._onDidSessionProgress.fire({ +- session, +- type: 'reasoning', +- content: e.data.deltaContent, +- }); +- })); +- } +- +- private _subscribeForLogging(): void { +- const wrapper = this._wrapper; +- const sessionId = this.sessionId; +- +- this._register(wrapper.onSessionStart(e => { +- this._logService.trace(`[Copilot:${sessionId}] Session started: model=${e.data.selectedModel ?? 'default'}, producer=${e.data.producer}`); +- })); +- +- this._register(wrapper.onSessionResume(e => { +- this._logService.trace(`[Copilot:${sessionId}] Session resumed: eventCount=${e.data.eventCount}`); +- })); +- +- this._register(wrapper.onSessionInfo(e => { +- this._logService.trace(`[Copilot:${sessionId}] Session info [${e.data.infoType}]: ${e.data.message}`); +- })); +- +- this._register(wrapper.onSessionModelChange(e => { +- this._logService.trace(`[Copilot:${sessionId}] Model changed: ${e.data.previousModel ?? '(none)'} -> ${e.data.newModel}`); +- })); +- +- this._register(wrapper.onSessionHandoff(e => { +- this._logService.trace(`[Copilot:${sessionId}] Session handoff: sourceType=${e.data.sourceType}, remoteSessionId=${e.data.remoteSessionId ?? '(none)'}`); +- })); +- +- this._register(wrapper.onSessionTruncation(e => { +- this._logService.trace(`[Copilot:${sessionId}] Session truncation: removed ${e.data.tokensRemovedDuringTruncation} tokens, ${e.data.messagesRemovedDuringTruncation} messages`); +- })); +- +- this._register(wrapper.onSessionSnapshotRewind(e => { +- this._logService.trace(`[Copilot:${sessionId}] Snapshot rewind: upTo=${e.data.upToEventId}, eventsRemoved=${e.data.eventsRemoved}`); +- })); +- +- this._register(wrapper.onSessionShutdown(e => { +- this._logService.trace(`[Copilot:${sessionId}] Session shutdown: type=${e.data.shutdownType}, premiumRequests=${e.data.totalPremiumRequests}, apiDuration=${e.data.totalApiDurationMs}ms`); +- })); +- +- this._register(wrapper.onSessionUsageInfo(e => { +- this._logService.trace(`[Copilot:${sessionId}] Usage info: ${e.data.currentTokens}/${e.data.tokenLimit} tokens, ${e.data.messagesLength} messages`); +- })); +- +- this._register(wrapper.onSessionCompactionStart(() => { +- this._logService.trace(`[Copilot:${sessionId}] Compaction started`); +- })); +- +- this._register(wrapper.onSessionCompactionComplete(e => { +- this._logService.trace(`[Copilot:${sessionId}] Compaction complete: success=${e.data.success}, tokensRemoved=${e.data.tokensRemoved ?? '?'}`); +- })); +- +- this._register(wrapper.onUserMessage(e => { +- this._logService.trace(`[Copilot:${sessionId}] User message: ${e.data.content.length} chars, ${e.data.attachments?.length ?? 0} attachments`); +- })); +- +- this._register(wrapper.onPendingMessagesModified(() => { +- this._logService.trace(`[Copilot:${sessionId}] Pending messages modified`); +- })); +- +- this._register(wrapper.onTurnStart(e => { +- this._logService.trace(`[Copilot:${sessionId}] Turn started: ${e.data.turnId}`); +- })); +- +- this._register(wrapper.onIntent(e => { +- this._logService.trace(`[Copilot:${sessionId}] Intent: ${e.data.intent}`); +- })); +- +- this._register(wrapper.onReasoning(e => { +- this._logService.trace(`[Copilot:${sessionId}] Reasoning: ${e.data.content.length} chars`); +- })); +- +- this._register(wrapper.onTurnEnd(e => { +- this._logService.trace(`[Copilot:${sessionId}] Turn ended: ${e.data.turnId}`); +- })); +- +- this._register(wrapper.onAbort(e => { +- this._logService.trace(`[Copilot:${sessionId}] Aborted: ${e.data.reason}`); +- })); +- +- this._register(wrapper.onToolUserRequested(e => { +- this._logService.trace(`[Copilot:${sessionId}] Tool user-requested: ${e.data.toolName} (${e.data.toolCallId})`); +- })); +- +- this._register(wrapper.onToolPartialResult(e => { +- this._logService.trace(`[Copilot:${sessionId}] Tool partial result: ${e.data.toolCallId} (${e.data.partialOutput.length} chars)`); +- })); +- +- this._register(wrapper.onToolProgress(e => { +- this._logService.trace(`[Copilot:${sessionId}] Tool progress: ${e.data.toolCallId} - ${e.data.progressMessage}`); +- })); +- +- this._register(wrapper.onSkillInvoked(e => { +- this._logService.trace(`[Copilot:${sessionId}] Skill invoked: ${e.data.name} (${e.data.path})`); +- })); +- +- this._register(wrapper.onSubagentStarted(e => { +- this._logService.trace(`[Copilot:${sessionId}] Subagent started: ${e.data.agentName} (${e.data.agentDisplayName})`); +- })); +- +- this._register(wrapper.onSubagentCompleted(e => { +- this._logService.trace(`[Copilot:${sessionId}] Subagent completed: ${e.data.agentName}`); +- })); +- +- this._register(wrapper.onSubagentFailed(e => { +- this._logService.error(`[Copilot:${sessionId}] Subagent failed: ${e.data.agentName} - ${e.data.error}`); +- })); +- +- this._register(wrapper.onSubagentSelected(e => { +- this._logService.trace(`[Copilot:${sessionId}] Subagent selected: ${e.data.agentName}`); +- })); +- +- this._register(wrapper.onHookStart(e => { +- this._logService.trace(`[Copilot:${sessionId}] Hook started: ${e.data.hookType} (${e.data.hookInvocationId})`); +- })); +- +- this._register(wrapper.onHookEnd(e => { +- this._logService.trace(`[Copilot:${sessionId}] Hook ended: ${e.data.hookType} (${e.data.hookInvocationId}), success=${e.data.success}`); +- })); +- +- this._register(wrapper.onSystemMessage(e => { +- this._logService.trace(`[Copilot:${sessionId}] System message [${e.data.role}]: ${e.data.content.length} chars`); +- })); +- } +- +- // ---- cleanup ------------------------------------------------------------ +- +- private _denyPendingPermissions(): void { +- for (const [, deferred] of this._pendingPermissions) { +- deferred.complete(false); +- } +- this._pendingPermissions.clear(); +- } +- +- private _cancelPendingUserInputs(): void { +- for (const [, pending] of this._pendingUserInputs) { +- pending.deferred.complete({ response: SessionInputResponseKind.Cancel }); +- } +- this._pendingUserInputs.clear(); +- } +-} +diff --git a/src/vs/platform/agentHost/node/copilot/copilotGitProject.ts b/src/vs/platform/agentHost/node/copilot/copilotGitProject.ts +deleted file mode 100644 +index c1e2d22f..00000000 +--- a/src/vs/platform/agentHost/node/copilot/copilotGitProject.ts ++++ /dev/null +@@ -1,87 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import * as cp from 'child_process'; +-import { Schemas } from '../../../../base/common/network.js'; +-import { basename } from '../../../../base/common/path.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import type { IAgentSessionProjectInfo } from '../../common/agentService.js'; +- +-export interface ICopilotSessionContext { +- readonly cwd?: string; +- readonly gitRoot?: string; +- readonly repository?: string; +-} +- +-function execGit(cwd: string, args: string[]): Promise { +- return new Promise((resolve, reject) => { +- cp.execFile('git', args, { cwd, encoding: 'utf8' }, (error, stdout) => { +- if (error) { +- reject(error); +- return; +- } +- resolve(stdout.trim()); +- }); +- }); +-} +- +-export async function resolveGitProject(workingDirectory: URI | undefined): Promise { +- if (!workingDirectory || workingDirectory.scheme !== Schemas.file) { +- return undefined; +- } +- +- const cwd = workingDirectory.fsPath; +- try { +- if ((await execGit(cwd, ['rev-parse', '--is-inside-work-tree'])) !== 'true') { +- return undefined; +- } +- } catch { +- return undefined; +- } +- +- let projectPath: string | undefined; +- try { +- const worktreeList = await execGit(cwd, ['worktree', 'list', '--porcelain']); +- projectPath = worktreeList.split(/\r?\n/).find(line => line.startsWith('worktree '))?.substring('worktree '.length); +- } catch { +- // Fall back to the current worktree root below. +- } +- +- if (!projectPath) { +- try { +- projectPath = await execGit(cwd, ['rev-parse', '--show-toplevel']); +- } catch { +- return undefined; +- } +- } +- +- const uri = URI.file(projectPath); +- return { uri, displayName: basename(uri.fsPath) || uri.toString() }; +-} +- +-export function projectFromRepository(repository: string): IAgentSessionProjectInfo | undefined { +- const uri = repository.includes('://') ? URI.parse(repository) : URI.parse(`https://github.com/${repository}`); +- const rawDisplayName = basename(uri.path) || repository.split('/').filter(Boolean).pop() || repository; +- const displayName = rawDisplayName.endsWith('.git') ? rawDisplayName.slice(0, -'.git'.length) : rawDisplayName; +- return { uri, displayName }; +-} +- +-export async function projectFromCopilotContext(context: ICopilotSessionContext | undefined): Promise { +- const workingDirectory = typeof context?.cwd === 'string' +- ? URI.file(context.cwd) +- : typeof context?.gitRoot === 'string' +- ? URI.file(context.gitRoot) +- : undefined; +- const gitProject = await resolveGitProject(workingDirectory); +- if (gitProject) { +- return gitProject; +- } +- +- if (context?.repository) { +- return projectFromRepository(context.repository); +- } +- +- return undefined; +-} +diff --git a/src/vs/platform/agentHost/node/copilot/copilotPluginConverters.ts b/src/vs/platform/agentHost/node/copilot/copilotPluginConverters.ts +deleted file mode 100644 +index c08d7e5f..00000000 +--- a/src/vs/platform/agentHost/node/copilot/copilotPluginConverters.ts ++++ /dev/null +@@ -1,341 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import { spawn } from 'child_process'; +-import type { CustomAgentConfig, MCPServerConfig, SessionConfig } from '@github/copilot-sdk'; +-import { OperatingSystem, OS } from '../../../../base/common/platform.js'; +-import { IFileService } from '../../../files/common/files.js'; +-import { McpServerType } from '../../../mcp/common/mcpPlatformTypes.js'; +-import type { IMcpServerDefinition, INamedPluginResource, IParsedHookCommand, IParsedHookGroup, IParsedPlugin } from '../../../agentPlugins/common/pluginParsers.js'; +-import { dirname } from '../../../../base/common/path.js'; +- +-type SessionHooks = NonNullable; +- +-// --------------------------------------------------------------------------- +-// MCP servers +-// --------------------------------------------------------------------------- +- +-/** +- * Converts parsed MCP server definitions into the SDK's `mcpServers` config. +- */ +-export function toSdkMcpServers(defs: readonly IMcpServerDefinition[]): Record { +- const result: Record = {}; +- for (const def of defs) { +- const config = def.configuration; +- if (config.type === McpServerType.LOCAL) { +- result[def.name] = { +- type: 'local', +- command: config.command, +- args: config.args ? [...config.args] : [], +- tools: ['*'], +- ...(config.env && { env: toStringEnv(config.env) }), +- ...(config.cwd && { cwd: config.cwd }), +- }; +- } else { +- result[def.name] = { +- type: 'http', +- url: config.url, +- tools: ['*'], +- ...(config.headers && { headers: { ...config.headers } }), +- }; +- } +- } +- return result; +-} +- +-/** +- * Ensures all env values are strings (the SDK requires `Record`). +- */ +-function toStringEnv(env: Record): Record { +- const result: Record = {}; +- for (const [key, value] of Object.entries(env)) { +- if (value !== null) { +- result[key] = String(value); +- } +- } +- return result; +-} +- +-// --------------------------------------------------------------------------- +-// Custom agents +-// --------------------------------------------------------------------------- +- +-/** +- * Converts parsed plugin agents into the SDK's `customAgents` config. +- * Reads each agent's `.md` file to use as the prompt. +- */ +-export async function toSdkCustomAgents(agents: readonly INamedPluginResource[], fileService: IFileService): Promise { +- const configs: CustomAgentConfig[] = []; +- for (const agent of agents) { +- try { +- const content = await fileService.readFile(agent.uri); +- configs.push({ +- name: agent.name, +- prompt: content.value.toString(), +- }); +- } catch { +- // Skip agents whose file cannot be read +- } +- } +- return configs; +-} +- +-// --------------------------------------------------------------------------- +-// Skill directories +-// --------------------------------------------------------------------------- +- +-/** +- * Converts parsed plugin skills into the SDK's `skillDirectories` config. +- * The SDK expects directory paths; we extract the parent directory of each SKILL.md. +- */ +-export function toSdkSkillDirectories(skills: readonly INamedPluginResource[]): string[] { +- const seen = new Set(); +- const result: string[] = []; +- for (const skill of skills) { +- // SKILL.md parent directory is the skill directory +- const dir = dirname(skill.uri.fsPath); +- if (!seen.has(dir)) { +- seen.add(dir); +- result.push(dir); +- } +- } +- return result; +-} +- +-// --------------------------------------------------------------------------- +-// Hooks +-// --------------------------------------------------------------------------- +- +-/** +- * Resolves the effective command for the current platform from a parsed hook command. +- */ +-function resolveEffectiveCommand(hook: IParsedHookCommand, os: OperatingSystem): string | undefined { +- if (os === OperatingSystem.Windows && hook.windows) { +- return hook.windows; +- } else if (os === OperatingSystem.Macintosh && hook.osx) { +- return hook.osx; +- } else if (os === OperatingSystem.Linux && hook.linux) { +- return hook.linux; +- } +- return hook.command; +-} +- +-/** +- * Executes a hook command as a shell process. Returns the stdout on success, +- * or throws on non-zero exit code or timeout. +- */ +-function executeHookCommand(hook: IParsedHookCommand, stdin?: string): Promise { +- const command = resolveEffectiveCommand(hook, OS); +- if (!command) { +- return Promise.resolve(''); +- } +- +- const timeout = (hook.timeout ?? 30) * 1000; +- const cwd = hook.cwd?.fsPath; +- +- return new Promise((resolve, reject) => { +- const isWindows = OS === OperatingSystem.Windows; +- const shell = isWindows ? 'cmd.exe' : '/bin/sh'; +- const shellArgs = isWindows ? ['/c', command] : ['-c', command]; +- +- const child = spawn(shell, shellArgs, { +- cwd, +- env: { ...process.env, ...hook.env }, +- stdio: ['pipe', 'pipe', 'pipe'], +- timeout, +- }); +- +- let stdout = ''; +- let stderr = ''; +- +- child.stdout.on('data', (data: Buffer) => { stdout += data.toString(); }); +- child.stderr.on('data', (data: Buffer) => { stderr += data.toString(); }); +- +- if (stdin) { +- child.stdin.write(stdin); +- child.stdin.end(); +- } else { +- child.stdin.end(); +- } +- +- child.on('error', reject); +- child.on('close', (code) => { +- if (code === 0) { +- resolve(stdout); +- } else { +- reject(new Error(`Hook command exited with code ${code}: ${stderr || stdout}`)); +- } +- }); +- }); +-} +- +-/** +- * Mapping from canonical hook type identifiers to SDK SessionHooks handler keys. +- */ +-const HOOK_TYPE_TO_SDK_KEY: Record = { +- 'PreToolUse': 'onPreToolUse', +- 'PostToolUse': 'onPostToolUse', +- 'UserPromptSubmit': 'onUserPromptSubmitted', +- 'SessionStart': 'onSessionStart', +- 'SessionEnd': 'onSessionEnd', +- 'ErrorOccurred': 'onErrorOccurred', +-}; +- +-/** +- * Converts parsed plugin hooks into SDK {@link SessionHooks} handler functions. +- * +- * Each handler executes the hook's shell commands sequentially when invoked. +- * Hook types that don't map to SDK handler keys are silently ignored. +- * +- * The optional `editTrackingHooks` parameter provides internal edit-tracking +- * callbacks from {@link CopilotAgentSession} that are merged with plugin hooks. +- */ +-export function toSdkHooks( +- hookGroups: readonly IParsedHookGroup[], +- editTrackingHooks?: { +- readonly onPreToolUse: (input: { toolName: string; toolArgs: unknown }) => Promise; +- readonly onPostToolUse: (input: { toolName: string; toolArgs: unknown }) => Promise; +- }, +-): SessionHooks { +- // Group all commands by SDK handler key +- const commandsByKey = new Map(); +- for (const group of hookGroups) { +- const sdkKey = HOOK_TYPE_TO_SDK_KEY[group.type]; +- if (!sdkKey) { +- continue; +- } +- const existing = commandsByKey.get(sdkKey) ?? []; +- existing.push(...group.commands); +- commandsByKey.set(sdkKey, existing); +- } +- +- const hooks: SessionHooks = {}; +- +- // Pre-tool-use handler +- const preToolCommands = commandsByKey.get('onPreToolUse'); +- if (preToolCommands?.length || editTrackingHooks) { +- hooks.onPreToolUse = async (input: { toolName: string; toolArgs: unknown }) => { +- await editTrackingHooks?.onPreToolUse(input); +- if (preToolCommands) { +- const stdin = JSON.stringify(input); +- for (const cmd of preToolCommands) { +- try { +- const output = await executeHookCommand(cmd, stdin); +- if (output.trim()) { +- try { +- const parsed = JSON.parse(output); +- if (parsed && typeof parsed === 'object') { +- return parsed; +- } +- } catch { +- // Non-JSON output is fine — no modification +- } +- } +- } catch { +- // Hook failures are non-fatal +- } +- } +- } +- }; +- } +- +- // Post-tool-use handler +- const postToolCommands = commandsByKey.get('onPostToolUse'); +- if (postToolCommands?.length || editTrackingHooks) { +- hooks.onPostToolUse = async (input: { toolName: string; toolArgs: unknown }) => { +- await editTrackingHooks?.onPostToolUse(input); +- if (postToolCommands) { +- const stdin = JSON.stringify(input); +- for (const cmd of postToolCommands) { +- try { +- await executeHookCommand(cmd, stdin); +- } catch { +- // Hook failures are non-fatal +- } +- } +- } +- }; +- } +- +- // User-prompt-submitted handler +- const promptCommands = commandsByKey.get('onUserPromptSubmitted'); +- if (promptCommands?.length) { +- hooks.onUserPromptSubmitted = async (input: { prompt: string }) => { +- const stdin = JSON.stringify(input); +- for (const cmd of promptCommands) { +- try { +- await executeHookCommand(cmd, stdin); +- } catch { +- // Hook failures are non-fatal +- } +- } +- }; +- } +- +- // Session-start handler +- const startCommands = commandsByKey.get('onSessionStart'); +- if (startCommands?.length) { +- hooks.onSessionStart = async (input: { source: string }) => { +- const stdin = JSON.stringify(input); +- for (const cmd of startCommands) { +- try { +- await executeHookCommand(cmd, stdin); +- } catch { +- // Hook failures are non-fatal +- } +- } +- }; +- } +- +- // Session-end handler +- const endCommands = commandsByKey.get('onSessionEnd'); +- if (endCommands?.length) { +- hooks.onSessionEnd = async (input: { reason: string }) => { +- const stdin = JSON.stringify(input); +- for (const cmd of endCommands) { +- try { +- await executeHookCommand(cmd, stdin); +- } catch { +- // Hook failures are non-fatal +- } +- } +- }; +- } +- +- // Error-occurred handler +- const errorCommands = commandsByKey.get('onErrorOccurred'); +- if (errorCommands?.length) { +- hooks.onErrorOccurred = async (input: { error: string }) => { +- const stdin = JSON.stringify(input); +- for (const cmd of errorCommands) { +- try { +- await executeHookCommand(cmd, stdin); +- } catch { +- // Hook failures are non-fatal +- } +- } +- }; +- } +- +- return hooks; +-} +- +-/** +- * Checks whether two sets of parsed plugins produce equivalent SDK config. +- * Used to determine if a session needs to be refreshed. +- */ +-export function parsedPluginsEqual(a: readonly IParsedPlugin[], b: readonly IParsedPlugin[]): boolean { +- // Simple structural comparison via JSON serialization. +- // We serialize only the essential fields, replacing URIs with strings. +- const serialize = (plugins: readonly IParsedPlugin[]) => { +- return JSON.stringify(plugins.map(p => ({ +- hooks: p.hooks.map(h => ({ type: h.type, commands: h.commands.map(c => ({ command: c.command, windows: c.windows, linux: c.linux, osx: c.osx, cwd: c.cwd?.toString(), env: c.env, timeout: c.timeout })) })), +- mcpServers: p.mcpServers.map(m => ({ name: m.name, configuration: m.configuration })), +- skills: p.skills.map(s => ({ uri: s.uri.toString(), name: s.name })), +- agents: p.agents.map(a => ({ uri: a.uri.toString(), name: a.name })), +- }))); +- }; +- return serialize(a) === serialize(b); +-} +diff --git a/src/vs/platform/agentHost/node/copilot/copilotSessionWrapper.ts b/src/vs/platform/agentHost/node/copilot/copilotSessionWrapper.ts +deleted file mode 100644 +index 36ad526d..00000000 +--- a/src/vs/platform/agentHost/node/copilot/copilotSessionWrapper.ts ++++ /dev/null +@@ -1,217 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import { CopilotSession, SessionEventPayload, SessionEventType } from '@github/copilot-sdk'; +-import { Emitter, Event } from '../../../../base/common/event.js'; +-import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js'; +- +-/** +- * Thin wrapper around {@link CopilotSession} that exposes each SDK event as a +- * proper VS Code `Event`. All subscriptions and the underlying SDK session +- * are cleaned up on dispose. +- */ +-export class CopilotSessionWrapper extends Disposable { +- +- constructor(readonly session: CopilotSession) { +- super(); +- this._register(toDisposable(() => { +- session.destroy().catch(() => { /* best-effort */ }); +- })); +- } +- +- get sessionId(): string { return this.session.sessionId; } +- +- private _onMessageDelta: Event> | undefined; +- get onMessageDelta(): Event> { +- return this._onMessageDelta ??= this._sdkEvent('assistant.message_delta'); +- } +- +- private _onMessage: Event> | undefined; +- get onMessage(): Event> { +- return this._onMessage ??= this._sdkEvent('assistant.message'); +- } +- +- private _onToolStart: Event> | undefined; +- get onToolStart(): Event> { +- return this._onToolStart ??= this._sdkEvent('tool.execution_start'); +- } +- +- private _onToolComplete: Event> | undefined; +- get onToolComplete(): Event> { +- return this._onToolComplete ??= this._sdkEvent('tool.execution_complete'); +- } +- +- private _onIdle: Event> | undefined; +- get onIdle(): Event> { +- return this._onIdle ??= this._sdkEvent('session.idle'); +- } +- +- private _onSessionStart: Event> | undefined; +- get onSessionStart(): Event> { +- return this._onSessionStart ??= this._sdkEvent('session.start'); +- } +- +- private _onSessionResume: Event> | undefined; +- get onSessionResume(): Event> { +- return this._onSessionResume ??= this._sdkEvent('session.resume'); +- } +- +- private _onSessionError: Event> | undefined; +- get onSessionError(): Event> { +- return this._onSessionError ??= this._sdkEvent('session.error'); +- } +- +- private _onSessionInfo: Event> | undefined; +- get onSessionInfo(): Event> { +- return this._onSessionInfo ??= this._sdkEvent('session.info'); +- } +- +- private _onSessionModelChange: Event> | undefined; +- get onSessionModelChange(): Event> { +- return this._onSessionModelChange ??= this._sdkEvent('session.model_change'); +- } +- +- private _onSessionHandoff: Event> | undefined; +- get onSessionHandoff(): Event> { +- return this._onSessionHandoff ??= this._sdkEvent('session.handoff'); +- } +- +- private _onSessionTruncation: Event> | undefined; +- get onSessionTruncation(): Event> { +- return this._onSessionTruncation ??= this._sdkEvent('session.truncation'); +- } +- +- private _onSessionSnapshotRewind: Event> | undefined; +- get onSessionSnapshotRewind(): Event> { +- return this._onSessionSnapshotRewind ??= this._sdkEvent('session.snapshot_rewind'); +- } +- +- private _onSessionShutdown: Event> | undefined; +- get onSessionShutdown(): Event> { +- return this._onSessionShutdown ??= this._sdkEvent('session.shutdown'); +- } +- +- private _onSessionUsageInfo: Event> | undefined; +- get onSessionUsageInfo(): Event> { +- return this._onSessionUsageInfo ??= this._sdkEvent('session.usage_info'); +- } +- +- private _onSessionCompactionStart: Event> | undefined; +- get onSessionCompactionStart(): Event> { +- return this._onSessionCompactionStart ??= this._sdkEvent('session.compaction_start'); +- } +- +- private _onSessionCompactionComplete: Event> | undefined; +- get onSessionCompactionComplete(): Event> { +- return this._onSessionCompactionComplete ??= this._sdkEvent('session.compaction_complete'); +- } +- +- private _onUserMessage: Event> | undefined; +- get onUserMessage(): Event> { +- return this._onUserMessage ??= this._sdkEvent('user.message'); +- } +- +- private _onPendingMessagesModified: Event> | undefined; +- get onPendingMessagesModified(): Event> { +- return this._onPendingMessagesModified ??= this._sdkEvent('pending_messages.modified'); +- } +- +- private _onTurnStart: Event> | undefined; +- get onTurnStart(): Event> { +- return this._onTurnStart ??= this._sdkEvent('assistant.turn_start'); +- } +- +- private _onIntent: Event> | undefined; +- get onIntent(): Event> { +- return this._onIntent ??= this._sdkEvent('assistant.intent'); +- } +- +- private _onReasoning: Event> | undefined; +- get onReasoning(): Event> { +- return this._onReasoning ??= this._sdkEvent('assistant.reasoning'); +- } +- +- private _onReasoningDelta: Event> | undefined; +- get onReasoningDelta(): Event> { +- return this._onReasoningDelta ??= this._sdkEvent('assistant.reasoning_delta'); +- } +- +- private _onTurnEnd: Event> | undefined; +- get onTurnEnd(): Event> { +- return this._onTurnEnd ??= this._sdkEvent('assistant.turn_end'); +- } +- +- private _onUsage: Event> | undefined; +- get onUsage(): Event> { +- return this._onUsage ??= this._sdkEvent('assistant.usage'); +- } +- +- private _onAbort: Event> | undefined; +- get onAbort(): Event> { +- return this._onAbort ??= this._sdkEvent('abort'); +- } +- +- private _onToolUserRequested: Event> | undefined; +- get onToolUserRequested(): Event> { +- return this._onToolUserRequested ??= this._sdkEvent('tool.user_requested'); +- } +- +- private _onToolPartialResult: Event> | undefined; +- get onToolPartialResult(): Event> { +- return this._onToolPartialResult ??= this._sdkEvent('tool.execution_partial_result'); +- } +- +- private _onToolProgress: Event> | undefined; +- get onToolProgress(): Event> { +- return this._onToolProgress ??= this._sdkEvent('tool.execution_progress'); +- } +- +- private _onSkillInvoked: Event> | undefined; +- get onSkillInvoked(): Event> { +- return this._onSkillInvoked ??= this._sdkEvent('skill.invoked'); +- } +- +- private _onSubagentStarted: Event> | undefined; +- get onSubagentStarted(): Event> { +- return this._onSubagentStarted ??= this._sdkEvent('subagent.started'); +- } +- +- private _onSubagentCompleted: Event> | undefined; +- get onSubagentCompleted(): Event> { +- return this._onSubagentCompleted ??= this._sdkEvent('subagent.completed'); +- } +- +- private _onSubagentFailed: Event> | undefined; +- get onSubagentFailed(): Event> { +- return this._onSubagentFailed ??= this._sdkEvent('subagent.failed'); +- } +- +- private _onSubagentSelected: Event> | undefined; +- get onSubagentSelected(): Event> { +- return this._onSubagentSelected ??= this._sdkEvent('subagent.selected'); +- } +- +- private _onHookStart: Event> | undefined; +- get onHookStart(): Event> { +- return this._onHookStart ??= this._sdkEvent('hook.start'); +- } +- +- private _onHookEnd: Event> | undefined; +- get onHookEnd(): Event> { +- return this._onHookEnd ??= this._sdkEvent('hook.end'); +- } +- +- private _onSystemMessage: Event> | undefined; +- get onSystemMessage(): Event> { +- return this._onSystemMessage ??= this._sdkEvent('system.message'); +- } +- +- private _sdkEvent(eventType: K): Event> { +- const emitter = this._register(new Emitter>()); +- const unsubscribe = this.session.on(eventType, (data: SessionEventPayload) => emitter.fire(data)); +- this._register(toDisposable(unsubscribe)); +- return emitter.event; +- } +-} +diff --git a/src/vs/platform/agentHost/node/copilot/copilotShellTools.ts b/src/vs/platform/agentHost/node/copilot/copilotShellTools.ts +deleted file mode 100644 +index b40abea2..00000000 +--- a/src/vs/platform/agentHost/node/copilot/copilotShellTools.ts ++++ /dev/null +@@ -1,455 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import type { Tool, ToolResultObject } from '@github/copilot-sdk'; +-import { generateUuid } from '../../../../base/common/uuid.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { removeAnsiEscapeCodes } from '../../../../base/common/strings.js'; +-import * as platform from '../../../../base/common/platform.js'; +-import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; +-import { ILogService } from '../../../log/common/log.js'; +-import { TerminalClaimKind, type ITerminalSessionClaim } from '../../common/state/protocol/state.js'; +-import { IAgentHostTerminalManager } from '../agentHostTerminalManager.js'; +- +-/** +- * Maximum scrollback content (in bytes) returned to the model in tool results. +- */ +-const MAX_OUTPUT_BYTES = 80_000; +- +-/** +- * Default command timeout in milliseconds (120 seconds). +- */ +-const DEFAULT_TIMEOUT_MS = 120_000; +- +-/** +- * The sentinel prefix used to detect command completion in terminal output. +- * The full sentinel format is: `<<_EXIT_>>`. +- */ +-const SENTINEL_PREFIX = '<<(); +- private readonly _toolCallShells = new Map(); +- +- constructor( +- private readonly _sessionUri: URI, +- @IAgentHostTerminalManager private readonly _terminalManager: IAgentHostTerminalManager, +- @ILogService private readonly _logService: ILogService, +- ) { } +- +- async getOrCreateShell( +- shellType: ShellType, +- turnId: string, +- toolCallId: string, +- cwd?: string, +- ): Promise { +- for (const shell of this._shells.values()) { +- if (shell.shellType === shellType && this._terminalManager.hasTerminal(shell.terminalUri)) { +- const exitCode = this._terminalManager.getExitCode(shell.terminalUri); +- if (exitCode === undefined) { +- this._trackToolCall(toolCallId, shell.id); +- return shell; +- } +- this._shells.delete(shell.id); +- } +- } +- +- const id = generateUuid(); +- const terminalUri = `agenthost-terminal://shell/${id}`; +- +- const claim: ITerminalSessionClaim = { +- kind: TerminalClaimKind.Session, +- session: this._sessionUri.toString(), +- turnId, +- toolCallId, +- }; +- +- const shellDisplayName = shellType === 'bash' ? 'Bash' : 'PowerShell'; +- +- await this._terminalManager.createTerminal({ +- terminal: terminalUri, +- claim, +- name: shellDisplayName, +- cwd, +- }, { shell: getShellExecutable(shellType) }); +- +- const shell: IManagedShell = { id, terminalUri, shellType }; +- this._shells.set(id, shell); +- this._trackToolCall(toolCallId, id); +- this._logService.info(`[ShellManager] Created ${shellType} shell ${id} (terminal=${terminalUri})`); +- return shell; +- } +- +- private _trackToolCall(toolCallId: string, shellId: string): void { +- this._toolCallShells.set(toolCallId, shellId); +- } +- +- getTerminalUriForToolCall(toolCallId: string): string | undefined { +- const shellId = this._toolCallShells.get(toolCallId); +- if (!shellId) { +- return undefined; +- } +- return this._shells.get(shellId)?.terminalUri; +- } +- +- getShell(id: string): IManagedShell | undefined { +- return this._shells.get(id); +- } +- +- listShells(): IManagedShell[] { +- const result: IManagedShell[] = []; +- for (const shell of this._shells.values()) { +- if (this._terminalManager.hasTerminal(shell.terminalUri)) { +- result.push(shell); +- } +- } +- return result; +- } +- +- shutdownShell(id: string): boolean { +- const shell = this._shells.get(id); +- if (!shell) { +- return false; +- } +- this._terminalManager.disposeTerminal(shell.terminalUri); +- this._shells.delete(id); +- this._logService.info(`[ShellManager] Shut down shell ${id}`); +- return true; +- } +- +- dispose(): void { +- for (const shell of this._shells.values()) { +- if (this._terminalManager.hasTerminal(shell.terminalUri)) { +- this._terminalManager.disposeTerminal(shell.terminalUri); +- } +- } +- this._shells.clear(); +- this._toolCallShells.clear(); +- } +-} +- +-// --------------------------------------------------------------------------- +-// Sentinel helpers +-// --------------------------------------------------------------------------- +- +-function makeSentinelId(): string { +- return generateUuid().replace(/-/g, ''); +-} +- +-function buildSentinelCommand(sentinelId: string, shellType: ShellType): string { +- if (shellType === 'powershell') { +- return `Write-Output "${SENTINEL_PREFIX}${sentinelId}_EXIT_$LASTEXITCODE>>>"`; +- } +- return `echo "${SENTINEL_PREFIX}${sentinelId}_EXIT_$?>>>"`; +-} +- +-function parseSentinel(content: string, sentinelId: string): { found: boolean; exitCode: number; outputBeforeSentinel: string } { +- const marker = `${SENTINEL_PREFIX}${sentinelId}_EXIT_`; +- const idx = content.indexOf(marker); +- if (idx === -1) { +- return { found: false, exitCode: -1, outputBeforeSentinel: content }; +- } +- +- const outputBeforeSentinel = content.substring(0, idx); +- const afterMarker = content.substring(idx + marker.length); +- const endIdx = afterMarker.indexOf('>>>'); +- const exitCodeStr = endIdx >= 0 ? afterMarker.substring(0, endIdx) : afterMarker.trim(); +- const exitCode = parseInt(exitCodeStr, 10); +- return { +- found: true, +- exitCode: isNaN(exitCode) ? -1 : exitCode, +- outputBeforeSentinel, +- }; +-} +- +-function prepareOutputForModel(rawOutput: string): string { +- let text = removeAnsiEscapeCodes(rawOutput).trim(); +- if (text.length > MAX_OUTPUT_BYTES) { +- text = text.substring(text.length - MAX_OUTPUT_BYTES); +- } +- return text; +-} +- +-// --------------------------------------------------------------------------- +-// Tool implementations +-// --------------------------------------------------------------------------- +- +-function makeSuccessResult(text: string): ToolResultObject { +- return { textResultForLlm: text, resultType: 'success' }; +-} +- +-function makeFailureResult(text: string, error?: string): ToolResultObject { +- return { textResultForLlm: text, resultType: 'failure', error }; +-} +- +-async function executeCommandInShell( +- shell: IManagedShell, +- command: string, +- timeoutMs: number, +- terminalManager: IAgentHostTerminalManager, +- logService: ILogService, +-): Promise { +- const sentinelId = makeSentinelId(); +- const sentinelCmd = buildSentinelCommand(sentinelId, shell.shellType); +- const disposables = new DisposableStore(); +- +- const contentBefore = terminalManager.getContent(shell.terminalUri) ?? ''; +- const offsetBefore = contentBefore.length; +- +- // PTY input uses \r for line endings — the PTY translates to \r\n +- const input = `${command}\r${sentinelCmd}\r`; +- terminalManager.writeInput(shell.terminalUri, input); +- +- return new Promise(resolve => { +- let resolved = false; +- const finish = (result: ToolResultObject) => { +- if (resolved) { +- return; +- } +- resolved = true; +- disposables.dispose(); +- resolve(result); +- }; +- +- const checkForSentinel = () => { +- const fullContent = terminalManager.getContent(shell.terminalUri) ?? ''; +- // Clamp offset: the terminal manager trims content when it exceeds +- // 100k chars (slices to last 80k). If trimming happened after we +- // captured offsetBefore, scan from the start of the current buffer. +- const clampedOffset = Math.min(offsetBefore, fullContent.length); +- const newContent = fullContent.substring(clampedOffset); +- const parsed = parseSentinel(newContent, sentinelId); +- if (parsed.found) { +- const output = prepareOutputForModel(parsed.outputBeforeSentinel); +- logService.info(`[ShellTool] Command completed with exit code ${parsed.exitCode}`); +- if (parsed.exitCode === 0) { +- finish(makeSuccessResult(`Exit code: ${parsed.exitCode}\n${output}`)); +- } else { +- finish(makeFailureResult(`Exit code: ${parsed.exitCode}\n${output}`)); +- } +- } +- }; +- +- disposables.add(terminalManager.onData(shell.terminalUri, () => { +- checkForSentinel(); +- })); +- +- disposables.add(terminalManager.onExit(shell.terminalUri, (exitCode: number) => { +- logService.info(`[ShellTool] Shell exited unexpectedly with code ${exitCode}`); +- const fullContent = terminalManager.getContent(shell.terminalUri) ?? ''; +- const newContent = fullContent.substring(offsetBefore); +- const output = prepareOutputForModel(newContent); +- finish(makeFailureResult(`Shell exited with code ${exitCode}\n${output}`)); +- })); +- +- disposables.add(terminalManager.onClaimChanged(shell.terminalUri, (claim) => { +- if (claim.kind === TerminalClaimKind.Session && !claim.toolCallId) { +- logService.info(`[ShellTool] Continuing in background (claim narrowed)`); +- finish(makeSuccessResult('The user chose to continue this command in the background. The terminal is still running.')); +- } +- })); +- +- const timer = setTimeout(() => { +- logService.warn(`[ShellTool] Command timed out after ${timeoutMs}ms`); +- const fullContent = terminalManager.getContent(shell.terminalUri) ?? ''; +- const newContent = fullContent.substring(offsetBefore); +- const output = prepareOutputForModel(newContent); +- finish(makeFailureResult( +- `Command timed out after ${Math.round(timeoutMs / 1000)}s. Partial output:\n${output}`, +- 'timeout', +- )); +- }, timeoutMs); +- disposables.add(toDisposable(() => clearTimeout(timer))); +- +- checkForSentinel(); +- }); +-} +- +-// --------------------------------------------------------------------------- +-// Public factory +-// --------------------------------------------------------------------------- +- +-interface IShellToolArgs { +- command: string; +- timeout?: number; +-} +- +-interface IWriteShellArgs { +- command: string; +-} +- +-interface IReadShellArgs { +- shell_id?: string; +-} +- +-interface IShutdownShellArgs { +- shell_id?: string; +-} +- +-/** +- * Creates the set of SDK {@link Tool} definitions that override the built-in +- * Copilot CLI shell tools with PTY-backed implementations. +- * +- * Returns tools for the platform-appropriate shell (bash or powershell), +- * including companion tools (read, write, shutdown, list). +- */ +-export function createShellTools( +- shellManager: ShellManager, +- terminalManager: IAgentHostTerminalManager, +- logService: ILogService, +- // eslint-disable-next-line @typescript-eslint/no-explicit-any +-): Tool[] { +- const shellType: ShellType = platform.isWindows ? 'powershell' : 'bash'; +- +- const primaryTool: Tool = { +- name: shellType, +- description: `Execute a command in a persistent ${shellType} shell. The shell is reused across calls.`, +- parameters: { +- type: 'object', +- properties: { +- command: { type: 'string', description: 'The command to execute' }, +- timeout: { type: 'number', description: 'Timeout in milliseconds (default 120000)' }, +- }, +- required: ['command'], +- }, +- overridesBuiltInTool: true, +- handler: async (args, invocation) => { +- const shell = await shellManager.getOrCreateShell( +- shellType, +- invocation.toolCallId, +- invocation.toolCallId, +- ); +- const timeoutMs = args.timeout ?? DEFAULT_TIMEOUT_MS; +- return executeCommandInShell(shell, args.command, timeoutMs, terminalManager, logService); +- }, +- }; +- +- const readTool: Tool = { +- name: `read_${shellType}`, +- description: `Read the latest output from a running ${shellType} shell.`, +- parameters: { +- type: 'object', +- properties: { +- shell_id: { type: 'string', description: 'Shell ID to read from (optional; uses latest shell if omitted)' }, +- }, +- }, +- overridesBuiltInTool: true, +- handler: (args) => { +- const shells = shellManager.listShells(); +- const shell = args.shell_id +- ? shellManager.getShell(args.shell_id) +- : shells[shells.length - 1]; +- if (!shell) { +- return makeFailureResult('No active shell found.', 'no_shell'); +- } +- const content = terminalManager.getContent(shell.terminalUri); +- if (!content) { +- return makeSuccessResult('(no output)'); +- } +- return makeSuccessResult(prepareOutputForModel(content)); +- }, +- }; +- +- const writeTool: Tool = { +- name: `write_${shellType}`, +- description: `Send input to a running ${shellType} shell (e.g. answering a prompt, sending Ctrl+C).`, +- parameters: { +- type: 'object', +- properties: { +- command: { type: 'string', description: 'Text to write to the shell stdin' }, +- }, +- required: ['command'], +- }, +- overridesBuiltInTool: true, +- handler: (args) => { +- const shells = shellManager.listShells(); +- const shell = shells[shells.length - 1]; +- if (!shell) { +- return makeFailureResult('No active shell found.', 'no_shell'); +- } +- terminalManager.writeInput(shell.terminalUri, args.command); +- return makeSuccessResult('Input sent to shell.'); +- }, +- }; +- +- const shutdownTool: Tool = { +- name: shellType === 'bash' ? 'bash_shutdown' : `${shellType}_shutdown`, +- description: `Stop a ${shellType} shell.`, +- parameters: { +- type: 'object', +- properties: { +- shell_id: { type: 'string', description: 'Shell ID to stop (optional; stops latest shell if omitted)' }, +- }, +- }, +- overridesBuiltInTool: true, +- handler: (args) => { +- if (args.shell_id) { +- const success = shellManager.shutdownShell(args.shell_id); +- return success +- ? makeSuccessResult('Shell stopped.') +- : makeFailureResult('Shell not found.', 'not_found'); +- } +- const shells = shellManager.listShells(); +- const shell = shells[shells.length - 1]; +- if (!shell) { +- return makeFailureResult('No active shell to stop.', 'no_shell'); +- } +- shellManager.shutdownShell(shell.id); +- return makeSuccessResult('Shell stopped.'); +- }, +- }; +- +- const listTool: Tool> = { +- name: `list_${shellType}`, +- description: `List active ${shellType} shell instances.`, +- parameters: { type: 'object', properties: {} }, +- overridesBuiltInTool: true, +- handler: () => { +- const shells = shellManager.listShells(); +- if (shells.length === 0) { +- return makeSuccessResult('No active shells.'); +- } +- const descriptions = shells.map(s => { +- const exitCode = terminalManager.getExitCode(s.terminalUri); +- const status = exitCode !== undefined ? `exited (${exitCode})` : 'running'; +- return `- ${s.id}: ${s.shellType} [${status}]`; +- }); +- return makeSuccessResult(descriptions.join('\n')); +- }, +- }; +- +- return [primaryTool, readTool, writeTool, shutdownTool, listTool]; +-} +diff --git a/src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts b/src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts +index 903158d8..31c74bc2 100644 +--- a/src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts ++++ b/src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts +@@ -40,4 +40,4 @@ export interface ISessionEventToolComplete { + success: boolean; +- result?: { content?: string }; +- error?: { message: string; code?: string }; ++ result?: { content?: string; }; ++ error?: { message: string; code?: string; }; + isUserRequested?: boolean; +@@ -54,3 +54,3 @@ export interface ISessionEventMessage { + content?: string; +- toolRequests?: readonly { toolCallId: string; name: string; arguments?: unknown; type?: 'function' | 'custom' }[]; ++ toolRequests?: readonly { toolCallId: string; name: string; arguments?: unknown; type?: 'function' | 'custom'; }[]; + reasoningOpaque?: string; +@@ -63,3 +63,3 @@ export interface ISessionEventMessage { + /** Minimal event shape for session history mapping. */ +-export type ISessionEvent = ISessionEventToolStart | ISessionEventToolComplete | ISessionEventMessage | ISessionEventSubagentStarted | { type: string; data?: unknown }; ++export type ISessionEvent = ISessionEventToolStart | ISessionEventToolComplete | ISessionEventMessage | ISessionEventSubagentStarted | { type: string; data?: unknown; }; + +@@ -88,3 +88,3 @@ export async function mapSessionEvents( + const result: (IAgentMessageEvent | IAgentToolStartEvent | IAgentToolCompleteEvent | IAgentSubagentStartedEvent)[] = []; +- const toolInfoByCallId = new Map | undefined }>(); ++ const toolInfoByCallId = new Map | undefined; }>(); + +diff --git a/src/vs/platform/agentHost/test/node/agentService.test.ts b/src/vs/platform/agentHost/test/node/agentService.test.ts +index 8533a728..ca1195b3 100644 +--- a/src/vs/platform/agentHost/test/node/agentService.test.ts ++++ b/src/vs/platform/agentHost/test/node/agentService.test.ts +@@ -202,3 +202,3 @@ suite('AgentService (node dispatcher)', () => { + // Manually add the session to the mock +- (agent as unknown as { _sessions: Map })._sessions.set(sessionId, sessionUri); ++ (agent as unknown as { _sessions: Map; })._sessions.set(sessionId, sessionUri); + +diff --git a/src/vs/platform/agentHost/test/node/copilotAgentForking.test.ts b/src/vs/platform/agentHost/test/node/copilotAgentForking.test.ts +deleted file mode 100644 +index d5577754..00000000 +--- a/src/vs/platform/agentHost/test/node/copilotAgentForking.test.ts ++++ /dev/null +@@ -1,705 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import assert from 'assert'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import { +- parseEventLog, +- serializeEventLog, +- findTurnBoundaryInEventLog, +- buildForkedEventLog, +- buildTruncatedEventLog, +- buildWorkspaceYaml, +- forkSessionInDb, +- truncateSessionInDb, +- type ICopilotEventLogEntry, +-} from '../../node/copilot/copilotAgentForking.js'; +- +-// ---- Test helpers ----------------------------------------------------------- +- +-function makeEntry(type: string, overrides?: Partial): ICopilotEventLogEntry { +- return { +- type, +- data: {}, +- id: `id-${Math.random().toString(36).slice(2, 8)}`, +- timestamp: new Date().toISOString(), +- parentId: null, +- ...overrides, +- }; +-} +- +-/** +- * Builds a minimal event log representing a multi-turn session. +- * Each turn = user.message → assistant.turn_start → assistant.message → assistant.turn_end. +- */ +-function buildTestEventLog(turnCount: number): ICopilotEventLogEntry[] { +- const entries: ICopilotEventLogEntry[] = []; +- let lastId: string | null = null; +- +- const sessionStart = makeEntry('session.start', { +- id: 'session-start-id', +- data: { sessionId: 'source-session', context: { cwd: '/test' } }, +- parentId: null, +- }); +- entries.push(sessionStart); +- lastId = sessionStart.id; +- +- for (let turn = 0; turn < turnCount; turn++) { +- const userMsg = makeEntry('user.message', { +- id: `user-msg-${turn}`, +- data: { content: `Turn ${turn} message` }, +- parentId: lastId, +- }); +- entries.push(userMsg); +- lastId = userMsg.id; +- +- const turnStart = makeEntry('assistant.turn_start', { +- id: `turn-start-${turn}`, +- data: { turnId: String(turn) }, +- parentId: lastId, +- }); +- entries.push(turnStart); +- lastId = turnStart.id; +- +- const assistantMsg = makeEntry('assistant.message', { +- id: `assistant-msg-${turn}`, +- data: { content: `Response ${turn}` }, +- parentId: lastId, +- }); +- entries.push(assistantMsg); +- lastId = assistantMsg.id; +- +- const turnEnd = makeEntry('assistant.turn_end', { +- id: `turn-end-${turn}`, +- parentId: lastId, +- }); +- entries.push(turnEnd); +- lastId = turnEnd.id; +- } +- +- return entries; +-} +- +-suite('CopilotAgentForking', () => { +- +- ensureNoDisposablesAreLeakedInTestSuite(); +- +- // ---- parseEventLog / serializeEventLog ------------------------------ +- +- suite('parseEventLog', () => { +- +- test('parses a single-line JSONL', () => { +- const entry = makeEntry('session.start'); +- const jsonl = JSON.stringify(entry); +- const result = parseEventLog(jsonl); +- assert.strictEqual(result.length, 1); +- assert.strictEqual(result[0].type, 'session.start'); +- }); +- +- test('parses multi-line JSONL', () => { +- const entries = [makeEntry('session.start'), makeEntry('user.message')]; +- const jsonl = entries.map(e => JSON.stringify(e)).join('\n'); +- const result = parseEventLog(jsonl); +- assert.strictEqual(result.length, 2); +- }); +- +- test('ignores empty lines', () => { +- const entry = makeEntry('session.start'); +- const jsonl = '\n' + JSON.stringify(entry) + '\n\n'; +- const result = parseEventLog(jsonl); +- assert.strictEqual(result.length, 1); +- }); +- +- test('empty input returns empty array', () => { +- assert.deepStrictEqual(parseEventLog(''), []); +- assert.deepStrictEqual(parseEventLog('\n\n'), []); +- }); +- }); +- +- suite('serializeEventLog', () => { +- +- test('round-trips correctly', () => { +- const entries = buildTestEventLog(2); +- const serialized = serializeEventLog(entries); +- const parsed = parseEventLog(serialized); +- assert.strictEqual(parsed.length, entries.length); +- for (let i = 0; i < entries.length; i++) { +- assert.strictEqual(parsed[i].id, entries[i].id); +- assert.strictEqual(parsed[i].type, entries[i].type); +- } +- }); +- +- test('ends with a newline', () => { +- const entries = [makeEntry('session.start')]; +- const serialized = serializeEventLog(entries); +- assert.ok(serialized.endsWith('\n')); +- }); +- }); +- +- // ---- findTurnBoundaryInEventLog ------------------------------------- +- +- suite('findTurnBoundaryInEventLog', () => { +- +- test('finds first turn boundary', () => { +- const entries = buildTestEventLog(3); +- const boundary = findTurnBoundaryInEventLog(entries, 0); +- // Turn 0: user.message(1) + turn_start(2) + assistant.message(3) + turn_end(4) +- // Index 4 = turn_end of turn 0 +- assert.strictEqual(boundary, 4); +- assert.strictEqual(entries[boundary].type, 'assistant.turn_end'); +- assert.strictEqual(entries[boundary].id, 'turn-end-0'); +- }); +- +- test('finds middle turn boundary', () => { +- const entries = buildTestEventLog(3); +- const boundary = findTurnBoundaryInEventLog(entries, 1); +- // Turn 1 ends at index 8 +- assert.strictEqual(boundary, 8); +- assert.strictEqual(entries[boundary].type, 'assistant.turn_end'); +- assert.strictEqual(entries[boundary].id, 'turn-end-1'); +- }); +- +- test('finds last turn boundary', () => { +- const entries = buildTestEventLog(3); +- const boundary = findTurnBoundaryInEventLog(entries, 2); +- assert.strictEqual(boundary, entries.length - 1); +- assert.strictEqual(entries[boundary].type, 'assistant.turn_end'); +- assert.strictEqual(entries[boundary].id, 'turn-end-2'); +- }); +- +- test('returns -1 for non-existent turn', () => { +- const entries = buildTestEventLog(2); +- assert.strictEqual(findTurnBoundaryInEventLog(entries, 5), -1); +- }); +- +- test('returns -1 for empty log', () => { +- assert.strictEqual(findTurnBoundaryInEventLog([], 0), -1); +- }); +- }); +- +- // ---- buildForkedEventLog -------------------------------------------- +- +- suite('buildForkedEventLog', () => { +- +- test('forks at turn 0', () => { +- const entries = buildTestEventLog(3); +- const forked = buildForkedEventLog(entries, 0, 'new-session-id'); +- +- // Should have session.start + turn 0 events (user.message, turn_start, assistant.message, turn_end) +- assert.strictEqual(forked.length, 5); +- assert.strictEqual(forked[0].type, 'session.start'); +- assert.strictEqual((forked[0].data as Record).sessionId, 'new-session-id'); +- }); +- +- test('forks at turn 1', () => { +- const entries = buildTestEventLog(3); +- const forked = buildForkedEventLog(entries, 1, 'new-session-id'); +- +- // session.start + 2 turns × 4 events = 9 events +- assert.strictEqual(forked.length, 9); +- }); +- +- test('generates unique UUIDs', () => { +- const entries = buildTestEventLog(2); +- const forked = buildForkedEventLog(entries, 0, 'new-session-id'); +- +- const ids = new Set(forked.map(e => e.id)); +- assert.strictEqual(ids.size, forked.length, 'All IDs should be unique'); +- +- // None should match the original +- for (const entry of forked) { +- assert.ok(!entries.some(e => e.id === entry.id), 'Should not reuse original IDs'); +- } +- }); +- +- test('re-chains parentId links', () => { +- const entries = buildTestEventLog(2); +- const forked = buildForkedEventLog(entries, 0, 'new-session-id'); +- +- // First event has no parent +- assert.strictEqual(forked[0].parentId, null); +- +- // Each subsequent event's parentId should be a valid ID in the forked log +- const idSet = new Set(forked.map(e => e.id)); +- for (let i = 1; i < forked.length; i++) { +- assert.ok( +- forked[i].parentId !== null && idSet.has(forked[i].parentId!), +- `Event ${i} (${forked[i].type}) should have a valid parentId`, +- ); +- } +- }); +- +- test('strips session.shutdown and session.resume events', () => { +- const entries = buildTestEventLog(2); +- // Insert lifecycle events +- entries.splice(5, 0, makeEntry('session.shutdown', { parentId: entries[4].id })); +- entries.splice(6, 0, makeEntry('session.resume', { parentId: entries[5].id })); +- +- const forked = buildForkedEventLog(entries, 1, 'new-session-id'); +- assert.ok(!forked.some(e => e.type === 'session.shutdown')); +- assert.ok(!forked.some(e => e.type === 'session.resume')); +- }); +- +- test('throws for invalid turn index', () => { +- const entries = buildTestEventLog(1); +- assert.throws(() => buildForkedEventLog(entries, 5, 'new-session-id')); +- }); +- +- test('falls back to last kept event when lifecycle event parent is stripped', () => { +- const entries = buildTestEventLog(2); +- // Insert shutdown event between turns, then make the next turn's +- // user.message point to the shutdown event (which will be stripped) +- const shutdownEntry = makeEntry('session.shutdown', { +- id: 'shutdown-1', +- parentId: entries[4].id, // turn-end-0 +- }); +- entries.splice(5, 0, shutdownEntry); +- // Next entry (user-msg-1) now points to the shutdown event +- entries[6] = { ...entries[6], parentId: 'shutdown-1' }; +- +- const forked = buildForkedEventLog(entries, 1, 'new-session-id'); +- +- // All parentIds should be valid +- const idSet = new Set(forked.map(e => e.id)); +- for (let i = 1; i < forked.length; i++) { +- assert.ok( +- forked[i].parentId !== null && idSet.has(forked[i].parentId!), +- `Event ${i} (${forked[i].type}) should have a valid parentId, got ${forked[i].parentId}`, +- ); +- } +- }); +- }); +- +- // ---- buildTruncatedEventLog ----------------------------------------- +- +- suite('buildTruncatedEventLog', () => { +- +- test('truncates to turn 0', () => { +- const entries = buildTestEventLog(3); +- const truncated = buildTruncatedEventLog(entries, 0); +- +- // New session.start + turn 0 events (user.message, turn_start, assistant.message, turn_end) +- assert.strictEqual(truncated.length, 5); +- assert.strictEqual(truncated[0].type, 'session.start'); +- }); +- +- test('truncates to turn 1', () => { +- const entries = buildTestEventLog(3); +- const truncated = buildTruncatedEventLog(entries, 1); +- +- // New session.start + 2 turns × 4 events = 9 events +- assert.strictEqual(truncated.length, 9); +- }); +- +- test('prepends fresh session.start', () => { +- const entries = buildTestEventLog(2); +- const truncated = buildTruncatedEventLog(entries, 0); +- +- assert.strictEqual(truncated[0].type, 'session.start'); +- assert.strictEqual(truncated[0].parentId, null); +- // Should not reuse original session.start ID +- assert.notStrictEqual(truncated[0].id, entries[0].id); +- }); +- +- test('re-chains parentId links', () => { +- const entries = buildTestEventLog(2); +- const truncated = buildTruncatedEventLog(entries, 0); +- +- const idSet = new Set(truncated.map(e => e.id)); +- for (let i = 1; i < truncated.length; i++) { +- assert.ok( +- truncated[i].parentId !== null && idSet.has(truncated[i].parentId!), +- `Event ${i} (${truncated[i].type}) should have a valid parentId`, +- ); +- } +- }); +- +- test('strips lifecycle events', () => { +- const entries = buildTestEventLog(3); +- // Add lifecycle events between turns +- entries.splice(5, 0, makeEntry('session.shutdown')); +- entries.splice(6, 0, makeEntry('session.resume')); +- +- const truncated = buildTruncatedEventLog(entries, 2); +- const lifecycleEvents = truncated.filter( +- e => e.type === 'session.shutdown' || e.type === 'session.resume', +- ); +- assert.strictEqual(lifecycleEvents.length, 0); +- }); +- +- test('throws for invalid turn index', () => { +- const entries = buildTestEventLog(1); +- assert.throws(() => buildTruncatedEventLog(entries, 5)); +- }); +- +- test('throws when no session.start exists', () => { +- const entries = [makeEntry('user.message')]; +- assert.throws(() => buildTruncatedEventLog(entries, 0)); +- }); +- }); +- +- // ---- buildWorkspaceYaml --------------------------------------------- +- +- suite('buildWorkspaceYaml', () => { +- +- test('contains required fields', () => { +- const yaml = buildWorkspaceYaml('test-id', '/home/user/project', 'Test summary'); +- assert.ok(yaml.includes('id: test-id')); +- assert.ok(yaml.includes('cwd: /home/user/project')); +- assert.ok(yaml.includes('summary: Test summary')); +- assert.ok(yaml.includes('summary_count: 0')); +- assert.ok(yaml.includes('created_at:')); +- assert.ok(yaml.includes('updated_at:')); +- }); +- }); +- +- // ---- SQLite operations (in-memory) ---------------------------------- +- +- suite('forkSessionInDb', () => { +- +- async function openTestDb(): Promise { +- const sqlite3 = await import('@vscode/sqlite3'); +- return new Promise((resolve, reject) => { +- const db = new sqlite3.default.Database(':memory:', (err: Error | null) => { +- if (err) { +- return reject(err); +- } +- resolve(db); +- }); +- }); +- } +- +- function exec(db: import('@vscode/sqlite3').Database, sql: string): Promise { +- return new Promise((resolve, reject) => { +- db.exec(sql, err => err ? reject(err) : resolve()); +- }); +- } +- +- function all(db: import('@vscode/sqlite3').Database, sql: string, params: unknown[] = []): Promise[]> { +- return new Promise((resolve, reject) => { +- db.all(sql, params, (err: Error | null, rows: Record[]) => { +- if (err) { +- return reject(err); +- } +- resolve(rows); +- }); +- }); +- } +- +- function close(db: import('@vscode/sqlite3').Database): Promise { +- return new Promise((resolve, reject) => { +- db.close(err => err ? reject(err) : resolve()); +- }); +- } +- +- async function setupSchema(db: import('@vscode/sqlite3').Database): Promise { +- await exec(db, ` +- CREATE TABLE sessions ( +- id TEXT PRIMARY KEY, +- cwd TEXT, +- repository TEXT, +- branch TEXT, +- summary TEXT, +- created_at TEXT, +- updated_at TEXT, +- host_type TEXT +- ); +- CREATE TABLE turns ( +- id INTEGER PRIMARY KEY AUTOINCREMENT, +- session_id TEXT NOT NULL, +- turn_index INTEGER NOT NULL, +- user_message TEXT, +- assistant_response TEXT, +- timestamp TEXT, +- UNIQUE(session_id, turn_index) +- ); +- CREATE TABLE session_files ( +- id INTEGER PRIMARY KEY AUTOINCREMENT, +- session_id TEXT NOT NULL, +- file_path TEXT, +- tool_name TEXT, +- turn_index INTEGER, +- first_seen_at TEXT +- ); +- CREATE VIRTUAL TABLE search_index USING fts5( +- content, +- session_id, +- source_type, +- source_id +- ); +- CREATE TABLE checkpoints ( +- id INTEGER PRIMARY KEY AUTOINCREMENT, +- session_id TEXT NOT NULL, +- checkpoint_number INTEGER, +- title TEXT, +- overview TEXT, +- history TEXT, +- work_done TEXT, +- technical_details TEXT, +- important_files TEXT, +- next_steps TEXT, +- created_at TEXT +- ); +- `); +- } +- +- async function seedTestData(db: import('@vscode/sqlite3').Database, sessionId: string, turnCount: number): Promise { +- await exec(db, ` +- INSERT INTO sessions (id, cwd, repository, branch, summary, created_at, updated_at, host_type) +- VALUES ('${sessionId}', '/test', 'test-repo', 'main', 'Test session', '2026-01-01', '2026-01-01', 'github'); +- `); +- for (let i = 0; i < turnCount; i++) { +- await exec(db, ` +- INSERT INTO turns (session_id, turn_index, user_message, assistant_response, timestamp) +- VALUES ('${sessionId}', ${i}, 'msg ${i}', 'resp ${i}', '2026-01-01'); +- `); +- await exec(db, ` +- INSERT INTO session_files (session_id, file_path, tool_name, turn_index, first_seen_at) +- VALUES ('${sessionId}', 'file${i}.ts', 'edit', ${i}, '2026-01-01'); +- `); +- } +- } +- +- test('copies session metadata', async () => { +- const db = await openTestDb(); +- try { +- await setupSchema(db); +- await seedTestData(db, 'source', 3); +- +- await forkSessionInDb(db, 'source', 'forked', 1); +- +- const sessions = await all(db, 'SELECT * FROM sessions WHERE id = ?', ['forked']); +- assert.strictEqual(sessions.length, 1); +- assert.strictEqual(sessions[0].cwd, '/test'); +- assert.strictEqual(sessions[0].repository, 'test-repo'); +- } finally { +- await close(db); +- } +- }); +- +- test('copies turns up to fork point', async () => { +- const db = await openTestDb(); +- try { +- await setupSchema(db); +- await seedTestData(db, 'source', 3); +- +- await forkSessionInDb(db, 'source', 'forked', 1); +- +- const turns = await all(db, 'SELECT * FROM turns WHERE session_id = ? ORDER BY turn_index', ['forked']); +- assert.strictEqual(turns.length, 2); // turns 0 and 1 +- assert.strictEqual(turns[0].turn_index, 0); +- assert.strictEqual(turns[1].turn_index, 1); +- } finally { +- await close(db); +- } +- }); +- +- test('copies session files up to fork point', async () => { +- const db = await openTestDb(); +- try { +- await setupSchema(db); +- await seedTestData(db, 'source', 3); +- +- await forkSessionInDb(db, 'source', 'forked', 1); +- +- const files = await all(db, 'SELECT * FROM session_files WHERE session_id = ?', ['forked']); +- assert.strictEqual(files.length, 2); // files from turns 0 and 1 +- } finally { +- await close(db); +- } +- }); +- +- test('does not affect source session', async () => { +- const db = await openTestDb(); +- try { +- await setupSchema(db); +- await seedTestData(db, 'source', 3); +- +- await forkSessionInDb(db, 'source', 'forked', 1); +- +- const sourceTurns = await all(db, 'SELECT * FROM turns WHERE session_id = ?', ['source']); +- assert.strictEqual(sourceTurns.length, 3); +- } finally { +- await close(db); +- } +- }); +- }); +- +- suite('truncateSessionInDb', () => { +- +- async function openTestDb(): Promise { +- const sqlite3 = await import('@vscode/sqlite3'); +- return new Promise((resolve, reject) => { +- const db = new sqlite3.default.Database(':memory:', (err: Error | null) => { +- if (err) { +- return reject(err); +- } +- resolve(db); +- }); +- }); +- } +- +- function exec(db: import('@vscode/sqlite3').Database, sql: string): Promise { +- return new Promise((resolve, reject) => { +- db.exec(sql, err => err ? reject(err) : resolve()); +- }); +- } +- +- function all(db: import('@vscode/sqlite3').Database, sql: string, params: unknown[] = []): Promise[]> { +- return new Promise((resolve, reject) => { +- db.all(sql, params, (err: Error | null, rows: Record[]) => { +- if (err) { +- return reject(err); +- } +- resolve(rows); +- }); +- }); +- } +- +- function close(db: import('@vscode/sqlite3').Database): Promise { +- return new Promise((resolve, reject) => { +- db.close(err => err ? reject(err) : resolve()); +- }); +- } +- +- async function setupSchema(db: import('@vscode/sqlite3').Database): Promise { +- await exec(db, ` +- CREATE TABLE sessions ( +- id TEXT PRIMARY KEY, +- cwd TEXT, +- repository TEXT, +- branch TEXT, +- summary TEXT, +- created_at TEXT, +- updated_at TEXT, +- host_type TEXT +- ); +- CREATE TABLE turns ( +- id INTEGER PRIMARY KEY AUTOINCREMENT, +- session_id TEXT NOT NULL, +- turn_index INTEGER NOT NULL, +- user_message TEXT, +- assistant_response TEXT, +- timestamp TEXT, +- UNIQUE(session_id, turn_index) +- ); +- CREATE VIRTUAL TABLE search_index USING fts5( +- content, +- session_id, +- source_type, +- source_id +- ); +- `); +- } +- +- test('removes turns after truncation point', async () => { +- const db = await openTestDb(); +- try { +- await setupSchema(db); +- await exec(db, ` +- INSERT INTO sessions (id, cwd, summary, created_at, updated_at) +- VALUES ('sess', '/test', 'Test', '2026-01-01', '2026-01-01'); +- `); +- for (let i = 0; i < 5; i++) { +- await exec(db, ` +- INSERT INTO turns (session_id, turn_index, user_message, timestamp) +- VALUES ('sess', ${i}, 'msg ${i}', '2026-01-01'); +- `); +- } +- +- await truncateSessionInDb(db, 'sess', 2); +- +- const turns = await all(db, 'SELECT * FROM turns WHERE session_id = ? ORDER BY turn_index', ['sess']); +- assert.strictEqual(turns.length, 3); // turns 0, 1, 2 +- assert.strictEqual(turns[0].turn_index, 0); +- assert.strictEqual(turns[2].turn_index, 2); +- } finally { +- await close(db); +- } +- }); +- +- test('updates session timestamp', async () => { +- const db = await openTestDb(); +- try { +- await setupSchema(db); +- await exec(db, ` +- INSERT INTO sessions (id, cwd, summary, created_at, updated_at) +- VALUES ('sess', '/test', 'Test', '2026-01-01', '2026-01-01'); +- `); +- await exec(db, ` +- INSERT INTO turns (session_id, turn_index, user_message, timestamp) +- VALUES ('sess', 0, 'msg 0', '2026-01-01'); +- `); +- +- await truncateSessionInDb(db, 'sess', 0); +- +- const sessions = await all(db, 'SELECT updated_at FROM sessions WHERE id = ?', ['sess']); +- assert.notStrictEqual(sessions[0].updated_at, '2026-01-01'); +- } finally { +- await close(db); +- } +- }); +- +- test('removes search index entries for truncated turns', async () => { +- const db = await openTestDb(); +- try { +- await setupSchema(db); +- await exec(db, ` +- INSERT INTO sessions (id, cwd, summary, created_at, updated_at) +- VALUES ('sess', '/test', 'Test', '2026-01-01', '2026-01-01'); +- `); +- for (let i = 0; i < 3; i++) { +- await exec(db, ` +- INSERT INTO turns (session_id, turn_index, user_message, timestamp) +- VALUES ('sess', ${i}, 'msg ${i}', '2026-01-01'); +- `); +- await exec(db, ` +- INSERT INTO search_index (content, session_id, source_type, source_id) +- VALUES ('content ${i}', 'sess', 'turn', 'sess:turn:${i}'); +- `); +- } +- +- await truncateSessionInDb(db, 'sess', 0); +- +- const searchEntries = await all(db, 'SELECT * FROM search_index WHERE session_id = ?', ['sess']); +- assert.strictEqual(searchEntries.length, 1); +- assert.strictEqual(searchEntries[0].source_id, 'sess:turn:0'); +- } finally { +- await close(db); +- } +- }); +- +- test('removes all turns when keepUpToTurnIndex is -1', async () => { +- const db = await openTestDb(); +- try { +- await setupSchema(db); +- await exec(db, ` +- INSERT INTO sessions (id, cwd, summary, created_at, updated_at) +- VALUES ('sess', '/test', 'Test', '2026-01-01', '2026-01-01'); +- `); +- for (let i = 0; i < 3; i++) { +- await exec(db, ` +- INSERT INTO turns (session_id, turn_index, user_message, timestamp) +- VALUES ('sess', ${i}, 'msg ${i}', '2026-01-01'); +- `); +- await exec(db, ` +- INSERT INTO search_index (content, session_id, source_type, source_id) +- VALUES ('content ${i}', 'sess', 'turn', 'sess:turn:${i}'); +- `); +- } +- +- await truncateSessionInDb(db, 'sess', -1); +- +- const turns = await all(db, 'SELECT * FROM turns WHERE session_id = ?', ['sess']); +- assert.strictEqual(turns.length, 0, 'all turns should be removed'); +- +- const searchEntries = await all(db, 'SELECT * FROM search_index WHERE session_id = ?', ['sess']); +- assert.strictEqual(searchEntries.length, 0, 'all search entries should be removed'); +- } finally { +- await close(db); +- } +- }); +- }); +-}); +diff --git a/src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts b/src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts +deleted file mode 100644 +index 3b02e51c..00000000 +--- a/src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts ++++ /dev/null +@@ -1,456 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import assert from 'assert'; +-import type { CopilotSession, SessionEvent, SessionEventPayload, SessionEventType, TypedSessionEventHandler } from '@github/copilot-sdk'; +-import { Emitter } from '../../../../base/common/event.js'; +-import { DisposableStore } from '../../../../base/common/lifecycle.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import { NullLogService, ILogService } from '../../../log/common/log.js'; +-import { IFileService } from '../../../files/common/files.js'; +-import { AgentSession, IAgentProgressEvent, IAgentUserInputRequestEvent } from '../../common/agentService.js'; +-import { IDiffComputeService } from '../../common/diffComputeService.js'; +-import { ISessionDataService } from '../../common/sessionDataService.js'; +-import { SessionInputAnswerState, SessionInputAnswerValueKind, SessionInputQuestionKind, SessionInputResponseKind } from '../../common/state/sessionState.js'; +-import { CopilotAgentSession, SessionWrapperFactory } from '../../node/copilot/copilotAgentSession.js'; +-import { CopilotSessionWrapper } from '../../node/copilot/copilotSessionWrapper.js'; +-import { InstantiationService } from '../../../instantiation/common/instantiationService.js'; +-import { ServiceCollection } from '../../../instantiation/common/serviceCollection.js'; +-import { createSessionDataService, createZeroDiffComputeService } from '../common/sessionTestHelpers.js'; +- +-// ---- Mock CopilotSession (SDK level) ---------------------------------------- +- +-/** +- * Minimal mock of the SDK's {@link CopilotSession}. Implements `on()` to +- * store typed handlers, and exposes `fire()` so tests can push events +- * through the real {@link CopilotSessionWrapper} event pipeline. +- */ +-class MockCopilotSession { +- readonly sessionId = 'test-session-1'; +- +- private readonly _handlers = new Map void>>(); +- +- on(eventType: K, handler: TypedSessionEventHandler): () => void { +- let set = this._handlers.get(eventType); +- if (!set) { +- set = new Set(); +- this._handlers.set(eventType, set); +- } +- set.add(handler as (event: SessionEvent) => void); +- return () => { set.delete(handler as (event: SessionEvent) => void); }; +- } +- +- /** Push an event through to all registered handlers of the given type. */ +- fire(type: K, data: SessionEventPayload['data']): void { +- const event = { type, data, id: 'evt-1', timestamp: new Date().toISOString(), parentId: null } as SessionEventPayload; +- const set = this._handlers.get(type); +- if (set) { +- for (const handler of set) { +- handler(event); +- } +- } +- } +- +- // Stubs for methods the wrapper / session class calls +- async send() { return ''; } +- async abort() { } +- async setModel() { } +- async getMessages() { return []; } +- async destroy() { } +-} +- +-// ---- Helpers ---------------------------------------------------------------- +- +-async function createAgentSession(disposables: DisposableStore, options?: { workingDirectory?: URI }): Promise<{ +- session: CopilotAgentSession; +- mockSession: MockCopilotSession; +- progressEvents: IAgentProgressEvent[]; +-}> { +- const progressEmitter = disposables.add(new Emitter()); +- const progressEvents: IAgentProgressEvent[] = []; +- disposables.add(progressEmitter.event(e => progressEvents.push(e))); +- +- const sessionUri = AgentSession.uri('copilot', 'test-session-1'); +- const mockSession = new MockCopilotSession(); +- +- const factory: SessionWrapperFactory = async () => new CopilotSessionWrapper(mockSession as unknown as CopilotSession); +- +- const services = new ServiceCollection(); +- services.set(ILogService, new NullLogService()); +- services.set(IFileService, { _serviceBrand: undefined } as IFileService); +- services.set(ISessionDataService, createSessionDataService()); +- services.set(IDiffComputeService, createZeroDiffComputeService()); +- const instantiationService = disposables.add(new InstantiationService(services)); +- +- const session = disposables.add(instantiationService.createInstance( +- CopilotAgentSession, +- sessionUri, +- 'test-session-1', +- options?.workingDirectory, +- progressEmitter, +- factory, +- undefined, // shellManager +- )); +- +- await session.initializeSession(); +- +- return { session, mockSession, progressEvents }; +-} +- +-// ---- Tests ------------------------------------------------------------------ +- +-suite('CopilotAgentSession', () => { +- +- const disposables = new DisposableStore(); +- +- teardown(() => disposables.clear()); +- ensureNoDisposablesAreLeakedInTestSuite(); +- +- // ---- permission handling ---- +- +- suite('permission handling', () => { +- +- test('auto-approves read inside working directory', async () => { +- const { session } = await createAgentSession(disposables, { workingDirectory: URI.file('/workspace') }); +- const result = await session.handlePermissionRequest({ +- kind: 'read', +- path: '/workspace/src/file.ts', +- toolCallId: 'tc-1', +- }); +- assert.strictEqual(result.kind, 'approved'); +- }); +- +- test('does not auto-approve read outside working directory', async () => { +- const { session, progressEvents } = await createAgentSession(disposables, { workingDirectory: URI.file('/workspace') }); +- +- // Kick off permission request but don't await — it will block +- const resultPromise = session.handlePermissionRequest({ +- kind: 'read', +- path: '/other/file.ts', +- toolCallId: 'tc-2', +- }); +- +- // Should have fired a tool_ready event +- assert.strictEqual(progressEvents.length, 1); +- assert.strictEqual(progressEvents[0].type, 'tool_ready'); +- +- // Respond to it +- assert.ok(session.respondToPermissionRequest('tc-2', true)); +- const result = await resultPromise; +- assert.strictEqual(result.kind, 'approved'); +- }); +- +- test('denies permission when no toolCallId', async () => { +- const { session } = await createAgentSession(disposables); +- const result = await session.handlePermissionRequest({ kind: 'write' }); +- assert.strictEqual(result.kind, 'denied-interactively-by-user'); +- }); +- +- test('denied-interactively when user denies', async () => { +- const { session, progressEvents } = await createAgentSession(disposables); +- const resultPromise = session.handlePermissionRequest({ +- kind: 'shell', +- toolCallId: 'tc-3', +- }); +- +- assert.strictEqual(progressEvents.length, 1); +- session.respondToPermissionRequest('tc-3', false); +- const result = await resultPromise; +- assert.strictEqual(result.kind, 'denied-interactively-by-user'); +- }); +- +- test('pending permissions are denied on dispose', async () => { +- const { session } = await createAgentSession(disposables); +- const resultPromise = session.handlePermissionRequest({ +- kind: 'write', +- toolCallId: 'tc-4', +- }); +- +- session.dispose(); +- const result = await resultPromise; +- assert.strictEqual(result.kind, 'denied-interactively-by-user'); +- }); +- +- test('pending permissions are denied on abort', async () => { +- const { session } = await createAgentSession(disposables); +- const resultPromise = session.handlePermissionRequest({ +- kind: 'write', +- toolCallId: 'tc-5', +- }); +- +- await session.abort(); +- const result = await resultPromise; +- assert.strictEqual(result.kind, 'denied-interactively-by-user'); +- }); +- +- test('respondToPermissionRequest returns false for unknown id', async () => { +- const { session } = await createAgentSession(disposables); +- assert.strictEqual(session.respondToPermissionRequest('unknown-id', true), false); +- }); +- }); +- +- // ---- sendSteering ---- +- +- suite('sendSteering', () => { +- +- test('fires steering_consumed after send resolves', async () => { +- const { session, progressEvents } = await createAgentSession(disposables); +- +- await session.sendSteering({ id: 'steer-1', userMessage: { text: 'focus on tests' } }); +- +- const consumed = progressEvents.find(e => e.type === 'steering_consumed'); +- assert.ok(consumed, 'should fire steering_consumed event'); +- assert.strictEqual((consumed as { id: string }).id, 'steer-1'); +- }); +- +- test('does not fire steering_consumed when send fails', async () => { +- const { session, mockSession, progressEvents } = await createAgentSession(disposables); +- +- mockSession.send = async () => { throw new Error('send failed'); }; +- +- await session.sendSteering({ id: 'steer-fail', userMessage: { text: 'will fail' } }); +- +- const consumed = progressEvents.find(e => e.type === 'steering_consumed'); +- assert.strictEqual(consumed, undefined, 'should not fire steering_consumed on failure'); +- }); +- }); +- +- // ---- event mapping ---- +- +- suite('event mapping', () => { +- +- test('tool_start event is mapped for non-hidden tools', async () => { +- const { mockSession, progressEvents } = await createAgentSession(disposables); +- mockSession.fire('tool.execution_start', { +- toolCallId: 'tc-10', +- toolName: 'bash', +- arguments: { command: 'echo hello' }, +- } as SessionEventPayload<'tool.execution_start'>['data']); +- +- assert.strictEqual(progressEvents.length, 1); +- assert.strictEqual(progressEvents[0].type, 'tool_start'); +- if (progressEvents[0].type === 'tool_start') { +- assert.strictEqual(progressEvents[0].toolCallId, 'tc-10'); +- assert.strictEqual(progressEvents[0].toolName, 'bash'); +- } +- }); +- +- test('hidden tools are not emitted as tool_start', async () => { +- const { mockSession, progressEvents } = await createAgentSession(disposables); +- mockSession.fire('tool.execution_start', { +- toolCallId: 'tc-11', +- toolName: 'report_intent', +- } as SessionEventPayload<'tool.execution_start'>['data']); +- +- assert.strictEqual(progressEvents.length, 0); +- }); +- +- test('tool_complete event produces past-tense message', async () => { +- const { mockSession, progressEvents } = await createAgentSession(disposables); +- +- // First fire tool_start so it's tracked +- mockSession.fire('tool.execution_start', { +- toolCallId: 'tc-12', +- toolName: 'bash', +- arguments: { command: 'ls' }, +- } as SessionEventPayload<'tool.execution_start'>['data']); +- +- // Then fire complete +- mockSession.fire('tool.execution_complete', { +- toolCallId: 'tc-12', +- success: true, +- result: { content: 'file1.ts\nfile2.ts' }, +- } as SessionEventPayload<'tool.execution_complete'>['data']); +- +- assert.strictEqual(progressEvents.length, 2); +- assert.strictEqual(progressEvents[1].type, 'tool_complete'); +- if (progressEvents[1].type === 'tool_complete') { +- assert.strictEqual(progressEvents[1].toolCallId, 'tc-12'); +- assert.ok(progressEvents[1].result.success); +- assert.ok(progressEvents[1].result.pastTenseMessage); +- } +- }); +- +- test('tool_complete for untracked tool is ignored', async () => { +- const { mockSession, progressEvents } = await createAgentSession(disposables); +- mockSession.fire('tool.execution_complete', { +- toolCallId: 'tc-untracked', +- success: true, +- } as SessionEventPayload<'tool.execution_complete'>['data']); +- +- assert.strictEqual(progressEvents.length, 0); +- }); +- +- test('idle event is forwarded', async () => { +- const { mockSession, progressEvents } = await createAgentSession(disposables); +- mockSession.fire('session.idle', {} as SessionEventPayload<'session.idle'>['data']); +- +- assert.strictEqual(progressEvents.length, 1); +- assert.strictEqual(progressEvents[0].type, 'idle'); +- }); +- +- test('error event is forwarded', async () => { +- const { mockSession, progressEvents } = await createAgentSession(disposables); +- mockSession.fire('session.error', { +- errorType: 'TestError', +- message: 'something went wrong', +- stack: 'Error: something went wrong', +- } as SessionEventPayload<'session.error'>['data']); +- +- assert.strictEqual(progressEvents.length, 1); +- assert.strictEqual(progressEvents[0].type, 'error'); +- if (progressEvents[0].type === 'error') { +- assert.strictEqual(progressEvents[0].errorType, 'TestError'); +- assert.strictEqual(progressEvents[0].message, 'something went wrong'); +- } +- }); +- +- test('message delta is forwarded', async () => { +- const { mockSession, progressEvents } = await createAgentSession(disposables); +- mockSession.fire('assistant.message_delta', { +- messageId: 'msg-1', +- deltaContent: 'Hello ', +- } as SessionEventPayload<'assistant.message_delta'>['data']); +- +- assert.strictEqual(progressEvents.length, 1); +- assert.strictEqual(progressEvents[0].type, 'delta'); +- if (progressEvents[0].type === 'delta') { +- assert.strictEqual(progressEvents[0].content, 'Hello '); +- } +- }); +- +- test('complete message with tool requests is forwarded', async () => { +- const { mockSession, progressEvents } = await createAgentSession(disposables); +- mockSession.fire('assistant.message', { +- messageId: 'msg-2', +- content: 'Let me help you.', +- toolRequests: [{ +- toolCallId: 'tc-20', +- name: 'bash', +- arguments: { command: 'ls' }, +- type: 'function', +- }], +- } as SessionEventPayload<'assistant.message'>['data']); +- +- assert.strictEqual(progressEvents.length, 1); +- assert.strictEqual(progressEvents[0].type, 'message'); +- if (progressEvents[0].type === 'message') { +- assert.strictEqual(progressEvents[0].content, 'Let me help you.'); +- assert.strictEqual(progressEvents[0].toolRequests?.length, 1); +- assert.strictEqual(progressEvents[0].toolRequests?.[0].toolCallId, 'tc-20'); +- } +- }); +- }); +- +- // ---- user input handling ---- +- +- suite('user input handling', () => { +- +- function assertUserInputEvent(event: IAgentProgressEvent): asserts event is IAgentUserInputRequestEvent { +- assert.strictEqual(event.type, 'user_input_request'); +- } +- +- test('handleUserInputRequest fires user_input_request progress event', async () => { +- const { session, progressEvents } = await createAgentSession(disposables); +- +- // Start the request (don't await — it blocks waiting for response) +- const resultPromise = session.handleUserInputRequest( +- { question: 'What is your name?' }, +- { sessionId: 'test-session-1' } +- ); +- +- // Verify progress event was fired +- assert.strictEqual(progressEvents.length, 1); +- const event = progressEvents[0]; +- assertUserInputEvent(event); +- assert.strictEqual(event.request.message, 'What is your name?'); +- const requestId = event.request.id; +- assert.ok(event.request.questions); +- const questionId = event.request.questions[0].id; +- +- // Respond to unblock the promise +- session.respondToUserInputRequest(requestId, SessionInputResponseKind.Accept, { +- [questionId]: { +- state: SessionInputAnswerState.Submitted, +- value: { kind: SessionInputAnswerValueKind.Text, value: 'Alice' } +- } +- }); +- +- const result = await resultPromise; +- assert.strictEqual(result.answer, 'Alice'); +- assert.strictEqual(result.wasFreeform, true); +- }); +- +- test('handleUserInputRequest with choices generates SingleSelect question', async () => { +- const { session, progressEvents } = await createAgentSession(disposables); +- +- const resultPromise = session.handleUserInputRequest( +- { question: 'Pick a color', choices: ['red', 'blue', 'green'] }, +- { sessionId: 'test-session-1' } +- ); +- +- assert.strictEqual(progressEvents.length, 1); +- const event = progressEvents[0]; +- assertUserInputEvent(event); +- assert.ok(event.request.questions); +- assert.strictEqual(event.request.questions.length, 1); +- assert.strictEqual(event.request.questions[0].kind, SessionInputQuestionKind.SingleSelect); +- if (event.request.questions[0].kind === SessionInputQuestionKind.SingleSelect) { +- assert.strictEqual(event.request.questions[0].options.length, 3); +- assert.strictEqual(event.request.questions[0].options[0].label, 'red'); +- } +- +- // Respond with a selected choice +- const questions = event.request.questions; +- session.respondToUserInputRequest(event.request.id, SessionInputResponseKind.Accept, { +- [questions[0].id]: { +- state: SessionInputAnswerState.Submitted, +- value: { kind: SessionInputAnswerValueKind.Selected, value: 'blue' } +- } +- }); +- +- const result = await resultPromise; +- assert.strictEqual(result.answer, 'blue'); +- assert.strictEqual(result.wasFreeform, false); +- }); +- +- test('handleUserInputRequest returns empty answer on cancel', async () => { +- const { session, progressEvents } = await createAgentSession(disposables); +- +- const resultPromise = session.handleUserInputRequest( +- { question: 'Cancel me' }, +- { sessionId: 'test-session-1' } +- ); +- +- const event = progressEvents[0]; +- assertUserInputEvent(event); +- session.respondToUserInputRequest(event.request.id, SessionInputResponseKind.Cancel); +- +- const result = await resultPromise; +- assert.strictEqual(result.answer, ''); +- assert.strictEqual(result.wasFreeform, true); +- }); +- +- test('respondToUserInputRequest returns false for unknown id', async () => { +- const { session } = await createAgentSession(disposables); +- assert.strictEqual(session.respondToUserInputRequest('unknown-id', SessionInputResponseKind.Accept), false); +- }); +- +- test('pending user inputs are cancelled on dispose', async () => { +- const { session } = await createAgentSession(disposables); +- +- const resultPromise = session.handleUserInputRequest( +- { question: 'Will be cancelled' }, +- { sessionId: 'test-session-1' } +- ); +- +- session.dispose(); +- const result = await resultPromise; +- assert.strictEqual(result.answer, ''); +- assert.strictEqual(result.wasFreeform, true); +- }); +- }); +-}); +diff --git a/src/vs/platform/agentHost/test/node/copilotGitProject.test.ts b/src/vs/platform/agentHost/test/node/copilotGitProject.test.ts +deleted file mode 100644 +index 6ae1694c..00000000 +--- a/src/vs/platform/agentHost/test/node/copilotGitProject.test.ts ++++ /dev/null +@@ -1,117 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import assert from 'assert'; +-import * as cp from 'child_process'; +-import * as fs from 'fs'; +-import { tmpdir } from 'os'; +-import { join } from '../../../../base/common/path.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { Promises } from '../../../../base/node/pfs.js'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import { getRandomTestPath } from '../../../../base/test/node/testUtils.js'; +-import { projectFromCopilotContext, projectFromRepository, resolveGitProject } from '../../node/copilot/copilotGitProject.js'; +- +-function execGit(cwd: string, args: string[]): Promise { +- return new Promise((resolve, reject) => { +- cp.execFile('git', args, { cwd, encoding: 'utf8' }, (error, stdout, stderr) => { +- if (error) { +- reject(new Error(stderr || error.message)); +- return; +- } +- resolve(stdout.trim()); +- }); +- }); +-} +- +-suite('Copilot Git Project', () => { +- ensureNoDisposablesAreLeakedInTestSuite(); +- +- let testDir: string; +- +- setup(async () => { +- testDir = getRandomTestPath(tmpdir(), 'vsctests', 'copilot-git-project'); +- await fs.promises.mkdir(testDir, { recursive: true }); +- }); +- +- teardown(async () => { +- await Promises.rm(testDir); +- }); +- +- async function createRepository(name: string): Promise { +- const repositoryPath = join(testDir, name); +- await fs.promises.mkdir(repositoryPath, { recursive: true }); +- await execGit(repositoryPath, ['init']); +- await execGit(repositoryPath, ['config', 'user.email', 'test@example.com']); +- await execGit(repositoryPath, ['config', 'user.name', 'Test User']); +- await fs.promises.writeFile(join(repositoryPath, 'README.md'), '# Test\n'); +- await execGit(repositoryPath, ['add', 'README.md']); +- await execGit(repositoryPath, ['commit', '-m', 'initial']); +- return repositoryPath; +- } +- +- test('resolves a repository project from a worktree working directory', async () => { +- const repositoryPath = await createRepository('source-repo'); +- const canonicalRepositoryPath = await fs.promises.realpath(repositoryPath); +- const worktreePath = join(testDir, 'worktree-checkout'); +- await execGit(repositoryPath, ['worktree', 'add', worktreePath]); +- +- const project = await resolveGitProject(URI.file(worktreePath)); +- +- assert.deepStrictEqual({ +- uri: project?.uri.toString(), +- displayName: project?.displayName, +- }, { +- uri: URI.file(canonicalRepositoryPath).toString(), +- displayName: 'source-repo', +- }); +- }); +- +- test('resolves the repository itself for a normal git working directory', async () => { +- const repositoryPath = await createRepository('normal-repo'); +- const canonicalRepositoryPath = await fs.promises.realpath(repositoryPath); +- +- const project = await resolveGitProject(URI.file(repositoryPath)); +- +- assert.deepStrictEqual({ +- uri: project?.uri.toString(), +- displayName: project?.displayName, +- }, { +- uri: URI.file(canonicalRepositoryPath).toString(), +- displayName: 'normal-repo', +- }); +- }); +- +- test('returns undefined outside a git working tree', async () => { +- const folder = join(testDir, 'plain-folder'); +- await fs.promises.mkdir(folder); +- +- assert.strictEqual(await resolveGitProject(URI.file(folder)), undefined); +- }); +- +- test('falls back to repository context when no git project is available', async () => { +- const project = await projectFromCopilotContext({ repository: 'microsoft/vscode' }); +- +- assert.deepStrictEqual({ +- uri: project?.uri.toString(), +- displayName: project?.displayName, +- }, { +- uri: 'https://github.com/microsoft/vscode', +- displayName: 'vscode', +- }); +- }); +- +- test('parses repository URLs', () => { +- const project = projectFromRepository('https://github.com/microsoft/vscode.git'); +- +- assert.deepStrictEqual({ +- uri: project?.uri.toString(), +- displayName: project?.displayName, +- }, { +- uri: 'https://github.com/microsoft/vscode.git', +- displayName: 'vscode', +- }); +- }); +-}); +diff --git a/src/vs/platform/agentHost/test/node/copilotPluginConverters.test.ts b/src/vs/platform/agentHost/test/node/copilotPluginConverters.test.ts +deleted file mode 100644 +index 4a18ade2..00000000 +--- a/src/vs/platform/agentHost/test/node/copilotPluginConverters.test.ts ++++ /dev/null +@@ -1,242 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ +- +-import assert from 'assert'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import { DisposableStore } from '../../../../base/common/lifecycle.js'; +-import { Schemas } from '../../../../base/common/network.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { VSBuffer } from '../../../../base/common/buffer.js'; +-import { FileService } from '../../../files/common/fileService.js'; +-import { InMemoryFileSystemProvider } from '../../../files/common/inMemoryFilesystemProvider.js'; +-import { NullLogService } from '../../../log/common/log.js'; +-import { McpServerType } from '../../../mcp/common/mcpPlatformTypes.js'; +-import { toSdkMcpServers, toSdkCustomAgents, toSdkSkillDirectories, parsedPluginsEqual } from '../../node/copilot/copilotPluginConverters.js'; +-import type { IMcpServerDefinition, INamedPluginResource, IParsedPlugin } from '../../../agentPlugins/common/pluginParsers.js'; +- +-suite('copilotPluginConverters', () => { +- +- const disposables = new DisposableStore(); +- let fileService: FileService; +- +- setup(() => { +- fileService = disposables.add(new FileService(new NullLogService())); +- disposables.add(fileService.registerProvider(Schemas.inMemory, disposables.add(new InMemoryFileSystemProvider()))); +- }); +- +- teardown(() => disposables.clear()); +- ensureNoDisposablesAreLeakedInTestSuite(); +- +- // ---- toSdkMcpServers ------------------------------------------------ +- +- suite('toSdkMcpServers', () => { +- +- test('converts local server definitions', () => { +- const defs: IMcpServerDefinition[] = [{ +- name: 'test-server', +- uri: URI.file('/plugin'), +- configuration: { +- type: McpServerType.LOCAL, +- command: 'node', +- args: ['server.js', '--port', '3000'], +- env: { NODE_ENV: 'production', PORT: 3000 as unknown as string }, +- cwd: '/workspace', +- }, +- }]; +- +- const result = toSdkMcpServers(defs); +- assert.deepStrictEqual(result, { +- 'test-server': { +- type: 'local', +- command: 'node', +- args: ['server.js', '--port', '3000'], +- tools: ['*'], +- env: { NODE_ENV: 'production', PORT: '3000' }, +- cwd: '/workspace', +- }, +- }); +- }); +- +- test('converts remote/http server definitions', () => { +- const defs: IMcpServerDefinition[] = [{ +- name: 'remote-server', +- uri: URI.file('/plugin'), +- configuration: { +- type: McpServerType.REMOTE, +- url: 'https://example.com/mcp', +- headers: { 'Authorization': 'Bearer token' }, +- }, +- }]; +- +- const result = toSdkMcpServers(defs); +- assert.deepStrictEqual(result, { +- 'remote-server': { +- type: 'http', +- url: 'https://example.com/mcp', +- tools: ['*'], +- headers: { 'Authorization': 'Bearer token' }, +- }, +- }); +- }); +- +- test('handles empty definitions', () => { +- const result = toSdkMcpServers([]); +- assert.deepStrictEqual(result, {}); +- }); +- +- test('omits optional fields when undefined', () => { +- const defs: IMcpServerDefinition[] = [{ +- name: 'minimal', +- uri: URI.file('/plugin'), +- configuration: { +- type: McpServerType.LOCAL, +- command: 'echo', +- }, +- }]; +- +- const result = toSdkMcpServers(defs); +- assert.strictEqual(result['minimal'].type, 'local'); +- assert.deepStrictEqual((result['minimal'] as { args?: string[] }).args, []); +- assert.strictEqual(Object.hasOwn(result['minimal'], 'env'), false); +- assert.strictEqual(Object.hasOwn(result['minimal'], 'cwd'), false); +- }); +- +- test('filters null values from env', () => { +- const defs: IMcpServerDefinition[] = [{ +- name: 'with-null-env', +- uri: URI.file('/plugin'), +- configuration: { +- type: McpServerType.LOCAL, +- command: 'test', +- env: { KEEP: 'value', DROP: null as unknown as string }, +- }, +- }]; +- +- const result = toSdkMcpServers(defs); +- const env = (result['with-null-env'] as { env?: Record }).env; +- assert.deepStrictEqual(env, { KEEP: 'value' }); +- }); +- }); +- +- // ---- toSdkCustomAgents ---------------------------------------------- +- +- suite('toSdkCustomAgents', () => { +- +- test('reads agent files and creates configs', async () => { +- const agentUri = URI.from({ scheme: Schemas.inMemory, path: '/agents/helper.md' }); +- await fileService.writeFile(agentUri, VSBuffer.fromString('You are a helpful assistant')); +- +- const agents: INamedPluginResource[] = [{ uri: agentUri, name: 'helper' }]; +- const result = await toSdkCustomAgents(agents, fileService); +- +- assert.deepStrictEqual(result, [{ +- name: 'helper', +- prompt: 'You are a helpful assistant', +- }]); +- }); +- +- test('skips agents whose files cannot be read', async () => { +- const agents: INamedPluginResource[] = [ +- { uri: URI.from({ scheme: Schemas.inMemory, path: '/nonexistent/agent.md' }), name: 'missing' }, +- ]; +- const result = await toSdkCustomAgents(agents, fileService); +- assert.deepStrictEqual(result, []); +- }); +- +- test('processes multiple agents, skipping failures', async () => { +- const goodUri = URI.from({ scheme: Schemas.inMemory, path: '/agents/good.md' }); +- await fileService.writeFile(goodUri, VSBuffer.fromString('Good agent')); +- +- const agents: INamedPluginResource[] = [ +- { uri: goodUri, name: 'good' }, +- { uri: URI.from({ scheme: Schemas.inMemory, path: '/agents/bad.md' }), name: 'bad' }, +- ]; +- const result = await toSdkCustomAgents(agents, fileService); +- assert.strictEqual(result.length, 1); +- assert.strictEqual(result[0].name, 'good'); +- }); +- }); +- +- // ---- toSdkSkillDirectories ------------------------------------------ +- +- suite('toSdkSkillDirectories', () => { +- +- test('extracts parent directories of skill URIs', () => { +- const skills: INamedPluginResource[] = [ +- { uri: URI.file('/plugins/skill-a/SKILL.md'), name: 'skill-a' }, +- { uri: URI.file('/plugins/skill-b/SKILL.md'), name: 'skill-b' }, +- ]; +- const result = toSdkSkillDirectories(skills); +- assert.strictEqual(result.length, 2); +- }); +- +- test('deduplicates directories', () => { +- const skills: INamedPluginResource[] = [ +- { uri: URI.file('/plugins/shared/SKILL.md'), name: 'skill-a' }, +- { uri: URI.file('/plugins/shared/SKILL.md'), name: 'skill-b' }, +- ]; +- const result = toSdkSkillDirectories(skills); +- assert.strictEqual(result.length, 1); +- }); +- +- test('handles empty input', () => { +- const result = toSdkSkillDirectories([]); +- assert.deepStrictEqual(result, []); +- }); +- }); +- +- // ---- parsedPluginsEqual --------------------------------------------- +- +- suite('parsedPluginsEqual', () => { +- +- function makePlugin(overrides?: Partial): IParsedPlugin { +- return { +- hooks: [], +- mcpServers: [], +- skills: [], +- agents: [], +- ...overrides, +- }; +- } +- +- test('returns true for identical empty plugins', () => { +- assert.strictEqual(parsedPluginsEqual([makePlugin()], [makePlugin()]), true); +- }); +- +- test('returns true for same content', () => { +- const a = makePlugin({ +- skills: [{ uri: URI.file('/a/SKILL.md'), name: 'a' }], +- mcpServers: [{ +- name: 'server', +- uri: URI.file('/mcp'), +- configuration: { type: McpServerType.LOCAL, command: 'node' }, +- }], +- }); +- const b = makePlugin({ +- skills: [{ uri: URI.file('/a/SKILL.md'), name: 'a' }], +- mcpServers: [{ +- name: 'server', +- uri: URI.file('/mcp'), +- configuration: { type: McpServerType.LOCAL, command: 'node' }, +- }], +- }); +- assert.strictEqual(parsedPluginsEqual([a], [b]), true); +- }); +- +- test('returns false for different content', () => { +- const a = makePlugin({ skills: [{ uri: URI.file('/a/SKILL.md'), name: 'a' }] }); +- const b = makePlugin({ skills: [{ uri: URI.file('/b/SKILL.md'), name: 'b' }] }); +- assert.strictEqual(parsedPluginsEqual([a], [b]), false); +- }); +- +- test('returns false for different lengths', () => { +- assert.strictEqual(parsedPluginsEqual([makePlugin()], [makePlugin(), makePlugin()]), false); +- }); +- +- test('returns true for empty arrays', () => { +- assert.strictEqual(parsedPluginsEqual([], []), true); +- }); +- }); +-}); +diff --git a/src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts b/src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts +index 9872642c..f679eae4 100644 +--- a/src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts ++++ b/src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts +@@ -49,3 +49,3 @@ suite('mapSessionEvents', () => { + assert.strictEqual(result[1].type, 'message'); +- assert.strictEqual((result[1] as { role: string }).role, 'assistant'); ++ assert.strictEqual((result[1] as { role: string; }).role, 'assistant'); + }); +@@ -69,3 +69,3 @@ suite('mapSessionEvents', () => { + +- const complete = result[1] as { result: { content?: readonly { type: string; text?: string }[] } }; ++ const complete = result[1] as { result: { content?: readonly { type: string; text?: string; }[]; }; }; + assert.ok(complete.result.content); +@@ -126,3 +126,3 @@ suite('mapSessionEvents', () => { + +- const content = (complete as { result: { content?: readonly Record[] } }).result.content; ++ const content = (complete as { result: { content?: readonly Record[]; }; }).result.content; + assert.ok(content); +@@ -134,3 +134,3 @@ suite('mapSessionEvents', () => { + // File edit URIs should be parseable +- const fileEdit = content[1] as { before: { uri: any; content: { uri: any } }; after: { uri: any; content: { uri: any } }; diff?: { added?: number; removed?: number } }; ++ const fileEdit = content[1] as { before: { uri: any; content: { uri: any; }; }; after: { uri: any; content: { uri: any; }; }; diff?: { added?: number; removed?: number; }; }; + const beforeFields = parseSessionDbUri(fileEdit.before.content.uri); +@@ -179,3 +179,3 @@ suite('mapSessionEvents', () => { + const result = await mapSessionEvents(session, db, events); +- const content = (result[1] as { result: { content?: readonly Record[] } }).result.content; ++ const content = (result[1] as { result: { content?: readonly Record[]; }; }).result.content; + assert.ok(content); +@@ -199,3 +199,3 @@ suite('mapSessionEvents', () => { + const result = await mapSessionEvents(session, undefined, events); +- const content = (result[1] as { result: { content?: readonly Record[] } }).result.content; ++ const content = (result[1] as { result: { content?: readonly Record[]; }; }).result.content; + assert.ok(content); +@@ -221,3 +221,3 @@ suite('mapSessionEvents', () => { + const result = await mapSessionEvents(session, db, events); +- const content = (result[1] as { result: { content?: readonly Record[] } }).result.content; ++ const content = (result[1] as { result: { content?: readonly Record[]; }; }).result.content; + assert.ok(content); +@@ -248,3 +248,3 @@ suite('mapSessionEvents', () => { + assert.strictEqual(result[0].type, 'subagent_started'); +- const event = result[0] as { type: string; toolCallId: string; agentName: string; agentDisplayName: string }; ++ const event = result[0] as { type: string; toolCallId: string; agentName: string; agentDisplayName: string; }; + assert.strictEqual(event.toolCallId, 'tc-1'); diff --git a/patches/fix-build-vsce.patch b/patches/fix-build-vsce.patch index 33a96a5..8a7fce4 100644 --- a/patches/fix-build-vsce.patch +++ b/patches/fix-build-vsce.patch @@ -1,5 +1,5 @@ diff --git a/build/.moduleignore b/build/.moduleignore -index ed36151..022d6ed 100644 +index f83624f8..00779f06 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -82,7 +82,7 @@ native-is-elevated/deps/** @@ -16,30 +16,30 @@ index ed36151..022d6ed 100644 +!@vscodium/vsce-sign/bin/** diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index 187726c..076f08b 100644 +index 080d97f3..180177c5 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -449,3 +449,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d +@@ -460,3 +460,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d '**/*.wasm', - '**/@vscode/vsce-sign/bin/*', + '**/@vscodium/vsce-sign/bin/*', ], [ diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts -index aacf25c..c4b0391 100644 +index a25395c9..d87ff9aa 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts -@@ -116,3 +116,3 @@ export function typeCheckExtensionStream(extensionPath: string, forWeb: boolean) +@@ -126,3 +126,3 @@ export function typeCheckExtensionStream(extensionPath: string, forWeb: boolean) function fromLocalNormal(extensionPath: string): Stream { - const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce'); + const vsce = require('@vscodium/vsce') as typeof import('@vscodium/vsce'); const result = es.through(); -@@ -138,3 +138,3 @@ function fromLocalNormal(extensionPath: string): Stream { +@@ -148,3 +148,3 @@ function fromLocalNormal(extensionPath: string): Stream { function fromLocalEsbuild(extensionPath: string, esbuildConfigFileName: string): Stream { - const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce'); + const vsce = require('@vscodium/vsce') as typeof import('@vscodium/vsce'); const result = es.through(); diff --git a/build/package-lock.json b/build/package-lock.json -index 644e16f..fac5de0 100644 +index 92f3b6a4..420464b0 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -51,3 +51,3 @@ @@ -64,9 +64,9 @@ index 644e16f..fac5de0 100644 + } + }, + "node_modules/@vscode/vsce/node_modules/brace-expansion": { -+ "version": "5.0.4", -+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", -+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", ++ "version": "5.0.5", ++ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", ++ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "extraneous": true, + "license": "MIT", + "dependencies": { @@ -260,7 +260,7 @@ index 644e16f..fac5de0 100644 }, - "node_modules/@vscode/vsce/node_modules/brace-expansion": { + "node_modules/@vscodium/vsce/node_modules/brace-expansion": { - "version": "5.0.4", + "version": "5.0.5", @@ -2172,3 +2065,3 @@ }, - "node_modules/@vscode/vsce/node_modules/chalk": { @@ -331,7 +331,7 @@ index 644e16f..fac5de0 100644 - } - }, "node_modules/@xmldom/xmldom": { -@@ -7001,2 +6886,12 @@ +@@ -7022,2 +6907,12 @@ }, + "node_modules/yazl": { + "version": "2.5.1", @@ -345,7 +345,7 @@ index 644e16f..fac5de0 100644 + }, "node_modules/yocto-queue": { diff --git a/build/package.json b/build/package.json -index 8a65120..a36d5c4 100644 +index 4746ea2f..49867df9 100644 --- a/build/package.json +++ b/build/package.json @@ -45,3 +45,3 @@ @@ -354,7 +354,7 @@ index 8a65120..a36d5c4 100644 + "@vscodium/vsce": "3.6.1-258428", "ansi-colors": "^3.2.3", diff --git a/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts b/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts -index 98535c5..cc37d7b 100644 +index 98535c5e..cc37d7b4 100644 --- a/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts +++ b/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts @@ -69,3 +69,3 @@ export class ExtensionSignatureVerificationService implements IExtensionSignatur diff --git a/patches/fix-disable-esbuild.patch b/patches/fix-disable-esbuild.patch new file mode 100644 index 0000000..8843a66 --- /dev/null +++ b/patches/fix-disable-esbuild.patch @@ -0,0 +1,8 @@ +diff --git a/build/buildConfig.ts b/build/buildConfig.ts +index a4299d26..a815dd51 100644 +--- a/build/buildConfig.ts ++++ b/build/buildConfig.ts +@@ -11,2 +11,2 @@ + */ +-export const useEsbuildTranspile = true; ++export const useEsbuildTranspile = false; diff --git a/patches/fix-keymap.patch b/patches/fix-keymap.patch index 8e1d965..a673741 100644 --- a/patches/fix-keymap.patch +++ b/patches/fix-keymap.patch @@ -1,5 +1,5 @@ diff --git a/.npmrc b/.npmrc -index a275846..87f881f 100644 +index 8255ca3e..98dc0d47 100644 --- a/.npmrc +++ b/.npmrc @@ -6,2 +6,3 @@ ignore-scripts=false @@ -7,7 +7,7 @@ index a275846..87f881f 100644 +build_from_source_native_keymap="no" legacy-peer-deps="true" diff --git a/build/.moduleignore b/build/.moduleignore -index ed36151..5b040cc 100644 +index f83624f8..aaa384bf 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -65,7 +65,7 @@ fsevents/test/** @@ -24,27 +24,27 @@ index ed36151..5b040cc 100644 +!@vscodium/native-keymap/build/Release/*.node diff --git a/eslint.config.js b/eslint.config.js -index 73f062a..f008259 100644 +index 51bda2c3..494a2c2c 100644 --- a/eslint.config.js +++ b/eslint.config.js -@@ -1481,3 +1481,3 @@ export default tseslint.config( +@@ -1526,3 +1526,3 @@ export default tseslint.config( 'node:module', - 'native-keymap', + '@vscodium/native-keymap', 'net', diff --git a/package-lock.json b/package-lock.json -index bc72a21..ae566c1 100644 +index 75b98719..88c2aee4 100644 --- a/package-lock.json +++ b/package-lock.json -@@ -32,2 +32,3 @@ +@@ -39,2 +39,3 @@ "@vscode/windows-registry": "^1.2.0", + "@vscodium/native-keymap": "3.3.7-258424", - "@xterm/addon-clipboard": "^0.3.0-beta.191", -@@ -49,3 +50,2 @@ + "@xterm/addon-clipboard": "^0.3.0-beta.197", +@@ -56,3 +57,2 @@ "native-is-elevated": "0.9.0", - "native-keymap": "^3.3.5", - "node-pty": "^1.2.0-beta.10", -@@ -4862,2 +4862,9 @@ + "node-pty": "^1.2.0-beta.12", +@@ -4168,2 +4168,9 @@ }, + "node_modules/@vscodium/native-keymap": { + "version": "3.3.7-258424", @@ -54,7 +54,7 @@ index bc72a21..ae566c1 100644 + "license": "MIT" + }, "node_modules/@wdio/config": { -@@ -15159,5 +15166,6 @@ +@@ -14280,5 +14287,6 @@ "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -64,7 +64,7 @@ index bc72a21..ae566c1 100644 + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" }, -@@ -15170,9 +15178,2 @@ +@@ -14291,9 +14299,2 @@ }, - "node_modules/native-keymap": { - "version": "3.3.9", @@ -74,7 +74,7 @@ index bc72a21..ae566c1 100644 - "license": "MIT" - }, "node_modules/natural-compare": { -@@ -16693,5 +16694,6 @@ +@@ -15805,5 +15806,6 @@ "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", @@ -84,22 +84,22 @@ index bc72a21..ae566c1 100644 + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", "dependencies": { -@@ -16702,3 +16704,3 @@ +@@ -15814,3 +15816,3 @@ "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", + "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", diff --git a/package.json b/package.json -index d727d5a..54d14ad 100644 +index b925a690..166f0cae 100644 --- a/package.json +++ b/package.json -@@ -119,3 +119,3 @@ +@@ -133,3 +133,3 @@ "native-is-elevated": "0.9.0", - "native-keymap": "^3.3.5", + "@vscodium/native-keymap": "3.3.7-258424", - "node-pty": "^1.2.0-beta.10", + "node-pty": "^1.2.0-beta.12", diff --git a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts -index c30c6da..ca6cea2 100644 +index c30c6da5..ca6cea22 100644 --- a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts +++ b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts @@ -44,8 +44,8 @@ flakySuite('Native Modules (all platforms)', () => { @@ -116,7 +116,7 @@ index c30c6da..ca6cea2 100644 + assert.ok(result, testErrorMessage('@vscodium/native-keymap')); }); diff --git a/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts -index 8950ce2..f31cea6 100644 +index 8950ce21..f31cea62 100644 --- a/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts +++ b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts @@ -5,3 +5,3 @@ diff --git a/patches/fix-policies.patch b/patches/fix-policies.patch index f2fadf6..35bf373 100644 --- a/patches/fix-policies.patch +++ b/patches/fix-policies.patch @@ -1,10 +1,8 @@ -# Fix: Replace @vscode/policy-watcher with @vscodium/policy-watcher -# Documentation: docs/patches.md#fix-policies diff --git a/build/.moduleignore b/build/.moduleignore -index 5b040cc..8d5fd71 100644 +index aaa384bf..25adcdbf 100644 --- a/build/.moduleignore +++ b/build/.moduleignore -@@ -128,9 +128,11 @@ vsda/** +@@ -153,9 +153,11 @@ vsda/** -@vscode/policy-watcher/build/** -@vscode/policy-watcher/.husky/** @@ -24,7 +22,7 @@ index 5b040cc..8d5fd71 100644 +!@vscodium/policy-watcher/build/Release/vscodium-policy-watcher.node diff --git a/build/lib/policies/basePolicy.ts b/build/lib/policies/basePolicy.ts -index 7f650ba..db927cb 100644 +index 7f650ba7..db927cb4 100644 --- a/build/lib/policies/basePolicy.ts +++ b/build/lib/policies/basePolicy.ts @@ -38,3 +38,3 @@ export abstract class BasePolicy implements Policy { @@ -33,7 +31,7 @@ index 7f650ba..db927cb 100644 + ``, ` `, diff --git a/build/lib/policies/render.ts b/build/lib/policies/render.ts -index 47b485d..8437fd4 100644 +index 47b485d1..8437fd47 100644 --- a/build/lib/policies/render.ts +++ b/build/lib/policies/render.ts @@ -49,3 +49,3 @@ export function renderADMX(regKey: string, versions: string[], categories: Categ @@ -67,27 +65,27 @@ index 47b485d..8437fd4 100644 + !!ORG_NAME!! PayloadType diff --git a/eslint.config.js b/eslint.config.js -index f008259..f87fda1 100644 +index 494a2c2c..c53b46aa 100644 --- a/eslint.config.js +++ b/eslint.config.js -@@ -1464,3 +1464,3 @@ export default tseslint.config( +@@ -1509,3 +1509,3 @@ export default tseslint.config( '@vscode/native-watchdog', - '@vscode/policy-watcher', + '@vscodium/policy-watcher', '@vscode/proxy-agent', diff --git a/package-lock.json b/package-lock.json -index ae566c1..3b3a5dd 100644 +index 88c2aee4..d8852aa8 100644 --- a/package-lock.json +++ b/package-lock.json -@@ -21,3 +21,2 @@ +@@ -28,3 +28,2 @@ "@vscode/native-watchdog": "^1.4.6", - "@vscode/policy-watcher": "^1.3.2", - "@vscode/proxy-agent": "^0.39.1", -@@ -33,2 +32,3 @@ + "@vscode/proxy-agent": "^0.41.0", +@@ -40,2 +39,3 @@ "@vscodium/native-keymap": "3.3.7-258424", + "@vscodium/policy-watcher": "^1.3.2-252465", - "@xterm/addon-clipboard": "^0.3.0-beta.191", -@@ -4538,22 +4538,2 @@ + "@xterm/addon-clipboard": "^0.3.0-beta.197", +@@ -3844,22 +3844,2 @@ }, - "node_modules/@vscode/policy-watcher": { - "version": "1.3.7", @@ -110,7 +108,7 @@ index ae566c1..3b3a5dd 100644 - } - }, "node_modules/@vscode/proxy-agent": { -@@ -4869,2 +4849,22 @@ +@@ -4175,2 +4155,22 @@ }, + "node_modules/@vscodium/policy-watcher": { + "version": "1.3.2-255408", @@ -134,16 +132,16 @@ index ae566c1..3b3a5dd 100644 + }, "node_modules/@wdio/config": { diff --git a/package.json b/package.json -index 54d14ad..47778c2 100644 +index 166f0cae..51cd5977 100644 --- a/package.json +++ b/package.json -@@ -91,3 +91,3 @@ +@@ -105,3 +105,3 @@ "@vscode/native-watchdog": "^1.4.6", - "@vscode/policy-watcher": "^1.3.2", + "@vscodium/policy-watcher": "^1.3.2-252465", - "@vscode/proxy-agent": "^0.39.1", + "@vscode/proxy-agent": "^0.41.0", diff --git a/src/vs/base/test/node/uri.perf.data.txt b/src/vs/base/test/node/uri.perf.data.txt -index ee0a24b..881ce36 100644 +index ee0a24b5..881ce36a 100644 --- a/src/vs/base/test/node/uri.perf.data.txt +++ b/src/vs/base/test/node/uri.perf.data.txt @@ -14698,48 +14698,48 @@ @@ -242,7 +240,7 @@ index ee0a24b..881ce36 100644 +/Users/example/node_modules/@vscodium/policy-watcher/src/windows/NumberPolicy.hh /Users/example/node_modules/@vscode/vscode-languagedetection/CODE_OF_CONDUCT.md diff --git a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts -index ca6cea2..32b22fe 100644 +index ca6cea22..32b22fed 100644 --- a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts +++ b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts @@ -62,5 +62,5 @@ flakySuite('Native Modules (all platforms)', () => { @@ -255,7 +253,7 @@ index ca6cea2..32b22fe 100644 + assert.ok(typeof watcher.createWatcher === 'function', testErrorMessage('@vscodium/policy-watcher')); }); diff --git a/src/vs/platform/policy/node/nativePolicyService.ts b/src/vs/platform/policy/node/nativePolicyService.ts -index feb4ba1..4d9e0c3 100644 +index 6039dbb7..f2ea09cf 100644 --- a/src/vs/platform/policy/node/nativePolicyService.ts +++ b/src/vs/platform/policy/node/nativePolicyService.ts @@ -8,3 +8,3 @@ import { IStringDictionary } from '../../../base/common/collections.js'; @@ -268,8 +266,8 @@ index feb4ba1..4d9e0c3 100644 - const { createWatcher } = await import('@vscode/policy-watcher'); + const { createWatcher } = await import('@vscodium/policy-watcher'); -@@ -31,3 +31,3 @@ export class NativePolicyService extends AbstractPolicyService implements IPolic - try { +@@ -32,3 +32,3 @@ export class NativePolicyService extends AbstractPolicyService implements IPolic + this.logService.trace(`Creating watcher for productName ${this.productName}`); - this.watcher.value = createWatcher(this.productName, policyDefinitions, update => { + this.watcher.value = createWatcher('VSCodium', this.productName, policyDefinitions, update => { this._onDidPolicyChange(update); diff --git a/patches/insider/system-extensions.patch b/patches/insider/system-extensions.patch index 1f95eb5..ec3712d 100644 --- a/patches/insider/system-extensions.patch +++ b/patches/insider/system-extensions.patch @@ -1,13 +1,13 @@ diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts -index 150908a..0759a8d 100644 +index 58422886..a1eac31e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts -@@ -104,2 +104,3 @@ export class Extension implements IExtension { +@@ -112,2 +112,3 @@ export class Extension implements IExtension { @IFileService private readonly fileService: IFileService, + // @ts-ignore @IProductService private readonly productService: IProductService -@@ -325,3 +326,3 @@ export class Extension implements IExtension { +@@ -341,3 +342,3 @@ export class Extension implements IExtension { // Do not allow updating system extensions in stable -- if (this.type === ExtensionType.System && this.productService.quality === 'stable') { -+ if (this.type === ExtensionType.System) { +- if (this.type === ExtensionType.System && this.productService.quality === 'stable' && !this.productService.builtInExtensionsEnabledWithAutoUpdates?.some(id => id.toLowerCase() === this.identifier.id.toLowerCase())) { ++ if (this.type === ExtensionType.System && !this.productService.builtInExtensionsEnabledWithAutoUpdates?.some(id => id.toLowerCase() === this.identifier.id.toLowerCase())) { return false; diff --git a/patches/linux/fix-build.patch b/patches/linux/fix-build.patch index f7479d1..3aafb3a 100644 --- a/patches/linux/fix-build.patch +++ b/patches/linux/fix-build.patch @@ -1,5 +1,5 @@ diff --git a/build/linux/dependencies-generator.ts b/build/linux/dependencies-generator.ts -index 874c802..04731cf 100644 +index 874c8026..04731cfd 100644 --- a/build/linux/dependencies-generator.ts +++ b/build/linux/dependencies-generator.ts @@ -13,3 +13,3 @@ import { type DebianArchString, isDebianArchString } from './debian/types.ts'; @@ -18,7 +18,7 @@ index 874c802..04731cf 100644 + // files.push(path.join(buildDir, 'bin', product.tunnelApplicationName)); // Add the main executable. diff --git a/build/package-lock.json b/build/package-lock.json -index b78c4c8..58ee897 100644 +index 507ec216..a7440867 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -17,3 +17,2 @@ @@ -189,7 +189,7 @@ index b78c4c8..58ee897 100644 - } - }, "node_modules/@esbuild/aix-ppc64": { -@@ -1367,5 +1320,5 @@ +@@ -1344,5 +1297,5 @@ "node_modules/@textlint/ast-node-types": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.2.2.tgz", @@ -198,7 +198,7 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.4.1.tgz", + "integrity": "sha512-XifMpBMdo0E1Fuh85YdcYAgy+okNg9WKBzIPIO4JUDnSWUVFihnogrM4cjDapeHkgzSgulwR8oJVJ17eyxI1bA==", "dev": true, -@@ -1374,5 +1327,5 @@ +@@ -1351,5 +1304,5 @@ "node_modules/@textlint/linter-formatter": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.2.2.tgz", @@ -207,7 +207,7 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.4.1.tgz", + "integrity": "sha512-kAV7Sup3vwvqxKvBbf9lx/JaPHkRybQp/LLvA73U1AorPZE6XyfBAFG24BbMiCs4OX1ax4g7kXRuFPgMLWRf+g==", "dev": true, -@@ -1382,8 +1335,8 @@ +@@ -1359,8 +1312,8 @@ "@azu/style-format": "^1.0.1", - "@textlint/module-interop": "15.2.2", - "@textlint/resolver": "15.2.2", @@ -221,7 +221,7 @@ index b78c4c8..58ee897 100644 + "debug": "^4.4.3", + "js-yaml": "^4.1.0", "lodash": "^4.17.21", -@@ -1459,2 +1412,9 @@ +@@ -1436,2 +1389,9 @@ }, + "node_modules/@textlint/linter-formatter/node_modules/emoji-regex": { + "version": "8.0.0", @@ -231,7 +231,7 @@ index b78c4c8..58ee897 100644 + "license": "MIT" + }, "node_modules/@textlint/linter-formatter/node_modules/has-flag": { -@@ -1476,2 +1436,17 @@ +@@ -1453,2 +1413,17 @@ }, + "node_modules/@textlint/linter-formatter/node_modules/string-width": { + "version": "4.2.3", @@ -249,7 +249,7 @@ index b78c4c8..58ee897 100644 + } + }, "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { -@@ -1503,5 +1478,5 @@ +@@ -1480,5 +1455,5 @@ "node_modules/@textlint/module-interop": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.2.2.tgz", @@ -258,7 +258,7 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.4.1.tgz", + "integrity": "sha512-jHtM2E5CR68P3z/+FGrEU5pml2fQVzEo2sez9FEjrVHSPCrHtqHcPaKfsYbQJjc9C48ObwaWrCzRNaL3KedNCQ==", "dev": true, -@@ -1510,5 +1485,5 @@ +@@ -1487,5 +1462,5 @@ "node_modules/@textlint/resolver": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.2.2.tgz", @@ -267,7 +267,7 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.4.1.tgz", + "integrity": "sha512-uVssyG3XXXKNY+O7NOajGvQZTyOuhPviwlq7Xek6ZT9K1eDQtA8074cPkAQoLMYhi/TUyOE5P5kpz42UF8Lmdw==", "dev": true, -@@ -1517,5 +1492,5 @@ +@@ -1494,5 +1469,5 @@ "node_modules/@textlint/types": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.2.2.tgz", @@ -276,12 +276,12 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.4.1.tgz", + "integrity": "sha512-WByVZ3zblbvuI+voWQplUP7seSTKXI9z6TMVXEB3dY3JFrZCIXWKNfLbETX5lZV7fYkCMaDtILO1l6s11wdbQA==", "dev": true, -@@ -1523,3 +1498,3 @@ +@@ -1500,3 +1475,3 @@ "dependencies": { - "@textlint/ast-node-types": "15.2.2" + "@textlint/ast-node-types": "15.4.1" } -@@ -1617,12 +1592,2 @@ +@@ -1594,12 +1569,2 @@ }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", @@ -294,7 +294,7 @@ index b78c4c8..58ee897 100644 - } - }, "node_modules/@types/gulp": { -@@ -1944,2 +1909,17 @@ +@@ -1921,2 +1886,17 @@ }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.2", @@ -312,11 +312,7 @@ index b78c4c8..58ee897 100644 + } + }, "node_modules/@vscode/iconv-lite-umd": { -@@ -2270,2 +2250,3 @@ - "dev": true, -+ "license": "ISC", - "bin": { -@@ -2347,5 +2328,5 @@ +@@ -2233,5 +2213,5 @@ "node_modules/ansi-escapes": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.1.tgz", @@ -325,11 +321,7 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", "dev": true, -@@ -2392,2 +2373,3 @@ - "dev": true, -+ "license": "BSD-2-Clause", - "dependencies": { -@@ -2422,17 +2404,7 @@ +@@ -2308,17 +2288,7 @@ "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -351,25 +343,25 @@ index b78c4c8..58ee897 100644 - "license": "BSD-3-Clause" + "license": "Python-2.0" }, -@@ -2480,2 +2452,3 @@ +@@ -2366,2 +2336,3 @@ "dev": true, + "license": "BSD-2-Clause", "dependencies": { -@@ -2564,2 +2537,3 @@ +@@ -2450,2 +2421,3 @@ "dev": true, + "license": "MIT", "optional": true, -@@ -2575,3 +2549,4 @@ +@@ -2461,3 +2433,4 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "dev": true, + "license": "ISC" }, -@@ -2677,2 +2652,3 @@ +@@ -2563,2 +2536,3 @@ "dev": true, -+ "license": "MIT", ++ "license": "BSD-2-Clause", "dependencies": { -@@ -2736,6 +2712,7 @@ +@@ -2622,6 +2596,7 @@ "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", @@ -380,7 +372,7 @@ index b78c4c8..58ee897 100644 "dev": true, + "license": "MIT", "dependencies": { -@@ -2744,9 +2721,13 @@ +@@ -2630,9 +2605,13 @@ "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", @@ -399,7 +391,11 @@ index b78c4c8..58ee897 100644 - "node": ">= 6" + "node": ">=20.18.1" }, -@@ -2961,6 +2942,7 @@ +@@ -2647,2 +2626,3 @@ + "dev": true, ++ "license": "MIT", + "dependencies": { +@@ -2847,6 +2827,7 @@ "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -410,13 +406,13 @@ index b78c4c8..58ee897 100644 "dev": true, + "license": "BSD-2-Clause", "engines": { -@@ -3119,3 +3101,4 @@ +@@ -3005,3 +2986,4 @@ } - ] + ], + "license": "BSD-2-Clause" }, -@@ -3137,6 +3120,7 @@ +@@ -3023,6 +3005,7 @@ "node_modules/domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", @@ -427,12 +423,12 @@ index b78c4c8..58ee897 100644 "dev": true, + "license": "BSD-2-Clause", "dependencies": { -@@ -3144,3 +3128,3 @@ +@@ -3030,3 +3013,3 @@ "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" + "domhandler": "^5.0.3" }, -@@ -3211,5 +3195,5 @@ +@@ -3097,5 +3080,5 @@ "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -441,7 +437,7 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, -@@ -3217,2 +3201,16 @@ +@@ -3103,2 +3086,16 @@ }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", @@ -458,7 +454,7 @@ index b78c4c8..58ee897 100644 + } + }, "node_modules/end-of-stream": { -@@ -3227,6 +3225,7 @@ +@@ -3113,6 +3110,7 @@ "node_modules/entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -469,7 +465,7 @@ index b78c4c8..58ee897 100644 "dev": true, + "license": "BSD-2-Clause", "engines": { -@@ -3367,16 +3366,2 @@ +@@ -3253,16 +3251,2 @@ }, - "node_modules/esprima": { - "version": "4.0.1", @@ -486,19 +482,19 @@ index b78c4c8..58ee897 100644 - } - }, "node_modules/events": { -@@ -3418,2 +3403,3 @@ +@@ -3433,2 +3417,3 @@ "dev": true, + "license": "ISC", "dependencies": { -@@ -3617,2 +3603,3 @@ +@@ -3625,2 +3610,3 @@ "dev": true, + "license": "ISC", "optional": true -@@ -3924,2 +3911,3 @@ +@@ -3828,2 +3814,3 @@ "dev": true, + "license": "MIT", "engines": { -@@ -3983,5 +3971,5 @@ +@@ -3887,5 +3874,5 @@ "node_modules/htmlparser2": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", @@ -507,7 +503,7 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", "dev": true, -@@ -3994,7 +3982,21 @@ +@@ -3898,7 +3885,21 @@ ], + "license": "MIT", "dependencies": { @@ -532,7 +528,7 @@ index b78c4c8..58ee897 100644 + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } -@@ -4048,2 +4050,15 @@ +@@ -3952,2 +3953,15 @@ }, + "node_modules/iconv-lite": { + "version": "0.6.3", @@ -548,7 +544,7 @@ index b78c4c8..58ee897 100644 + } + }, "node_modules/ieee754": { -@@ -4080,5 +4095,5 @@ +@@ -3984,5 +3998,5 @@ "node_modules/index-to-position": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", @@ -557,11 +553,15 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", "dev": true, -@@ -4114,2 +4129,3 @@ +@@ -4018,2 +4032,3 @@ "dev": true, + "license": "MIT", "optional": true -@@ -4288,5 +4304,5 @@ +@@ -4037,2 +4052,3 @@ + "dev": true, ++ "license": "ISC", + "bin": { +@@ -4192,5 +4208,5 @@ "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", @@ -570,25 +570,25 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, -@@ -4294,4 +4310,3 @@ +@@ -4198,4 +4214,3 @@ "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, -@@ -4360,2 +4375,3 @@ - "dev": true, -+ "license": "BSD-2-Clause", - "dependencies": { -@@ -4438,2 +4454,3 @@ +@@ -4342,2 +4357,3 @@ "hasInstallScript": true, + "license": "MIT", "optional": true, -@@ -4449,2 +4466,3 @@ +@@ -4353,2 +4369,3 @@ + "dev": true, ++ "license": "BSD-2-Clause", + "dependencies": { +@@ -4410,2 +4427,3 @@ "dev": true, + "license": "MIT", "dependencies": { -@@ -4531,9 +4549,2 @@ +@@ -4435,9 +4453,2 @@ }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", @@ -598,19 +598,11 @@ index b78c4c8..58ee897 100644 - "license": "Python-2.0" - }, "node_modules/matcher": { -@@ -4543,2 +4554,3 @@ - "dev": true, -+ "license": "(MIT OR WTFPL)", - "optional": true, -@@ -4556,2 +4568,3 @@ +@@ -4598,2 +4609,3 @@ "dev": true, + "license": "MIT", - "optional": true, -@@ -4693,2 +4706,3 @@ - "dev": true, -+ "license": "ISC", "optional": true -@@ -4709,6 +4723,7 @@ +@@ -4614,6 +4626,7 @@ "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -621,11 +613,19 @@ index b78c4c8..58ee897 100644 "dev": true, + "license": "MIT", "optional": true -@@ -4755,2 +4770,3 @@ +@@ -4625,2 +4638,3 @@ + "dev": true, ++ "license": "(MIT OR WTFPL)", + "optional": true, +@@ -4638,2 +4652,3 @@ "dev": true, + "license": "MIT", + "optional": true, +@@ -4660,2 +4675,3 @@ + "dev": true, ++ "license": "ISC", "optional": true -@@ -4770,5 +4786,5 @@ +@@ -4675,5 +4691,5 @@ "node_modules/node-sarif-builder": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.2.0.tgz", @@ -634,12 +634,12 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.3.1.tgz", + "integrity": "sha512-8z5dAbhpxmk/WRQHXlv4V0h+9Y4Ugk+w08lyhV/7E/CQX9yDdBc3025/EG+RSMJU2aPFh/IQ7XDV7Ti5TLt/TA==", "dev": true, -@@ -4780,3 +4796,3 @@ +@@ -4685,3 +4701,3 @@ "engines": { - "node": ">=18" + "node": ">=20" } -@@ -4857,5 +4873,5 @@ +@@ -4762,5 +4778,5 @@ "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", @@ -648,7 +648,7 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, -@@ -4985,5 +5001,5 @@ +@@ -4890,5 +4906,5 @@ "node_modules/p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", @@ -657,7 +657,7 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, -@@ -5063,8 +5079,9 @@ +@@ -4968,8 +4984,9 @@ "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -671,7 +671,7 @@ index b78c4c8..58ee897 100644 - "entities": "^4.4.0" + "entities": "^6.0.0" }, -@@ -5075,8 +5092,22 @@ +@@ -4980,8 +4997,9 @@ "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", @@ -681,25 +681,25 @@ index b78c4c8..58ee897 100644 + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", "dev": true, + "license": "MIT", -+ "dependencies": { + "dependencies": { +- "domhandler": "^5.0.2", + "domhandler": "^5.0.3", -+ "parse5": "^7.0.0" -+ }, -+ "funding": { -+ "url": "https://github.com/inikulin/parse5?sponsor=1" -+ } -+ }, + "parse5": "^7.0.0" +@@ -4992,2 +5010,28 @@ + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", - "dependencies": { -- "domhandler": "^5.0.2", - "parse5": "^7.0.0" -@@ -5087,2 +5118,15 @@ - }, ++ "dependencies": { ++ "parse5": "^7.0.0" ++ }, ++ "funding": { ++ "url": "https://github.com/inikulin/parse5?sponsor=1" ++ } ++ }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -713,8 +713,8 @@ index b78c4c8..58ee897 100644 + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, - "node_modules/path-is-absolute": { -@@ -5106,5 +5150,5 @@ + "node_modules/path-expression-matcher": { +@@ -5027,5 +5071,5 @@ "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", @@ -723,20 +723,16 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "dev": true, -@@ -5237,2 +5281,3 @@ - "dev": true, -+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "optional": true, -@@ -5244,3 +5289,3 @@ +@@ -5165,3 +5209,3 @@ "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", + "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", -@@ -5292,2 +5337,3 @@ +@@ -5282,2 +5326,3 @@ "dev": true, -+ "license": "ISC", - "dependencies": { -@@ -5385,22 +5431,2 @@ ++ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, +@@ -5306,22 +5351,2 @@ }, - "node_modules/rc-config-loader/node_modules/argparse": { - "version": "2.0.1", @@ -759,7 +755,11 @@ index b78c4c8..58ee897 100644 - } - }, "node_modules/read": { -@@ -5591,7 +5617,15 @@ +@@ -5331,2 +5356,3 @@ + "dev": true, ++ "license": "ISC", + "dependencies": { +@@ -5512,7 +5538,15 @@ }, + "node_modules/safer-buffer": { + "version": "2.1.2", @@ -779,27 +779,27 @@ index b78c4c8..58ee897 100644 + "dev": true, + "license": "ISC" }, -@@ -5642,2 +5676,3 @@ +@@ -5563,2 +5597,3 @@ "dev": true, + "license": "MIT", "optional": true -@@ -5649,2 +5684,3 @@ +@@ -5570,2 +5605,3 @@ "dev": true, + "license": "MIT", "optional": true, -@@ -5789,2 +5825,3 @@ +@@ -5710,2 +5746,3 @@ ], + "license": "BSD-3-Clause", "optional": true -@@ -5810,2 +5847,3 @@ +@@ -5731,2 +5768,3 @@ ], + "license": "MIT", "optional": true, -@@ -5889,2 +5927,3 @@ +@@ -5862,2 +5900,3 @@ "dev": true, + "license": "MIT", "engines": { -@@ -5968,5 +6007,5 @@ +@@ -5889,5 +5928,5 @@ "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -808,7 +808,7 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, -@@ -5990,31 +6029,11 @@ +@@ -5911,31 +5950,11 @@ "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", @@ -846,7 +846,7 @@ index b78c4c8..58ee897 100644 + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } -@@ -6031,2 +6050,9 @@ +@@ -5952,2 +5971,9 @@ }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", @@ -856,18 +856,18 @@ index b78c4c8..58ee897 100644 + "license": "MIT" + }, "node_modules/string-width/node_modules/strip-ansi": { -@@ -6124,4 +6150,5 @@ +@@ -6045,4 +6071,5 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo= sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, + "license": "MIT", "optional": true, -@@ -6159,2 +6186,3 @@ +@@ -6092,2 +6119,3 @@ "dev": true, + "license": "MIT", "dependencies": { -@@ -6245,2 +6273,24 @@ +@@ -6166,2 +6194,24 @@ }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", @@ -892,7 +892,7 @@ index b78c4c8..58ee897 100644 + } + }, "node_modules/table/node_modules/strip-ansi": { -@@ -6376,5 +6426,5 @@ +@@ -6297,5 +6347,5 @@ "node_modules/tmp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", @@ -901,14 +901,14 @@ index b78c4c8..58ee897 100644 + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, -@@ -6500,4 +6550,5 @@ +@@ -6421,4 +6471,5 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, + "license": "Apache-2.0", "optional": true, -@@ -6549,2 +6600,12 @@ +@@ -6470,2 +6521,12 @@ }, + "node_modules/undici": { + "version": "7.16.0", @@ -921,13 +921,13 @@ index b78c4c8..58ee897 100644 + } + }, "node_modules/undici-types": { -@@ -6588,3 +6649,4 @@ +@@ -6509,3 +6570,4 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true + "dev": true, + "license": "MIT" }, -@@ -6768,2 +6830,25 @@ +@@ -6690,2 +6752,25 @@ }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", @@ -953,7 +953,7 @@ index b78c4c8..58ee897 100644 + } + }, "node_modules/which": { -@@ -6872,2 +6957,24 @@ +@@ -6794,2 +6879,24 @@ }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", @@ -978,7 +978,7 @@ index b78c4c8..58ee897 100644 + } + }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { -@@ -6898,27 +7005,2 @@ +@@ -6820,27 +6927,2 @@ }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", @@ -1006,7 +1006,7 @@ index b78c4c8..58ee897 100644 - } - }, "node_modules/wrappy": { -@@ -6975,2 +7057,47 @@ +@@ -6897,2 +6979,47 @@ }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", @@ -1055,10 +1055,61 @@ index b78c4c8..58ee897 100644 + }, "node_modules/yauzl": { diff --git a/build/package.json b/build/package.json -index 785f04f..e523427 100644 +index 3493ef4e..a3fa30b0 100644 --- a/build/package.json +++ b/build/package.json @@ -11,3 +11,2 @@ "@electron/get": "^2.0.0", - "@electron/osx-sign": "^2.0.0", "@types/ansi-colors": "^3.2.0", +diff --git a/package-lock.json b/package-lock.json +index 75b98719..bb0879b3 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -5829,2 +5829,11 @@ + }, ++ "node_modules/buildcheck": { ++ "version": "0.0.7", ++ "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", ++ "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", ++ "optional": true, ++ "engines": { ++ "node": ">=10.0.0" ++ } ++ }, + "node_modules/bundle-name": { +@@ -6825,2 +6834,16 @@ + }, ++ "node_modules/cpu-features": { ++ "version": "0.0.10", ++ "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", ++ "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", ++ "hasInstallScript": true, ++ "optional": true, ++ "dependencies": { ++ "buildcheck": "~0.0.6", ++ "nan": "^2.19.0" ++ }, ++ "engines": { ++ "node": ">=10.0.0" ++ } ++ }, + "node_modules/crc-32": { +diff --git a/package.json b/package.json +index b925a690..96b8b994 100644 +--- a/package.json ++++ b/package.json +@@ -255,3 +255,3 @@ + "ssh2": { +- "cpu-features": "0.0.0" ++ "cpu-features": "0.0.10" + } +diff --git a/remote/package.json b/remote/package.json +index 5b9d937b..ef1851c1 100644 +--- a/remote/package.json ++++ b/remote/package.json +@@ -55,3 +55,3 @@ + "ssh2": { +- "cpu-features": "0.0.0" ++ "cpu-features": "0.0.10" + } diff --git a/patches/update-electron.patch b/patches/update-electron.patch.no similarity index 100% rename from patches/update-electron.patch rename to patches/update-electron.patch.no diff --git a/patches/use-github-pat.patch b/patches/use-github-pat.patch index 8cb15a3..48b9c07 100644 --- a/patches/use-github-pat.patch +++ b/patches/use-github-pat.patch @@ -1,5 +1,5 @@ diff --git a/extensions/github-authentication/src/common/env.ts b/extensions/github-authentication/src/common/env.ts -index 5456fb8..18fd732 100644 +index 56cad4be..18fd7327 100644 --- a/extensions/github-authentication/src/common/env.ts +++ b/extensions/github-authentication/src/common/env.ts @@ -7,24 +7,4 @@ import { AuthProviderType } from '../github'; @@ -8,9 +8,9 @@ index 5456fb8..18fd732 100644 - 'vscode', - 'vscode-insiders', - 'vscode-exploration', -- 'vscode-sessions', -- 'vscode-sessions-insiders', -- 'vscode-sessions-exploration', +- 'vscode-agents', +- 'vscode-agents-insiders', +- 'vscode-agents-exploration', - // On Windows, some browsers don't seem to redirect back to OSS properly. - // As a result, you get stuck in the auth flow. We exclude this from the - // list until we can figure out a way to fix this behavior in browsers. diff --git a/patches/version-1-update.patch b/patches/version-1-update.patch index 28042b8..af2df0f 100644 --- a/patches/version-1-update.patch +++ b/patches/version-1-update.patch @@ -1,14 +1,14 @@ diff --git a/src/vs/platform/update/common/update.ts b/src/vs/platform/update/common/update.ts -index bc90a03..f8885b9 100644 +index 92ae7b96..c3fe6214 100644 --- a/src/vs/platform/update/common/update.ts +++ b/src/vs/platform/update/common/update.ts -@@ -54,3 +54,4 @@ export const enum UpdateType { +@@ -55,3 +55,4 @@ export const enum UpdateType { Archive, - Snap + Snap, + WindowsInstaller, } -@@ -120 +121,38 @@ export interface IUpdateService { +@@ -123 +124,38 @@ export interface IUpdateService { } + +export type Architecture = @@ -49,18 +49,21 @@ index bc90a03..f8885b9 100644 + | "user"; \ No newline at end of file diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts -index c943bca..d216af7 100644 +index e13d1ba5..3767c907 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts -@@ -17,4 +17,5 @@ import { ILogService } from '../../log/common/log.js'; +@@ -17,3 +17,3 @@ import { ILogService } from '../../log/common/log.js'; import { IProductService } from '../../product/common/productService.js'; -import { IRequestService } from '../../request/common/request.js'; --import { AvailableForDownload, DisablementReason, IUpdateService, State, StateType, UpdateType } from '../common/update.js'; +import { asJson, IRequestService, NO_FETCH_TELEMETRY } from '../../request/common/request.js'; + import { StorageScope, StorageTarget } from '../../storage/common/storage.js'; +@@ -21,3 +21,4 @@ import { IApplicationStorageMainService } from '../../storage/electron-main/stor + import { ITelemetryService } from '../../telemetry/common/telemetry.js'; +-import { AvailableForDownload, DisablementReason, IUpdateService, State, StateType, UpdateType } from '../common/update.js'; +import { Architecture, AvailableForDownload, DisablementReason, IUpdate, IUpdateService, Platform, State, StateType, Target, UpdateType } from '../common/update.js'; +import * as semver from 'semver'; -@@ -25,12 +26,8 @@ export interface IUpdateURLOptions { +@@ -30,12 +31,8 @@ export interface IUpdateURLOptions { -export function createUpdateURL(baseUpdateUrl: string, platform: string, quality: string, commit: string, options?: IUpdateURLOptions): string { - const url = new URL(`${baseUpdateUrl}/api/update/${platform}/${quality}/${commit}`); @@ -78,12 +81,12 @@ index c943bca..d216af7 100644 - - return url.toString(); } -@@ -322,3 +319,3 @@ export abstract class AbstractUpdateService implements IUpdateService { +@@ -434,3 +431,3 @@ export abstract class AbstractUpdateService implements IUpdateService { - if (mode === 'none') { + if (mode === 'none' || mode === 'manual') { return undefined; -@@ -332,18 +329,37 @@ export abstract class AbstractUpdateService implements IUpdateService { +@@ -444,18 +441,37 @@ export abstract class AbstractUpdateService implements IUpdateService { + return this._isLatestVersion(url, false) + .then((result) => { @@ -135,15 +138,15 @@ index c943bca..d216af7 100644 + }) } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts -index 40b38a2..81f772d 100644 +index 3ddb310e..26738a64 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -16,3 +16,3 @@ import { ILogService } from '../../log/common/log.js'; import { IProductService } from '../../product/common/productService.js'; -import { asJson, IRequestService } from '../../request/common/request.js'; +import { asJson, IRequestService, NO_FETCH_TELEMETRY } from '../../request/common/request.js'; - import { ITelemetryService } from '../../telemetry/common/telemetry.js'; -@@ -99,15 +99,4 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau + import { IApplicationStorageMainService } from '../../storage/electron-main/storageMainService.js'; +@@ -101,15 +101,4 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau - protected buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined { - const assetID = this.productService.darwinUniversalAssetId ?? (process.arch === 'x64' ? 'darwin' : 'darwin-arm64'); @@ -161,7 +164,7 @@ index 40b38a2..81f772d 100644 + protected buildUpdateFeedUrl(quality: string, _commit: string, _options?: IUpdateURLOptions): string | undefined { + return createUpdateURL(this.productService, quality, process.platform, process.arch); } -@@ -153,4 +142,30 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau +@@ -145,4 +134,30 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau - this.logService.trace('update#doCheckForUpdates - using Electron autoUpdater', { url, explicit, background }); - electron.autoUpdater.checkForUpdates(); @@ -194,39 +197,41 @@ index 40b38a2..81f772d 100644 + this.setState(State.Idle(UpdateType.Setup, message)); + }); } -@@ -167,3 +182,3 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau +@@ -159,3 +174,3 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau try { - const context = await this.requestService.request({ url, headers, callSite: 'updateService.darwin.checkForUpdates' }, CancellationToken.None); + const context = await this.requestService.request({ url, headers, callSite: NO_FETCH_TELEMETRY }, CancellationToken.None); const statusCode = context.res.statusCode; diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts -index 0eb5d74..16bd215 100644 +index 2be53f61..8776a7f6 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -5,3 +5,2 @@ -import { CancellationToken } from '../../../base/common/cancellation.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; -@@ -13,4 +12,4 @@ import { INativeHostMainService } from '../../native/electron-main/nativeHostMai +@@ -13,6 +12,6 @@ import { INativeHostMainService } from '../../native/electron-main/nativeHostMai import { IProductService } from '../../product/common/productService.js'; -import { asJson, IRequestService } from '../../request/common/request.js'; --import { AvailableForDownload, IUpdate, State, UpdateType } from '../common/update.js'; +import { IRequestService } from '../../request/common/request.js'; + import { IApplicationStorageMainService } from '../../storage/electron-main/storageMainService.js'; + import { ITelemetryService } from '../../telemetry/common/telemetry.js'; +-import { AvailableForDownload, IUpdate, State, UpdateType } from '../common/update.js'; +import { AvailableForDownload, State, UpdateType } from '../common/update.js'; import { AbstractUpdateService, createUpdateURL, IUpdateURLOptions } from './abstractUpdateService.js'; -@@ -32,4 +31,4 @@ export class LinuxUpdateService extends AbstractUpdateService { +@@ -36,4 +35,4 @@ export class LinuxUpdateService extends AbstractUpdateService { - protected buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string { - return createUpdateURL(this.productService.updateUrl!, `linux-${process.arch}`, quality, commit, options); + protected buildUpdateFeedUrl(quality: string, _commit: string, _options?: IUpdateURLOptions): string { + return createUpdateURL(this.productService, quality, process.platform, process.arch); } -@@ -41,2 +40,4 @@ export class LinuxUpdateService extends AbstractUpdateService { +@@ -45,2 +44,4 @@ export class LinuxUpdateService extends AbstractUpdateService { + this.setState(State.CheckingForUpdates(explicit)); + const internalOrg = this.getInternalOrg(); -@@ -44,17 +45,26 @@ export class LinuxUpdateService extends AbstractUpdateService { +@@ -48,17 +49,26 @@ export class LinuxUpdateService extends AbstractUpdateService { const url = this.buildUpdateFeedUrl(this.quality, this.productService.commit!, { background, internalOrg }); - this.setState(State.CheckingForUpdates(explicit)); @@ -264,24 +269,25 @@ index 0eb5d74..16bd215 100644 + this.setState(State.Idle(UpdateType.Archive, message)); diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts -index d02d7c3..d934da1 100644 +index 222db559..0037e002 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -14,3 +14,2 @@ import { CancellationToken, CancellationTokenSource } from '../../../base/common import { memoize } from '../../../base/common/decorators.js'; -import { hash } from '../../../base/common/hash.js'; import * as path from '../../../base/common/path.js'; -@@ -31,6 +30,6 @@ import { INativeHostMainService } from '../../native/electron-main/nativeHostMai +@@ -32,7 +31,7 @@ import { INativeHostMainService } from '../../native/electron-main/nativeHostMai import { IProductService } from '../../product/common/productService.js'; -import { asJson, IRequestService } from '../../request/common/request.js'; +import { IRequestService, NO_FETCH_TELEMETRY } from '../../request/common/request.js'; + import { IApplicationStorageMainService } from '../../storage/electron-main/storageMainService.js'; import { ITelemetryService } from '../../telemetry/common/telemetry.js'; -import { AvailableForDownload, DisablementReason, IUpdate, State, StateType, UpdateType } from '../common/update.js'; -import { AbstractUpdateService, createUpdateURL, getUpdateRequestHeaders, IUpdateURLOptions, UpdateErrorClassification } from './abstractUpdateService.js'; +import { AvailableForDownload, DisablementReason, IUpdate, State, StateType, Target, UpdateType } from '../common/update.js'; +import { AbstractUpdateService, createUpdateURL, IUpdateURLOptions } from './abstractUpdateService.js'; - import { INodeProcess } from '../../../base/common/platform.js'; -@@ -49,5 +48,9 @@ function getUpdateType(): UpdateType { + +@@ -50,5 +49,9 @@ function getUpdateType(): UpdateType { if (typeof _updateType === 'undefined') { - _updateType = existsSync(path.join(path.dirname(process.execPath), 'unins000.exe')) - ? UpdateType.Setup @@ -294,12 +300,12 @@ index d02d7c3..d934da1 100644 + _updateType = UpdateType.Archive; + } } -@@ -164,3 +167,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -158,3 +161,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun } else { - const fastUpdatesEnabled = this.configurationService.getValue('update.enableWindowsBackgroundUpdates'); + const fastUpdatesEnabled = getUpdateType() === UpdateType.Setup && this.configurationService.getValue('update.enableWindowsBackgroundUpdates'); // GC for background updates in system setup happens via inno_setup since it requires -@@ -182,12 +185,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -178,12 +181,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - protected buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined { - let platform = `win32-${process.arch}`; @@ -330,14 +336,14 @@ index d02d7c3..d934da1 100644 - return createUpdateURL(this.productService.updateUrl!, platform, quality, commit, options); + return createUpdateURL(this.productService, quality, process.platform, process.arch, target); } -@@ -199,6 +212,2 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -195,6 +208,2 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - const internalOrg = this.getInternalOrg(); - const background = !explicit && !internalOrg; - const url = this.buildUpdateFeedUrl(this.quality, pendingCommit ?? this.productService.commit!, { background, internalOrg }); - // Only set CheckingForUpdates if we're not already in Overwriting state -@@ -208,9 +217,13 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -204,9 +213,13 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - const headers = getUpdateRequestHeaders(this.productService.version); - this.requestService.request({ url, headers, callSite: 'updateService.win32.checkForUpdates' }, CancellationToken.None) @@ -356,7 +362,7 @@ index d02d7c3..d934da1 100644 - if (!update || !update.url || !update.version || !update.productVersion) { + if(!result) { // If we were checking for an overwrite update and found nothing newer, -@@ -226,2 +239,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -222,2 +235,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + const { lastest, update } = result; + @@ -366,12 +372,12 @@ index d02d7c3..d934da1 100644 + } + if (updateType === UpdateType.Archive) { -@@ -258,3 +278,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -247,3 +267,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - return this.requestService.request({ url: update.url, callSite: 'updateService.win32.downloadUpdate' }, CancellationToken.None) + return this.requestService.request({ url: update.url, callSite: NO_FETCH_TELEMETRY }, CancellationToken.None) .then(context => { -@@ -303,8 +323,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -292,8 +312,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun }) - .then(undefined, err => { - this.telemetryService.publicLog2<{ messageHash: string }, UpdateErrorClassification>('update:error', { messageHash: String(hash(String(err))) }); @@ -383,7 +389,7 @@ index d02d7c3..d934da1 100644 - const message: string | undefined = explicit ? (err.message || err) : undefined; + const message: string | undefined = explicit ? (error.message || error) : undefined; -@@ -368,20 +387,31 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -357,20 +376,31 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun await pfs.Promises.writeFile(this.availableUpdate.updateFilePath, 'flag'); - const child = spawn(this.availableUpdate.packagePath, - [ diff --git a/patches/windows/appx.patch b/patches/windows/appx.patch index 60e992d..b3cdf4a 100644 --- a/patches/windows/appx.patch +++ b/patches/windows/appx.patch @@ -1,8 +1,8 @@ diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index d3ab651..d067b5b 100644 +index 080d97f3..6b1b4032 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -432,19 +432,2 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d +@@ -620,19 +620,2 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d .pipe(rename(f => f.dirname = `policies/${f.dirname}`))); - - if (quality === 'stable' || quality === 'insider') { @@ -23,10 +23,10 @@ index d3ab651..d067b5b 100644 - } } else if (platform === 'linux') { diff --git a/build/gulpfile.vscode.win32.ts b/build/gulpfile.vscode.win32.ts -index a7b01f0..43c93b8 100644 +index 7a711276..e6a838e2 100644 --- a/build/gulpfile.vscode.win32.ts +++ b/build/gulpfile.vscode.win32.ts -@@ -117,8 +117,2 @@ function buildWin32Setup(arch: string, target: string): task.CallbackTask { +@@ -133,8 +133,2 @@ function buildWin32Setup(arch: string, target: string): task.CallbackTask { - if (quality === 'stable' || quality === 'insider') { - definitions['AppxPackage'] = `${quality === 'stable' ? 'code' : 'code_insider'}_${arch}.appx`; @@ -34,4 +34,4 @@ index a7b01f0..43c93b8 100644 - definitions['AppxPackageName'] = `${product.win32AppUserModelId}`; - } - - packageInnoSetup(issPath, { definitions }, cb as (err?: Error | null) => void); + fs.writeFileSync(productJsonPath, JSON.stringify(productJson, undefined, '\t')); diff --git a/prepare_vscode.sh b/prepare_vscode.sh index 1761c7f..eac0559 100755 --- a/prepare_vscode.sh +++ b/prepare_vscode.sh @@ -13,6 +13,8 @@ cp -f LICENSE vscode/LICENSE.txt cd vscode || { echo "'vscode' dir not found"; exit 1; } +rm -rf extensions/copilot + { set +x; } 2>/dev/null # {{{ product.json diff --git a/product.json b/product.json index 0706b16..1bdd011 100644 --- a/product.json +++ b/product.json @@ -49,7 +49,6 @@ "extensionsEnabledWithApiProposalVersion": [ "GitHub.copilot-chat", "ms-vscode.vscode-commander", - "ms-vscode.vscode-copilot-vision", "GitHub.vscode-pull-request-github" ], "extensionEnabledApiProposals": { @@ -246,10 +245,14 @@ ], "GitHub.copilot": [ "inlineCompletionsAdditions", + "interactive", + "terminalDataWriteEvent", "devDeviceId" ], "GitHub.copilot-nightly": [ "inlineCompletionsAdditions", + "interactive", + "terminalDataWriteEvent", "devDeviceId" ], "GitHub.copilot-chat": [ @@ -303,9 +306,11 @@ "taskExecutionTerminal", "dataChannels", "chatSessionsProvider", + "chatSessionCustomizationProvider", "devDeviceId", "contribEditorContentMenu", "tokenInformation", + "toolInvocationApproveCombination", "chatPromptFiles", "mcpServerDefinitions", "tabInputMultiDiff", @@ -379,7 +384,6 @@ "terminalDataWriteEvent", "chatParticipantAdditions" ], - "vscjava.vscode-java-pack": [], "ms-dotnettools.csdevkit": [ "inlineCompletionsAdditions" ], @@ -403,9 +407,6 @@ "languageModelSystem", "mcpServerDefinitions" ], - "ms-toolsai.datawrangler": [], - "ms-vscode.vscode-commander": [], - "ms-vscode.vscode-websearchforcopilot": [], "ms-vscode.vscode-copilot-vision": [ "chatReferenceBinaryData", "codeActionAI" @@ -413,6 +414,14 @@ "ms-autodev.vscode-autodev": [ "chatParticipantAdditions" ], + "codetrek.haystack-search": [ + "fileSearchProvider", + "textSearchProvider2" + ], + "vscjava.migrate-java-to-azure": [ + "chatParticipantAdditions", + "chatParticipantPrivate" + ], "vscjava.vscode-java-upgrade": [ "chatParticipantAdditions", "chatParticipantPrivate" diff --git a/upstream/insider.json b/upstream/insider.json index 7b0d84b..0909ccb 100644 --- a/upstream/insider.json +++ b/upstream/insider.json @@ -1,4 +1,4 @@ { - "tag": "1.112.0", - "commit": "07ff9d6178ede9a1bd12ad3399074d726ebe6e43" + "tag": "1.116.0", + "commit": "560a9dba96f961efea7b1612916f89e5d5d4d679" }