From 07ebfc0b13ed1777191b02ad23e0b22c23110fd5 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Thu, 26 Mar 2026 16:49:53 +0100 Subject: [PATCH 01/79] feat: update electron to 39.8.4 (#2764) --- patches/update-electron.patch | 203 +++++++++++++++++++++++++++++++ patches/update-electron.patch.no | 203 ------------------------------- 2 files changed, 203 insertions(+), 203 deletions(-) create mode 100644 patches/update-electron.patch delete mode 100644 patches/update-electron.patch.no diff --git a/patches/update-electron.patch b/patches/update-electron.patch new file mode 100644 index 00000000000..d428ae359c3 --- /dev/null +++ b/patches/update-electron.patch @@ -0,0 +1,203 @@ +diff --git a/.npmrc b/.npmrc +index a275846..e31402c 100644 +--- a/.npmrc ++++ b/.npmrc +@@ -1,3 +1,3 @@ + disturl="https://electronjs.org/headers" +-target="39.8.0" ++target="39.8.4" + ms_build_id="13470701" +diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt +index 5d8343f..1fd0e54 100644 +--- a/build/checksums/electron.txt ++++ b/build/checksums/electron.txt +@@ -1,75 +1,75 @@ +-d70954386008ad2c65d9849bb89955ab3c7dd08763256ae0d91d8604e8894d64 *chromedriver-v39.8.0-darwin-arm64.zip +-2f6b654337133c13440aafdaf9e8b15f5ebb244e7d49f20977f03438e9bb8adb *chromedriver-v39.8.0-darwin-x64.zip +-ef8681bb6b6af42cdf0e14c9ce188f035e01620781308c06cd3c6b922aaea2e6 *chromedriver-v39.8.0-linux-arm64.zip +-c03fea6ac2b743d771407dc5f58809f44d2a885b1830b847957823cac2e7b222 *chromedriver-v39.8.0-linux-armv7l.zip +-4bb7c6d9b3a7bfdd89edd0db98e63599ebf6dacdb888d5985bbb73f6153acc0c *chromedriver-v39.8.0-linux-x64.zip +-aad1f6f970b5636d637c1c242766fbaa5bebe2707a605a38aadc7b40724b3d11 *chromedriver-v39.8.0-mas-arm64.zip +-e89ebebe3a135d3ce40168152a0aabfd055b9fa6b118262a6df18405fd2ea433 *chromedriver-v39.8.0-mas-x64.zip +-232e1a0460f6a59056499cccfff3265bf92eae22f20f02f2419e5e49552aaed7 *chromedriver-v39.8.0-win32-arm64.zip +-ab92f46cc55da7c719175b50203c734781828389b8b3a1a535204bf0dc7d1296 *chromedriver-v39.8.0-win32-ia32.zip +-a40eb521063e4ea6791ed4005815fa8ac259c1febc850246a83a47ce120121ce *chromedriver-v39.8.0-win32-x64.zip +-d6a33b4c3c0de845ea23d1e2614c6c6d3bbe35b771bb63ae521c4db11373b021 *electron-api.json +-5425323fdb23167870075e944ec6cf3ae383fbe45ad141d08b1d9689030ccd05 *electron-v39.8.0-darwin-arm64-dsym-snapshot.zip +-aa32ab00ee58d8827cd53ca561b8c26b7cb7e2ad8cb0801acdda117ee728388e *electron-v39.8.0-darwin-arm64-dsym.zip +-f94e589804a3394a4735543b888927be873f8f402899d0debe32a9dc570d6285 *electron-v39.8.0-darwin-arm64-symbols.zip +-681d82c2ec6677ff0bf12f5bb1808b5a51dcbf10894bd0298641015119a3e04d *electron-v39.8.0-darwin-arm64.zip +-a95e83b5cde762a37e64229e5669b0c19b95aac148689d96ca344535109eb983 *electron-v39.8.0-darwin-x64-dsym-snapshot.zip +-8c989d8ca835ecdd93d49d9627f5548272c0ed03e263392b21ed287960b29e41 *electron-v39.8.0-darwin-x64-dsym.zip +-b4b6fda9c5b9063a104318645aa29ef4738dd099da2b722e3e9b6dde5e098418 *electron-v39.8.0-darwin-x64-symbols.zip +-ec53f2ba79498410323bb96a19ce98741bf28666cc9d83e07d11dadcc5506f38 *electron-v39.8.0-darwin-x64.zip +-9141e64f9d4ea7f0e6a43ae364c8232a0dac79ecec44de2d4a0e5d688fbb742c *electron-v39.8.0-linux-arm64-debug.zip +-5fac949d5331abaff0643dbcda7cc187e548cd4bf9d198c1ffc361383bfaa79f *electron-v39.8.0-linux-arm64-symbols.zip +-c9db883fa671237fbc16256cf89aba55b9fcfbd9825fec32a6d57724a6446fe1 *electron-v39.8.0-linux-arm64.zip +-b26ac10e84f6b7d338c13a38547aa66b5e9afbe2f1355b183ebc2ff8f428cfa9 *electron-v39.8.0-linux-armv7l-debug.zip +-16c47c008a8783f6c8d6387fe01ea15425161befbf4211e4667bbdd6bb806ef0 *electron-v39.8.0-linux-armv7l-symbols.zip +-b1b37fd450a5081a876c2b00b6ca007d454747a7d1d8f04feb16119d6ace94c6 *electron-v39.8.0-linux-armv7l.zip +-1e8039cdf60b27785771c9e3f3c4c39fad37602bb0e6b75a30f83c57fdbef069 *electron-v39.8.0-linux-x64-debug.zip +-ff9ca169c6e79649dd4c5a49a82a8d4b1761b62fbe14c15c61bf534381a9f653 *electron-v39.8.0-linux-x64-symbols.zip +-854076cc4c63d6d6c320df1ca3f4bd7084ef9f9bb47c7b75d80feb2c2ed920b4 *electron-v39.8.0-linux-x64.zip +-91bc313cbd009435552d8d5efff5d6ed0ff15465743c2629dac1cfe99ac34e4d *electron-v39.8.0-mas-arm64-dsym-snapshot.zip +-974f10f80ec6c65f8d9f2ac1ccd8c4395bb34e24e2b09dc0ff80bd351099692e *electron-v39.8.0-mas-arm64-dsym.zip +-b3878bc9198cff324b7c829ce2fbea7a4ee505f2f99b0bb3c11ac5e60651be59 *electron-v39.8.0-mas-arm64-symbols.zip +-48dac99c757a850b0db7b38c1b95e08270f690a7ea1b58872e45308c2f7c8c93 *electron-v39.8.0-mas-arm64.zip +-1a6e4df1092f89ed46833938d6dd1b3036640037bd09f0630a369ae386a7c872 *electron-v39.8.0-mas-x64-dsym-snapshot.zip +-81425eb867527341af64c00726bd462957fec4d5f073922df891d830addbc5bc *electron-v39.8.0-mas-x64-dsym.zip +-748ce154e894a27b117b46354cc288dc9442fade844c637b59fe1c1f3f7c625d *electron-v39.8.0-mas-x64-symbols.zip +-91f8f7d4eb1a42ac4fa0eaa93034c8e6155ccb50718f9f55541ce2be4a4ed6d0 *electron-v39.8.0-mas-x64.zip +-b775b7584afb84e52b0a770e1e63a2f17384b66eeebe845e0c5c82beacaf7e93 *electron-v39.8.0-win32-arm64-pdb.zip +-ac62373d11ed682b4fcdae27de2bd72ebf7d46d3b569f5fcf242de01786d0948 *electron-v39.8.0-win32-arm64-symbols.zip +-b701e63ca5d443d9bd1b653ea0e2b7479f0d834a3d1bd9f10a3b745d29607154 *electron-v39.8.0-win32-arm64-toolchain-profile.zip +-08b79fa5deabbcace447f1e15eb99b3b117b42a84b71ad5b0f52d2da68a34192 *electron-v39.8.0-win32-arm64.zip +-f4fb798d76a0c2f80717ef1607571537dbbb07f1cc5f177048bcfd17046c2255 *electron-v39.8.0-win32-ia32-pdb.zip +-37c1d2988793604294724b648589fca6459472021189abab1550d5e1eecff1a7 *electron-v39.8.0-win32-ia32-symbols.zip +-b701e63ca5d443d9bd1b653ea0e2b7479f0d834a3d1bd9f10a3b745d29607154 *electron-v39.8.0-win32-ia32-toolchain-profile.zip +-59b70a12abedb550795614bc74c5803787e824da3529a631fdb5c2b5aad00196 *electron-v39.8.0-win32-ia32.zip +-0357c6fb0d7198c45cba0e8c939473ea1d971e1efe801bc84e2c559141b368e7 *electron-v39.8.0-win32-x64-pdb.zip +-8e6f4e8516d15aecde5244beac315067c13513c7074383086523eef2638a5e8d *electron-v39.8.0-win32-x64-symbols.zip +-b701e63ca5d443d9bd1b653ea0e2b7479f0d834a3d1bd9f10a3b745d29607154 *electron-v39.8.0-win32-x64-toolchain-profile.zip +-9edc111b22aee1a0efb5103d6d3b48645af57b48214eeb48f75f9edfc3e271d6 *electron-v39.8.0-win32-x64.zip +-b6eca0e05fcff2464382278dff52367f6f21eb1a580dd8a0a954fc16397ab085 *electron.d.ts +-27cf8e375bc22ceea6b3d42132f2927ea544edac2b8b2c5dc3c10b5df8dfb027 *ffmpeg-v39.8.0-darwin-arm64.zip +-321d9c07f74c6cf77027ec07d888fb7b634d6589207e3c9e016c43e277ca9944 *ffmpeg-v39.8.0-darwin-x64.zip +-52ae6eccbdb4a9403a6c3eb46b356a28940ec25958b6b9181fb2f38e612e40ed *ffmpeg-v39.8.0-linux-arm64.zip +-622cb781fb1e3b9617e7e60c36384427f7b0d9b5ad888e9bc356a83b050e13f1 *ffmpeg-v39.8.0-linux-armv7l.zip +-ba441851788008362f013bf2983b22b0042af8df31bf90123328f928cc067492 *ffmpeg-v39.8.0-linux-x64.zip +-27cf8e375bc22ceea6b3d42132f2927ea544edac2b8b2c5dc3c10b5df8dfb027 *ffmpeg-v39.8.0-mas-arm64.zip +-321d9c07f74c6cf77027ec07d888fb7b634d6589207e3c9e016c43e277ca9944 *ffmpeg-v39.8.0-mas-x64.zip +-3ba7c7507181e0d4836f70f3d8800b4e9ba379e1086e9e89fda7ff9b3b9ad2cb *ffmpeg-v39.8.0-win32-arm64.zip +-f37e7d51b8403e2ed8ca192bc6ae759cf63d80010e747b15eeb7120b575578b2 *ffmpeg-v39.8.0-win32-ia32.zip +-b252e232438010f9683e8fd10c3bf0631df78e42a6ae11d6cb7aa7e6ac11185f *ffmpeg-v39.8.0-win32-x64.zip +-365735192f58a7f7660100227ec348ba3df604415ff5264b54d93cb6cf5f6f6f *hunspell_dictionaries.zip +-6384ee31daa39de4dd4bd3aa225cdb14cdddb7f463a2c1663b38a79e122a13e2 *libcxx-objects-v39.8.0-linux-arm64.zip +-9748b3272e52a8274fe651def2d6ae2dad7a3771b520dd105f46f4020ba9d63b *libcxx-objects-v39.8.0-linux-armv7l.zip +-74d47a155ecc6c2054418c7c3e0540f32b983ebdc65e8b4ea5d3e257d29b3f4f *libcxx-objects-v39.8.0-linux-x64.zip +-c0755fbb84011664bd36459fc6e06a603078dccd3b7b260f6ed6ad1d409f79f7 *libcxx_headers.zip +-3ea41e9bd56e8f52ab8562c1406ba9416abe3993640935e981cbbd77c0f2654b *libcxxabi_headers.zip +-befcd6067f35d911a6a87b927e79dc531cb7bea39e85f86a65e9ab82ef0cece1 *mksnapshot-v39.8.0-darwin-arm64.zip +-f0e692655298ffed60630c3e6490ced69e9d8726e85bcaecfa34485f3a991469 *mksnapshot-v39.8.0-darwin-x64.zip +-d5d0901cd1eafdf921d2a0d1565829cf60f454a71ce74fa60db98780fd8a1a96 *mksnapshot-v39.8.0-linux-arm64-x64.zip +-1bc0a3294d258a59846aa5c5359cd8b0f43831ebd7c3e1dde9a6cfaa39d845bf *mksnapshot-v39.8.0-linux-armv7l-x64.zip +-4e414dbe75f460cb34508608db984aa6f4d274f333fa327a3d631da4a516da8f *mksnapshot-v39.8.0-linux-x64.zip +-c51c86e3a11ad75fb4f7559798f6d64ec7def19583c96ce08de7ee5796568841 *mksnapshot-v39.8.0-mas-arm64.zip +-6544d1e93adea1e9a694f9b9f539d96f84df647d9c9319b29d4fc88751ff9075 *mksnapshot-v39.8.0-mas-x64.zip +-372b4685c53f19ccc72c33d78c1283d9389c72f42cd48224439fe4f89199caa0 *mksnapshot-v39.8.0-win32-arm64-x64.zip +-199e9244f4522a4a02aece09a6a33887b24d7ec837640d39c930170e4b3caa57 *mksnapshot-v39.8.0-win32-ia32.zip +-970e979e7a8b70f300f7854cb571756d9049bc42b44a6153a9ce3a18e1a83243 *mksnapshot-v39.8.0-win32-x64.zip ++1e91e08b27242bfa38e3321eac27a76b4527b5240d9b7fa55b34b27272c7d20d *chromedriver-v39.8.4-darwin-arm64.zip ++e6036542dea19cf60b0d1ae6b7be1cdfb741363f7af8243720566363a9d04a23 *chromedriver-v39.8.4-darwin-x64.zip ++464e5f0c7ae0dcf4817436d6289bcee5cd655e7da96debf8e4723a10f2545362 *chromedriver-v39.8.4-linux-arm64.zip ++2be3059d746a33bf3e4c0cf89131401294ef270e152a0e567ffb279a77cd3bba *chromedriver-v39.8.4-linux-armv7l.zip ++dfce0407bf9304f1df3f053e305d8c7660ab7ac2b6f4b81bedab343d16bcc48d *chromedriver-v39.8.4-linux-x64.zip ++28c4f1b14580fc2a0b35cb9a9b29e8742e117311df8843c1ba31439249b368c3 *chromedriver-v39.8.4-mas-arm64.zip ++381581e0ba9ca49682d39b7cdf05d5f3c2c477c2db7878738103051bffb02c5d *chromedriver-v39.8.4-mas-x64.zip ++22c4c9e1fdbb80b02bfca06de84407ff4fae79072f192b4e4c9260ccd2d55ded *chromedriver-v39.8.4-win32-arm64.zip ++d2771daa0fdd9375b6b82b8fbdc61781fba3ab3aa104a57d0e4a5262b8aa7baa *chromedriver-v39.8.4-win32-ia32.zip ++984738998aa9f5947f11446386d5c26dc5836de5869be4a11a8db6c1a21a9801 *chromedriver-v39.8.4-win32-x64.zip ++21469b4c0fba6ff2470f3ca046c4fedaa0e7169de6849d3a515df1229583e72b *electron-api.json ++4ef051701b2d01f19f8d3d29d41d27e3a9c6881c9defefc3883b35157234ffb0 *electron-v39.8.4-darwin-arm64-dsym-snapshot.zip ++115a40e2f14a492cc5ea9948e61e8c00ad758c568a74c94551acde0a0c6ff892 *electron-v39.8.4-darwin-arm64-dsym.zip ++d744d733681a36a242abb8b997c58571b49edb12db616bbd966111b1ff9f2114 *electron-v39.8.4-darwin-arm64-symbols.zip ++47636d2c4e07dc587fad991d2e682a8a8aa42281732e33323418e16e4d31245d *electron-v39.8.4-darwin-arm64.zip ++8196638bd34cc5815bd91de60f2d5eaa1119c8d473519c208e640cf68a1d9410 *electron-v39.8.4-darwin-x64-dsym-snapshot.zip ++f6cc6d79870acb14efda0bfb33f1e78b96fcab3bdbecc6d91c505dddb519f09f *electron-v39.8.4-darwin-x64-dsym.zip ++14204efe7f170b82e9c369d060f8b05d5a32aceb9800330861dbc05482ada505 *electron-v39.8.4-darwin-x64-symbols.zip ++48bc4c03e3dab5678ddc447378ec639803d687d75939575675a273f054b98237 *electron-v39.8.4-darwin-x64.zip ++8c5e0eb3e15b90b2397dc641255c8bac797a0e4a2f999ea77e4461d667add977 *electron-v39.8.4-linux-arm64-debug.zip ++25df0c1a03bdad3a19f5a9d6385efadc249b48a512cbb7967bccffe1a123a007 *electron-v39.8.4-linux-arm64-symbols.zip ++67d9c084e664ee2a5f76dce2efb13f79aff60e014deb06174af82b6cfd43b101 *electron-v39.8.4-linux-arm64.zip ++514dbca9bf81f82f6574ae906057b78f13c4bd781eba9033ed58d6609ce944a8 *electron-v39.8.4-linux-armv7l-debug.zip ++3baa152b9ae413da46ced437291b7bd65dca390168a62f9ba01b7f8c9bc76ed4 *electron-v39.8.4-linux-armv7l-symbols.zip ++a2d835231443afc8022a99a3ab94b7b4a90ebfbda77c2fbefafca430753174fe *electron-v39.8.4-linux-armv7l.zip ++8ea7223c43a03779fafc6272742ebca104f584b3952abcf17b186af3624803d7 *electron-v39.8.4-linux-x64-debug.zip ++4d834e7574dda95f1bd10a7758ed93fbc12d2aa4aa6cc6fad091a37ea422d196 *electron-v39.8.4-linux-x64-symbols.zip ++66f1ea702595deda724f6c6246acfddc84a317c7f0262bd7fd350af50dbbe4e9 *electron-v39.8.4-linux-x64.zip ++ce85bfbd66c6731004e7d035a2bf65b2c0cb95a9716ee7e156061c791bd723a7 *electron-v39.8.4-mas-arm64-dsym-snapshot.zip ++b745d57275df62904d779fe757446adfe515252edc00fe8e8667fe63e9975dda *electron-v39.8.4-mas-arm64-dsym.zip ++14706a26e78fcdc34be2f12229920cc178b6b773eeede0aee87875336e4a6a0c *electron-v39.8.4-mas-arm64-symbols.zip ++9a30bfe87c8d62f39798a5a69a63085429f6f1ba1975e7d552dc35c6c7c41a59 *electron-v39.8.4-mas-arm64.zip ++8ee820ece1f08d637ddbea9dae1b9fbe014be88fa20feb1e261a6921c2977e6c *electron-v39.8.4-mas-x64-dsym-snapshot.zip ++044c25405f83c6012c238990b534f47430e308c2c787e1f6d7fe0fb41728b818 *electron-v39.8.4-mas-x64-dsym.zip ++05c1f4fc148222481dc3e88388cbd5451bffdd436293cc0613a9ab1904e76cfa *electron-v39.8.4-mas-x64-symbols.zip ++51c310921e5740a91a75d89819f89757210707a913691fe07d60e84100102d42 *electron-v39.8.4-mas-x64.zip ++3aacf6f224b5438350c1ea5524cb0c962f6d60f8e00f297fc872dce9633aed7d *electron-v39.8.4-win32-arm64-pdb.zip ++180f870fbe3aff8d4ca837ff41b5e1dd04d63b0ae0cc2fcf8d7e20e518f4fcb6 *electron-v39.8.4-win32-arm64-symbols.zip ++b701e63ca5d443d9bd1b653ea0e2b7479f0d834a3d1bd9f10a3b745d29607154 *electron-v39.8.4-win32-arm64-toolchain-profile.zip ++4316324e59d8bceee77366a4ed7a8d3f087ccdf2232b9cac61d600c28fcf0876 *electron-v39.8.4-win32-arm64.zip ++110fa00b9defb546e6110022be00c2f8ef2306b7104e2a1f749dc2b74d467e57 *electron-v39.8.4-win32-ia32-pdb.zip ++ae8c656e2d43493a6c245235ca17f1ddd8ae9632f73348a8f76d76240c5f33f6 *electron-v39.8.4-win32-ia32-symbols.zip ++b701e63ca5d443d9bd1b653ea0e2b7479f0d834a3d1bd9f10a3b745d29607154 *electron-v39.8.4-win32-ia32-toolchain-profile.zip ++f260df3274199f29e32e89efb7bcd846db30d6ccdcc46cc3be9c19be9bc16566 *electron-v39.8.4-win32-ia32.zip ++2e37fd5956a3b73d99e569ea71cc463ba550440835f7672d0651c309d2489c52 *electron-v39.8.4-win32-x64-pdb.zip ++b1601c04324691324b2943aeda1309dbd8dac20cc5f4df32f4aa12d5a68c4b0c *electron-v39.8.4-win32-x64-symbols.zip ++b701e63ca5d443d9bd1b653ea0e2b7479f0d834a3d1bd9f10a3b745d29607154 *electron-v39.8.4-win32-x64-toolchain-profile.zip ++8acc4bb73f05a32eba18bd08e7bc4636e41b2fd00cb538cb90becffbca997789 *electron-v39.8.4-win32-x64.zip ++3675395eef7139544965c3408f1426e4af36055627c5ec0c2fe5abf4cecf6977 *electron.d.ts ++27cf8e375bc22ceea6b3d42132f2927ea544edac2b8b2c5dc3c10b5df8dfb027 *ffmpeg-v39.8.4-darwin-arm64.zip ++321d9c07f74c6cf77027ec07d888fb7b634d6589207e3c9e016c43e277ca9944 *ffmpeg-v39.8.4-darwin-x64.zip ++52ae6eccbdb4a9403a6c3eb46b356a28940ec25958b6b9181fb2f38e612e40ed *ffmpeg-v39.8.4-linux-arm64.zip ++622cb781fb1e3b9617e7e60c36384427f7b0d9b5ad888e9bc356a83b050e13f1 *ffmpeg-v39.8.4-linux-armv7l.zip ++ba441851788008362f013bf2983b22b0042af8df31bf90123328f928cc067492 *ffmpeg-v39.8.4-linux-x64.zip ++27cf8e375bc22ceea6b3d42132f2927ea544edac2b8b2c5dc3c10b5df8dfb027 *ffmpeg-v39.8.4-mas-arm64.zip ++321d9c07f74c6cf77027ec07d888fb7b634d6589207e3c9e016c43e277ca9944 *ffmpeg-v39.8.4-mas-x64.zip ++71d478432519dda32a53c47eb5fcb97d23265273ff7939da54cccf7a19f6f87e *ffmpeg-v39.8.4-win32-arm64.zip ++3c52c43a62ebf1ebbe0c02611ad4399eb0131257849b707156417ffcbaa144c8 *ffmpeg-v39.8.4-win32-ia32.zip ++5613f1bd387d6c91f4c3901b22643c5400c696e2dacbb7d9893d0e63edc51ae9 *ffmpeg-v39.8.4-win32-x64.zip ++d720519f7417fe58d60d54085a0c32e8e7b65883cad909ee55d1b9b78f86362d *hunspell_dictionaries.zip ++a4c6e3aef474b21e7f002858ddef194da1b64e8b20ecabdaea9533d1d2712738 *libcxx-objects-v39.8.4-linux-arm64.zip ++bd9f1876dddbc413fc3328707f4f7f9daa2df7cb81d4d9e13f520168ec9eb76a *libcxx-objects-v39.8.4-linux-armv7l.zip ++8403bc29cd82a40f422c63fdbaa61b85d28ff58e5e90e378a97c0715133b87c4 *libcxx-objects-v39.8.4-linux-x64.zip ++9bbb19892567a3a9abd0f6288f9f203fa3051387c3e4625fc52eb49adcbe96e5 *libcxx_headers.zip ++9b988e2bb379c6d3094872f600944ad3284510cf225f86368c4f43270b89673c *libcxxabi_headers.zip ++f106faec00f811971cce77bb39a2ef08ddeb3e6b3b6f96991a4ab889c06636a3 *mksnapshot-v39.8.4-darwin-arm64.zip ++23ca2a8fbe7d870c745c6808dced54889883d259fd6a601174b81891dfd1a052 *mksnapshot-v39.8.4-darwin-x64.zip ++23b78843edf3ad2ad2a38eb88578105a75335c397bd4f7cc1fdf54f890fb9d9b *mksnapshot-v39.8.4-linux-arm64-x64.zip ++582d768b97f13d5991fb96dbbba54f7c5da4248634952d77eac342ecd5db29a5 *mksnapshot-v39.8.4-linux-armv7l-x64.zip ++677e242eca2487d1427e035cc9e54750a3bb2528f761af483e5a6d67f81f69dc *mksnapshot-v39.8.4-linux-x64.zip ++fadb9ec8cbf598ef49d5dac34c764a2d2487d41111e3201dd13ad6f09d48c591 *mksnapshot-v39.8.4-mas-arm64.zip ++2a912c91dffeae2e8a97b7e49fe67f7dfb7d13f8e441bb5c2c40d2583ae504ca *mksnapshot-v39.8.4-mas-x64.zip ++fc5478a848c95a1412efdc550d16e4e83c7c2db5f98c658da2430c4275e5f6cb *mksnapshot-v39.8.4-win32-arm64-x64.zip ++b279c6292e1937a9c6aa7421415434e8f40ed7ba818851f57227cdedbdd19e7b *mksnapshot-v39.8.4-win32-ia32.zip ++c5b457d293d35f98d9d548613fce3c6ee111242760d381ed39d6c721b3d09918 *mksnapshot-v39.8.4-win32-x64.zip +\ No newline at end of file +diff --git a/cgmanifest.json b/cgmanifest.json +index 1b1e171..09d805b 100644 +--- a/cgmanifest.json ++++ b/cgmanifest.json +@@ -531,4 +531,4 @@ + "repositoryUrl": "https://github.com/electron/electron", +- "commitHash": "69c8cbf259da0f84e9c1db04958516a68f7170aa", +- "tag": "39.8.0" ++ "commitHash": "7007907df08d02da98f513dcbdb430ab51be59c7", ++ "tag": "39.8.4" + } +diff --git a/package-lock.json b/package-lock.json +index 3795213..9d261de 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -104,3 +104,3 @@ + "deemon": "^1.13.6", +- "electron": "39.8.0", ++ "electron": "39.8.4", + "eslint": "^9.36.0", +@@ -8389,5 +8389,5 @@ + "node_modules/electron": { +- "version": "39.8.0", +- "resolved": "https://registry.npmjs.org/electron/-/electron-39.8.0.tgz", +- "integrity": "sha512-K+f3YelSyh9Q4LgUXuIhLB4kq73LJrqnIbe8ih9vpWi+iSdPebj0w7FRYwILCMDoyBQMFC9LicYHuIPmZzdKlg==", ++ "version": "39.8.4", ++ "resolved": "https://registry.npmjs.org/electron/-/electron-39.8.4.tgz", ++ "integrity": "sha512-eXYKxr4y+s31xs78keVJYg+XY20tGQMQzyIhZvc5L0XRDH2Gp08mbeFlbR1OjAeM5h5l/T2JYT2MFK2kYe2fMg==", + "dev": true, +diff --git a/package.json b/package.json +index 295d4b3..6e707ed 100644 +--- a/package.json ++++ b/package.json +@@ -174,3 +174,3 @@ + "deemon": "^1.13.6", +- "electron": "39.8.0", ++ "electron": "39.8.4", + "eslint": "^9.36.0", diff --git a/patches/update-electron.patch.no b/patches/update-electron.patch.no deleted file mode 100644 index 63dde91df35..00000000000 --- a/patches/update-electron.patch.no +++ /dev/null @@ -1,203 +0,0 @@ -diff --git a/.npmrc b/.npmrc -index e4a5cc2..e3be238 100644 ---- a/.npmrc -+++ b/.npmrc -@@ -1,3 +1,3 @@ - disturl="https://electronjs.org/headers" --target="39.2.3" -+target="39.2.7" - ms_build_id="12895514" -diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt -index ace84ba..5da4475 100644 ---- a/build/checksums/electron.txt -+++ b/build/checksums/electron.txt -@@ -1,75 +1,75 @@ --1e88807c749e69c9a1b2abef105cf30dbec4fddc365afcaa624b1e2df80fe636 *chromedriver-v39.2.3-darwin-arm64.zip --5cadee0db7684ae48a7f9f4f1310c3f6e1518b0fa88cf3efb36f58984763d43d *chromedriver-v39.2.3-darwin-x64.zip --8de5ed25a12029ca999455c1cadf28341ec5e0de87a3a0c27dbb24df99f154b1 *chromedriver-v39.2.3-linux-arm64.zip --766b16d8b1297738a0d1fa7e44d992142558f6e12820197746913385590f033e *chromedriver-v39.2.3-linux-armv7l.zip --f35049fe3d8dbfdb7c541b59bdca6982b571761bb8cb7fc85515ceaea9451de9 *chromedriver-v39.2.3-linux-x64.zip --bffe049ac205d87d14d8d2fb61c8f4dfd72b6d60fcd72ebedf7ef78c90ed52d9 *chromedriver-v39.2.3-mas-arm64.zip --95a7142ba2ba6a418c6d804729dbe4f1fee897cd9ecaf32e554bb9cabff52b9c *chromedriver-v39.2.3-mas-x64.zip --da1a59e49c16f7b0924b8b43847a19c93110f7d3b5d511cc41d7ec43a5d3807a *chromedriver-v39.2.3-win32-arm64.zip --9ba84c1e03e31dd630439d53c975b51c21aa4038526dc01970b94464303db5c7 *chromedriver-v39.2.3-win32-ia32.zip --82d88829e894277d737188afe22a2c82611107f7b31aeb221ae67e56a580dceb *chromedriver-v39.2.3-win32-x64.zip --aca80a76b97d4b0aa3001882bd8cb7a8fb3f1df75cbc4f0d74eaad0c9df53c9b *electron-api.json --0fb6f376da5f1bb06125134cd8e33d79a76c4d47b0bc51d20c3359e092095b98 *electron-v39.2.3-darwin-arm64-dsym-snapshot.zip --6a9e67878637191edcefbd36b070137c3ca4f674307c255864eb9720128905c4 *electron-v39.2.3-darwin-arm64-dsym.zip --30fd6a23a4a70de3882525c1666af98a2cf07e0826c54bef8f466efb25b1d2ec *electron-v39.2.3-darwin-arm64-symbols.zip --2128a27c1b0fd80be9d608fb293639f76611b4108eca1e045c933fd04097a7b1 *electron-v39.2.3-darwin-arm64.zip --68435db35b408d7eb3b9f208f2a7aa803bb8578f409ee99bab435118951a21a5 *electron-v39.2.3-darwin-x64-dsym-snapshot.zip --59e821dbe0083d4e28a77dff5f72fa65c0db7e7966d760ebb5a41af92da43958 *electron-v39.2.3-darwin-x64-dsym.zip --cdbe6988a9c9277d5a1acd2f3aaf08e603050f3dae0c10dee4b10d7a6f7cf818 *electron-v39.2.3-darwin-x64-symbols.zip --f8085a04dc35bfe0c32c36e6feffde07de16459bf36dfab422760181717f5ac0 *electron-v39.2.3-darwin-x64.zip --ce57eb6bd0ddfa1d37d8a35615276aeb60c19ae0636f21da3270cf07844074b4 *electron-v39.2.3-linux-arm64-debug.zip --d2652381b24dc05c57a4ce4835b6efc796e6af14419ec80a9ab31f1c3c53f143 *electron-v39.2.3-linux-arm64-symbols.zip --c58c5904d6015cbbfa5f04fbda5c83b9a276a3565b5f3fa166795c789b055cdd *electron-v39.2.3-linux-arm64.zip --f0f0be5ea43c0fe84b9609dd5d2b98904c2d4bb8ced9c7c72b72cef377f2734a *electron-v39.2.3-linux-armv7l-debug.zip --f08ae5371aca8a9f3775a6855c74da71d8817bd9f135c3ba975d428d14f3c42f *electron-v39.2.3-linux-armv7l-symbols.zip --d7c2f0b5038c49b1e637f8dbda945be4e6f3a6d7ebf802543e6ef5093c9641ff *electron-v39.2.3-linux-armv7l.zip --aa8b9e4b5eed3a0d2271c01d34551d7dc3e9be30a68af06604c1e2cd3cf93223 *electron-v39.2.3-linux-x64-debug.zip --d5ebf9628e055b03c90d2d6d4ed86f443b900e264ff34061c953541e27fad5f9 *electron-v39.2.3-linux-x64-symbols.zip --5eb51ebcb60487c4fc3a5b74ffb57a03eefd48def32200adf310ffaba4153d64 *electron-v39.2.3-linux-x64.zip --f6cc53c0a45c73779c837d71693f54cc18b12b7148c82c689e2b059772182b84 *electron-v39.2.3-mas-arm64-dsym-snapshot.zip --0caf9b7b958a7d2ba7e6f757f885842efda3ebc794a2ac048b90cde2926281ee *electron-v39.2.3-mas-arm64-dsym.zip --c3164da6588c546e728b6fa0754042328cdb43e28dbb0fbcfbda740ed58038fe *electron-v39.2.3-mas-arm64-symbols.zip --36ea0a98a0480096b4bc6e22c194e999cdfd7f1263c51f08d2815985a8a39ef7 *electron-v39.2.3-mas-arm64.zip --73d356aa3b51cb261d30f0c27ce354b904d17c3c05c124a1f41112d085e66852 *electron-v39.2.3-mas-x64-dsym-snapshot.zip --083f53e15a93404b309754df6b5e785785b28e01fdab08a89a45e5024f44e046 *electron-v39.2.3-mas-x64-dsym.zip --cdd8aaf3b90aedc8c09a44efa03ec67e8426102fad7333ff6bfc257dc6fa01b7 *electron-v39.2.3-mas-x64-symbols.zip --517d26f9b76b23976d0fc1dcc366e2b50b782592d9b0fc1d814dd1e7ce66efef *electron-v39.2.3-mas-x64.zip --1a83af2259feb361f7ceb79e047b701ea8297d616487d9d6a79530014d5000c7 *electron-v39.2.3-win32-arm64-pdb.zip --a154f036378a81859804f660773f6d434770fc311af86dfe01ace5346b9dc788 *electron-v39.2.3-win32-arm64-symbols.zip --4aae37230f86b1590f102aa038268299bfb55ce2bf3b76ac4d6159e7b6a69f8e *electron-v39.2.3-win32-arm64-toolchain-profile.zip --b68d623d70c4d0ed76c979027d2a4f6a16bc8dee6f243f5bc2064b4bb52bb34d *electron-v39.2.3-win32-arm64.zip --be73842257d098ac911b3363e0c11b1d51ab8f6ebd641e512a2e15ccbea73193 *electron-v39.2.3-win32-ia32-pdb.zip --5f65391f51b5d46d5e0ec7018f3febc0f5b6f072b57310d6d6c9b014de911ff4 *electron-v39.2.3-win32-ia32-symbols.zip --4aae37230f86b1590f102aa038268299bfb55ce2bf3b76ac4d6159e7b6a69f8e *electron-v39.2.3-win32-ia32-toolchain-profile.zip --6668fadbdd0283225f4bc60c711f8cd8ac316f43f486cd8a1f62a6a35f89cf7a *electron-v39.2.3-win32-ia32.zip --430aa905803772476fc1f943e87e4a319d33880d88e08472504531b96834dff1 *electron-v39.2.3-win32-x64-pdb.zip --9adb254e6ee0d96311cc8056049814436b7e973757d026aac3b533820be027ec *electron-v39.2.3-win32-x64-symbols.zip --4aae37230f86b1590f102aa038268299bfb55ce2bf3b76ac4d6159e7b6a69f8e *electron-v39.2.3-win32-x64-toolchain-profile.zip --d4365ad128bbdcb3df99dc4a0ad9de85c5e920903070a473b55377253b6c3fdd *electron-v39.2.3-win32-x64.zip --feb2f068cd1e2f70bdd7816c13e58dcff9add18fdc8c8e19145a5fd343be541a *electron.d.ts --4fe4db7f974c64497ddc07c3955a7d83dcfeba61bcec704b33638a4848038d49 *ffmpeg-v39.2.3-darwin-arm64.zip --8fa2eb8ce5bdf2ecc4cf1f5ebc0f46a4e466fb4841513d482b99838b265995af *ffmpeg-v39.2.3-darwin-x64.zip --bc72228a7380bc491783602d823bbe2d75e9e417d9b93a40a64be6ff5e3a1bcc *ffmpeg-v39.2.3-linux-arm64.zip --322698b5ebfae62c34e98c2589b0906b99c15a8181ca3b6d1ffe166ec7d99ab1 *ffmpeg-v39.2.3-linux-armv7l.zip --40d23294d7bcc48cb3f647f278672021e969a6332cd3cbb06ee681833759626a *ffmpeg-v39.2.3-linux-x64.zip --4fe4db7f974c64497ddc07c3955a7d83dcfeba61bcec704b33638a4848038d49 *ffmpeg-v39.2.3-mas-arm64.zip --8fa2eb8ce5bdf2ecc4cf1f5ebc0f46a4e466fb4841513d482b99838b265995af *ffmpeg-v39.2.3-mas-x64.zip --d324af171e0ae820ec72075924ace2bda96e837ccc79e22b652dda6f82b673b6 *ffmpeg-v39.2.3-win32-arm64.zip --d982077305d0e4296bed95eb7d2f1048a90b06cfb84d5ddf2a1928e1f07c4dba *ffmpeg-v39.2.3-win32-ia32.zip --fa65c30f970f9724f4353d068a640592b09a15593b943fa7544cd07e9cace90e *ffmpeg-v39.2.3-win32-x64.zip --244cd79cf68540e83449ad7d73183416413b3d603cee4496ec07705cbd9338ee *hunspell_dictionaries.zip --f995e05259eeae64f0e6fbb6d2863aa2fc5846e3ff2dfb3cd22defc3bbbb68d7 *libcxx-objects-v39.2.3-linux-arm64.zip --3607b4a15aa5f2dbd9e2338ca5451ad8ff646bdac415f9845352d53be1c26ddf *libcxx-objects-v39.2.3-linux-armv7l.zip --b5020533566dbf22b0b890caa766eb2f4d11675fb1c79c2f41bc54da45a34fc2 *libcxx-objects-v39.2.3-linux-x64.zip --919a2cc35920b21fbcc5834e858c400f51b607f084c593883c637dba27b9d29a *libcxx_headers.zip --34e4b44f9c5e08b557a2caed55456ce7690abab910196a783a2a47b58d2b9ac9 *libcxxabi_headers.zip --661d3578cabe5c98d806d5eeeaee48ac0c997114b9cd76388581e58f6d1c2ce1 *mksnapshot-v39.2.3-darwin-arm64.zip --c3032c90522e4491e3de641fade3c90be109269108d4ff39b55dbf7331e6eb9a *mksnapshot-v39.2.3-darwin-x64.zip --bcd8fb45f3b093208346dc2dd2e0b5b70d117e26a70b9619921b26a7f99ba310 *mksnapshot-v39.2.3-linux-arm64-x64.zip --647762d3d8b01b5123ec11ea5b6984d7b78a26c79ea4d159a3b9fa780de03321 *mksnapshot-v39.2.3-linux-armv7l-x64.zip --86c0febd8e9ddd8b700c6fb633ec1406bf4fe19ddc2801cb50c01ad345c8ce6e *mksnapshot-v39.2.3-linux-x64.zip --3676ffc5f489b7d7faafe36fdb5f0f4ce98c8d6fcedfacf6feded7f21b2a50ea *mksnapshot-v39.2.3-mas-arm64.zip --728936a18c11727d32730c89060dca2d998e7df9159f12bcba2bdf1b51584aad *mksnapshot-v39.2.3-mas-x64.zip --a3ef9ab1ad5c8172c029dcc36abdc979ecf01f235516120f666595d4d5d02aee *mksnapshot-v39.2.3-win32-arm64-x64.zip --02584df98255591438ffcc6589bd1ee60af8b8897d08079e7a7dd054e09614fe *mksnapshot-v39.2.3-win32-ia32.zip --d4dd9de8637d7d8240b7a0686916c0fe84058ad00db9422f5491fbbd7a53cf4b *mksnapshot-v39.2.3-win32-x64.zip -+ab4c5ce64b92082b15f11ed2a89766fa5542b33d656872678ca0aee99e51a7c8 *chromedriver-v39.2.7-darwin-arm64.zip -+976f03f6e5e1680e5f8449bd04da531aabec0b664ff462a14f0d41fad0b437af *chromedriver-v39.2.7-darwin-x64.zip -+28649b04333820f826ea658d18f8111e0a187b3afc498af05b5c59b27ac00155 *chromedriver-v39.2.7-linux-arm64.zip -+149033ccf7f909214c7d69788bdef2e4ce164cae1091a2f8220f62e495576f9b *chromedriver-v39.2.7-linux-armv7l.zip -+6a071551518eddc688dd348d3e63b0c55f744589a041943e5706bebfd5337f19 *chromedriver-v39.2.7-linux-x64.zip -+824ea4699fd6aa6822e453496ebf576174d04e0f0991843b77eb346a842725bc *chromedriver-v39.2.7-mas-arm64.zip -+aa991650a765b2bc168f8b742341048fa030ee9e3bd0d0799e1b1d29a4c55d0b *chromedriver-v39.2.7-mas-x64.zip -+a8fc4467bf9be10de3e341648ccd6ad6d618b4456a744137e9f19bd5f9d9bd37 *chromedriver-v39.2.7-win32-arm64.zip -+01b247563a054617530e454646b086352bc03e02ad4f18e5b65b4e3dfd276a1e *chromedriver-v39.2.7-win32-ia32.zip -+a8bc2b9052ac8dadeaf88ea9cd6e46ec0032eee2345a0548741bfed922520579 *chromedriver-v39.2.7-win32-x64.zip -+23486b3effffe5b3bc3ca70261fc9abe2396fd5d018652494f73e3f48cfe57cf *electron-api.json -+8bee9e905544e60e08468efca91481ec467ab8f108a81846c365782ba0fc737c *electron-v39.2.7-darwin-arm64-dsym-snapshot.zip -+3be97c3152cd4a84a6fe4013f7e4712422015f4beeb13eb35f8b4d223307d39a *electron-v39.2.7-darwin-arm64-dsym.zip -+6d5551120d0564fc5596a3b724258da2ce632663d12782c8fdf15a2cc461ed95 *electron-v39.2.7-darwin-arm64-symbols.zip -+bda657a77c074ee0c6a0e5d5f6de17918d7cf959306b454f6fadb07a08588883 *electron-v39.2.7-darwin-arm64.zip -+39f0aab332506455337edff540d007c509e72d8c419cdc57f88a0312848f51c9 *electron-v39.2.7-darwin-x64-dsym-snapshot.zip -+1efed54563ede59d7ae9ba3d548b3e93ede1a4e5dfa510ca22036ea2dd8a2956 *electron-v39.2.7-darwin-x64-dsym.zip -+3b9bfe84905870c9c36939ffac545d388213ffbb296b969f35ae2a098f6a32b7 *electron-v39.2.7-darwin-x64-symbols.zip -+d7535e64ad54efcf0fae84d7fea4c2ee4727eec99c78d2a5acc695285cb0a9f0 *electron-v39.2.7-darwin-x64.zip -+59a3bd71f9c1b355dfbc43f233126cd32b82a53439f0d419e6349044d39e8bbf *electron-v39.2.7-linux-arm64-debug.zip -+1b326f1a5bea47d9be742554434ddf4f094d7bcdd256f440b808359dc78fcd33 *electron-v39.2.7-linux-arm64-symbols.zip -+445465a43bd2ffaec09877f4ed46385065632a4683c2806cc6211cc73c110024 *electron-v39.2.7-linux-arm64.zip -+300c8d11d82cd1257b08e5a08c5e315f758133b627c0271a0f249ba3cb4533d2 *electron-v39.2.7-linux-armv7l-debug.zip -+034dca3c137c7bfe0736456c1aa0941721e3a9f3a8a72a2786cb817d4edb0f9d *electron-v39.2.7-linux-armv7l-symbols.zip -+5de99e9f4de8c9ac2fb93df725e834e3e93194c08c99968def7f7b78594fc97c *electron-v39.2.7-linux-armv7l.zip -+64ef2ae24ae0869ebadb34b178fd7e8375d750d7afe39b42cfa28824f0d11445 *electron-v39.2.7-linux-x64-debug.zip -+63466c4b6024ae38fdb38ff116abd561b9e36b8d4cd8f8aefbe41289950dba0c *electron-v39.2.7-linux-x64-symbols.zip -+2f5285ef563dca154aa247696dddef545d3d895dd9b227ed423ea0d43737c22c *electron-v39.2.7-linux-x64.zip -+ef5a108c1d10148aa031300da10c78feee797afe4ca2a2839819fd8434529860 *electron-v39.2.7-mas-arm64-dsym-snapshot.zip -+9dd01dc9071b1db9d8fb5e9c81eaa96f551db0a982994881e5750cde2432b0f0 *electron-v39.2.7-mas-arm64-dsym.zip -+2cf34289d79906c81b3dfd043fbe19a9604cecedd9ebda6576fa3c6f27edfe23 *electron-v39.2.7-mas-arm64-symbols.zip -+5658d58eacb99fb2a22df0d52ca0507d79f03c85515a123d5e9bee5e0749b93d *electron-v39.2.7-mas-arm64.zip -+92cd45c3fa64e2889fd1bc6b165c4d12bea40786ce59d6d204cadec6039a8e2a *electron-v39.2.7-mas-x64-dsym-snapshot.zip -+21464abc837aeab1609fbfa33aa82793e9d32a597db28ea4da483a9d6b6c668a *electron-v39.2.7-mas-x64-dsym.zip -+8d6e7ffee482514b62465e418049bdf717d308118461e5d97480f5a0eb0b9e20 *electron-v39.2.7-mas-x64-symbols.zip -+e3b4169ab7bf3bc35cc720ef99032acd3d0eb1521524b5c4667898758dd4e9a3 *electron-v39.2.7-mas-x64.zip -+3f1d549214a2430d57e5ab8d3cc9d89363340b16905014e35417c632a94732f6 *electron-v39.2.7-win32-arm64-pdb.zip -+984e1d7718bc920e75a38b114ff73fa52647349763f76e91b64458e5d0fde65f *electron-v39.2.7-win32-arm64-symbols.zip -+ed66f333ff7b385b2f40845178dc2dc4f25cc887510d766433392733fdd272a3 *electron-v39.2.7-win32-arm64-toolchain-profile.zip -+56c6f8d957239b7e8d5a214255f39007d44abc98f701ab61054afa83ad46e80f *electron-v39.2.7-win32-arm64.zip -+c885a8af3226f28081106fa89106f4668b907a53ab3997f3b101b487a76d2878 *electron-v39.2.7-win32-ia32-pdb.zip -+34edebab8fb5458d97a23461213b39360b5652f8dd6fe8bf7f9c10a17b25a1d2 *electron-v39.2.7-win32-ia32-symbols.zip -+ed66f333ff7b385b2f40845178dc2dc4f25cc887510d766433392733fdd272a3 *electron-v39.2.7-win32-ia32-toolchain-profile.zip -+85acd7db5dbb39e16d6c798a649342969569caa2c71d6b5bb1f0c8ae96bca32e *electron-v39.2.7-win32-ia32.zip -+e6a8e1164106548a1cdf266c615d259feada249e1449df8af1f7e04252575e86 *electron-v39.2.7-win32-x64-pdb.zip -+90e1feeff5968265b68d8343e27b9f329b27882747633dd10555740de67d58cc *electron-v39.2.7-win32-x64-symbols.zip -+ed66f333ff7b385b2f40845178dc2dc4f25cc887510d766433392733fdd272a3 *electron-v39.2.7-win32-x64-toolchain-profile.zip -+3464537fa4be6b7b073f1c9b694ac2eb1f632d6ec36f6eeac9e00d8a279f188c *electron-v39.2.7-win32-x64.zip -+40c772eb189d100087b75da6c2ad1aeb044f1d661c90543592546a654b0b6d5b *electron.d.ts -+5a904c2edd12542ce2b6685938cdafe21cf90cd552f2f654058353d1a3d8ee43 *ffmpeg-v39.2.7-darwin-arm64.zip -+91fc23e9008f43ad3c46f690186d77b291a803451b6d89ac82aadb8ae2dd7995 *ffmpeg-v39.2.7-darwin-x64.zip -+a44607619c6742c1f9d729265a687b467a25ba397081ac12bc2c0d9ab4bea37b *ffmpeg-v39.2.7-linux-arm64.zip -+8128ec9be261e2c1017f9b8213f948426119306e5d3acdb59392f32b2c2f0204 *ffmpeg-v39.2.7-linux-armv7l.zip -+a201a2a64a49ab39def2d38a73e92358ebb57ecae99b0bbc8058353c4be23ea1 *ffmpeg-v39.2.7-linux-x64.zip -+5a904c2edd12542ce2b6685938cdafe21cf90cd552f2f654058353d1a3d8ee43 *ffmpeg-v39.2.7-mas-arm64.zip -+91fc23e9008f43ad3c46f690186d77b291a803451b6d89ac82aadb8ae2dd7995 *ffmpeg-v39.2.7-mas-x64.zip -+6fa4278a41d9c5d733369aa4cce694ba219eb72f7fd181060547c3a4920b5902 *ffmpeg-v39.2.7-win32-arm64.zip -+12b9e02c0fd07e8bc233c7c4ebab5c737eca05c41f1c5178867cad313433561b *ffmpeg-v39.2.7-win32-ia32.zip -+caedeb04aa648af14b5a20c9ca902c97eb531a456c7965639465f8764b5d95e0 *ffmpeg-v39.2.7-win32-x64.zip -+f1320ff95f2cce0f0f7225b45f2b9340aeb38b341b4090f0e58f58dc2da2f3a9 *hunspell_dictionaries.zip -+8f4ffd7534f21e40621c515bacd178b809c2e52d1687867c60dfdb97ed17fecb *libcxx-objects-v39.2.7-linux-arm64.zip -+0497730c82e1e76b6a4c22b1af4ebb7821ff6ccb838b78503c0cc93d8a8f03ee *libcxx-objects-v39.2.7-linux-armv7l.zip -+271e3538eb241f1bc83a103ea7d4c8408ee6bd38322ed50dca781f54d002a590 *libcxx-objects-v39.2.7-linux-x64.zip -+9a243728553395448f783591737fb229a327499d6853b51e201c36e4aaa9796f *libcxx_headers.zip -+db3018609bce502c307c59074b3d5273080a68fb50ac1e7fc580994a2e80cc25 *libcxxabi_headers.zip -+509d0890d1a524efe2c68aae18d2c8fd6537e788b94c9f63fd9f9ca3be98fdb9 *mksnapshot-v39.2.7-darwin-arm64.zip -+f0a98b428a6a1f8dc4a4663e876a3984157ac8757922cde7461f19755942c180 *mksnapshot-v39.2.7-darwin-x64.zip -+22fda3b708ab14325b2bfba8e875fbf48b6eacea347ecf1ef41cf24b09b4af8f *mksnapshot-v39.2.7-linux-arm64-x64.zip -+e7b89dbab3449c0a1862b4d129b3ee384cb5bcd53e149eae05df14744ee55cb5 *mksnapshot-v39.2.7-linux-armv7l-x64.zip -+53b3ed9f3a69444915ef1eef688c8f8168d52c3d5232834b8aa249cf210b41b6 *mksnapshot-v39.2.7-linux-x64.zip -+181d962eaa93d8d997b1daf99ae016b3d9d8a5ae037c96a8475490396a8d655f *mksnapshot-v39.2.7-mas-arm64.zip -+de005b619da1c1afcd8f8b6c70facb1dc388c46a66f8eff3058c8a08323df173 *mksnapshot-v39.2.7-mas-x64.zip -+6eea0bee6097cf2cfe3ae42b35f847304697c4a4eec84f5b60d1cbbe324a8490 *mksnapshot-v39.2.7-win32-arm64-x64.zip -+3e769269aa0b51ef9664a982235bc9299fc58743dcf7bce585d49a9f4a074abd *mksnapshot-v39.2.7-win32-ia32.zip -+51337124892bf76d214f89975d42ec0474199cdfac2f9e08664d86ae8e6ba43e *mksnapshot-v39.2.7-win32-x64.zip -\ No newline at end of file -diff --git a/cgmanifest.json b/cgmanifest.json -index 1148b4e..88150cc 100644 ---- a/cgmanifest.json -+++ b/cgmanifest.json -@@ -531,4 +531,4 @@ - "repositoryUrl": "https://github.com/electron/electron", -- "commitHash": "14565211f7fd33f3fe2f75ec1254cfa57d5bc848", -- "tag": "39.2.3" -+ "commitHash": "4d18062d0f0ca34c455bc7ec032dd7959a0365b6", -+ "tag": "39.2.7" - } -diff --git a/package-lock.json b/package-lock.json -index 2b1154b..cbe427e 100644 ---- a/package-lock.json -+++ b/package-lock.json -@@ -100,3 +100,3 @@ - "deemon": "^1.13.6", -- "electron": "39.2.3", -+ "electron": "39.2.7", - "eslint": "^9.36.0", -@@ -6188,5 +6188,5 @@ - "node_modules/electron": { -- "version": "39.2.3", -- "resolved": "https://registry.npmjs.org/electron/-/electron-39.2.3.tgz", -- "integrity": "sha512-j7k7/bj3cNA29ty54FzEMRUoqirE+RBQPhPFP+XDuM93a1l2WcDPiYumxKWz+iKcXxBJLFdMIAlvtLTB/RfCkg==", -+ "version": "39.2.7", -+ "resolved": "https://registry.npmjs.org/electron/-/electron-39.2.7.tgz", -+ "integrity": "sha512-KU0uFS6LSTh4aOIC3miolcbizOFP7N1M46VTYVfqIgFiuA2ilfNaOHLDS9tCMvwwHRowAsvqBrh9NgMXcTOHCQ==", - "dev": true, -diff --git a/package.json b/package.json -index 9ef8381..f732fa8 100644 ---- a/package.json -+++ b/package.json -@@ -162,3 +162,3 @@ - "deemon": "^1.13.6", -- "electron": "39.2.3", -+ "electron": "39.2.7", - "eslint": "^9.36.0", From 9e80af72b778bdd0b84045a3f3597b007116ac02 Mon Sep 17 00:00:00 2001 From: Alex Garcia <115115137+lex-ibm@users.noreply.github.com> Date: Thu, 26 Mar 2026 21:23:38 -0500 Subject: [PATCH 02/79] fix(ppc64): add mismatch arch (#2766) --- patches/linux/arch-1-ppc64le.patch | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/patches/linux/arch-1-ppc64le.patch b/patches/linux/arch-1-ppc64le.patch index f94aa0f866e..f01b6d370eb 100644 --- a/patches/linux/arch-1-ppc64le.patch +++ b/patches/linux/arch-1-ppc64le.patch @@ -137,9 +137,10 @@ diff --git a/build/linux/debian/install-sysroot.ts b/build/linux/debian/install- index 7b6ac8b..dde47ba 100644 --- a/build/linux/debian/install-sysroot.ts +++ b/build/linux/debian/install-sysroot.ts -@@ -158,2 +158,6 @@ export async function getVSCodeSysroot(arch: DebianArchString, isMusl: boolean = +@@ -158,2 +158,7 @@ export async function getVSCodeSysroot(arch: DebianArchString, isMusl: boolean = break; + case 'ppc64le': ++ case 'ppc64el': + expectedName = `powerpc64le-linux-gnu${prefix}.tar.gz`; + triple = `powerpc64le-linux-gnu`; + break; From 693c0a5de939eecf671585b0d2662fc058314e78 Mon Sep 17 00:00:00 2001 From: Alex Garcia <115115137+lex-ibm@users.noreply.github.com> Date: Fri, 27 Mar 2026 12:25:06 -0500 Subject: [PATCH 03/79] feat(ppc64): add RPM, DEB and CLI (#2767) --- build_cli.sh | 17 +++++++++++++++- check_tags.sh | 31 ++++++++++++++++++++---------- patches/linux/arch-1-ppc64le.patch | 5 +++++ release_notes.md | 14 +++++++++++++- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/build_cli.sh b/build_cli.sh index a2af2d47fff..21d3b46a65b 100755 --- a/build_cli.sh +++ b/build_cli.sh @@ -62,7 +62,7 @@ else if [[ "${VSCODE_ARCH}" == "arm64" ]]; then VSCODE_CLI_TARGET="aarch64-unknown-linux-gnu" - + if [[ "${CI_BUILD}" != "no" ]]; then export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc @@ -81,6 +81,21 @@ else export CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ export PKG_CONFIG_ALLOW_CROSS=1 fi + elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then + VSCODE_CLI_TARGET="powerpc64le-unknown-linux-gnu" + + # Use system libs instead of @vscode/openssl-prebuilt + mkdir -p openssl/out/ppc64le-linux/ + ln -sf /usr/lib/powerpc64le-linux-gnu openssl/out/ppc64le-linux/lib + ln -sf /usr/include openssl/out/ppc64le-linux/include + + if [[ "${CI_BUILD}" != "no" ]] && [[ "$(uname -m)" != "ppc64le" ]]; then + export CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_LINKER=powerpc64le-linux-gnu-gcc-10 + export CC_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-gcc-10 + export CXX_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-g++-10 + export PKG_CONFIG_ALLOW_CROSS=1 + fi + elif [[ "${VSCODE_ARCH}" == "x64" ]]; then VSCODE_CLI_TARGET="x86_64-unknown-linux-gnu" fi diff --git a/check_tags.sh b/check_tags.sh index e8b8ccc94de..aa4b9d5be30 100755 --- a/check_tags.sh +++ b/check_tags.sh @@ -362,8 +362,20 @@ elif [[ "${ASSETS}" != "null" ]]; then # linux-ppc64le if [[ "${VSCODE_ARCH}" == "ppc64le" || "${CHECK_ALL}" == "yes" ]]; then export SHOULD_BUILD_APPIMAGE="no" - export SHOULD_BUILD_DEB="no" - export SHOULD_BUILD_RPM="no" + + if [[ -z $( contains "ppc64el.deb" ) ]]; then + echo "Building on Linux PowerPC64LE because we have no DEB" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_DEB="no" + fi + + if [[ -z $( contains "ppc64le.rpm" ) ]]; then + echo "Building on Linux PowerPC64LE because we have no RPM" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_RPM="no" + fi if [[ -z $( contains "${APP_NAME}-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then echo "Building on Linux PowerPC64LE because we have no TAR" @@ -372,7 +384,6 @@ elif [[ "${ASSETS}" != "null" ]]; then export SHOULD_BUILD_TAR="no" fi - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then echo "Building on Linux PowerPC64LE because we have no REH archive" export SHOULD_BUILD="yes" @@ -387,7 +398,12 @@ elif [[ "${ASSETS}" != "null" ]]; then export SHOULD_BUILD_REH_WEB="no" fi - export SHOULD_BUILD_CLI="no" + if [[ -z $( contains "${APP_NAME_LC}-cli-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux PowerPC64LE because we have no CLI archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_CLI="no" + fi if [[ "${SHOULD_BUILD}" != "yes" ]]; then echo "Already have all the Linux PowerPC64LE builds" @@ -617,12 +633,7 @@ else if [[ "${IS_SPEARHEAD}" == "yes" ]]; then export SHOULD_BUILD_SRC="yes" elif [[ "${OS_NAME}" == "linux" ]]; then - if [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then - SHOULD_BUILD_DEB="no" - SHOULD_BUILD_RPM="no" - SHOULD_BUILD_TAR="no" - SHOULD_BUILD_CLI="no" - elif [[ "${VSCODE_ARCH}" == "riscv64" ]]; then + if [[ "${VSCODE_ARCH}" == "riscv64" ]]; then SHOULD_BUILD_DEB="no" SHOULD_BUILD_RPM="no" SHOULD_BUILD_CLI="no" diff --git a/patches/linux/arch-1-ppc64le.patch b/patches/linux/arch-1-ppc64le.patch index f01b6d370eb..2333431ba45 100644 --- a/patches/linux/arch-1-ppc64le.patch +++ b/patches/linux/arch-1-ppc64le.patch @@ -312,6 +312,11 @@ index 44c8597..679aacb 100644 + } else if cfg!(target_arch = "powerpc64") { + Platform::LinuxPPC64LE } else { +@@ -213,2 +215,4 @@ async fn check_glibcxx_version() -> Result { + const DEFAULT_LIB_PATH: &str = "/usr/lib64/libstdc++.so.6"; ++ #[cfg(target_arch = "powerpc64")] ++ const DEFAULT_LIB_PATH: &str = "/usr/lib64/libstdc++.so.6"; + #[cfg(any(target_arch = "x86", target_arch = "arm"))] diff --git a/resources/server/bin/helpers/check-requirements-linux.sh b/resources/server/bin/helpers/check-requirements-linux.sh index 8ea4c0b..94028e3 100644 --- a/resources/server/bin/helpers/check-requirements-linux.sh diff --git a/release_notes.md b/release_notes.md index c9fdf950b76..d0e88952e09 100644 --- a/release_notes.md +++ b/release_notes.md @@ -221,7 +221,15 @@ update vscode to [@@MS_TAG@@](@@MS_URL@@) - + + + + + + + + + @@ -233,6 +241,10 @@ update vscode to [@@MS_TAG@@](@@MS_URL@@) + + + +
LinuxLinux.deb@@BINARY_NAME@@_@@VERSION@@_ppc64el.deb
.rpm@@BINARY_NAME@@-@@VERSION@@-el8.ppc64le.rpm
.tar.gz @@APP_NAME@@-linux-ppc64le-@@VERSION@@@@QUALITY@@.tar.gz
Web Host @@APP_NAME_LC@@-reh-web-linux-ppc64le-@@VERSION@@@@QUALITY@@.tar.gz
CLI@@APP_NAME_LC@@-cli-linux-ppc64le-@@VERSION@@@@QUALITY@@.tar.gz
## RISC-V 64bits From 8eabf502e04ff2936cdddc7b6af7995487e0c1ff Mon Sep 17 00:00:00 2001 From: Alex Garcia <115115137+lex-ibm@users.noreply.github.com> Date: Tue, 31 Mar 2026 03:31:34 -0500 Subject: [PATCH 04/79] feat(ppc64): use Electron 41.y.z (#2771) --- build/linux/package_bin.sh | 3 ++- build/linux/ppc64le/electron.sh | 2 +- build/linux/ppc64le/electron.sha256sums | 21 +++++++++++---------- patches/linux/arch-1-ppc64le.patch | 17 +++++++++++++++++ 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/build/linux/package_bin.sh b/build/linux/package_bin.sh index ecb1eccd21b..4671cb78403 100755 --- a/build/linux/package_bin.sh +++ b/build/linux/package_bin.sh @@ -30,6 +30,7 @@ elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 export VSCODE_SKIP_SETUPENV=1 export VSCODE_ELECTRON_REPOSITORY='lex-ibm/electron-ppc64le-build-scripts' + export IGNORE_ELECTRON_VERSION="yes" elif [[ "${VSCODE_ARCH}" == "riscv64" ]]; then export VSCODE_ELECTRON_REPOSITORY='riscv-forks/electron-riscv-releases' export ELECTRON_SKIP_BINARY_DOWNLOAD=1 @@ -56,7 +57,7 @@ if [[ -f "../build/linux/${VSCODE_ARCH}/electron.sh" ]]; then TARGET=$( npm config get target ) # Only fails at different major versions - if [[ "${ELECTRON_VERSION%%.*}" != "${TARGET%%.*}" ]]; then + if [[ "${ELECTRON_VERSION%%.*}" != "${TARGET%%.*}" ]] && [[ "${IGNORE_ELECTRON_VERSION}" != "yes" ]]; then # Fail the pipeline if electron target doesn't match what is used. echo "Electron ${VSCODE_ARCH} binary version doesn't match target electron version!" echo "Releases available at: https://github.com/${VSCODE_ELECTRON_REPOSITORY}/releases" diff --git a/build/linux/ppc64le/electron.sh b/build/linux/ppc64le/electron.sh index bd858988feb..1eb1e3286b8 100644 --- a/build/linux/ppc64le/electron.sh +++ b/build/linux/ppc64le/electron.sh @@ -2,5 +2,5 @@ set -ex -export ELECTRON_VERSION="39.2.7" +export ELECTRON_VERSION="41.0.3" export VSCODE_ELECTRON_TAG="v${ELECTRON_VERSION}" diff --git a/build/linux/ppc64le/electron.sha256sums b/build/linux/ppc64le/electron.sha256sums index 26ebd03810a..4e7e2a0ca2e 100644 --- a/build/linux/ppc64le/electron.sha256sums +++ b/build/linux/ppc64le/electron.sha256sums @@ -1,10 +1,11 @@ -bacff46523cea806df9788d9e24f7f53fad2317f18afdcbc596b86863dd40805 *chromedriver-v39.2.7-linux-ppc64le.zip -b83820b37325c0a6ce0bbb98344b54f70ef7c2a949eea61bcb423b18c623a742 *electron-v39.2.7-linux-ppc64le-debug.zip -7d3b4ff4320a54572f9e1e0286702a0bed3e1596a2cb34f8fdc455acf3b9234f *electron-v39.2.7-linux-ppc64le-symbols.zip -6974cf1c8a550019b04762222742b8f1d9d76387594a191d3522cd65da075db1 *electron-v39.2.7-linux-ppc64le.zip -40c772eb189d100087b75da6c2ad1aeb044f1d661c90543592546a654b0b6d5b *electron.d.ts -0c923001d08e474d0dcd3b747b4f9a4bfca685d755ec08de8e44556a63f9ad3a *hunspell_dictionaries.zip -ee57f79e88f50f199a6aeb87fa45c83d1bd0f92eb72e00787cfdf4cf11863562 *libcxx-objects-v39.2.7-linux-ppc64le.zip -a8709029737d3073758ccb384161a37d91f16e5a3f8110ca8e2c30f83ef8d7e6 *libcxx_headers.zip -238dcec817528659a86b0cd3d7dabe301e65b4cab25e45c5bbab7642a8849c02 *libcxxabi_headers.zip -be033ed825bd8be92bb6ca86ff81f0907e60aa999aa011f5ddf1360abb19429b *mksnapshot-v39.2.7-linux-ppc64le.zip +120e0e5915c8f1a49904737e82bf50bc72563f8556fc3c5997c118908b377874 *chromedriver-v41.0.3-linux-ppc64le.zip +4601e7692cac011610c245d8c58f14a73b415fccf520194f14674f81e4ce2c41 *electron-v41.0.3-linux-ppc64le-debug.zip +498a74b78699ff9d4819310262d96e861dd7d899f38f8f625a311968b076b29b *electron-v41.0.3-linux-ppc64le-symbols.zip +77e86aa200d9e19b5eb3a51f273ee410788c21977f2ea76b1239d79aa2e1adbf *electron-v41.0.3-linux-ppc64le.zip +e246066ccc328729e6a9f2a0d829bc893391cf54ed199b809bb7e47993fed88f *electron.d.ts +687a36388a7cf06427d7fa7e3db7fca4ab0118c5eab2baeac128c1989a600cff *ffmpeg-v41.0.3-linux-ppc64le.zip +70ba677d4bea3e7164c82f6b5a36ac6b740317fb1dd149a0eaf1ff151da44cb2 *hunspell_dictionaries.zip +3e85df4843497ce567ad36ddf826cd1af7dfe9f883daad4623736ddd012dbfd1 *libcxx-objects-v41.0.3-linux-ppc64le.zip +5587472eeaf9bfb76d0f187ecc19d11d60bc05f5e7c8fa822f2370210c30b95d *libcxx_headers.zip +f531ddd2782b4b19caf622c0355c93892965e22861279cc891675230c1310905 *libcxxabi_headers.zip +6cb0e67dc5a23de0ebbe89253e1f24bf9d4ca41213a1b9f784f2afaa2cf61324 *mksnapshot-v41.0.3-linux-ppc64le.zip \ No newline at end of file diff --git a/patches/linux/arch-1-ppc64le.patch b/patches/linux/arch-1-ppc64le.patch index 2333431ba45..9c6059cd014 100644 --- a/patches/linux/arch-1-ppc64le.patch +++ b/patches/linux/arch-1-ppc64le.patch @@ -351,3 +351,20 @@ index 021ad01..60cd058 100644 LINUX_ARMHF = 'linux-armhf', + LINUX_PPC64LE = 'linux-ppc64le', +diff --git a/build/npm/preinstall.ts b/build/npm/preinstall.ts +index dd53ff4..0bca5e8 100644 +--- a/build/npm/preinstall.ts ++++ b/build/npm/preinstall.ts +@@ -147,7 +147,11 @@ function installHeaders() { + cwd: localHeaderPath + }); + } catch (error) { +- throw new Error(`Error applying v8-source-location.patch: ${(error as Error).message}`); ++ if (process.env['VSCODE_ARCH'] === 'ppc64le') { ++ console.log('Ignoring patch error for ppc64le architecture'); ++ } else { ++ throw new Error(`Error applying v8-source-location.patch: ${(error as Error).message}`); ++ } + } + } + } From 31837ad2a94bf6296be5d718f56b0ed8a78abb3a Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 3 Apr 2026 18:01:18 +0200 Subject: [PATCH 05/79] fix(ppc64): update sysroot checksum (#2775) --- patches/linux/arch-1-ppc64le.patch | 53 ++++++++++++++---------------- patches/linux/arch-4-s390x.patch | 32 +++++++++--------- 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/patches/linux/arch-1-ppc64le.patch b/patches/linux/arch-1-ppc64le.patch index 9c6059cd014..3fa1f7c30a7 100644 --- a/patches/linux/arch-1-ppc64le.patch +++ b/patches/linux/arch-1-ppc64le.patch @@ -27,17 +27,18 @@ index 5294177..1e33aeb 100755 + TRIPLE="powerpc64le-linux-gnu" fi diff --git a/build/checksums/vscode-sysroot.txt b/build/checksums/vscode-sysroot.txt -index 847383e..37186f4 100644 +index 847383e..04962e7 100644 --- a/build/checksums/vscode-sysroot.txt +++ b/build/checksums/vscode-sysroot.txt -@@ -7 +7,2 @@ ac4b6b14b4cec027a22a51bbbb049b3504958a78106c8a8d5cec144206b767d1 x86_64-linux-g +@@ -7 +7,3 @@ ac4b6b14b4cec027a22a51bbbb049b3504958a78106c8a8d5cec144206b767d1 x86_64-linux-g 1ebb6ef1fe2983269fd0855a88f9c9a37f9b515d16524a9146198e4cabdf34f7 x86_64-linux-gnu-glibc-2.28-gcc-8.5.0.tar.gz +fa8176d27be18bb0eeb7f55b0fa22255050b430ef68c29136599f02976eb0b1b powerpc64le-linux-gnu-glibc-2.28.tar.gz ++bdab0a372cf5bfdc154226cc5ed72d417096c725085d193509d1f12e0debec66 powerpc64le-linux-gnu-glibc-2.28-gcc-10.5.0.tar.gz diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts -index 012df0b..a7da071 100644 +index 904130f..42888f0 100644 --- a/build/gulpfile.reh.ts +++ b/build/gulpfile.reh.ts -@@ -52,2 +52,3 @@ const BUILD_TARGETS = [ +@@ -56,2 +56,3 @@ const BUILD_TARGETS = [ { platform: 'linux', arch: 'arm64' }, + { platform: 'linux', arch: 'ppc64le' }, { platform: 'alpine', arch: 'arm64' }, @@ -66,10 +67,10 @@ index c5d2163..da4fc1f 100644 + { arch: 'ppc64le' }, ]; diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index ce46a41..3389ede 100644 +index 5e373f6..4e1fcf6 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -614,2 +614,3 @@ const BUILD_TARGETS = [ +@@ -702,2 +702,3 @@ const BUILD_TARGETS = [ { platform: 'linux', arch: 'arm64' }, + { platform: 'linux', arch: 'ppc64le' }, ]; @@ -134,7 +135,7 @@ index 78bfb66..5acd3a0 100644 + ], }; diff --git a/build/linux/debian/install-sysroot.ts b/build/linux/debian/install-sysroot.ts -index 7b6ac8b..dde47ba 100644 +index 7b6ac8b..ef7dd2b 100644 --- a/build/linux/debian/install-sysroot.ts +++ b/build/linux/debian/install-sysroot.ts @@ -158,2 +158,7 @@ export async function getVSCodeSysroot(arch: DebianArchString, isMusl: boolean = @@ -278,6 +279,19 @@ index c6a01da..3f3c3f5 100644 - return ['x86_64', 'armv7hl', 'aarch64'].includes(s); + return ['x86_64', 'armv7hl', 'aarch64', 'ppc64le'].includes(s); } +diff --git a/build/npm/preinstall.ts b/build/npm/preinstall.ts +index dd53ff4..ca427d9 100644 +--- a/build/npm/preinstall.ts ++++ b/build/npm/preinstall.ts +@@ -149,3 +149,7 @@ function installHeaders() { + } catch (error) { +- throw new Error(`Error applying v8-source-location.patch: ${(error as Error).message}`); ++ if (process.env['VSCODE_ARCH'] === 'ppc64le') { ++ console.log('Ignoring patch error for ppc64le architecture'); ++ } else { ++ throw new Error(`Error applying v8-source-location.patch: ${(error as Error).message}`); ++ } + } diff --git a/cli/src/update_service.rs b/cli/src/update_service.rs index 3b7ef5c..ec97760 100644 --- a/cli/src/update_service.rs @@ -304,7 +318,7 @@ index 3b7ef5c..ec97760 100644 + Platform::LinuxPPC64LE => "LinuxPPC64LE", Platform::DarwinX64 => "DarwinX64", diff --git a/cli/src/util/prereqs.rs b/cli/src/util/prereqs.rs -index 44c8597..679aacb 100644 +index 44c8597..205c274 100644 --- a/cli/src/util/prereqs.rs +++ b/cli/src/util/prereqs.rs @@ -82,2 +82,4 @@ impl PreReqChecker { @@ -312,7 +326,7 @@ index 44c8597..679aacb 100644 + } else if cfg!(target_arch = "powerpc64") { + Platform::LinuxPPC64LE } else { -@@ -213,2 +215,4 @@ async fn check_glibcxx_version() -> Result { +@@ -207,2 +209,4 @@ async fn check_glibcxx_version() -> Result { const DEFAULT_LIB_PATH: &str = "/usr/lib64/libstdc++.so.6"; + #[cfg(target_arch = "powerpc64")] + const DEFAULT_LIB_PATH: &str = "/usr/lib64/libstdc++.so.6"; @@ -344,27 +358,10 @@ index ab11f6b..fd1ec31 100644 + } return TargetPlatform.UNKNOWN; diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts -index 021ad01..60cd058 100644 +index 3e5b309..0fbbb67 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts -@@ -342,2 +342,3 @@ export const enum TargetPlatform { +@@ -343,2 +343,3 @@ export const enum TargetPlatform { LINUX_ARMHF = 'linux-armhf', + LINUX_PPC64LE = 'linux-ppc64le', -diff --git a/build/npm/preinstall.ts b/build/npm/preinstall.ts -index dd53ff4..0bca5e8 100644 ---- a/build/npm/preinstall.ts -+++ b/build/npm/preinstall.ts -@@ -147,7 +147,11 @@ function installHeaders() { - cwd: localHeaderPath - }); - } catch (error) { -- throw new Error(`Error applying v8-source-location.patch: ${(error as Error).message}`); -+ if (process.env['VSCODE_ARCH'] === 'ppc64le') { -+ console.log('Ignoring patch error for ppc64le architecture'); -+ } else { -+ throw new Error(`Error applying v8-source-location.patch: ${(error as Error).message}`); -+ } - } - } - } diff --git a/patches/linux/arch-4-s390x.patch b/patches/linux/arch-4-s390x.patch index 163b71a1a2b..348584cecbf 100644 --- a/patches/linux/arch-4-s390x.patch +++ b/patches/linux/arch-4-s390x.patch @@ -1,5 +1,5 @@ diff --git a/build/azure-pipelines/linux/setup-env.sh b/build/azure-pipelines/linux/setup-env.sh -index 6ff90d4..381150d 100755 +index 67bc741..4f0521b 100755 --- a/build/azure-pipelines/linux/setup-env.sh +++ b/build/azure-pipelines/linux/setup-env.sh @@ -88,2 +88,14 @@ elif [ "$npm_config_arch" == "ppc64" ]; then @@ -18,17 +18,17 @@ index 6ff90d4..381150d 100755 + export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_REMOTE_SYSROOT_DIR/s390x-linux-gnu/s390x-linux-gnu/sysroot -L$VSCODE_REMOTE_SYSROOT_DIR/s390x-linux-gnu/s390x-linux-gnu/sysroot/usr/lib/s390x-linux-gnu -L$VSCODE_REMOTE_SYSROOT_DIR/s390x-linux-gnu/s390x-linux-gnu/sysroot/lib/s390x-linux-gnu" fi diff --git a/build/checksums/vscode-sysroot.txt b/build/checksums/vscode-sysroot.txt -index 3fedbe9..3c4e291 100644 +index dd159f8..8e8cbe9 100644 --- a/build/checksums/vscode-sysroot.txt +++ b/build/checksums/vscode-sysroot.txt -@@ -8 +8,2 @@ f82c8dacbb9dd85819e4801909eb4e842ac12c899632aa75b4839383a18c7501 arm-rpi-linux- - fa8176d27be18bb0eeb7f55b0fa22255050b430ef68c29136599f02976eb0b1b powerpc64le-linux-gnu-glibc-2.28.tar.gz +@@ -8 +8,2 @@ ac4b6b14b4cec027a22a51bbbb049b3504958a78106c8a8d5cec144206b767d1 x86_64-linux-g + bdab0a372cf5bfdc154226cc5ed72d417096c725085d193509d1f12e0debec66 powerpc64le-linux-gnu-glibc-2.28-gcc-10.5.0.tar.gz +7055f3d40e7195fb1e13f0fbaf5ffadf781bddaca5fd5e0d9972f4157a203fb5 s390x-linux-gnu-glibc-2.28.tar.gz diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts -index af8403a..a697f8c 100644 +index e6ff02c..65f7efe 100644 --- a/build/gulpfile.reh.ts +++ b/build/gulpfile.reh.ts -@@ -55,2 +55,3 @@ const BUILD_TARGETS = [ +@@ -59,2 +59,3 @@ const BUILD_TARGETS = [ { platform: 'linux', arch: 'loong64' }, + { platform: 'linux', arch: 's390x' }, { platform: 'alpine', arch: 'arm64' }, @@ -57,10 +57,10 @@ index e146586..ca18865 100644 + { arch: 's390x' }, ]; diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index d5f3ed8..ac87a54 100644 +index e1e5265..295a755 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -520,2 +520,3 @@ const BUILD_TARGETS = [ +@@ -705,2 +705,3 @@ const BUILD_TARGETS = [ { platform: 'linux', arch: 'loong64' }, + { platform: 'linux', arch: 's390x' }, ]; @@ -78,7 +78,7 @@ index 5ee005e..71fb92e 100644 + break; } diff --git a/build/linux/debian/dep-lists.ts b/build/linux/debian/dep-lists.ts -index 7ea5910..9498993 100644 +index 0e28f90..6e81b12 100644 --- a/build/linux/debian/dep-lists.ts +++ b/build/linux/debian/dep-lists.ts @@ -221,2 +221,42 @@ export const referenceGeneratedDepsByArch = { @@ -125,10 +125,10 @@ index 7ea5910..9498993 100644 + ], }; diff --git a/build/linux/debian/install-sysroot.ts b/build/linux/debian/install-sysroot.ts -index 6e5246c..89fac34 100644 +index aece9b9..52a25ce 100644 --- a/build/linux/debian/install-sysroot.ts +++ b/build/linux/debian/install-sysroot.ts -@@ -166,2 +166,6 @@ export async function getVSCodeSysroot(arch: DebianArchString, isMusl: boolean = +@@ -167,2 +167,6 @@ export async function getVSCodeSysroot(arch: DebianArchString, isMusl: boolean = break; + case 's390x': + expectedName = `s390x-linux-gnu${prefix}.tar.gz`; @@ -149,10 +149,10 @@ index 5d63ed2..5f793e2 100644 + return ['amd64', 'armhf', 'arm64', 'ppc64el', 'ppc64le', 'riscv64', 's390x'].includes(s); } diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts -index fba6ee4..33efc80 100644 +index c7e0820..03528c0 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts -@@ -416,2 +416,102 @@ export const referenceGeneratedDepsByArch = { +@@ -418,2 +418,102 @@ export const referenceGeneratedDepsByArch = { 'xdg-utils' + ], + "s390x": [ @@ -277,7 +277,7 @@ index dc9faac..9078cc9 100644 + s390x) LDCONFIG_ARCH="64bit";; esac diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts -index 7162d83..f6bb8fd 100644 +index 9a67e9d..aeb7dc0 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -51,2 +51,3 @@ export function TargetPlatformToString(targetPlatform: TargetPlatform) { @@ -295,10 +295,10 @@ index 7162d83..f6bb8fd 100644 + } return TargetPlatform.UNKNOWN; diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts -index f93e0ca..c4cc8c9 100644 +index 7b11edb..b8a6193 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts -@@ -334,2 +334,3 @@ export const enum TargetPlatform { +@@ -346,2 +346,3 @@ export const enum TargetPlatform { LINUX_LOONG64 = 'linux-loong64', + LINUX_S390X = 'linux-s390x', From 7e281e251146f1a796731159741bb9dcd6ba9204 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 3 Apr 2026 18:04:54 +0200 Subject: [PATCH 06/79] refactor: split some scripts by os (#2776) --- build/alpine/check_tags.sh | 54 ++++ build/linux/check_tags.sh | 345 ++++++++++++++++++++ build/linux/prepare_assets.sh | 55 ++++ build/osx/check_tags.sh | 40 +++ build/osx/prepare_assets.sh | 84 +++++ build/windows/check_tags.sh | 105 +++++++ build/windows/prepare_assets.sh | 53 ++++ check_tags.sh | 542 +------------------------------- prepare_assets.sh | 189 +---------- 9 files changed, 743 insertions(+), 724 deletions(-) create mode 100644 build/alpine/check_tags.sh create mode 100644 build/linux/check_tags.sh create mode 100644 build/linux/prepare_assets.sh create mode 100644 build/osx/check_tags.sh create mode 100644 build/osx/prepare_assets.sh create mode 100644 build/windows/check_tags.sh create mode 100644 build/windows/prepare_assets.sh diff --git a/build/alpine/check_tags.sh b/build/alpine/check_tags.sh new file mode 100644 index 00000000000..05032557208 --- /dev/null +++ b/build/alpine/check_tags.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +if [[ "${CHECK_ONLY_REH}" == "yes" ]]; then + if [[ -z $( contains "${APP_NAME_LC}-reh-alpine-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Alpine ${VSCODE_ARCH} because we have no REH archive" + export SHOULD_BUILD="yes" + else + echo "Already have the Alpine REH ${VSCODE_ARCH} archive" + export SHOULD_BUILD_REH="no" + fi + + if [[ -z $( contains "${APP_NAME_LC}-reh-web-alpine-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Alpine ${VSCODE_ARCH} because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + echo "Already have the Alpine REH-web ${VSCODE_ARCH} archive" + export SHOULD_BUILD_REH_WEB="no" + fi +else + + # alpine-arm64 + if [[ "${VSCODE_ARCH}" == "arm64" || "${CHECK_ALL}" == "yes" ]]; then + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-alpine-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Alpine arm64 because we have no REH archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-alpine-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Alpine arm64 because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH_WEB="no" + fi + fi + + # alpine-x64 + if [[ "${VSCODE_ARCH}" == "x64" || "${CHECK_ALL}" == "yes" ]]; then + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-alpine-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Alpine x64 because we have no REH archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-alpine-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Alpine x64 because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH_WEB="no" + fi + fi +fi diff --git a/build/linux/check_tags.sh b/build/linux/check_tags.sh new file mode 100644 index 00000000000..8962ea897fb --- /dev/null +++ b/build/linux/check_tags.sh @@ -0,0 +1,345 @@ +#!/usr/bin/env bash + +if [[ "${CHECK_ONLY_REH}" == "yes" ]]; then + + if [[ -z $( contains "${APP_NAME_LC}-reh-linux-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux ${VSCODE_ARCH} because we have no REH archive" + export SHOULD_BUILD="yes" + else + echo "Already have the Linux REH ${VSCODE_ARCH} archive" + export SHOULD_BUILD_REH="no" + fi + + if [[ -z $( contains "${APP_NAME_LC}-reh-web-linux-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux ${VSCODE_ARCH} because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + echo "Already have the Linux REH-web ${VSCODE_ARCH} archive" + export SHOULD_BUILD_REH_WEB="no" + fi + +else + + # linux-arm64 + if [[ "${VSCODE_ARCH}" == "arm64" || "${CHECK_ALL}" == "yes" ]]; then + if [[ -z $( contains "arm64.deb" ) ]]; then + echo "Building on Linux arm64 because we have no DEB" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_DEB="no" + fi + + if [[ -z $( contains "aarch64.rpm" ) ]]; then + echo "Building on Linux arm64 because we have no RPM" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_RPM="no" + fi + + if [[ -z $( contains "${APP_NAME}-linux-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux arm64 because we have no TAR" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_TAR="no" + fi + + if [[ -z $( contains "arm64.snap" ) || "${FORCE_LINUX_SNAP}" == "true" ]]; then + echo "Building on Linux arm64 because we have no SNAP" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_SNAP="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux arm64 because we have no REH archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux arm64 because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH_WEB="no" + fi + + export SHOULD_BUILD_APPIMAGE="no" + + if [[ -z $( contains "${APP_NAME_LC}-cli-linux-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux arm64 because we have no CLI archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_CLI="no" + fi + + + if [[ "${SHOULD_BUILD}" != "yes" ]]; then + echo "Already have all the Linux arm64 builds" + fi + fi + + # linux-armhf + if [[ "${VSCODE_ARCH}" == "armhf" || "${CHECK_ALL}" == "yes" ]]; then + if [[ -z $( contains "armhf.deb" ) ]]; then + echo "Building on Linux arm because we have no DEB" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_DEB="no" + fi + + if [[ -z $( contains "armv7hl.rpm" ) ]]; then + echo "Building on Linux arm because we have no RPM" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_RPM="no" + fi + + if [[ -z $( contains "${APP_NAME}-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux arm because we have no TAR" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_TAR="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux arm because we have no REH archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux arm because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH_WEB="no" + fi + + export SHOULD_BUILD_APPIMAGE="no" + + if [[ -z $( contains "${APP_NAME_LC}-cli-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux arm because we have no CLI archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_CLI="no" + fi + + + if [[ "${SHOULD_BUILD}" != "yes" ]]; then + echo "Already have all the Linux arm builds" + fi + fi + + # linux-ppc64le + if [[ "${VSCODE_ARCH}" == "ppc64le" || "${CHECK_ALL}" == "yes" ]]; then + export SHOULD_BUILD_APPIMAGE="no" + + if [[ -z $( contains "ppc64el.deb" ) ]]; then + echo "Building on Linux PowerPC64LE because we have no DEB" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_DEB="no" + fi + + if [[ -z $( contains "ppc64le.rpm" ) ]]; then + echo "Building on Linux PowerPC64LE because we have no RPM" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_RPM="no" + fi + + if [[ -z $( contains "${APP_NAME}-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux PowerPC64LE because we have no TAR" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_TAR="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux PowerPC64LE because we have no REH archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux PowerPC64LE because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH_WEB="no" + fi + + if [[ -z $( contains "${APP_NAME_LC}-cli-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux PowerPC64LE because we have no CLI archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_CLI="no" + fi + + if [[ "${SHOULD_BUILD}" != "yes" ]]; then + echo "Already have all the Linux PowerPC64LE builds" + fi + fi + + # linux-riscv64 + if [[ "${VSCODE_ARCH}" == "riscv64" || "${CHECK_ALL}" == "yes" ]]; then + export SHOULD_BUILD_DEB="no" + export SHOULD_BUILD_RPM="no" + export SHOULD_BUILD_APPIMAGE="no" + + if [[ -z $( contains "${APP_NAME}-linux-riscv64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux RISC-V 64 because we have no TAR" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_TAR="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-riscv64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux RISC-V 64 because we have no REH archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-riscv64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux RISC-V 64 because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH_WEB="no" + fi + + export SHOULD_BUILD_CLI="no" + + if [[ "${SHOULD_BUILD}" != "yes" ]]; then + echo "Already have all the Linux riscv64 builds" + fi + fi + + # linux-loong64 + if [[ "${VSCODE_ARCH}" == "loong64" || "${CHECK_ALL}" == "yes" ]]; then + export SHOULD_BUILD_DEB="no" + export SHOULD_BUILD_RPM="no" + export SHOULD_BUILD_APPIMAGE="no" + + if [[ -z $( contains "${APP_NAME}-linux-loong64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux Loong64 because we have no TAR" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_TAR="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-loong64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux Loong64 because we have no REH archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-loong64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux Loong64 because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH_WEB="no" + fi + + export SHOULD_BUILD_CLI="no" + + if [[ "${SHOULD_BUILD}" != "yes" ]]; then + echo "Already have all the Linux Loong64 builds" + fi + fi + + # linux-s390x + if [[ "${VSCODE_ARCH}" == "s390x" || "${CHECK_ALL}" == "yes" ]]; then + SHOULD_BUILD_APPIMAGE="no" + SHOULD_BUILD_DEB="no" + SHOULD_BUILD_RPM="no" + SHOULD_BUILD_TAR="no" + + if [[ -z $( contains "${APP_NAME_LC}-reh-linux-s390x-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux s390x because we have no REH archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH="no" + fi + + if [[ -z $( contains "${APP_NAME_LC}-reh-web-linux-s390x-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux s390x because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH_WEB="no" + fi + + export SHOULD_BUILD_CLI="no" + + if [[ "${SHOULD_BUILD}" != "yes" ]]; then + echo "Already have all the Linux s390x builds" + fi + fi + + # linux-x64 + if [[ "${VSCODE_ARCH}" == "x64" || "${CHECK_ALL}" == "yes" ]]; then + if [[ -z $( contains "amd64.deb" ) ]]; then + echo "Building on Linux x64 because we have no DEB" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_DEB="no" + fi + + if [[ -z $( contains "x86_64.rpm" ) ]]; then + echo "Building on Linux x64 because we have no RPM" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_RPM="no" + fi + + if [[ -z $( contains "${APP_NAME}-linux-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux x64 because we have no TAR" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_TAR="no" + fi + + if [[ "${DISABLE_APPIMAGE}" == "yes" ]]; then + export SHOULD_BUILD_APPIMAGE="no" + elif [[ -z $( contains "x86_64.AppImage" ) ]]; then + echo "Building on Linux x64 because we have no AppImage" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_APPIMAGE="no" + fi + + if [[ -z $( contains "amd64.snap" ) || "${FORCE_LINUX_SNAP}" == "true" ]]; then + echo "Building on Linux x64 because we have no SNAP" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_SNAP="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux x64 because we have no REH archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH="no" + fi + + if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux x64 because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH_WEB="no" + fi + + if [[ -z $( contains "${APP_NAME_LC}-cli-linux-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Linux x64 because we have no CLI archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_CLI="no" + fi + + if [[ "${SHOULD_BUILD}" != "yes" ]]; then + echo "Already have all the Linux x64 builds" + fi + fi +fi diff --git a/build/linux/prepare_assets.sh b/build/linux/prepare_assets.sh new file mode 100644 index 00000000000..8100b810587 --- /dev/null +++ b/build/linux/prepare_assets.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +cd vscode || { echo "'vscode' dir not found"; exit 1; } + +if [[ "${SHOULD_BUILD_APPIMAGE}" != "no" && "${VSCODE_ARCH}" != "x64" ]]; then + SHOULD_BUILD_APPIMAGE="no" +fi + +if [[ "${SHOULD_BUILD_DEB}" != "no" || "${SHOULD_BUILD_APPIMAGE}" != "no" ]]; then + npm run gulp "vscode-linux-${VSCODE_ARCH}-prepare-deb" + npm run gulp "vscode-linux-${VSCODE_ARCH}-build-deb" +fi + +if [[ "${SHOULD_BUILD_RPM}" != "no" ]]; then + npm run gulp "vscode-linux-${VSCODE_ARCH}-prepare-rpm" + npm run gulp "vscode-linux-${VSCODE_ARCH}-build-rpm" +fi + +if [[ "${SHOULD_BUILD_APPIMAGE}" != "no" ]]; then + . ../build/linux/appimage/build.sh +fi + +cd .. + +if [[ "${CI_BUILD}" == "no" ]]; then + . ./stores/snapcraft/build.sh + + if [[ "${SKIP_ASSETS}" == "no" ]]; then + mv stores/snapcraft/build/*.snap assets/ + fi +fi + +if [[ "${SHOULD_BUILD_TAR}" != "no" ]]; then + echo "Building and moving TAR" + cd "VSCode-linux-${VSCODE_ARCH}" + tar czf "../assets/${APP_NAME}-linux-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" . + cd .. +fi + +if [[ "${SHOULD_BUILD_DEB}" != "no" ]]; then + echo "Moving DEB" + mv vscode/.build/linux/deb/*/deb/*.deb assets/ +fi + +if [[ "${SHOULD_BUILD_RPM}" != "no" ]]; then + echo "Moving RPM" + mv vscode/.build/linux/rpm/*/*.rpm assets/ +fi + +if [[ "${SHOULD_BUILD_APPIMAGE}" != "no" ]]; then + echo "Moving AppImage" + mv build/linux/appimage/out/*.AppImage* assets/ + + find assets -name '*.AppImage*' -exec bash -c 'mv $0 ${0/_-_/-}' {} \; +fi diff --git a/build/osx/check_tags.sh b/build/osx/check_tags.sh new file mode 100644 index 00000000000..33be9f683d3 --- /dev/null +++ b/build/osx/check_tags.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +if [[ -z $( contains "${APP_NAME}-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" ) ]]; then + echo "Building on MacOS because we have no ZIP" + export SHOULD_BUILD="yes" +else + export SHOULD_BUILD_ZIP="no" +fi + +if [[ -z $( contains ".${VSCODE_ARCH}.${RELEASE_VERSION}.dmg" ) ]]; then + echo "Building on MacOS because we have no DMG" + export SHOULD_BUILD="yes" +else + export SHOULD_BUILD_DMG="no" +fi + +if [[ -z $( contains "${APP_NAME_LC}-reh-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on MacOS because we have no REH archive" + export SHOULD_BUILD="yes" +else + export SHOULD_BUILD_REH="no" +fi + +if [[ -z $( contains "${APP_NAME_LC}-reh-web-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on MacOS because we have no REH-web archive" + export SHOULD_BUILD="yes" +else + export SHOULD_BUILD_REH_WEB="no" +fi + +if [[ -z $( contains "${APP_NAME_LC}-cli-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on MacOS because we have no CLI archive" + export SHOULD_BUILD="yes" +else + export SHOULD_BUILD_CLI="no" +fi + +if [[ "${SHOULD_BUILD}" != "yes" ]]; then + echo "Already have all the MacOS builds" +fi diff --git a/build/osx/prepare_assets.sh b/build/osx/prepare_assets.sh new file mode 100644 index 00000000000..8bc2d193153 --- /dev/null +++ b/build/osx/prepare_assets.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +if [[ -n "${CERTIFICATE_OSX_P12_DATA}" ]]; then + if [[ "${CI_BUILD}" == "no" ]]; then + RUNNER_TEMP="${TMPDIR}" + fi + + CERTIFICATE_P12="${APP_NAME}.p12" + KEYCHAIN="${RUNNER_TEMP}/buildagent.keychain" + AGENT_TEMPDIRECTORY="${RUNNER_TEMP}" + # shellcheck disable=SC2006 + KEYCHAINS=`security list-keychains | xargs` + + rm -f "${KEYCHAIN}" + + echo "${CERTIFICATE_OSX_P12_DATA}" | base64 --decode > "${CERTIFICATE_P12}" + + echo "+ create temporary keychain" + security create-keychain -p pwd "${KEYCHAIN}" + security set-keychain-settings -lut 21600 "${KEYCHAIN}" + security unlock-keychain -p pwd "${KEYCHAIN}" + # shellcheck disable=SC2086 + security list-keychains -s $KEYCHAINS "${KEYCHAIN}" + # security show-keychain-info "${KEYCHAIN}" + + echo "+ import certificate to keychain" + security import "${CERTIFICATE_P12}" -k "${KEYCHAIN}" -P "${CERTIFICATE_OSX_P12_PASSWORD}" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd "${KEYCHAIN}" > /dev/null + # security find-identity "${KEYCHAIN}" + + CODESIGN_IDENTITY="$( security find-identity -v -p codesigning "${KEYCHAIN}" | grep -oEi "([0-9A-F]{40})" | head -n 1 )" + + echo "+ signing" + export CODESIGN_IDENTITY AGENT_TEMPDIRECTORY + + DEBUG="electron-osx-sign*" node vscode/build/darwin/sign.ts "$( pwd )" + # codesign --display --entitlements :- "" + + echo "+ notarize" + + cd "VSCode-darwin-${VSCODE_ARCH}" + ZIP_FILE="./${APP_NAME}-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" + + zip -r -X -y "${ZIP_FILE}" ./*.app + + xcrun notarytool store-credentials "${APP_NAME}" --apple-id "${CERTIFICATE_OSX_ID}" --team-id "${CERTIFICATE_OSX_TEAM_ID}" --password "${CERTIFICATE_OSX_APP_PASSWORD}" --keychain "${KEYCHAIN}" + # xcrun notarytool history --keychain-profile "${APP_NAME}" --keychain "${KEYCHAIN}" + xcrun notarytool submit "${ZIP_FILE}" --keychain-profile "${APP_NAME}" --wait --keychain "${KEYCHAIN}" + + echo "+ attach staple" + xcrun stapler staple ./*.app + # spctl --assess -vv --type install ./*.app + + rm "${ZIP_FILE}" + + cd .. +fi + +if [[ "${SHOULD_BUILD_ZIP}" != "no" ]]; then + echo "Building and moving ZIP" + cd "VSCode-darwin-${VSCODE_ARCH}" + zip -r -X -y "../assets/${APP_NAME}-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" ./*.app + cd .. +fi + +if [[ -n "${CERTIFICATE_OSX_P12_DATA}" && "${SHOULD_BUILD_DMG}" != "no" ]]; then + echo "Building and moving DMG" + pushd "VSCode-darwin-${VSCODE_ARCH}" + npx create-dmg ./*.app . + mv ./*.dmg "../assets/${APP_NAME}.${VSCODE_ARCH}.${RELEASE_VERSION}.dmg" + popd +fi + +if [[ "${SHOULD_BUILD_SRC}" == "yes" ]]; then + git archive --format tar.gz --output="./assets/${APP_NAME}-${RELEASE_VERSION}-src.tar.gz" HEAD + git archive --format zip --output="./assets/${APP_NAME}-${RELEASE_VERSION}-src.zip" HEAD +fi + +if [[ -n "${CERTIFICATE_OSX_P12_DATA}" ]]; then + echo "+ clean" + security delete-keychain "${KEYCHAIN}" + # shellcheck disable=SC2086 + security list-keychains -s $KEYCHAINS +fi diff --git a/build/windows/check_tags.sh b/build/windows/check_tags.sh new file mode 100644 index 00000000000..0f8b5c97c93 --- /dev/null +++ b/build/windows/check_tags.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +# windows-arm64 +if [[ "${VSCODE_ARCH}" == "arm64" ]]; then + if [[ -z $( contains "${APP_NAME}Setup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" ) ]]; then + echo "Building on Windows arm64 because we have no system setup" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_EXE_SYS="no" + fi + + if [[ -z $( contains "UserSetup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" ) ]]; then + echo "Building on Windows arm64 because we have no user setup" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_EXE_USR="no" + fi + + if [[ -z $( contains "${APP_NAME}-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" ) ]]; then + echo "Building on Windows arm64 because we have no zip" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_ZIP="no" + fi + + export SHOULD_BUILD_REH="no" + export SHOULD_BUILD_REH_WEB="no" + + if [[ -z $( contains "${APP_NAME_LC}-cli-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Windows arm64 because we have no CLI archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_CLI="no" + fi + + if [[ "${SHOULD_BUILD}" != "yes" ]]; then + echo "Already have all the Windows arm64 builds" + fi + +# windows-x64 +else + if [[ -z $( contains "${APP_NAME}Setup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" ) ]]; then + echo "Building on Windows x64 because we have no system setup" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_EXE_SYS="no" + fi + + if [[ -z $( contains "UserSetup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" ) ]]; then + echo "Building on Windows x64 because we have no user setup" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_EXE_USR="no" + fi + + if [[ -z $( contains "${APP_NAME}-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" ) ]]; then + echo "Building on Windows x64 because we have no zip" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_ZIP="no" + fi + + if [[ "${DISABLE_MSI}" == "yes" ]]; then + export SHOULD_BUILD_MSI="no" + elif [[ -z $( contains "${APP_NAME}-${VSCODE_ARCH}-${RELEASE_VERSION}.msi" ) ]]; then + echo "Building on Windows x64 because we have no msi" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_MSI="no" + fi + + if [[ "${DISABLE_MSI}" == "yes" ]]; then + export SHOULD_BUILD_MSI_NOUP="no" + elif [[ -z $( contains "${APP_NAME}-${VSCODE_ARCH}-updates-disabled-${RELEASE_VERSION}.msi" ) ]]; then + echo "Building on Windows x64 because we have no updates-disabled msi" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_MSI_NOUP="no" + fi + + if [[ -z $( contains "${APP_NAME_LC}-reh-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Windows x64 because we have no REH archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH="no" + fi + + if [[ -z $( contains "${APP_NAME_LC}-reh-web-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Windows x64 because we have no REH-web archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_REH_WEB="no" + fi + + if [[ -z $( contains "${APP_NAME_LC}-cli-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then + echo "Building on Windows x64 because we have no CLI archive" + export SHOULD_BUILD="yes" + else + export SHOULD_BUILD_CLI="no" + fi + + if [[ "${SHOULD_BUILD}" != "yes" ]]; then + echo "Already have all the Windows x64 builds" + fi +fi diff --git a/build/windows/prepare_assets.sh b/build/windows/prepare_assets.sh new file mode 100644 index 00000000000..346c220a12e --- /dev/null +++ b/build/windows/prepare_assets.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +cd vscode || { echo "'vscode' dir not found"; exit 1; } + +npm run gulp "vscode-win32-${VSCODE_ARCH}-inno-updater" + +if [[ "${SHOULD_BUILD_ZIP}" != "no" ]]; then + 7z.exe a -tzip "../assets/${APP_NAME}-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" -x!CodeSignSummary*.md -x!tools "../VSCode-win32-${VSCODE_ARCH}/*" -r +fi + +# . ../build/windows/appx/build.sh + +if [[ "${SHOULD_BUILD_EXE_SYS}" != "no" ]]; then + npm run gulp "vscode-win32-${VSCODE_ARCH}-system-setup" +fi + +if [[ "${SHOULD_BUILD_EXE_USR}" != "no" ]]; then + npm run gulp "vscode-win32-${VSCODE_ARCH}-user-setup" +fi + +if [[ "${VSCODE_ARCH}" == "ia32" || "${VSCODE_ARCH}" == "x64" ]]; then + if [[ "${SHOULD_BUILD_MSI}" != "no" ]]; then + . ../build/windows/msi/build.sh + fi + + if [[ "${SHOULD_BUILD_MSI_NOUP}" != "no" ]]; then + . ../build/windows/msi/build-updates-disabled.sh + fi +fi + +cd .. + +if [[ "${SHOULD_BUILD_EXE_SYS}" != "no" ]]; then + echo "Moving System EXE" + mv "vscode\\.build\\win32-${VSCODE_ARCH}\\system-setup\\VSCodeSetup.exe" "assets\\${APP_NAME}Setup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" +fi + +if [[ "${SHOULD_BUILD_EXE_USR}" != "no" ]]; then + echo "Moving User EXE" + mv "vscode\\.build\\win32-${VSCODE_ARCH}\\user-setup\\VSCodeSetup.exe" "assets\\${APP_NAME}UserSetup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" +fi + +if [[ "${VSCODE_ARCH}" == "ia32" || "${VSCODE_ARCH}" == "x64" ]]; then + if [[ "${SHOULD_BUILD_MSI}" != "no" ]]; then + echo "Moving MSI" + mv "build\\windows\\msi\\releasedir\\${APP_NAME}-${VSCODE_ARCH}-${RELEASE_VERSION}.msi" assets/ + fi + + if [[ "${SHOULD_BUILD_MSI_NOUP}" != "no" ]]; then + echo "Moving MSI with disabled updates" + mv "build\\windows\\msi\\releasedir\\${APP_NAME}-${VSCODE_ARCH}-updates-disabled-${RELEASE_VERSION}.msi" assets/ + fi +fi diff --git a/check_tags.sh b/check_tags.sh index aa4b9d5be30..053bb5f6418 100755 --- a/check_tags.sh +++ b/check_tags.sh @@ -83,550 +83,16 @@ elif [[ "${ASSETS}" != "null" ]]; then fi # macos elif [[ "${OS_NAME}" == "osx" ]]; then - if [[ -z $( contains "${APP_NAME}-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" ) ]]; then - echo "Building on MacOS because we have no ZIP" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_ZIP="no" - fi - - if [[ -z $( contains ".${VSCODE_ARCH}.${RELEASE_VERSION}.dmg" ) ]]; then - echo "Building on MacOS because we have no DMG" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_DMG="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-reh-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on MacOS because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-reh-web-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on MacOS because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-cli-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on MacOS because we have no CLI archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_CLI="no" - fi - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the MacOS builds" - fi + . ./build/osx/check_tags.sh elif [[ "${OS_NAME}" == "windows" ]]; then - - # windows-arm64 - if [[ "${VSCODE_ARCH}" == "arm64" ]]; then - if [[ -z $( contains "${APP_NAME}Setup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" ) ]]; then - echo "Building on Windows arm64 because we have no system setup" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_EXE_SYS="no" - fi - - if [[ -z $( contains "UserSetup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" ) ]]; then - echo "Building on Windows arm64 because we have no user setup" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_EXE_USR="no" - fi - - if [[ -z $( contains "${APP_NAME}-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" ) ]]; then - echo "Building on Windows arm64 because we have no zip" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_ZIP="no" - fi - - export SHOULD_BUILD_REH="no" - export SHOULD_BUILD_REH_WEB="no" - - if [[ -z $( contains "${APP_NAME_LC}-cli-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Windows arm64 because we have no CLI archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_CLI="no" - fi - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the Windows arm64 builds" - fi - - # windows-x64 - else - if [[ -z $( contains "${APP_NAME}Setup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" ) ]]; then - echo "Building on Windows x64 because we have no system setup" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_EXE_SYS="no" - fi - - if [[ -z $( contains "UserSetup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" ) ]]; then - echo "Building on Windows x64 because we have no user setup" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_EXE_USR="no" - fi - - if [[ -z $( contains "${APP_NAME}-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" ) ]]; then - echo "Building on Windows x64 because we have no zip" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_ZIP="no" - fi - - if [[ "${DISABLE_MSI}" == "yes" ]]; then - export SHOULD_BUILD_MSI="no" - elif [[ -z $( contains "${APP_NAME}-${VSCODE_ARCH}-${RELEASE_VERSION}.msi" ) ]]; then - echo "Building on Windows x64 because we have no msi" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_MSI="no" - fi - - if [[ "${DISABLE_MSI}" == "yes" ]]; then - export SHOULD_BUILD_MSI_NOUP="no" - elif [[ -z $( contains "${APP_NAME}-${VSCODE_ARCH}-updates-disabled-${RELEASE_VERSION}.msi" ) ]]; then - echo "Building on Windows x64 because we have no updates-disabled msi" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_MSI_NOUP="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-reh-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Windows x64 because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-reh-web-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Windows x64 because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-cli-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Windows x64 because we have no CLI archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_CLI="no" - fi - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the Windows x64 builds" - fi - fi + . ./build/windows/check_tags.sh else if [[ "${OS_NAME}" == "linux" ]]; then - if [[ "${CHECK_ONLY_REH}" == "yes" ]]; then - - if [[ -z $( contains "${APP_NAME_LC}-reh-linux-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux ${VSCODE_ARCH} because we have no REH archive" - export SHOULD_BUILD="yes" - else - echo "Already have the Linux REH ${VSCODE_ARCH} archive" - export SHOULD_BUILD_REH="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-reh-web-linux-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux ${VSCODE_ARCH} because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - echo "Already have the Linux REH-web ${VSCODE_ARCH} archive" - export SHOULD_BUILD_REH_WEB="no" - fi - - else - - # linux-arm64 - if [[ "${VSCODE_ARCH}" == "arm64" || "${CHECK_ALL}" == "yes" ]]; then - if [[ -z $( contains "arm64.deb" ) ]]; then - echo "Building on Linux arm64 because we have no DEB" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_DEB="no" - fi - - if [[ -z $( contains "aarch64.rpm" ) ]]; then - echo "Building on Linux arm64 because we have no RPM" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_RPM="no" - fi - - if [[ -z $( contains "${APP_NAME}-linux-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm64 because we have no TAR" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_TAR="no" - fi - - if [[ -z $( contains "arm64.snap" ) || "${FORCE_LINUX_SNAP}" == "true" ]]; then - echo "Building on Linux arm64 because we have no SNAP" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_SNAP="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm64 because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm64 because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - - export SHOULD_BUILD_APPIMAGE="no" - - if [[ -z $( contains "${APP_NAME_LC}-cli-linux-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm64 because we have no CLI archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_CLI="no" - fi - - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the Linux arm64 builds" - fi - fi - - # linux-armhf - if [[ "${VSCODE_ARCH}" == "armhf" || "${CHECK_ALL}" == "yes" ]]; then - if [[ -z $( contains "armhf.deb" ) ]]; then - echo "Building on Linux arm because we have no DEB" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_DEB="no" - fi - - if [[ -z $( contains "armv7hl.rpm" ) ]]; then - echo "Building on Linux arm because we have no RPM" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_RPM="no" - fi - - if [[ -z $( contains "${APP_NAME}-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm because we have no TAR" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_TAR="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - - export SHOULD_BUILD_APPIMAGE="no" - - if [[ -z $( contains "${APP_NAME_LC}-cli-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm because we have no CLI archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_CLI="no" - fi - - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the Linux arm builds" - fi - fi - - # linux-ppc64le - if [[ "${VSCODE_ARCH}" == "ppc64le" || "${CHECK_ALL}" == "yes" ]]; then - export SHOULD_BUILD_APPIMAGE="no" - - if [[ -z $( contains "ppc64el.deb" ) ]]; then - echo "Building on Linux PowerPC64LE because we have no DEB" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_DEB="no" - fi - - if [[ -z $( contains "ppc64le.rpm" ) ]]; then - echo "Building on Linux PowerPC64LE because we have no RPM" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_RPM="no" - fi - - if [[ -z $( contains "${APP_NAME}-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux PowerPC64LE because we have no TAR" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_TAR="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux PowerPC64LE because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux PowerPC64LE because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-cli-linux-ppc64le-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux PowerPC64LE because we have no CLI archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_CLI="no" - fi - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the Linux PowerPC64LE builds" - fi - fi - - # linux-riscv64 - if [[ "${VSCODE_ARCH}" == "riscv64" || "${CHECK_ALL}" == "yes" ]]; then - export SHOULD_BUILD_DEB="no" - export SHOULD_BUILD_RPM="no" - export SHOULD_BUILD_APPIMAGE="no" - - if [[ -z $( contains "${APP_NAME}-linux-riscv64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux RISC-V 64 because we have no TAR" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_TAR="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-riscv64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux RISC-V 64 because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-riscv64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux RISC-V 64 because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - - export SHOULD_BUILD_CLI="no" - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the Linux riscv64 builds" - fi - fi - - # linux-loong64 - if [[ "${VSCODE_ARCH}" == "loong64" || "${CHECK_ALL}" == "yes" ]]; then - export SHOULD_BUILD_DEB="no" - export SHOULD_BUILD_RPM="no" - export SHOULD_BUILD_APPIMAGE="no" - - if [[ -z $( contains "${APP_NAME}-linux-loong64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux Loong64 because we have no TAR" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_TAR="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-loong64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux Loong64 because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-loong64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux Loong64 because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - - export SHOULD_BUILD_CLI="no" - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the Linux Loong64 builds" - fi - fi - - # linux-s390x - if [[ "${VSCODE_ARCH}" == "s390x" || "${CHECK_ALL}" == "yes" ]]; then - SHOULD_BUILD_APPIMAGE="no" - SHOULD_BUILD_DEB="no" - SHOULD_BUILD_RPM="no" - SHOULD_BUILD_TAR="no" - - if [[ -z $( contains "${APP_NAME_LC}-reh-linux-s390x-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux s390x because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-reh-web-linux-s390x-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux s390x because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - - export SHOULD_BUILD_CLI="no" - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the Linux s390x builds" - fi - fi - - # linux-x64 - if [[ "${VSCODE_ARCH}" == "x64" || "${CHECK_ALL}" == "yes" ]]; then - if [[ -z $( contains "amd64.deb" ) ]]; then - echo "Building on Linux x64 because we have no DEB" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_DEB="no" - fi - - if [[ -z $( contains "x86_64.rpm" ) ]]; then - echo "Building on Linux x64 because we have no RPM" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_RPM="no" - fi - - if [[ -z $( contains "${APP_NAME}-linux-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux x64 because we have no TAR" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_TAR="no" - fi - - if [[ "${DISABLE_APPIMAGE}" == "yes" ]]; then - export SHOULD_BUILD_APPIMAGE="no" - elif [[ -z $( contains "x86_64.AppImage" ) ]]; then - echo "Building on Linux x64 because we have no AppImage" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_APPIMAGE="no" - fi - - if [[ -z $( contains "amd64.snap" ) || "${FORCE_LINUX_SNAP}" == "true" ]]; then - echo "Building on Linux x64 because we have no SNAP" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_SNAP="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux x64 because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux x64 because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-cli-linux-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux x64 because we have no CLI archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_CLI="no" - fi - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the Linux x64 builds" - fi - fi - fi + . ./build/linux/check_tags.sh fi if [[ "${OS_NAME}" == "alpine" ]] || [[ "${OS_NAME}" == "linux" && "${CHECK_ALL}" == "yes" ]]; then - - if [[ "${CHECK_ONLY_REH}" == "yes" ]]; then - if [[ -z $( contains "${APP_NAME_LC}-reh-alpine-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Alpine ${VSCODE_ARCH} because we have no REH archive" - export SHOULD_BUILD="yes" - else - echo "Already have the Alpine REH ${VSCODE_ARCH} archive" - export SHOULD_BUILD_REH="no" - fi - - if [[ -z $( contains "${APP_NAME_LC}-reh-web-alpine-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Alpine ${VSCODE_ARCH} because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - echo "Already have the Alpine REH-web ${VSCODE_ARCH} archive" - export SHOULD_BUILD_REH_WEB="no" - fi - else - - # alpine-arm64 - if [[ "${VSCODE_ARCH}" == "arm64" || "${CHECK_ALL}" == "yes" ]]; then - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-alpine-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Alpine arm64 because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-alpine-arm64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Alpine arm64 because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - fi - - # alpine-x64 - if [[ "${VSCODE_ARCH}" == "x64" || "${CHECK_ALL}" == "yes" ]]; then - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-alpine-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Alpine x64 because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-alpine-x64-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Alpine x64 because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - fi - fi + . ./build/alpine/check_tags.sh fi fi else diff --git a/prepare_assets.sh b/prepare_assets.sh index 269cefac265..5abfd565de5 100755 --- a/prepare_assets.sh +++ b/prepare_assets.sh @@ -8,198 +8,15 @@ APP_NAME_LC="$( echo "${APP_NAME}" | awk '{print tolower($0)}' )" mkdir -p assets if [[ "${OS_NAME}" == "osx" ]]; then - if [[ -n "${CERTIFICATE_OSX_P12_DATA}" ]]; then - if [[ "${CI_BUILD}" == "no" ]]; then - RUNNER_TEMP="${TMPDIR}" - fi - - CERTIFICATE_P12="${APP_NAME}.p12" - KEYCHAIN="${RUNNER_TEMP}/buildagent.keychain" - AGENT_TEMPDIRECTORY="${RUNNER_TEMP}" - # shellcheck disable=SC2006 - KEYCHAINS=`security list-keychains | xargs` - - rm -f "${KEYCHAIN}" - - echo "${CERTIFICATE_OSX_P12_DATA}" | base64 --decode > "${CERTIFICATE_P12}" - - echo "+ create temporary keychain" - security create-keychain -p pwd "${KEYCHAIN}" - security set-keychain-settings -lut 21600 "${KEYCHAIN}" - security unlock-keychain -p pwd "${KEYCHAIN}" - # shellcheck disable=SC2086 - security list-keychains -s $KEYCHAINS "${KEYCHAIN}" - # security show-keychain-info "${KEYCHAIN}" - - echo "+ import certificate to keychain" - security import "${CERTIFICATE_P12}" -k "${KEYCHAIN}" -P "${CERTIFICATE_OSX_P12_PASSWORD}" -T /usr/bin/codesign - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd "${KEYCHAIN}" > /dev/null - # security find-identity "${KEYCHAIN}" - - CODESIGN_IDENTITY="$( security find-identity -v -p codesigning "${KEYCHAIN}" | grep -oEi "([0-9A-F]{40})" | head -n 1 )" - - echo "+ signing" - export CODESIGN_IDENTITY AGENT_TEMPDIRECTORY - - DEBUG="electron-osx-sign*" node vscode/build/darwin/sign.ts "$( pwd )" - # codesign --display --entitlements :- "" - - echo "+ notarize" - - cd "VSCode-darwin-${VSCODE_ARCH}" - ZIP_FILE="./${APP_NAME}-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" - - zip -r -X -y "${ZIP_FILE}" ./*.app - - xcrun notarytool store-credentials "${APP_NAME}" --apple-id "${CERTIFICATE_OSX_ID}" --team-id "${CERTIFICATE_OSX_TEAM_ID}" --password "${CERTIFICATE_OSX_APP_PASSWORD}" --keychain "${KEYCHAIN}" - # xcrun notarytool history --keychain-profile "${APP_NAME}" --keychain "${KEYCHAIN}" - xcrun notarytool submit "${ZIP_FILE}" --keychain-profile "${APP_NAME}" --wait --keychain "${KEYCHAIN}" - - echo "+ attach staple" - xcrun stapler staple ./*.app - # spctl --assess -vv --type install ./*.app - - rm "${ZIP_FILE}" - - cd .. - fi - - if [[ "${SHOULD_BUILD_ZIP}" != "no" ]]; then - echo "Building and moving ZIP" - cd "VSCode-darwin-${VSCODE_ARCH}" - zip -r -X -y "../assets/${APP_NAME}-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" ./*.app - cd .. - fi - - if [[ -n "${CERTIFICATE_OSX_P12_DATA}" && "${SHOULD_BUILD_DMG}" != "no" ]]; then - echo "Building and moving DMG" - pushd "VSCode-darwin-${VSCODE_ARCH}" - npx create-dmg ./*.app . - mv ./*.dmg "../assets/${APP_NAME}.${VSCODE_ARCH}.${RELEASE_VERSION}.dmg" - popd - fi - - if [[ "${SHOULD_BUILD_SRC}" == "yes" ]]; then - git archive --format tar.gz --output="./assets/${APP_NAME}-${RELEASE_VERSION}-src.tar.gz" HEAD - git archive --format zip --output="./assets/${APP_NAME}-${RELEASE_VERSION}-src.zip" HEAD - fi - - if [[ -n "${CERTIFICATE_OSX_P12_DATA}" ]]; then - echo "+ clean" - security delete-keychain "${KEYCHAIN}" - # shellcheck disable=SC2086 - security list-keychains -s $KEYCHAINS - fi + . ./build/osx/prepare_assets.sh VSCODE_PLATFORM="darwin" elif [[ "${OS_NAME}" == "windows" ]]; then - cd vscode || { echo "'vscode' dir not found"; exit 1; } - - npm run gulp "vscode-win32-${VSCODE_ARCH}-inno-updater" - - if [[ "${SHOULD_BUILD_ZIP}" != "no" ]]; then - 7z.exe a -tzip "../assets/${APP_NAME}-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" -x!CodeSignSummary*.md -x!tools "../VSCode-win32-${VSCODE_ARCH}/*" -r - fi - - # . ../build/windows/appx/build.sh - - if [[ "${SHOULD_BUILD_EXE_SYS}" != "no" ]]; then - npm run gulp "vscode-win32-${VSCODE_ARCH}-system-setup" - fi - - if [[ "${SHOULD_BUILD_EXE_USR}" != "no" ]]; then - npm run gulp "vscode-win32-${VSCODE_ARCH}-user-setup" - fi - - if [[ "${VSCODE_ARCH}" == "ia32" || "${VSCODE_ARCH}" == "x64" ]]; then - if [[ "${SHOULD_BUILD_MSI}" != "no" ]]; then - . ../build/windows/msi/build.sh - fi - - if [[ "${SHOULD_BUILD_MSI_NOUP}" != "no" ]]; then - . ../build/windows/msi/build-updates-disabled.sh - fi - fi - - cd .. - - if [[ "${SHOULD_BUILD_EXE_SYS}" != "no" ]]; then - echo "Moving System EXE" - mv "vscode\\.build\\win32-${VSCODE_ARCH}\\system-setup\\VSCodeSetup.exe" "assets\\${APP_NAME}Setup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" - fi - - if [[ "${SHOULD_BUILD_EXE_USR}" != "no" ]]; then - echo "Moving User EXE" - mv "vscode\\.build\\win32-${VSCODE_ARCH}\\user-setup\\VSCodeSetup.exe" "assets\\${APP_NAME}UserSetup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" - fi - - if [[ "${VSCODE_ARCH}" == "ia32" || "${VSCODE_ARCH}" == "x64" ]]; then - if [[ "${SHOULD_BUILD_MSI}" != "no" ]]; then - echo "Moving MSI" - mv "build\\windows\\msi\\releasedir\\${APP_NAME}-${VSCODE_ARCH}-${RELEASE_VERSION}.msi" assets/ - fi - - if [[ "${SHOULD_BUILD_MSI_NOUP}" != "no" ]]; then - echo "Moving MSI with disabled updates" - mv "build\\windows\\msi\\releasedir\\${APP_NAME}-${VSCODE_ARCH}-updates-disabled-${RELEASE_VERSION}.msi" assets/ - fi - fi + . ./build/windows/prepare_assets.sh VSCODE_PLATFORM="win32" else - cd vscode || { echo "'vscode' dir not found"; exit 1; } - - if [[ "${SHOULD_BUILD_APPIMAGE}" != "no" && "${VSCODE_ARCH}" != "x64" ]]; then - SHOULD_BUILD_APPIMAGE="no" - fi - - if [[ "${SHOULD_BUILD_DEB}" != "no" || "${SHOULD_BUILD_APPIMAGE}" != "no" ]]; then - npm run gulp "vscode-linux-${VSCODE_ARCH}-prepare-deb" - npm run gulp "vscode-linux-${VSCODE_ARCH}-build-deb" - fi - - if [[ "${SHOULD_BUILD_RPM}" != "no" ]]; then - npm run gulp "vscode-linux-${VSCODE_ARCH}-prepare-rpm" - npm run gulp "vscode-linux-${VSCODE_ARCH}-build-rpm" - fi - - if [[ "${SHOULD_BUILD_APPIMAGE}" != "no" ]]; then - . ../build/linux/appimage/build.sh - fi - - cd .. - - if [[ "${CI_BUILD}" == "no" ]]; then - . ./stores/snapcraft/build.sh - - if [[ "${SKIP_ASSETS}" == "no" ]]; then - mv stores/snapcraft/build/*.snap assets/ - fi - fi - - if [[ "${SHOULD_BUILD_TAR}" != "no" ]]; then - echo "Building and moving TAR" - cd "VSCode-linux-${VSCODE_ARCH}" - tar czf "../assets/${APP_NAME}-linux-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" . - cd .. - fi - - if [[ "${SHOULD_BUILD_DEB}" != "no" ]]; then - echo "Moving DEB" - mv vscode/.build/linux/deb/*/deb/*.deb assets/ - fi - - if [[ "${SHOULD_BUILD_RPM}" != "no" ]]; then - echo "Moving RPM" - mv vscode/.build/linux/rpm/*/*.rpm assets/ - fi - - if [[ "${SHOULD_BUILD_APPIMAGE}" != "no" ]]; then - echo "Moving AppImage" - mv build/linux/appimage/out/*.AppImage* assets/ - - find assets -name '*.AppImage*' -exec bash -c 'mv $0 ${0/_-_/-}' {} \; - fi + . ./build/linux/prepare_assets.sh VSCODE_PLATFORM="linux" fi From 5b99338d80108b40be8f5a4dbcdc4f8b860e63a2 Mon Sep 17 00:00:00 2001 From: Alex Garcia <115115137+lex-ibm@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:39:50 -0500 Subject: [PATCH 07/79] feat(ppc64): export SYSROOT variables for prepare_assets.sh (#2778) --- .github/workflows/insider-linux.yml | 4 ++++ .github/workflows/stable-linux.yml | 4 ++++ build/linux/package_bin.sh | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/.github/workflows/insider-linux.yml b/.github/workflows/insider-linux.yml index b1b17a87cd3..2c17da8d588 100644 --- a/.github/workflows/insider-linux.yml +++ b/.github/workflows/insider-linux.yml @@ -231,6 +231,7 @@ jobs: if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Build + id: build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} npm_config_arch: ${{ matrix.npm_arch }} @@ -241,6 +242,9 @@ jobs: env: SHOULD_BUILD_REH: 'no' SHOULD_BUILD_REH_WEB: 'no' + VSCODE_SYSROOT_REPOSITORY: ${{ steps.build.outputs.VSCODE_SYSROOT_REPOSITORY }} + VSCODE_SYSROOT_VERSION: ${{ steps.build.outputs.VSCODE_SYSROOT_VERSION }} + VSCODE_SYSROOT_PREFIX: ${{ steps.build.outputs.VSCODE_SYSROOT_PREFIX }} run: ./prepare_assets.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') diff --git a/.github/workflows/stable-linux.yml b/.github/workflows/stable-linux.yml index 456bfba3434..ea2ebfde3c0 100644 --- a/.github/workflows/stable-linux.yml +++ b/.github/workflows/stable-linux.yml @@ -236,6 +236,7 @@ jobs: if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Build + id: build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} npm_config_arch: ${{ matrix.npm_arch }} @@ -246,6 +247,9 @@ jobs: env: SHOULD_BUILD_REH: 'no' SHOULD_BUILD_REH_WEB: 'no' + VSCODE_SYSROOT_REPOSITORY: ${{ steps.build.outputs.VSCODE_SYSROOT_REPOSITORY }} + VSCODE_SYSROOT_VERSION: ${{ steps.build.outputs.VSCODE_SYSROOT_VERSION }} + VSCODE_SYSROOT_PREFIX: ${{ steps.build.outputs.VSCODE_SYSROOT_PREFIX }} run: ./prepare_assets.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') diff --git a/build/linux/package_bin.sh b/build/linux/package_bin.sh index 4671cb78403..a16417c7c25 100755 --- a/build/linux/package_bin.sh +++ b/build/linux/package_bin.sh @@ -145,4 +145,10 @@ find "../VSCode-linux-${VSCODE_ARCH}" -print0 | xargs -0 touch -c . ../build_cli.sh +if [[ -n "${GITHUB_OUTPUT}" ]]; then + echo "VSCODE_SYSROOT_REPOSITORY=${VSCODE_SYSROOT_REPOSITORY:-}" >> "${GITHUB_OUTPUT}" + echo "VSCODE_SYSROOT_VERSION=${VSCODE_SYSROOT_VERSION:-}" >> "${GITHUB_OUTPUT}" + echo "VSCODE_SYSROOT_PREFIX=${VSCODE_SYSROOT_PREFIX:-}" >> "${GITHUB_OUTPUT}" +fi + cd .. From 1f18978fd4a029b6117bb3e7f92fafcc2a45eedd Mon Sep 17 00:00:00 2001 From: Alex Garcia <115115137+lex-ibm@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:45:48 -0500 Subject: [PATCH 08/79] fix(ppc64): update sysroot prefix (#2785) --- build/linux/package_bin.sh | 1 + build/linux/prepare_assets.sh | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/build/linux/package_bin.sh b/build/linux/package_bin.sh index a16417c7c25..9258eae7c73 100755 --- a/build/linux/package_bin.sh +++ b/build/linux/package_bin.sh @@ -26,6 +26,7 @@ if [[ "${VSCODE_ARCH}" == "arm64" || "${VSCODE_ARCH}" == "armhf" ]]; then elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then export VSCODE_SYSROOT_REPOSITORY='VSCodium/vscode-linux-build-agent' export VSCODE_SYSROOT_VERSION='20240129-253798' + export VSCODE_SYSROOT_PREFIX="-glibc-2.28" export ELECTRON_SKIP_BINARY_DOWNLOAD=1 export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 export VSCODE_SKIP_SETUPENV=1 diff --git a/build/linux/prepare_assets.sh b/build/linux/prepare_assets.sh index 8100b810587..364f956154e 100644 --- a/build/linux/prepare_assets.sh +++ b/build/linux/prepare_assets.sh @@ -2,6 +2,18 @@ cd vscode || { echo "'vscode' dir not found"; exit 1; } +if [[ -z "${VSCODE_SYSROOT_REPOSITORY}" ]]; then + unset VSCODE_SYSROOT_REPOSITORY +fi + +if [[ -z "${VSCODE_SYSROOT_VERSION}" ]]; then + unset VSCODE_SYSROOT_VERSION +fi + +if [[ -z "${VSCODE_SYSROOT_PREFIX}" ]]; then + unset VSCODE_SYSROOT_PREFIX +fi + if [[ "${SHOULD_BUILD_APPIMAGE}" != "no" && "${VSCODE_ARCH}" != "x64" ]]; then SHOULD_BUILD_APPIMAGE="no" fi From 7a41cce96d0c906962970f6783e55f483d9d07a2 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 17 Apr 2026 02:30:41 +0200 Subject: [PATCH 09/79] fix(update): update isLatestVersion() to compare versions (#2777) --- patches/version-1-update.patch | 241 +++++++++++++++++++++++---------- 1 file changed, 166 insertions(+), 75 deletions(-) diff --git a/patches/version-1-update.patch b/patches/version-1-update.patch index 168fa340cff..28042b81739 100644 --- a/patches/version-1-update.patch +++ b/patches/version-1-update.patch @@ -49,17 +49,18 @@ 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..1395594 100644 +index c943bca..d216af7 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts -@@ -17,4 +17,4 @@ import { ILogService } from '../../log/common/log.js'; +@@ -17,4 +17,5 @@ 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 { IRequestService, NO_FETCH_TELEMETRY } from '../../request/common/request.js'; -+import { Architecture, AvailableForDownload, DisablementReason, IUpdateService, Platform, State, StateType, Target, UpdateType } from '../common/update.js'; ++import { asJson, IRequestService, NO_FETCH_TELEMETRY } from '../../request/common/request.js'; ++import { Architecture, AvailableForDownload, DisablementReason, IUpdate, IUpdateService, Platform, State, StateType, Target, UpdateType } from '../common/update.js'; ++import * as semver from 'semver'; -@@ -25,12 +25,8 @@ export interface IUpdateURLOptions { +@@ -25,12 +26,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}`); @@ -77,18 +78,64 @@ index c943bca..1395594 100644 - - return url.toString(); } -@@ -322,3 +318,3 @@ export abstract class AbstractUpdateService implements IUpdateService { +@@ -322,3 +319,3 @@ export abstract class AbstractUpdateService implements IUpdateService { - if (mode === 'none') { + if (mode === 'none' || mode === 'manual') { return undefined; -@@ -336,3 +332,3 @@ export abstract class AbstractUpdateService implements IUpdateService { - try { +@@ -332,18 +329,37 @@ export abstract class AbstractUpdateService implements IUpdateService { + ++ return this._isLatestVersion(url, false) ++ .then((result) => { ++ return Promise.resolve(result ? result.lastest : result); ++ }) ++ .then(undefined, (error) => { ++ this.logService.error('update#isLatestVersion(): failed to check for updates'); ++ this.logService.error(error); ++ ++ return Promise.resolve(undefined); ++ }); ++ } ++ ++ _isLatestVersion(url: string, explicit: boolean): Promise<{lastest: boolean, update: IUpdate} | undefined> { + const headers = getUpdateRequestHeaders(this.productService.version); +- this.logService.trace('update#isLatestVersion() - checking update server', { url, headers }); + +- try { - const context = await this.requestService.request({ url, headers, callSite: 'updateService.isLatestVersion' }, token); -+ const context = await this.requestService.request({ url, headers, callSite: NO_FETCH_TELEMETRY }, token); - const statusCode = context.res.statusCode; +- const statusCode = context.res.statusCode; +- this.logService.trace('update#isLatestVersion() - response', { statusCode }); +- // The update server replies with 204 (No Content) when no +- // update is available - that's all we want to know. +- return statusCode === 204; ++ this.logService.info('update#isLatestVersion() - checking update server', { url, headers }); + +- } catch (error) { +- this.logService.error('update#isLatestVersion(): failed to check for updates'); +- this.logService.error(error); +- return undefined; +- } ++ return this.requestService.request({ url, headers, callSite: NO_FETCH_TELEMETRY }, CancellationToken.None) ++ .then(asJson) ++ .then(update => { ++ if (!update || !update.url || !update.version || !update.productVersion) { ++ this.setState(State.Idle(UpdateType.Setup, undefined, explicit || undefined)); ++ ++ return Promise.resolve(undefined); ++ } ++ ++ const fetchedVersion = /\d+\.\d+\.\d+\.\d+/.test(update.productVersion) ? update.productVersion.replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') : update.productVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); ++ const currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); ++ ++ this.logService.info('update#isLatestVersion() - found version', fetchedVersion, currentVersion); ++ ++ const lastest = semver.compareBuild(currentVersion, fetchedVersion) >= 0; ++ ++ return Promise.resolve({ lastest, update }); ++ }) + } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts -index 40b38a2..323919e 100644 +index 40b38a2..81f772d 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'; @@ -96,11 +143,7 @@ index 40b38a2..323919e 100644 -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'; -@@ -22,2 +22,3 @@ import { AbstractUpdateService, createUpdateURL, getUpdateRequestHeaders, IUpdat - import { INodeProcess } from '../../../base/common/platform.js'; -+import * as semver from 'semver'; - -@@ -99,15 +100,4 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau +@@ -99,15 +99,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'); @@ -118,107 +161,127 @@ index 40b38a2..323919e 100644 + protected buildUpdateFeedUrl(quality: string, _commit: string, _options?: IUpdateURLOptions): string | undefined { + return createUpdateURL(this.productService, quality, process.platform, process.arch); } -@@ -154,3 +144,30 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau - this.logService.trace('update#doCheckForUpdates - using Electron autoUpdater', { url, explicit, background }); +@@ -153,4 +142,30 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau + +- this.logService.trace('update#doCheckForUpdates - using Electron autoUpdater', { url, explicit, background }); - electron.autoUpdater.checkForUpdates(); -+ this.requestService.request({ url, callSite: NO_FETCH_TELEMETRY }, CancellationToken.None) -+ .then(asJson) -+ .then(update => { -+ if (!update || !update.url || !update.version || !update.productVersion) { -+ this.setState(State.Idle(UpdateType.Setup, undefined, explicit || undefined)); ++ this.logService.info('update#doCheckForUpdates', { url, explicit, background }); + ++ this._isLatestVersion(url, explicit) ++ .then((result) => { ++ if(!result) { + return Promise.resolve(null); + } + -+ const fetchedVersion = /\d+\.\d+\.\d+\.\d+/.test(update.productVersion) ? update.productVersion.replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') : update.productVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3') -+ const currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3') -+ -+ if(semver.compareBuild(currentVersion, fetchedVersion) >= 0) { ++ if(result.lastest) { + this.setState(State.Idle(UpdateType.Setup, undefined, explicit || undefined)); + } + else { ++ this.logService.info('update#doCheckForUpdates - using Electron autoUpdater'); ++ + electron.autoUpdater.setFeedURL({ url }); + electron.autoUpdater.checkForUpdates(); + } + + return Promise.resolve(null); + }) -+ .then(undefined, err => { -+ this.logService.error(err); ++ .then(undefined, (error) => { ++ this.logService.error(error); ++ + // only show message when explicitly checking for updates -+ const message: string | undefined = explicit ? (err.message || err) : undefined; ++ const message: string | undefined = explicit ? (error.message || error) : undefined; ++ + this.setState(State.Idle(UpdateType.Setup, message)); + }); } -@@ -167,3 +184,3 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau +@@ -167,3 +182,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..8ce708e 100644 +index 0eb5d74..16bd215 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts -@@ -13,5 +13,6 @@ import { INativeHostMainService } from '../../native/electron-main/nativeHostMai +@@ -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 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 { AvailableForDownload, IUpdate, State, UpdateType } from '../common/update.js'; +-import { AvailableForDownload, IUpdate, State, UpdateType } from '../common/update.js'; ++import { IRequestService } from '../../request/common/request.js'; ++import { AvailableForDownload, State, UpdateType } from '../common/update.js'; import { AbstractUpdateService, createUpdateURL, IUpdateURLOptions } from './abstractUpdateService.js'; -+import * as semver from 'semver'; - -@@ -32,4 +33,4 @@ export class LinuxUpdateService extends AbstractUpdateService { +@@ -32,4 +31,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); } -@@ -46,3 +47,3 @@ export class LinuxUpdateService extends AbstractUpdateService { +@@ -41,2 +40,4 @@ export class LinuxUpdateService extends AbstractUpdateService { + ++ this.setState(State.CheckingForUpdates(explicit)); ++ + const internalOrg = this.getInternalOrg(); +@@ -44,17 +45,26 @@ export class LinuxUpdateService extends AbstractUpdateService { + const url = this.buildUpdateFeedUrl(this.quality, this.productService.commit!, { background, internalOrg }); +- this.setState(State.CheckingForUpdates(explicit)); - this.requestService.request({ url, callSite: 'updateService.linux.checkForUpdates' }, CancellationToken.None) -+ this.requestService.request({ url, callSite: NO_FETCH_TELEMETRY }, CancellationToken.None) - .then(asJson) -@@ -51,5 +52,17 @@ export class LinuxUpdateService extends AbstractUpdateService { - this.setState(State.Idle(UpdateType.Archive, undefined, explicit || undefined)); -- } else { +- .then(asJson) +- .then(update => { +- if (!update || !update.url || !update.version || !update.productVersion) { ++ this.logService.info('update#doCheckForUpdates', { url, explicit, background }); + ++ this._isLatestVersion(url, explicit) ++ .then((result) => { ++ if(!result) { + return Promise.resolve(null); + } + -+ const fetchedVersion = /\d+\.\d+\.\d+\.\d+/.test(update.productVersion) ? update.productVersion.replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') : update.productVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3') -+ const currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3') -+ -+ if(semver.compareBuild(currentVersion, fetchedVersion) >= 0) { -+ this.setState(State.Idle(UpdateType.Archive, undefined, explicit || undefined)); -+ } -+ else { - this.setState(State.AvailableForDownload(update)); ++ if(result.lastest) { + this.setState(State.Idle(UpdateType.Archive, undefined, explicit || undefined)); +- } else { +- this.setState(State.AvailableForDownload(update)); } ++ else { ++ this.setState(State.AvailableForDownload(result.update)); ++ } + + return Promise.resolve(null); }) +- .then(undefined, err => { +- this.logService.error(err); ++ .then(undefined, (error) => { ++ this.logService.error(error); ++ + // only show message when explicitly checking for updates +- const message: string | undefined = explicit ? (err.message || err) : undefined; ++ const message: string | undefined = explicit ? (error.message || error) : undefined; ++ + 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..4e8c541 100644 +index d02d7c3..d934da1 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,7 +30,8 @@ import { INativeHostMainService } from '../../native/electron-main/nativeHostMai +@@ -31,6 +30,6 @@ import { INativeHostMainService } from '../../native/electron-main/nativeHostMai 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 { IRequestService, NO_FETCH_TELEMETRY } from '../../request/common/request.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, getUpdateRequestHeaders, IUpdateURLOptions } from './abstractUpdateService.js'; ++import { AbstractUpdateService, createUpdateURL, IUpdateURLOptions } from './abstractUpdateService.js'; import { INodeProcess } from '../../../base/common/platform.js'; -+import * as semver from 'semver'; - -@@ -49,5 +49,9 @@ function getUpdateType(): UpdateType { +@@ -49,5 +48,9 @@ function getUpdateType(): UpdateType { if (typeof _updateType === 'undefined') { - _updateType = existsSync(path.join(path.dirname(process.execPath), 'unins000.exe')) - ? UpdateType.Setup @@ -231,12 +294,12 @@ index d02d7c3..4e8c541 100644 + _updateType = UpdateType.Archive; + } } -@@ -164,3 +168,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -164,3 +167,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 +186,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -182,12 +185,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - protected buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined { - let platform = `win32-${process.arch}`; @@ -245,7 +308,7 @@ index d02d7c3..4e8c541 100644 - platform += '-archive'; - } else if (this.productService.target === 'user') { - platform += '-user'; -+ protected buildUpdateFeedUrl(quality: string, _commit: string, _options?: IUpdateURLOptions): string | undefined { ++ protected buildUpdateFeedUrl(quality: string, _commit: string, _options?: IUpdateURLOptions): string { + let target: Target; + + switch (getUpdateType()) { @@ -267,32 +330,60 @@ index d02d7c3..4e8c541 100644 - return createUpdateURL(this.productService.updateUrl!, platform, quality, commit, options); + return createUpdateURL(this.productService, quality, process.platform, process.arch, target); } -@@ -209,3 +223,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - const headers = getUpdateRequestHeaders(this.productService.version); +@@ -199,6 +212,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 + +- const headers = getUpdateRequestHeaders(this.productService.version); - this.requestService.request({ url, headers, callSite: 'updateService.win32.checkForUpdates' }, CancellationToken.None) -+ this.requestService.request({ url, headers, callSite: NO_FETCH_TELEMETRY }, CancellationToken.None) - .then(asJson) -@@ -226,2 +240,10 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +- .then(asJson) +- .then(update => { ++ const internalOrg = this.getInternalOrg(); ++ const background = !explicit && !internalOrg; ++ const url = this.buildUpdateFeedUrl(this.quality, pendingCommit ?? this.productService.commit!, { background, internalOrg }); ++ ++ this.logService.info('update#doCheckForUpdates', { url, explicit, background }); ++ ++ this._isLatestVersion(url, explicit) ++ .then((result) => { + const updateType = getUpdateType(); -+ const fetchedVersion = /\d+\.\d+\.\d+\.\d+/.test(update.productVersion) ? update.productVersion.replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') : update.productVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3') -+ const currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3') +- 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 + ++ const { lastest, update } = result; + -+ if(semver.compareBuild(currentVersion, fetchedVersion) >= 0) { ++ if(lastest) { + this.setState(State.Idle(updateType, undefined, explicit || undefined)); + return Promise.resolve(null); + } + if (updateType === UpdateType.Archive) { -@@ -258,3 +280,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -258,3 +278,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 => { -@@ -304,3 +326,2 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - .then(undefined, err => { +@@ -303,8 +323,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + }) +- .then(undefined, err => { - this.telemetryService.publicLog2<{ messageHash: string }, UpdateErrorClassification>('update:error', { messageHash: String(hash(String(err))) }); - this.logService.error(err); -@@ -368,20 +389,31 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +- this.logService.error(err); ++ .then(undefined, (error) => { ++ this.logService.error(error); + + // only show message when explicitly checking for updates +- 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 await pfs.Promises.writeFile(this.availableUpdate.updateFilePath, 'flag'); - const child = spawn(this.availableUpdate.packagePath, - [ From ddf1059d9d0995120c8f90790da5499bd1a50968 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 18 Apr 2026 02:56:46 +0200 Subject: [PATCH 10/79] ci: split build and publish workflows (#2795) --- .github/dependabot.yml | 13 +- .github/workflows/ci-build-linux.yml | 370 ++++++++++++++++++ .github/workflows/ci-build-macos.yml | 94 +++++ .github/workflows/ci-build-windows.yml | 163 ++++++++ .github/workflows/lint-zizmor.yml | 30 ++ .github/workflows/lock.yml | 17 - .github/workflows/mod-lock-closed-threads.yml | 22 ++ .github/workflows/mod-stale-issue-pr.yml | 24 ++ ...er-linux.yml => publish-insider-linux.yml} | 204 ++++------ ...er-macos.yml => publish-insider-macos.yml} | 62 +-- ...head.yml => publish-insider-spearhead.yml} | 18 +- ...indows.yml => publish-insider-windows.yml} | 131 ++----- ...ble-linux.yml => publish-stable-linux.yml} | 212 ++++------ ...ble-macos.yml => publish-stable-macos.yml} | 63 +-- ...rhead.yml => publish-stable-spearhead.yml} | 25 +- ...windows.yml => publish-stable-windows.yml} | 128 ++---- .github/workflows/stale.yml | 25 -- justfile | 10 + 18 files changed, 1019 insertions(+), 592 deletions(-) create mode 100644 .github/workflows/ci-build-linux.yml create mode 100644 .github/workflows/ci-build-macos.yml create mode 100644 .github/workflows/ci-build-windows.yml create mode 100644 .github/workflows/lint-zizmor.yml delete mode 100644 .github/workflows/lock.yml create mode 100644 .github/workflows/mod-lock-closed-threads.yml create mode 100644 .github/workflows/mod-stale-issue-pr.yml rename .github/workflows/{insider-linux.yml => publish-insider-linux.yml} (73%) rename .github/workflows/{insider-macos.yml => publish-insider-macos.yml} (61%) rename .github/workflows/{insider-spearhead.yml => publish-insider-spearhead.yml} (82%) rename .github/workflows/{insider-windows.yml => publish-insider-windows.yml} (66%) rename .github/workflows/{stable-linux.yml => publish-stable-linux.yml} (73%) rename .github/workflows/{stable-macos.yml => publish-stable-macos.yml} (61%) rename .github/workflows/{stable-spearhead.yml => publish-stable-spearhead.yml} (75%) rename .github/workflows/{stable-windows.yml => publish-stable-windows.yml} (66%) delete mode 100644 .github/workflows/stale.yml create mode 100644 justfile diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 42e1f84bb98..4f56e1391ab 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,12 +1,9 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - version: 2 updates: - - package-ecosystem: "github-actions" - directory: "/" + - package-ecosystem: github-actions + directory: / target-branch: insider schedule: - interval: "weekly" + interval: weekly + cooldown: + default-days: 7 diff --git a/.github/workflows/ci-build-linux.yml b/.github/workflows/ci-build-linux.yml new file mode 100644 index 00000000000..32ee97fe022 --- /dev/null +++ b/.github/workflows/ci-build-linux.yml @@ -0,0 +1,370 @@ +name: CI - Build - Linux + +on: + workflow_dispatch: + inputs: + generate_assets: + type: boolean + description: Generate assets + checkout_pr: + type: string + description: Checkout PR + push: + branches: + - master + - insider + paths-ignore: + - "**/*.md" + pull_request: + branches: + - "**" + paths-ignore: + - "**/*.md" + +env: + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true + APP_NAME: VSCodium + VSCODE_QUALITY: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'insider' || 'stable' }} + BINARY_NAME: ${{ env.VSCODE_QUALITY == 'insider' && 'codium-insiders' || 'codium' }} + DISABLE_UPDATE: yes + GH_REPO_PATH: ${{ github.repository }} + GITHUB_BRANCH: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.event.head }} + ORG_NAME: ${{ github.repository_owner }} + OS_NAME: linux + +permissions: {} + +jobs: + compile: + runs-on: ubuntu-22.04 + env: + VSCODE_ARCH: x64 + outputs: + BUILD_SOURCEVERSION: ${{ env.BUILD_SOURCEVERSION }} + MS_COMMIT: ${{ env.MS_COMMIT }} + MS_TAG: ${{ env.MS_TAG }} + RELEASE_VERSION: ${{ env.RELEASE_VERSION }} + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false + + - name: Switch to relevant branch + env: + PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} + run: ./get_pr.sh + + - name: Setup GCC + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 + with: + version: 10 + platform: x64 + + - name: Setup Node.js environment + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: .nvmrc + + - name: Setup Python 3 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.11" + + - name: Install libkrb5-dev + run: sudo apt-get update -y && sudo apt-get install -y libkrb5-dev + + - name: Clone VSCode repo + run: ./get_repo.sh + + - name: Build + env: + SHOULD_BUILD_REH: no + SHOULD_BUILD_REH_WEB: no + run: ./build.sh + + - name: Compress vscode artifact + run: | + find vscode -type f -not -path "*/node_modules/*" -not -path "vscode/.build/node/*" -not -path "vscode/.git/*" > vscode.txt + echo "vscode/.build/extensions/node_modules" >> vscode.txt + echo "vscode/.git" >> vscode.txt + tar -czf vscode.tar.gz -T vscode.txt + + - name: Upload vscode artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: vscode + path: ./vscode.tar.gz + retention-days: 1 + + build: + needs: + - compile + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - slug: X64 + vscode_arch: x64 + npm_arch: x64 + image: vscodium/vscodium-linux-build-agent:focal-x64 + - slug: ARM64 + vscode_arch: arm64 + npm_arch: arm64 + image: vscodium/vscodium-linux-build-agent:focal-arm64 + - slug: ARM32 + vscode_arch: armhf + npm_arch: arm + image: vscodium/vscodium-linux-build-agent:focal-armhf + - slug: RISCV64 + vscode_arch: riscv64 + npm_arch: riscv64 + image: vscodium/vscodium-linux-build-agent:focal-riscv64 + - slug: LOONG64 + vscode_arch: loong64 + npm_arch: loong64 + image: vscodium/vscodium-linux-build-agent:crimson-loong64 + - slug: PPC64 + vscode_arch: ppc64le + npm_arch: ppc64 + image: vscodium/vscodium-linux-build-agent:focal-ppc64le + container: + image: ${{ matrix.image }} + env: + BUILD_SOURCEVERSION: ${{ needs.compile.outputs.BUILD_SOURCEVERSION }} + DISABLED: ${{ vars[format('DISABLE_{0}_LINUX_APP_{1}', toUpper(env.VSCODE_QUALITY), matrix.slug)] }} + MS_COMMIT: ${{ needs.compile.outputs.MS_COMMIT }} + MS_TAG: ${{ needs.compile.outputs.MS_TAG }} + RELEASE_VERSION: ${{ needs.compile.outputs.RELEASE_VERSION }} + VSCODE_ARCH: ${{ matrix.vscode_arch }} + outputs: + RELEASE_VERSION: ${{ env.RELEASE_VERSION }} + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false + if: env.DISABLED != 'yes' + + - name: Switch to relevant branch + env: + PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} + run: ./get_pr.sh + if: env.DISABLED != 'yes' + + - name: Install GH + run: ./build/linux/install_gh.sh + if: env.DISABLED != 'yes' + + - name: Install dependencies + run: ./build/linux/deps.sh + if: env.DISABLED != 'yes' + + - uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0 + if: env.DISABLED != 'yes' + + - name: Download vscode artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: vscode + if: env.DISABLED != 'yes' + + - name: Build + id: build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + npm_config_arch: ${{ matrix.npm_arch }} + run: ./build/linux/package_bin.sh + if: env.DISABLED != 'yes' + + - name: Prepare assets + env: + SHOULD_BUILD_APPIMAGE: ${{ (vars[format('DISABLE_{0}_APPIMAGE', toUpper(env.VSCODE_QUALITY)] == 'yes') && 'no' || 'yes' }} + SHOULD_BUILD_REH: 'no' + SHOULD_BUILD_REH_WEB: 'no' + VSCODE_SYSROOT_REPOSITORY: ${{ steps.build.outputs.VSCODE_SYSROOT_REPOSITORY }} + VSCODE_SYSROOT_VERSION: ${{ steps.build.outputs.VSCODE_SYSROOT_VERSION }} + VSCODE_SYSROOT_PREFIX: ${{ steps.build.outputs.VSCODE_SYSROOT_PREFIX }} + run: ./prepare_assets.sh + if: env.DISABLED != 'yes' && github.event.inputs.generate_assets == 'true' + + - name: Upload assets + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: bin-${{ matrix.vscode_arch }} + path: assets/ + retention-days: 3 + if: env.DISABLED != 'yes' && github.event.inputs.generate_assets == 'true' + + reh_linux: + needs: + - compile + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + include: + - slug: X64 + vscode_arch: x64 + npm_arch: x64 + - slug: ARM64 + vscode_arch: arm64 + npm_arch: arm64 + - slug: ARM32 + vscode_arch: armhf + npm_arch: arm + - slug: PPC64 + vscode_arch: ppc64le + npm_arch: ppc64 + - slug: RISCV64 + vscode_arch: riscv64 + npm_arch: riscv64 + - slug: LOONG64 + vscode_arch: loong64 + npm_arch: loong64 + - slug: S390X + vscode_arch: s390x + npm_arch: s390x + env: + BUILD_SOURCEVERSION: ${{ needs.compile.outputs.BUILD_SOURCEVERSION }} + DISABLED: ${{ vars[format('DISABLE_{0}_LINUX_REH_{1}', toUpper(env.VSCODE_QUALITY), matrix.slug)] }} + MS_COMMIT: ${{ needs.compile.outputs.MS_COMMIT }} + MS_TAG: ${{ needs.compile.outputs.MS_TAG }} + RELEASE_VERSION: ${{ needs.compile.outputs.RELEASE_VERSION }} + VSCODE_ARCH: ${{ matrix.vscode_arch }} + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false + if: env.DISABLED != 'yes' + + - name: Switch to relevant branch + env: + PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} + run: ./get_pr.sh + if: env.DISABLED != 'yes' + + - name: Setup GCC + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 + with: + version: 10 + platform: x64 + if: env.DISABLED != 'yes' + + - name: Setup Node.js environment + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: '.nvmrc' + if: env.DISABLED != 'yes' + + - name: Setup Python 3 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + if: env.DISABLED != 'yes' + + - name: Install libkrb5-dev + run: sudo apt-get update -y && sudo apt-get install -y libkrb5-dev + if: env.DISABLED != 'yes' + + - name: Install GH + run: ./build/linux/install_gh.sh + if: env.DISABLED != 'yes' + + - name: Download vscode artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: vscode + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + + - name: Build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + npm_config_arch: ${{ matrix.npm_arch }} + run: ./build/linux/package_reh.sh + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + + - name: Upload assets + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: reh-linux-${{ matrix.vscode_arch }} + path: assets/ + retention-days: 3 + if: env.DISABLED != 'yes' && github.event.inputs.generate_assets == 'true' + + reh_alpine: + needs: + - compile + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + include: + - slug: X64 + vscode_arch: x64 + npm_arch: x64 + - slug: ARM64 + vscode_arch: arm64 + npm_arch: arm64 + env: + BUILD_SOURCEVERSION: ${{ needs.compile.outputs.BUILD_SOURCEVERSION }} + DISABLED: ${{ vars[format('DISABLE_{0}_ALPINE_REH_{1}', toUpper(env.VSCODE_QUALITY), matrix.slug)] }} + MS_COMMIT: ${{ needs.compile.outputs.MS_COMMIT }} + MS_TAG: ${{ needs.compile.outputs.MS_TAG }} + OS_NAME: alpine + RELEASE_VERSION: ${{ needs.compile.outputs.RELEASE_VERSION }} + VSCODE_ARCH: ${{ matrix.vscode_arch }} + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false + + - name: Switch to relevant branch + env: + PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} + run: ./get_pr.sh + + - name: Setup GCC + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 + with: + version: 10 + platform: x64 + + - name: Setup Node.js environment + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: '.nvmrc' + + - name: Install GH + run: ./build/linux/install_gh.sh + + - name: Install libkrb5-dev + run: sudo apt-get update -y && sudo apt-get install -y libkrb5-dev + + - name: Download vscode artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: vscode + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + + - name: Build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + npm_config_arch: ${{ matrix.npm_arch }} + run: ./build/alpine/package_reh.sh + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + + - name: Upload assets + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: reh-alpine-${{ matrix.vscode_arch }} + path: assets/ + retention-days: 3 + if: env.DISABLED != 'yes' && github.event.inputs.generate_assets == 'true' diff --git a/.github/workflows/ci-build-macos.yml b/.github/workflows/ci-build-macos.yml new file mode 100644 index 00000000000..30c5363c07e --- /dev/null +++ b/.github/workflows/ci-build-macos.yml @@ -0,0 +1,94 @@ +name: CI - Build - macOS + +on: + workflow_dispatch: + inputs: + generate_assets: + type: boolean + description: Generate assets + checkout_pr: + type: string + description: Checkout PR + push: + branches: + - master + - insider + paths-ignore: + - "**/*.md" + pull_request: + branches: + - "**" + paths-ignore: + - "**/*.md" + +env: + APP_NAME: VSCodium + VSCODE_QUALITY: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'insider' || 'stable' }} + BINARY_NAME: ${{ env.VSCODE_QUALITY == 'insider' && 'codium-insiders' || 'codium' }} + GH_REPO_PATH: ${{ github.repository }} + GITHUB_BRANCH: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.event.head }} + ORG_NAME: ${{ github.repository_owner }} + OS_NAME: osx + +permissions: {} + +jobs: + build: + runs-on: ${{ matrix.runner }} + env: + VSCODE_ARCH: ${{ matrix.vscode_arch }} + strategy: + fail-fast: false + matrix: + include: + - runner: macos-15-intel + vscode_arch: x64 + - runner: [self-hosted, macOS, ARM64] + vscode_arch: arm64 + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false + + - name: Switch to relevant branch + env: + PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} + run: . get_pr.sh + + - name: Setup Node.js environment + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: '.nvmrc' + + - name: Setup Python 3 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + if: env.VSCODE_ARCH == 'x64' + + - name: Clone VSCode repo + run: . get_repo.sh + + - name: Check existing VSCodium tags/releases + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: . check_tags.sh + + - name: Build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./build.sh + + - name: Prepare assets + run: ./prepare_assets.sh + if: env.SHOULD_BUILD == 'yes' && github.event.inputs.generate_assets == 'true' + + - name: Upload assets + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: bin-${{ matrix.vscode_arch }} + path: assets/ + retention-days: 3 + if: env.SHOULD_BUILD == 'yes' && github.event.inputs.generate_assets == 'true' diff --git a/.github/workflows/ci-build-windows.yml b/.github/workflows/ci-build-windows.yml new file mode 100644 index 00000000000..fc843b5e1fc --- /dev/null +++ b/.github/workflows/ci-build-windows.yml @@ -0,0 +1,163 @@ +name: CI - Build - Windows + +on: + workflow_dispatch: + inputs: + generate_assets: + type: boolean + description: Generate assets + checkout_pr: + type: string + description: Checkout PR + push: + branches: + - master + - insider + paths-ignore: + - "**/*.md" + pull_request: + branches: + - "**" + paths-ignore: + - "**/*.md" + +env: + APP_NAME: VSCodium + VSCODE_QUALITY: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'insider' || 'stable' }} + BINARY_NAME: ${{ env.VSCODE_QUALITY == 'insider' && 'codium-insiders' || 'codium' }} + GH_REPO_PATH: ${{ github.repository }} + GITHUB_BRANCH: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.event.head }} + ORG_NAME: ${{ github.repository_owner }} + OS_NAME: windows + +permissions: {} + +jobs: + compile: + runs-on: windows-2022 + defaults: + run: + shell: bash + env: + VSCODE_ARCH: 'x64' + outputs: + BUILD_SOURCEVERSION: ${{ env.BUILD_SOURCEVERSION }} + MS_COMMIT: ${{ env.MS_COMMIT }} + MS_TAG: ${{ env.MS_TAG }} + RELEASE_VERSION: ${{ env.RELEASE_VERSION }} + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false + + - name: Switch to relevant branch + env: + PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} + run: ./get_pr.sh + + - name: Setup Node.js environment + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: '.nvmrc' + + - name: Setup Python 3 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + + - name: Clone VSCode repo + run: ./get_repo.sh + + - name: Build + env: + SHOULD_BUILD_REH: no + SHOULD_BUILD_REH_WEB: no + run: ./build.sh + + - name: Compress vscode artifact + run: | + find vscode -type f -not -path "*/node_modules/*" -not -path "vscode/.build/node/*" -not -path "vscode/.git/*" > vscode.txt + echo "vscode/.build/extensions/node_modules" >> vscode.txt + echo "vscode/.git" >> vscode.txt + tar -czf vscode.tar.gz -T vscode.txt + + - name: Upload vscode artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: vscode + path: ./vscode.tar.gz + retention-days: 1 + + build: + needs: + - compile + runs-on: windows-2022 + strategy: + fail-fast: false + matrix: + vscode_arch: + - x64 + - arm64 + defaults: + run: + shell: bash + env: + BUILD_SOURCEVERSION: ${{ needs.compile.outputs.BUILD_SOURCEVERSION }} + MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} + MS_TAG: ${{ needs.check.outputs.MS_TAG }} + RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} + VSCODE_ARCH: ${{ matrix.vscode_arch }} + outputs: + RELEASE_VERSION: ${{ env.RELEASE_VERSION }} + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false + + - name: Switch to relevant branch + env: + PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} + run: ./get_pr.sh + + - name: Setup Node.js environment + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: '.nvmrc' + + - name: Setup Python 3 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + + - name: Download vscode artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: vscode + + - name: Build + env: + DISABLE_MSI: ${{ vars[format('DISABLE_{0}_MSI', toUpper(env.VSCODE_QUALITY)] }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + npm_config_arch: ${{ matrix.vscode_arch }} + npm_config_target_arch: ${{ matrix.vscode_arch }} + run: ./build/windows/package.sh + + - name: Prepare assets + run: ./prepare_assets.sh + if: github.event.inputs.generate_assets == 'true' + + - name: Prepare checksums + run: ./prepare_checksums.sh + if: github.event.inputs.generate_assets == 'true' + + - name: Upload assets + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: bin-${{ matrix.vscode_arch }} + path: assets/ + retention-days: 3 + if: github.event.inputs.generate_assets == 'true' diff --git a/.github/workflows/lint-zizmor.yml b/.github/workflows/lint-zizmor.yml new file mode 100644 index 00000000000..d26de7cc0e5 --- /dev/null +++ b/.github/workflows/lint-zizmor.yml @@ -0,0 +1,30 @@ +name: Lint - zizmor + +on: + push: + branches: + - master + - insider + paths-ignore: + - "**/*.md" + pull_request: + branches: + - "**" + paths-ignore: + - "**/*.md" + +permissions: {} + +jobs: + zizmor: + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - name: Checkout repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml deleted file mode 100644 index 05a1c3c4527..00000000000 --- a/.github/workflows/lock.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Lock Closed Threads - -on: - schedule: - - cron: '0 2 * * *' - -jobs: - lock: - runs-on: ubuntu-latest - steps: - - uses: dessant/lock-threads@v6 - with: - github-token: ${{ github.token }} - issue-inactive-days: '90' - pr-inactive-days: '90' - discussion-inactive-days: '90' - log-output: true diff --git a/.github/workflows/mod-lock-closed-threads.yml b/.github/workflows/mod-lock-closed-threads.yml new file mode 100644 index 00000000000..a9a4084c591 --- /dev/null +++ b/.github/workflows/mod-lock-closed-threads.yml @@ -0,0 +1,22 @@ +name: Moderation - Lock Closed Threads + +on: + schedule: + - cron: 0 2 * * * + +permissions: + issues: write + pull-requests: write + discussions: write + +jobs: + lock: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0 + with: + github-token: ${{ github.token }} + issue-inactive-days: "90" + pr-inactive-days: "90" + discussion-inactive-days: "90" + log-output: true diff --git a/.github/workflows/mod-stale-issue-pr.yml b/.github/workflows/mod-stale-issue-pr.yml new file mode 100644 index 00000000000..a2bb6b530aa --- /dev/null +++ b/.github/workflows/mod-stale-issue-pr.yml @@ -0,0 +1,24 @@ +name: Moderation - Stale Issues & PR + +on: + schedule: + - cron: 0 1 * * * + +permissions: + issues: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + with: + days-before-stale: 90 + days-before-close: 30 + operations-per-run: 1024 + stale-issue-message: This issue has been automatically marked as stale. **If this issue is still affecting you, please leave any comment**, and we'll keep it open. If you have any new additional information, please include it with your comment! + close-issue-message: This issue has been closed due to inactivity, and will not be monitored. If this is a bug and you can reproduce this issue, please open a new issue. + exempt-issue-labels: discussion,never-stale + stale-pr-message: This PR has been automatically marked as stale. + close-pr-message: This PR has been closed due to inactivity, and will not be monitored. + only-pr-labels: needs-information diff --git a/.github/workflows/insider-linux.yml b/.github/workflows/publish-insider-linux.yml similarity index 73% rename from .github/workflows/insider-linux.yml rename to .github/workflows/publish-insider-linux.yml index 2c17da8d588..37a78458abd 100644 --- a/.github/workflows/insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -1,37 +1,19 @@ -name: insider-linux +name: Publish - Insider - Linux on: workflow_dispatch: - inputs: - force_version: - type: boolean - description: Force update version - generate_assets: - type: boolean - description: Generate assets - checkout_pr: - type: string - description: Checkout PR repository_dispatch: - types: [insider] - push: - branches: [ insider ] - paths-ignore: - - '**/*.md' - - 'upstream/*.json' - pull_request: - branches: [ insider ] - paths-ignore: - - '**/*.md' + types: + - publish-insider env: ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true APP_NAME: VSCodium ASSETS_REPOSITORY: ${{ github.repository }}-insiders BINARY_NAME: codium-insiders - DISABLE_UPDATE: 'yes' + DISABLE_UPDATE: yes GH_REPO_PATH: ${{ github.repository }} - GITHUB_BRANCH: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'insider' }} + GITHUB_BRANCH: insider ORG_NAME: ${{ github.repository_owner }} OS_NAME: linux VERSIONS_REPOSITORY: ${{ github.repository_owner }}/versions @@ -40,6 +22,10 @@ env: jobs: check: runs-on: ubuntu-latest + permissions: {} + env: + SHOULD_BUILD: yes + SHOULD_DEPLOY: yes outputs: MS_COMMIT: ${{ env.MS_COMMIT }} MS_TAG: ${{ env.MS_TAG }} @@ -48,69 +34,58 @@ jobs: SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false - name: Clone VSCode repo run: ./get_repo.sh - - name: Check PR or cron - env: - GENERATE_ASSETS: ${{ github.event.inputs.generate_assets }} - run: ./check_cron_or_pr.sh - - name: Check existing VSCodium tags/releases env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CHECK_ALL: 'yes' + CHECK_ALL: yes run: ./check_tags.sh compile: needs: - check runs-on: ubuntu-22.04 + permissions: {} env: MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} - SHOULD_BUILD: ${{ (needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true') && 'yes' || 'no' }} - VSCODE_ARCH: 'x64' + SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} + VSCODE_ARCH: x64 outputs: BUILD_SOURCEVERSION: ${{ env.BUILD_SOURCEVERSION }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false if: env.SHOULD_BUILD == 'yes' - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh - - name: Setup GCC - uses: egor-tensin/setup-gcc@v2 + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 with: version: 10 platform: x64 + if: env.SHOULD_BUILD == 'yes' - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: - node-version-file: '.nvmrc' + node-version-file: .nvmrc if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.11' + python-version: "3.11" if: env.SHOULD_BUILD == 'yes' - name: Install libkrb5-dev @@ -123,8 +98,8 @@ jobs: - name: Build env: - SHOULD_BUILD_REH: 'no' - SHOULD_BUILD_REH_WEB: 'no' + SHOULD_BUILD_REH: no + SHOULD_BUILD_REH_WEB: no run: ./build.sh if: env.SHOULD_BUILD == 'yes' @@ -137,7 +112,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Upload vscode artifact - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: vscode path: ./vscode.tar.gz @@ -149,6 +124,8 @@ jobs: - check - compile runs-on: ubuntu-latest + environment: publish + permissions: {} strategy: fail-fast: false matrix: @@ -185,7 +162,7 @@ jobs: MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} - SHOULD_BUILD: ${{ (needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true') && 'yes' || 'no' }} + SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} outputs: @@ -194,15 +171,10 @@ jobs: SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Install GH @@ -221,11 +193,11 @@ jobs: run: ./build/linux/deps.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - - uses: actions-rust-lang/setup-rust-toolchain@v1 + - uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0 if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Download vscode artifact - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vscode if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' @@ -246,9 +218,11 @@ jobs: VSCODE_SYSROOT_VERSION: ${{ steps.build.outputs.VSCODE_SYSROOT_VERSION }} VSCODE_SYSROOT_PREFIX: ${{ steps.build.outputs.VSCODE_SYSROOT_PREFIX }} run: ./prepare_assets.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Release + permissions: + contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} @@ -257,25 +231,18 @@ jobs: - name: Update versions repo env: - FORCE_UPDATE: ${{ github.event.inputs.force_version }} GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - - name: Upload assets - uses: actions/upload-artifact@v7 - with: - name: bin-${{ matrix.vscode_arch }} - path: assets/ - retention-days: 3 - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'no' && github.event.inputs.generate_assets == 'true' - reh_linux: needs: - check - compile runs-on: ubuntu-22.04 + environment: publish + permissions: {} strategy: fail-fast: false matrix: @@ -310,35 +277,30 @@ jobs: SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} - if: needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true' + if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - if: env.DISABLED != 'yes' - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false if: env.DISABLED != 'yes' - name: Setup GCC - uses: egor-tensin/setup-gcc@v2 + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 with: version: 10 platform: x64 if: env.DISABLED != 'yes' - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version-file: '.nvmrc' if: env.DISABLED != 'yes' - name: Setup Python 3 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.11' if: env.DISABLED != 'yes' @@ -359,38 +321,34 @@ jobs: if: env.DISABLED != 'yes' - name: Download vscode artifact - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vscode - if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} npm_config_arch: ${{ matrix.npm_arch }} run: ./build/linux/package_reh.sh - if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Release + permissions: + contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - - name: Upload assets - uses: actions/upload-artifact@v7 - with: - name: reh-linux-${{ matrix.vscode_arch }} - path: assets/ - retention-days: 3 - if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'no' && github.event.inputs.generate_assets == 'true' - reh_alpine: needs: - check - compile runs-on: ubuntu-22.04 + environment: publish + permissions: {} strategy: fail-fast: false matrix: @@ -411,26 +369,22 @@ jobs: SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} - if: needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true' + if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false - name: Setup GCC - uses: egor-tensin/setup-gcc@v2 + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 with: version: 10 platform: x64 - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version-file: '.nvmrc' @@ -449,45 +403,41 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Download vscode artifact - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vscode - if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} npm_config_arch: ${{ matrix.npm_arch }} run: ./build/alpine/package_reh.sh - if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Release + permissions: + contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - - name: Upload assets - uses: actions/upload-artifact@v7 - with: - name: reh-alpine-${{ matrix.vscode_arch }} - path: assets/ - retention-days: 3 - if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'no' && github.event.inputs.generate_assets == 'true' - aur: needs: - check - build runs-on: ubuntu-latest + environment: publish + permissions: {} strategy: fail-fast: false matrix: include: - package_name: vscodium-insiders-bin - package_name: vscodium-insiders - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' && github.event.inputs.generate_assets != 'true' + if: needs.check.outputs.SHOULD_DEPLOY == 'yes' steps: - name: Get version @@ -496,7 +446,7 @@ jobs: run: echo "PACKAGE_VERSION=${RELEASE_VERSION/-*/}" >> "${GITHUB_ENV}" - name: Publish ${{ matrix.package_name }} - uses: zokugun/github-actions-aur-releaser@v1 + uses: zokugun/github-actions-aur-releaser@4348c8a4124434a85d0a5e7457d0ef4079dab490 # v1 with: package_name: ${{ matrix.package_name }} package_version: ${{ env.PACKAGE_VERSION }} @@ -509,6 +459,8 @@ jobs: - check - build runs-on: ubuntu-latest + environment: publish + permissions: {} env: RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SNAP_NAME: codium-insiders @@ -521,18 +473,14 @@ jobs: if: needs.check.outputs.SHOULD_DEPLOY == 'yes' && needs.check.outputs.SHOULD_BUILD_SNAP != 'no' && vars.DISABLE_INSIDER_SNAP != 'yes' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh - - - uses: docker/setup-qemu-action@v4 + - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 - - uses: diddlesnaps/snapcraft-multiarch-action@v1 + - uses: diddlesnaps/snapcraft-multiarch-action@cfd7a246fad6bea65bb92f69a1c8d07898c231e5 # v1.9.0 with: path: stores/snapcraft/insider architecture: ${{ matrix.platform }} @@ -543,7 +491,9 @@ jobs: # snap: ${{ steps.build.outputs.snap }} # isClassic: 'true' - - uses: svenstaro/upload-release-action@v2 + - uses: svenstaro/upload-release-action@29e53e917877a24fad85510ded594ab3c9ca12de # latest + permissions: + contents: write with: repo_name: ${{ env.ASSETS_REPOSITORY }} repo_token: ${{ secrets.STRONGER_GITHUB_TOKEN }} @@ -555,11 +505,13 @@ jobs: - check - build runs-on: ubuntu-latest - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' && github.event.inputs.generate_assets != 'true' + environment: publish + permissions: {} + if: needs.check.outputs.SHOULD_DEPLOY == 'yes' steps: - name: Trigger repository rebuild - uses: peter-evans/repository-dispatch@v4 + uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 with: token: ${{ secrets.STRONGER_GITHUB_TOKEN }} repository: VSCodium/repositories-linux diff --git a/.github/workflows/insider-macos.yml b/.github/workflows/publish-insider-macos.yml similarity index 61% rename from .github/workflows/insider-macos.yml rename to .github/workflows/publish-insider-macos.yml index 79828c1da48..ec05dab8e31 100644 --- a/.github/workflows/insider-macos.yml +++ b/.github/workflows/publish-insider-macos.yml @@ -1,35 +1,17 @@ -name: insider-macos +name: Publish - Insider - macOS on: workflow_dispatch: - inputs: - force_version: - type: boolean - description: Force update version - generate_assets: - type: boolean - description: Generate assets - checkout_pr: - type: string - description: Checkout PR repository_dispatch: - types: [insider] - push: - branches: [ insider ] - paths-ignore: - - '**/*.md' - - 'upstream/*.json' - pull_request: - branches: [ insider ] - paths-ignore: - - '**/*.md' + types: + - publish-insider env: APP_NAME: VSCodium ASSETS_REPOSITORY: ${{ github.repository }}-insiders BINARY_NAME: codium-insiders - GITHUB_BRANCH: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'insider' }} GH_REPO_PATH: ${{ github.repository }} + GITHUB_BRANCH: insider ORG_NAME: ${{ github.repository_owner }} OS_NAME: osx VERSIONS_REPOSITORY: ${{ github.repository_owner }}/versions @@ -38,7 +20,11 @@ env: jobs: build: runs-on: ${{ matrix.runner }} + environment: publish + permissions: {} env: + SHOULD_BUILD: yes + SHOULD_DEPLOY: yes VSCODE_ARCH: ${{ matrix.vscode_arch }} strategy: fail-fast: false @@ -50,34 +36,25 @@ jobs: vscode_arch: arm64 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: . get_pr.sh + persist-credentials: false - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: - node-version-file: '.nvmrc' + node-version-file: .nvmrc - name: Setup Python 3 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.11' + python-version: "3.11" if: env.VSCODE_ARCH == 'x64' - name: Clone VSCode repo run: . get_repo.sh - - name: Check PR or cron - env: - GENERATE_ASSETS: ${{ github.event.inputs.generate_assets }} - run: . check_cron_or_pr.sh - - name: Check existing VSCodium tags/releases env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -98,7 +75,7 @@ jobs: CERTIFICATE_OSX_P12_PASSWORD: ${{ secrets.CERTIFICATE_OSX_NEW_P12_PASSWORD }} CERTIFICATE_OSX_TEAM_ID: ${{ secrets.CERTIFICATE_OSX_NEW_TEAM_ID }} run: ./prepare_assets.sh - if: env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Release env: @@ -109,20 +86,11 @@ jobs: - name: Update versions repo env: - FORCE_UPDATE: ${{ github.event.inputs.force_version }} GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - - name: Upload assets - uses: actions/upload-artifact@v7 - with: - name: bin-${{ matrix.vscode_arch }} - path: assets/ - retention-days: 3 - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'no' && github.event.inputs.generate_assets == 'true' - - name: Clean up keychain if: always() run: | diff --git a/.github/workflows/insider-spearhead.yml b/.github/workflows/publish-insider-spearhead.yml similarity index 82% rename from .github/workflows/insider-spearhead.yml rename to .github/workflows/publish-insider-spearhead.yml index f18c68b17ac..e8230830488 100644 --- a/.github/workflows/insider-spearhead.yml +++ b/.github/workflows/publish-insider-spearhead.yml @@ -1,4 +1,4 @@ -name: insider-spearhead +name: Publish - Insider - Spearhead on: workflow_dispatch: @@ -16,11 +16,14 @@ on: type: boolean description: Disable dispatch schedule: - - cron: '0 7 * * *' + - cron: 0 7 * * * jobs: build: runs-on: macos-15 + environment: publish + permissions: + contents: write # Release env: APP_NAME: VSCodium ASSETS_REPOSITORY: ${{ github.repository }}-insiders @@ -35,12 +38,13 @@ jobs: VSCODE_QUALITY: insider steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: insider + persist-credentials: false - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version-file: '.nvmrc' @@ -61,7 +65,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v7 + uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0 with: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} passphrase: ${{ secrets.GPG_PASSPHRASE }} @@ -92,7 +96,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Dispatch builds - uses: peter-evans/repository-dispatch@v4 + uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 with: - event-type: insider + event-type: publish-insider if: github.event.inputs.dont_dispatch != 'true' && (env.SHOULD_BUILD == 'yes' || github.event.inputs.force_dispatch == 'true') diff --git a/.github/workflows/insider-windows.yml b/.github/workflows/publish-insider-windows.yml similarity index 66% rename from .github/workflows/insider-windows.yml rename to .github/workflows/publish-insider-windows.yml index 367f7f1e482..a4a92ea4309 100644 --- a/.github/workflows/insider-windows.yml +++ b/.github/workflows/publish-insider-windows.yml @@ -1,35 +1,17 @@ -name: insider-windows +name: Publish - Insider - Windows on: workflow_dispatch: - inputs: - force_version: - type: boolean - description: Force update version - generate_assets: - type: boolean - description: Generate assets - checkout_pr: - type: string - description: Checkout PR repository_dispatch: - types: [insider] - push: - branches: [ insider ] - paths-ignore: - - '**/*.md' - - 'upstream/*.json' - pull_request: - branches: [ insider ] - paths-ignore: - - '**/*.md' + types: + - publish-insider env: APP_NAME: VSCodium ASSETS_REPOSITORY: ${{ github.repository }}-insiders BINARY_NAME: codium-insiders - GITHUB_BRANCH: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'insider' }} GH_REPO_PATH: ${{ github.repository }} + GITHUB_BRANCH: insider ORG_NAME: ${{ github.repository_owner }} OS_NAME: windows VERSIONS_REPOSITORY: ${{ github.repository_owner }}/versions @@ -38,6 +20,10 @@ env: jobs: check: runs-on: ubuntu-latest + permissions: {} + env: + SHOULD_BUILD: yes + SHOULD_DEPLOY: yes outputs: MS_COMMIT: ${{ env.MS_COMMIT }} MS_TAG: ${{ env.MS_TAG }} @@ -46,33 +32,25 @@ jobs: SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false - name: Clone VSCode repo run: ./get_repo.sh - - name: Check PR or cron - env: - GENERATE_ASSETS: ${{ github.event.inputs.generate_assets }} - run: ./check_cron_or_pr.sh - - name: Check existing VSCodium tags/releases env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CHECK_ALL: 'yes' + CHECK_ALL: yes run: ./check_tags.sh compile: needs: - check runs-on: windows-2022 + permissions: {} defaults: run: shell: bash @@ -80,52 +58,38 @@ jobs: MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} - SHOULD_BUILD: ${{ (needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true') && 'yes' || 'no' }} - VSCODE_ARCH: 'x64' + SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} + VSCODE_ARCH: x64 outputs: BUILD_SOURCEVERSION: ${{ env.BUILD_SOURCEVERSION }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false if: env.SHOULD_BUILD == 'yes' - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh - - # - name: Setup GCC - # uses: egor-tensin/setup-gcc@v1 - # with: - # version: 10 - # platform: x64 - - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: - node-version-file: '.nvmrc' + node-version-file: .nvmrc if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.11' + python-version: "3.11" if: env.SHOULD_BUILD == 'yes' - # - name: Install libkrb5-dev - # run: sudo apt-get update -y && sudo apt-get install -y libkrb5-dev - # if: env.SHOULD_BUILD == 'yes' - - name: Clone VSCode repo run: ./get_repo.sh if: env.SHOULD_BUILD == 'yes' - name: Build env: - SHOULD_BUILD_REH: 'no' - SHOULD_BUILD_REH_WEB: 'no' + SHOULD_BUILD_REH: no + SHOULD_BUILD_REH_WEB: no run: ./build.sh if: env.SHOULD_BUILD == 'yes' @@ -138,7 +102,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Upload vscode artifact - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: vscode path: ./vscode.tar.gz @@ -150,6 +114,8 @@ jobs: - check - compile runs-on: windows-2022 + environment: publish + permissions: {} strategy: fail-fast: false matrix: @@ -164,7 +130,7 @@ jobs: MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} - SHOULD_BUILD: ${{ (needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true') && 'yes' || 'no' }} + SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} outputs: @@ -172,25 +138,20 @@ jobs: SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - if: env.SHOULD_BUILD == 'yes' - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false if: env.SHOULD_BUILD == 'yes' - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version-file: '.nvmrc' if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.11' if: env.SHOULD_BUILD == 'yes' @@ -203,7 +164,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Download vscode artifact - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vscode if: env.SHOULD_BUILD == 'yes' @@ -218,21 +179,21 @@ jobs: - name: Prepare assets run: ./prepare_assets.sh - if: env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Upload unsigned artifacts id: upload-unsigned-artifacts - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: unsigned-${{ matrix.vscode_arch }} path: | assets/*.exe assets/*.msi retention-days: 1 - if: env.SHOULD_BUILD == 'yes' && vars.DISABLE_INSIDER_WINDOWS_SIGNING != 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.SHOULD_BUILD == 'yes' && vars.DISABLE_INSIDER_WINDOWS_SIGNING != 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Signing - uses: signpath/github-action-submit-signing-request@v2 + uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2.2 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }} @@ -242,13 +203,13 @@ jobs: artifact-configuration-slug: ${{ matrix.vscode_arch }} wait-for-completion: true # 3h to manually approve the request - wait-for-completion-timeout-in-seconds: 10800 + wait-for-completion-timeout-in-seconds: 28800 output-artifact-directory: assets/ - if: env.SHOULD_BUILD == 'yes' && vars.DISABLE_INSIDER_WINDOWS_SIGNING != 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.SHOULD_BUILD == 'yes' && vars.DISABLE_INSIDER_WINDOWS_SIGNING != 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Prepare checksums run: ./prepare_checksums.sh - if: env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Release env: @@ -259,23 +220,16 @@ jobs: - name: Update versions repo env: - FORCE_UPDATE: ${{ github.event.inputs.force_version }} GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - - name: Upload assets - uses: actions/upload-artifact@v7 - with: - name: bin-${{ matrix.vscode_arch }} - path: assets/ - retention-days: 3 - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'no' && github.event.inputs.generate_assets == 'true' - winget: needs: build runs-on: windows-2022 + environment: publish + permissions: {} defaults: run: shell: bash @@ -285,9 +239,10 @@ jobs: if: needs.build.outputs.SHOULD_DEPLOY == 'yes' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false - name: Check version run: ./stores/winget/check_version.sh @@ -295,7 +250,7 @@ jobs: RELEASE_VERSION: ${{ needs.build.outputs.RELEASE_VERSION }} - name: Release to WinGet - uses: vedantmgoyal9/winget-releaser@main + uses: vedantmgoyal9/winget-releaser@4ffc7888bffd451b357355dc214d43bb9f23917e with: identifier: ${{ env.APP_IDENTIFIER }} version: ${{ env.RELEASE_VERSION }} diff --git a/.github/workflows/stable-linux.yml b/.github/workflows/publish-stable-linux.yml similarity index 73% rename from .github/workflows/stable-linux.yml rename to .github/workflows/publish-stable-linux.yml index ea2ebfde3c0..f97eb6ca6a5 100644 --- a/.github/workflows/stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -1,39 +1,19 @@ -name: stable-linux +name: Publish - Stable - Linux on: workflow_dispatch: - inputs: - force_version: - type: boolean - description: Force update version - generate_assets: - type: boolean - description: Generate assets - force_snap: - type: boolean - description: Force Snap - checkout_pr: - type: string - description: Checkout PR repository_dispatch: - types: [stable] - push: - branches: [ master ] - paths-ignore: - - '**/*.md' - - 'upstream/*.json' - pull_request: - branches: [ master ] - paths-ignore: - - '**/*.md' + types: + - publish-stable env: ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true APP_NAME: VSCodium ASSETS_REPOSITORY: ${{ github.repository }} BINARY_NAME: codium - DISABLE_UPDATE: 'yes' + DISABLE_UPDATE: yes GH_REPO_PATH: ${{ github.repository }} + GITHUB_BRANCH: master ORG_NAME: ${{ github.repository_owner }} OS_NAME: linux VERSIONS_REPOSITORY: ${{ github.repository_owner }}/versions @@ -42,6 +22,10 @@ env: jobs: check: runs-on: ubuntu-latest + permissions: {} + env: + SHOULD_BUILD: yes + SHOULD_DEPLOY: yes outputs: MS_COMMIT: ${{ env.MS_COMMIT }} MS_TAG: ${{ env.MS_TAG }} @@ -50,27 +34,18 @@ jobs: SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false - name: Clone VSCode repo run: ./get_repo.sh - - name: Check PR or cron - env: - GENERATE_ASSETS: ${{ github.event.inputs.generate_assets }} - run: ./check_cron_or_pr.sh - - name: Check existing VSCodium tags/releases env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CHECK_ALL: 'yes' + CHECK_ALL: yes FORCE_LINUX_SNAP: ${{ github.event.inputs.force_snap }} run: ./check_tags.sh @@ -78,44 +53,40 @@ jobs: needs: - check runs-on: ubuntu-22.04 + permissions: {} env: MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} - SHOULD_BUILD: ${{ (needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true') && 'yes' || 'no' }} - VSCODE_ARCH: 'x64' + SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} + VSCODE_ARCH: x64 outputs: BUILD_SOURCEVERSION: ${{ env.BUILD_SOURCEVERSION }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - if: env.SHOULD_BUILD == 'yes' - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false if: env.SHOULD_BUILD == 'yes' - name: Setup GCC - uses: egor-tensin/setup-gcc@v2 + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 with: version: 10 platform: x64 if: env.SHOULD_BUILD == 'yes' - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: - node-version-file: '.nvmrc' + node-version-file: .nvmrc if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.11' + python-version: "3.11" if: env.SHOULD_BUILD == 'yes' - name: Install libkrb5-dev @@ -128,8 +99,8 @@ jobs: - name: Build env: - SHOULD_BUILD_REH: 'no' - SHOULD_BUILD_REH_WEB: 'no' + SHOULD_BUILD_REH: no + SHOULD_BUILD_REH_WEB: no run: ./build.sh if: env.SHOULD_BUILD == 'yes' @@ -142,7 +113,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Upload vscode artifact - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: vscode path: ./vscode.tar.gz @@ -154,6 +125,8 @@ jobs: - check - compile runs-on: ubuntu-latest + environment: publish + permissions: {} strategy: fail-fast: false matrix: @@ -190,7 +163,7 @@ jobs: MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} - SHOULD_BUILD: ${{ (needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true') && 'yes' || 'no' }} + SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} outputs: @@ -199,17 +172,12 @@ jobs: SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh - if: env.DISABLED != 'yes' - - name: Install GH run: ./build/linux/install_gh.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' @@ -226,11 +194,11 @@ jobs: run: ./build/linux/deps.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - - uses: actions-rust-lang/setup-rust-toolchain@v1 + - uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0 if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Download vscode artifact - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vscode if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' @@ -251,9 +219,11 @@ jobs: VSCODE_SYSROOT_VERSION: ${{ steps.build.outputs.VSCODE_SYSROOT_VERSION }} VSCODE_SYSROOT_PREFIX: ${{ steps.build.outputs.VSCODE_SYSROOT_PREFIX }} run: ./prepare_assets.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Release + permissions: + contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} @@ -262,25 +232,18 @@ jobs: - name: Update versions repo env: - FORCE_UPDATE: ${{ github.event.inputs.force_version }} GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - - name: Upload assets - uses: actions/upload-artifact@v7 - with: - name: bin-${{ matrix.vscode_arch }} - path: assets/ - retention-days: 3 - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'no' && github.event.inputs.generate_assets == 'true' - reh_linux: needs: - check - compile runs-on: ubuntu-22.04 + environment: publish + permissions: {} strategy: fail-fast: false matrix: @@ -315,35 +278,30 @@ jobs: SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} - if: needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true' + if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - if: env.DISABLED != 'yes' - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false if: env.DISABLED != 'yes' - name: Setup GCC - uses: egor-tensin/setup-gcc@v2 + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 with: version: 10 platform: x64 if: env.DISABLED != 'yes' - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version-file: '.nvmrc' if: env.DISABLED != 'yes' - name: Setup Python 3 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.11' if: env.DISABLED != 'yes' @@ -364,38 +322,34 @@ jobs: if: env.DISABLED != 'yes' - name: Download vscode artifact - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vscode - if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} npm_config_arch: ${{ matrix.npm_arch }} run: ./build/linux/package_reh.sh - if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Release + permissions: + contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - - name: Upload assets - uses: actions/upload-artifact@v7 - with: - name: reh-linux-${{ matrix.vscode_arch }} - path: assets/ - retention-days: 3 - if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'no' && github.event.inputs.generate_assets == 'true' - reh_alpine: needs: - check - compile runs-on: ubuntu-22.04 + environment: publish + permissions: {} strategy: fail-fast: false matrix: @@ -416,26 +370,22 @@ jobs: SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} - if: needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true' + if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false - name: Setup GCC - uses: egor-tensin/setup-gcc@v2 + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 with: version: 10 platform: x64 - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version-file: '.nvmrc' @@ -454,51 +404,45 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Download vscode artifact - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vscode - if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} npm_config_arch: ${{ matrix.npm_arch }} run: ./build/alpine/package_reh.sh - if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no' || github.event.inputs.generate_assets == 'true') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Release + permissions: + contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - - name: Upload assets - uses: actions/upload-artifact@v7 - with: - name: reh-alpine-${{ matrix.vscode_arch }} - path: assets/ - retention-days: 3 - if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'no' && github.event.inputs.generate_assets == 'true' - aur: needs: - check - build runs-on: ubuntu-latest + environment: publish + permissions: {} strategy: fail-fast: false matrix: include: - package_name: vscodium package_type: stable - # - package_name: vscodium-git - # package_type: rolling if: needs.check.outputs.SHOULD_DEPLOY == 'yes' steps: - name: Publish ${{ matrix.package_name }} - uses: zokugun/github-actions-aur-releaser@v1 + uses: zokugun/github-actions-aur-releaser@4348c8a4124434a85d0a5e7457d0ef4079dab490 # v1 with: package_name: ${{ matrix.package_name }} package_type: ${{ matrix.package_type }} @@ -511,6 +455,8 @@ jobs: - check - build runs-on: ubuntu-latest + environment: publish + permissions: {} env: RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SNAP_NAME: codium @@ -524,14 +470,10 @@ jobs: if: needs.check.outputs.SHOULD_DEPLOY == 'yes' && needs.check.outputs.SHOULD_BUILD_SNAP != 'no' && vars.DISABLE_STABLE_SNAP != 'yes' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false - name: Check version env: @@ -541,23 +483,23 @@ jobs: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }} run: ./stores/snapcraft/check_version.sh - - uses: docker/setup-qemu-action@v4 + - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 if: env.SHOULD_BUILD == 'yes' - - uses: diddlesnaps/snapcraft-multiarch-action@v1 + - uses: diddlesnaps/snapcraft-multiarch-action@cfd7a246fad6bea65bb92f69a1c8d07898c231e5 # v1.9.0 with: path: stores/snapcraft/stable architecture: ${{ matrix.platform }} id: build if: env.SHOULD_BUILD == 'yes' - - uses: diddlesnaps/snapcraft-review-action@v1 + - uses: diddlesnaps/snapcraft-review-action@40554b42331cf84dab19ef98c382620427f13482 # v1.3.1 with: snap: ${{ steps.build.outputs.snap }} isClassic: 'true' if: env.SHOULD_DEPLOY_TO_RELEASE == 'yes' || env.SHOULD_DEPLOY_TO_STORE == 'yes' - - uses: svenstaro/upload-release-action@v2 + - uses: svenstaro/upload-release-action@29e53e917877a24fad85510ded594ab3c9ca12de # latest with: repo_name: ${{ env.ASSETS_REPOSITORY }} repo_token: ${{ secrets.STRONGER_GITHUB_TOKEN }} @@ -565,7 +507,7 @@ jobs: tag: ${{ env.RELEASE_VERSION }} if: env.SHOULD_DEPLOY_TO_RELEASE == 'yes' - - uses: snapcore/action-publish@master + - uses: snapcore/action-publish@214b86e5ca036ead1668c79afb81e550e6c54d40 env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }} with: @@ -578,7 +520,9 @@ jobs: - check - build runs-on: ubuntu-latest - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' && github.event.inputs.generate_assets != 'true' + environment: publish + permissions: {} + if: needs.check.outputs.SHOULD_DEPLOY == 'yes' steps: - name: Trigger repository rebuild @@ -591,11 +535,13 @@ jobs: - check - build runs-on: ubuntu-latest - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' && github.event.inputs.generate_assets != 'true' + environment: publish + permissions: {} + if: needs.check.outputs.SHOULD_DEPLOY == 'yes' steps: - name: Trigger repository rebuild - uses: peter-evans/repository-dispatch@v4 + uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 with: token: ${{ secrets.STRONGER_GITHUB_TOKEN }} repository: VSCodium/repositories-linux diff --git a/.github/workflows/stable-macos.yml b/.github/workflows/publish-stable-macos.yml similarity index 61% rename from .github/workflows/stable-macos.yml rename to .github/workflows/publish-stable-macos.yml index 1c370d9e10d..4afd993609f 100644 --- a/.github/workflows/stable-macos.yml +++ b/.github/workflows/publish-stable-macos.yml @@ -1,34 +1,17 @@ -name: stable-macos +name: Publish - Stable - macOS on: workflow_dispatch: - inputs: - force_version: - type: boolean - description: Force update version - generate_assets: - type: boolean - description: Generate assets - checkout_pr: - type: string - description: Checkout PR repository_dispatch: - types: [stable] - push: - branches: [ master ] - paths-ignore: - - '**/*.md' - - 'upstream/*.json' - pull_request: - branches: [ master ] - paths-ignore: - - '**/*.md' + types: + - publish-stable env: APP_NAME: VSCodium ASSETS_REPOSITORY: ${{ github.repository }} BINARY_NAME: codium GH_REPO_PATH: ${{ github.repository }} + GITHUB_BRANCH: master ORG_NAME: ${{ github.repository_owner }} OS_NAME: osx VERSIONS_REPOSITORY: ${{ github.repository_owner }}/versions @@ -37,7 +20,11 @@ env: jobs: build: runs-on: ${{ matrix.runner }} + environment: publish + permissions: {} env: + SHOULD_BUILD: yes + SHOULD_DEPLOY: yes VSCODE_ARCH: ${{ matrix.vscode_arch }} strategy: fail-fast: false @@ -49,34 +36,25 @@ jobs: vscode_arch: arm64 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: . get_pr.sh + persist-credentials: false - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: - node-version-file: '.nvmrc' + node-version-file: .nvmrc - name: Setup Python 3 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.11' + python-version: "3.11" if: env.VSCODE_ARCH == 'x64' - name: Clone VSCode repo run: . get_repo.sh - - name: Check PR or cron - env: - GENERATE_ASSETS: ${{ github.event.inputs.generate_assets }} - run: . check_cron_or_pr.sh - - name: Check existing VSCodium tags/releases env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -97,7 +75,7 @@ jobs: CERTIFICATE_OSX_P12_PASSWORD: ${{ secrets.CERTIFICATE_OSX_NEW_P12_PASSWORD }} CERTIFICATE_OSX_TEAM_ID: ${{ secrets.CERTIFICATE_OSX_NEW_TEAM_ID }} run: ./prepare_assets.sh - if: env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Release env: @@ -107,19 +85,10 @@ jobs: - name: Update versions repo env: - FORCE_UPDATE: ${{ github.event.inputs.force_version }} GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh - if: env.SHOULD_DEPLOY == 'yes' - - - name: Upload assets - uses: actions/upload-artifact@v7 - with: - name: bin-${{ matrix.vscode_arch }} - path: assets/ - retention-days: 3 - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'no' && github.event.inputs.generate_assets == 'true' + if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Clean up keychain if: always() diff --git a/.github/workflows/stable-spearhead.yml b/.github/workflows/publish-stable-spearhead.yml similarity index 75% rename from .github/workflows/stable-spearhead.yml rename to .github/workflows/publish-stable-spearhead.yml index 4acaee4e154..ba2b44153a7 100644 --- a/.github/workflows/stable-spearhead.yml +++ b/.github/workflows/publish-stable-spearhead.yml @@ -1,4 +1,4 @@ -name: stable-spearhead +name: Publish - Stable - Spearhead on: workflow_dispatch: @@ -12,12 +12,18 @@ on: dont_update: type: boolean description: Don't update VSCode + dont_dispatch: + type: boolean + description: Disable dispatch schedule: - - cron: '0 18 * * *' + - cron: 0 18 * * * jobs: build: runs-on: macos-15 + environment: publish + permissions: + contents: write # Release env: APP_NAME: VSCodium ASSETS_REPOSITORY: ${{ github.repository }} @@ -32,10 +38,13 @@ jobs: VSCODE_QUALITY: stable steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: master + persist-credentials: false - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version-file: '.nvmrc' @@ -56,7 +65,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v7 + uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0 with: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} passphrase: ${{ secrets.GPG_PASSPHRASE }} @@ -87,7 +96,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Dispatch builds - uses: peter-evans/repository-dispatch@v4 + uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 with: - event-type: stable - if: env.SHOULD_BUILD == 'yes' || github.event.inputs.force_dispatch == 'true' + event-type: publish-stable + if: github.event.inputs.dont_dispatch != 'true' && (env.SHOULD_BUILD == 'yes' || github.event.inputs.force_dispatch == 'true') diff --git a/.github/workflows/stable-windows.yml b/.github/workflows/publish-stable-windows.yml similarity index 66% rename from .github/workflows/stable-windows.yml rename to .github/workflows/publish-stable-windows.yml index 8bea37f105d..f92d333bf51 100644 --- a/.github/workflows/stable-windows.yml +++ b/.github/workflows/publish-stable-windows.yml @@ -1,34 +1,17 @@ -name: stable-windows +name: Publish - Stable - Windows on: workflow_dispatch: - inputs: - force_version: - type: boolean - description: Force update version - generate_assets: - type: boolean - description: Generate assets - checkout_pr: - type: string - description: Checkout PR repository_dispatch: - types: [stable] - push: - branches: [ master ] - paths-ignore: - - '**/*.md' - - 'upstream/*.json' - pull_request: - branches: [ master ] - paths-ignore: - - '**/*.md' + types: + - publish-stable env: APP_NAME: VSCodium ASSETS_REPOSITORY: ${{ github.repository }} BINARY_NAME: codium GH_REPO_PATH: ${{ github.repository }} + GITHUB_BRANCH: master ORG_NAME: ${{ github.repository_owner }} OS_NAME: windows VERSIONS_REPOSITORY: ${{ github.repository_owner }}/versions @@ -37,6 +20,10 @@ env: jobs: check: runs-on: ubuntu-latest + permissions: {} + env: + SHOULD_BUILD: yes + SHOULD_DEPLOY: yes outputs: MS_COMMIT: ${{ env.MS_COMMIT }} MS_TAG: ${{ env.MS_TAG }} @@ -45,33 +32,25 @@ jobs: SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false - name: Clone VSCode repo run: ./get_repo.sh - - name: Check PR or cron - env: - GENERATE_ASSETS: ${{ github.event.inputs.generate_assets }} - run: ./check_cron_or_pr.sh - - name: Check existing VSCodium tags/releases env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CHECK_ALL: 'yes' + CHECK_ALL: yes run: ./check_tags.sh compile: needs: - check runs-on: windows-2022 + permissions: {} defaults: run: shell: bash @@ -79,52 +58,38 @@ jobs: MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} - SHOULD_BUILD: ${{ (needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true') && 'yes' || 'no' }} - VSCODE_ARCH: 'x64' + SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} + VSCODE_ARCH: x64 outputs: BUILD_SOURCEVERSION: ${{ env.BUILD_SOURCEVERSION }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false if: env.SHOULD_BUILD == 'yes' - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh - - # - name: Setup GCC - # uses: egor-tensin/setup-gcc@v1 - # with: - # version: 10 - # platform: x64 - - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: - node-version-file: '.nvmrc' + node-version-file: .nvmrc if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.11' + python-version: "3.11" if: env.SHOULD_BUILD == 'yes' - # - name: Install libkrb5-dev - # run: sudo apt-get update -y && sudo apt-get install -y libkrb5-dev - # if: env.SHOULD_BUILD == 'yes' - - name: Clone VSCode repo run: ./get_repo.sh if: env.SHOULD_BUILD == 'yes' - name: Build env: - SHOULD_BUILD_REH: 'no' - SHOULD_BUILD_REH_WEB: 'no' + SHOULD_BUILD_REH: no + SHOULD_BUILD_REH_WEB: no run: ./build.sh if: env.SHOULD_BUILD == 'yes' @@ -137,7 +102,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Upload vscode artifact - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: vscode path: ./vscode.tar.gz @@ -149,6 +114,8 @@ jobs: - check - compile runs-on: windows-2022 + environment: publish + permissions: {} strategy: fail-fast: false matrix: @@ -163,7 +130,7 @@ jobs: MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} - SHOULD_BUILD: ${{ (needs.check.outputs.SHOULD_BUILD == 'yes' || github.event.inputs.generate_assets == 'true') && 'yes' || 'no' }} + SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} outputs: @@ -171,25 +138,20 @@ jobs: SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} - if: env.SHOULD_BUILD == 'yes' - - - name: Switch to relevant branch - env: - PULL_REQUEST_ID: ${{ github.event.inputs.checkout_pr }} - run: ./get_pr.sh + persist-credentials: false if: env.SHOULD_BUILD == 'yes' - name: Setup Node.js environment - uses: actions/setup-node@v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version-file: '.nvmrc' if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.11' if: env.SHOULD_BUILD == 'yes' @@ -202,7 +164,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Download vscode artifact - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vscode if: env.SHOULD_BUILD == 'yes' @@ -217,21 +179,21 @@ jobs: - name: Prepare assets run: ./prepare_assets.sh - if: env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Upload unsigned artifacts id: upload-unsigned-artifacts - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: unsigned-${{ matrix.vscode_arch }} path: | assets/*.exe assets/*.msi retention-days: 1 - if: env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Signing - uses: signpath/github-action-submit-signing-request@v2 + uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2.2 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }} @@ -243,11 +205,11 @@ jobs: # 8h to manually approve the request wait-for-completion-timeout-in-seconds: 28800 output-artifact-directory: assets/ - if: env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Prepare checksums run: ./prepare_checksums.sh - if: env.SHOULD_BUILD == 'yes' && (env.SHOULD_DEPLOY == 'yes' || github.event.inputs.generate_assets == 'true') + if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Release env: @@ -257,23 +219,16 @@ jobs: - name: Update versions repo env: - FORCE_UPDATE: ${{ github.event.inputs.force_version }} GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - - name: Upload assets - uses: actions/upload-artifact@v7 - with: - name: bin-${{ matrix.vscode_arch }} - path: assets/ - retention-days: 3 - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'no' && github.event.inputs.generate_assets == 'true' - winget: needs: build runs-on: windows-2022 + environment: publish + permissions: {} defaults: run: shell: bash @@ -282,9 +237,10 @@ jobs: if: needs.build.outputs.SHOULD_DEPLOY == 'yes' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ env.GITHUB_BRANCH }} + persist-credentials: false - name: Check version run: ./stores/winget/check_version.sh @@ -292,7 +248,7 @@ jobs: RELEASE_VERSION: ${{ needs.build.outputs.RELEASE_VERSION }} - name: Release to WinGet - uses: vedantmgoyal9/winget-releaser@main + uses: vedantmgoyal9/winget-releaser@4ffc7888bffd451b357355dc214d43bb9f23917e with: identifier: ${{ env.APP_IDENTIFIER }} version: ${{ env.RELEASE_VERSION }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index d3391096d3e..00000000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Stale Issues - -on: - schedule: - - cron: '0 1 * * *' - -permissions: - issues: write - -jobs: - stale: - runs-on: ubuntu-latest - steps: - - uses: actions/stale@v10 - with: - days-before-stale: 180 - days-before-close: 30 - stale-issue-label: stale - operations-per-run: 1024 - stale-issue-message: > - This issue has been automatically marked as stale. **If this issue is still affecting you, please leave any comment**, and we'll keep it open. If you have any new additional information, please include it with your comment! - close-issue-message: > - This issue has been closed due to inactivity, and will not be monitored. If this is a bug and you can reproduce this issue, please open a new issue. - exempt-issue-labels: discussion,never-stale - only-pr-labels: needs-information diff --git a/justfile b/justfile new file mode 100644 index 00000000000..2d6300889cd --- /dev/null +++ b/justfile @@ -0,0 +1,10 @@ +set shell := ["bash", "-uc"] + +ci-lint: + zizmor . + +ci-lint-fix: + zizmor . --fix=all + +ci-update: + PINACT_MIN_AGE=7 pinact run From 7132e64cf0e5bfe81401cfd43f2eb306c3ee0d21 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 18 Apr 2026 03:08:04 +0200 Subject: [PATCH 11/79] ci: don't use env variable or upper function --- .github/workflows/ci-build-linux.yml | 12 ++++++------ .github/workflows/ci-build-macos.yml | 4 ++-- .github/workflows/ci-build-windows.yml | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci-build-linux.yml b/.github/workflows/ci-build-linux.yml index 32ee97fe022..dc09cbf47c8 100644 --- a/.github/workflows/ci-build-linux.yml +++ b/.github/workflows/ci-build-linux.yml @@ -24,13 +24,13 @@ on: env: ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true APP_NAME: VSCodium - VSCODE_QUALITY: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'insider' || 'stable' }} - BINARY_NAME: ${{ env.VSCODE_QUALITY == 'insider' && 'codium-insiders' || 'codium' }} + BINARY_NAME: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'codium-insiders' || 'codium' }} DISABLE_UPDATE: yes GH_REPO_PATH: ${{ github.repository }} GITHUB_BRANCH: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.event.head }} ORG_NAME: ${{ github.repository_owner }} OS_NAME: linux + VSCODE_QUALITY: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'insider' || 'stable' }} permissions: {} @@ -134,7 +134,7 @@ jobs: image: ${{ matrix.image }} env: BUILD_SOURCEVERSION: ${{ needs.compile.outputs.BUILD_SOURCEVERSION }} - DISABLED: ${{ vars[format('DISABLE_{0}_LINUX_APP_{1}', toUpper(env.VSCODE_QUALITY), matrix.slug)] }} + DISABLED: ${{ vars[format('DISABLE_{0}_LINUX_APP_{1}', ((github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'INSIDER' || 'STABLE'), matrix.slug)] }} MS_COMMIT: ${{ needs.compile.outputs.MS_COMMIT }} MS_TAG: ${{ needs.compile.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.compile.outputs.RELEASE_VERSION }} @@ -182,7 +182,7 @@ jobs: - name: Prepare assets env: - SHOULD_BUILD_APPIMAGE: ${{ (vars[format('DISABLE_{0}_APPIMAGE', toUpper(env.VSCODE_QUALITY)] == 'yes') && 'no' || 'yes' }} + SHOULD_BUILD_APPIMAGE: ${{ (vars[format('DISABLE_{0}_APPIMAGE', ((github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'INSIDER' || 'STABLE'))] == 'yes' && 'no' || 'yes' }} SHOULD_BUILD_REH: 'no' SHOULD_BUILD_REH_WEB: 'no' VSCODE_SYSROOT_REPOSITORY: ${{ steps.build.outputs.VSCODE_SYSROOT_REPOSITORY }} @@ -230,7 +230,7 @@ jobs: npm_arch: s390x env: BUILD_SOURCEVERSION: ${{ needs.compile.outputs.BUILD_SOURCEVERSION }} - DISABLED: ${{ vars[format('DISABLE_{0}_LINUX_REH_{1}', toUpper(env.VSCODE_QUALITY), matrix.slug)] }} + DISABLED: ${{ vars[format('DISABLE_{0}_LINUX_REH_{1}', ((github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'INSIDER' || 'STABLE'), matrix.slug)] }} MS_COMMIT: ${{ needs.compile.outputs.MS_COMMIT }} MS_TAG: ${{ needs.compile.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.compile.outputs.RELEASE_VERSION }} @@ -313,7 +313,7 @@ jobs: npm_arch: arm64 env: BUILD_SOURCEVERSION: ${{ needs.compile.outputs.BUILD_SOURCEVERSION }} - DISABLED: ${{ vars[format('DISABLE_{0}_ALPINE_REH_{1}', toUpper(env.VSCODE_QUALITY), matrix.slug)] }} + DISABLED: ${{ vars[format('DISABLE_{0}_ALPINE_REH_{1}', ((github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'INSIDER' || 'STABLE'), matrix.slug)] }} MS_COMMIT: ${{ needs.compile.outputs.MS_COMMIT }} MS_TAG: ${{ needs.compile.outputs.MS_TAG }} OS_NAME: alpine diff --git a/.github/workflows/ci-build-macos.yml b/.github/workflows/ci-build-macos.yml index 30c5363c07e..b386c7f5b6b 100644 --- a/.github/workflows/ci-build-macos.yml +++ b/.github/workflows/ci-build-macos.yml @@ -23,12 +23,12 @@ on: env: APP_NAME: VSCodium - VSCODE_QUALITY: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'insider' || 'stable' }} - BINARY_NAME: ${{ env.VSCODE_QUALITY == 'insider' && 'codium-insiders' || 'codium' }} + BINARY_NAME: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'codium-insiders' || 'codium' }} GH_REPO_PATH: ${{ github.repository }} GITHUB_BRANCH: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.event.head }} ORG_NAME: ${{ github.repository_owner }} OS_NAME: osx + VSCODE_QUALITY: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'insider' || 'stable' }} permissions: {} diff --git a/.github/workflows/ci-build-windows.yml b/.github/workflows/ci-build-windows.yml index fc843b5e1fc..8fd81ece4ea 100644 --- a/.github/workflows/ci-build-windows.yml +++ b/.github/workflows/ci-build-windows.yml @@ -23,12 +23,12 @@ on: env: APP_NAME: VSCodium - VSCODE_QUALITY: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'insider' || 'stable' }} - BINARY_NAME: ${{ env.VSCODE_QUALITY == 'insider' && 'codium-insiders' || 'codium' }} + BINARY_NAME: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'codium-insiders' || 'codium' }} GH_REPO_PATH: ${{ github.repository }} GITHUB_BRANCH: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.event.head }} ORG_NAME: ${{ github.repository_owner }} OS_NAME: windows + VSCODE_QUALITY: ${{ (github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'insider' || 'stable' }} permissions: {} @@ -140,7 +140,7 @@ jobs: - name: Build env: - DISABLE_MSI: ${{ vars[format('DISABLE_{0}_MSI', toUpper(env.VSCODE_QUALITY)] }} + DISABLE_MSI: ${{ vars[format('DISABLE_{0}_MSI', ((github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'INSIDER' || 'STABLE')] }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} npm_config_arch: ${{ matrix.vscode_arch }} npm_config_target_arch: ${{ matrix.vscode_arch }} From 9ca7fc4b608842516fe4986f2f7ca9940e126736 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 18 Apr 2026 03:20:59 +0200 Subject: [PATCH 12/79] ci: fix expressions and permissions --- .github/workflows/ci-build-linux.yml | 2 +- .github/workflows/ci-build-windows.yml | 2 +- .github/workflows/publish-insider-linux.yml | 22 ++++++++----------- .github/workflows/publish-insider-macos.yml | 5 +++-- .github/workflows/publish-insider-windows.yml | 8 ++++--- .github/workflows/publish-stable-linux.yml | 17 ++++++-------- .github/workflows/publish-stable-macos.yml | 5 +++-- .github/workflows/publish-stable-windows.yml | 8 ++++--- 8 files changed, 34 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci-build-linux.yml b/.github/workflows/ci-build-linux.yml index dc09cbf47c8..306a4bf9183 100644 --- a/.github/workflows/ci-build-linux.yml +++ b/.github/workflows/ci-build-linux.yml @@ -182,7 +182,7 @@ jobs: - name: Prepare assets env: - SHOULD_BUILD_APPIMAGE: ${{ (vars[format('DISABLE_{0}_APPIMAGE', ((github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'INSIDER' || 'STABLE'))] == 'yes' && 'no' || 'yes' }} + SHOULD_BUILD_APPIMAGE: ${{ vars[format('DISABLE_{0}_APPIMAGE', ((github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'INSIDER' || 'STABLE'))] == 'yes' && 'no' || 'yes' }} SHOULD_BUILD_REH: 'no' SHOULD_BUILD_REH_WEB: 'no' VSCODE_SYSROOT_REPOSITORY: ${{ steps.build.outputs.VSCODE_SYSROOT_REPOSITORY }} diff --git a/.github/workflows/ci-build-windows.yml b/.github/workflows/ci-build-windows.yml index 8fd81ece4ea..9091d23f4ab 100644 --- a/.github/workflows/ci-build-windows.yml +++ b/.github/workflows/ci-build-windows.yml @@ -140,7 +140,7 @@ jobs: - name: Build env: - DISABLE_MSI: ${{ vars[format('DISABLE_{0}_MSI', ((github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'INSIDER' || 'STABLE')] }} + DISABLE_MSI: ${{ vars[format('DISABLE_{0}_MSI', ((github.ref == 'refs/heads/insider' || (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'insider')) && 'INSIDER' || 'STABLE'))] }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} npm_config_arch: ${{ matrix.vscode_arch }} npm_config_target_arch: ${{ matrix.vscode_arch }} diff --git a/.github/workflows/publish-insider-linux.yml b/.github/workflows/publish-insider-linux.yml index 37a78458abd..076b039080e 100644 --- a/.github/workflows/publish-insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -1,7 +1,7 @@ name: Publish - Insider - Linux on: - workflow_dispatch: + workflow_dispatch: {} repository_dispatch: types: - publish-insider @@ -125,7 +125,8 @@ jobs: - compile runs-on: ubuntu-latest environment: publish - permissions: {} + permissions: + contents: write strategy: fail-fast: false matrix: @@ -221,8 +222,6 @@ jobs: if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Release - permissions: - contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} @@ -242,7 +241,8 @@ jobs: - compile runs-on: ubuntu-22.04 environment: publish - permissions: {} + permissions: + contents: write strategy: fail-fast: false matrix: @@ -334,8 +334,6 @@ jobs: if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Release - permissions: - contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} @@ -348,7 +346,8 @@ jobs: - compile runs-on: ubuntu-22.04 environment: publish - permissions: {} + permissions: + contents: write strategy: fail-fast: false matrix: @@ -416,8 +415,6 @@ jobs: if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Release - permissions: - contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} @@ -460,7 +457,8 @@ jobs: - build runs-on: ubuntu-latest environment: publish - permissions: {} + permissions: + contents: write env: RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SNAP_NAME: codium-insiders @@ -492,8 +490,6 @@ jobs: # isClassic: 'true' - uses: svenstaro/upload-release-action@29e53e917877a24fad85510ded594ab3c9ca12de # latest - permissions: - contents: write with: repo_name: ${{ env.ASSETS_REPOSITORY }} repo_token: ${{ secrets.STRONGER_GITHUB_TOKEN }} diff --git a/.github/workflows/publish-insider-macos.yml b/.github/workflows/publish-insider-macos.yml index ec05dab8e31..ccd6a6c8c46 100644 --- a/.github/workflows/publish-insider-macos.yml +++ b/.github/workflows/publish-insider-macos.yml @@ -1,7 +1,7 @@ name: Publish - Insider - macOS on: - workflow_dispatch: + workflow_dispatch: {} repository_dispatch: types: - publish-insider @@ -21,7 +21,8 @@ jobs: build: runs-on: ${{ matrix.runner }} environment: publish - permissions: {} + permissions: + contents: write env: SHOULD_BUILD: yes SHOULD_DEPLOY: yes diff --git a/.github/workflows/publish-insider-windows.yml b/.github/workflows/publish-insider-windows.yml index a4a92ea4309..a5adcd86e55 100644 --- a/.github/workflows/publish-insider-windows.yml +++ b/.github/workflows/publish-insider-windows.yml @@ -1,7 +1,7 @@ name: Publish - Insider - Windows on: - workflow_dispatch: + workflow_dispatch: {} repository_dispatch: types: - publish-insider @@ -115,7 +115,8 @@ jobs: - compile runs-on: windows-2022 environment: publish - permissions: {} + permissions: + contents: write strategy: fail-fast: false matrix: @@ -229,7 +230,8 @@ jobs: needs: build runs-on: windows-2022 environment: publish - permissions: {} + permissions: + contents: write defaults: run: shell: bash diff --git a/.github/workflows/publish-stable-linux.yml b/.github/workflows/publish-stable-linux.yml index f97eb6ca6a5..b2cc47a1e91 100644 --- a/.github/workflows/publish-stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -1,7 +1,7 @@ name: Publish - Stable - Linux on: - workflow_dispatch: + workflow_dispatch: {} repository_dispatch: types: - publish-stable @@ -126,7 +126,8 @@ jobs: - compile runs-on: ubuntu-latest environment: publish - permissions: {} + permissions: + contents: write strategy: fail-fast: false matrix: @@ -222,8 +223,6 @@ jobs: if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' - name: Release - permissions: - contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} @@ -243,7 +242,8 @@ jobs: - compile runs-on: ubuntu-22.04 environment: publish - permissions: {} + permissions: + contents: write strategy: fail-fast: false matrix: @@ -335,8 +335,6 @@ jobs: if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Release - permissions: - contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} @@ -349,7 +347,8 @@ jobs: - compile runs-on: ubuntu-22.04 environment: publish - permissions: {} + permissions: + contents: write strategy: fail-fast: false matrix: @@ -417,8 +416,6 @@ jobs: if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') - name: Release - permissions: - contents: write env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} diff --git a/.github/workflows/publish-stable-macos.yml b/.github/workflows/publish-stable-macos.yml index 4afd993609f..f894bf473a5 100644 --- a/.github/workflows/publish-stable-macos.yml +++ b/.github/workflows/publish-stable-macos.yml @@ -1,7 +1,7 @@ name: Publish - Stable - macOS on: - workflow_dispatch: + workflow_dispatch: {} repository_dispatch: types: - publish-stable @@ -21,7 +21,8 @@ jobs: build: runs-on: ${{ matrix.runner }} environment: publish - permissions: {} + permissions: + contents: write env: SHOULD_BUILD: yes SHOULD_DEPLOY: yes diff --git a/.github/workflows/publish-stable-windows.yml b/.github/workflows/publish-stable-windows.yml index f92d333bf51..d287e2c3672 100644 --- a/.github/workflows/publish-stable-windows.yml +++ b/.github/workflows/publish-stable-windows.yml @@ -1,7 +1,7 @@ name: Publish - Stable - Windows on: - workflow_dispatch: + workflow_dispatch: {} repository_dispatch: types: - publish-stable @@ -115,7 +115,8 @@ jobs: - compile runs-on: windows-2022 environment: publish - permissions: {} + permissions: + contents: write strategy: fail-fast: false matrix: @@ -228,7 +229,8 @@ jobs: needs: build runs-on: windows-2022 environment: publish - permissions: {} + permissions: + contents: write defaults: run: shell: bash From 27c3b34038497658d4351e1af0f0a5f0e1381fbb Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 18 Apr 2026 03:29:20 +0200 Subject: [PATCH 13/79] ci: fix build workflows --- .github/workflows/ci-build-linux.yml | 1 + .github/workflows/ci-build-macos.yml | 6 +----- .github/workflows/ci-build-windows.yml | 1 + 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-build-linux.yml b/.github/workflows/ci-build-linux.yml index 306a4bf9183..0fc3d3bbd55 100644 --- a/.github/workflows/ci-build-linux.yml +++ b/.github/workflows/ci-build-linux.yml @@ -80,6 +80,7 @@ jobs: - name: Build env: + SHOULD_BUILD: yes SHOULD_BUILD_REH: no SHOULD_BUILD_REH_WEB: no run: ./build.sh diff --git a/.github/workflows/ci-build-macos.yml b/.github/workflows/ci-build-macos.yml index b386c7f5b6b..16967a07020 100644 --- a/.github/workflows/ci-build-macos.yml +++ b/.github/workflows/ci-build-macos.yml @@ -71,13 +71,9 @@ jobs: - name: Clone VSCode repo run: . get_repo.sh - - name: Check existing VSCodium tags/releases - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: . check_tags.sh - - name: Build env: + SHOULD_BUILD: yes GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ./build.sh diff --git a/.github/workflows/ci-build-windows.yml b/.github/workflows/ci-build-windows.yml index 9091d23f4ab..b620a5a16cf 100644 --- a/.github/workflows/ci-build-windows.yml +++ b/.github/workflows/ci-build-windows.yml @@ -72,6 +72,7 @@ jobs: - name: Build env: + SHOULD_BUILD: yes SHOULD_BUILD_REH: no SHOULD_BUILD_REH_WEB: no run: ./build.sh From 55edf6dfa9b9533236abf519adc20dbd6872ad4d Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 18 Apr 2026 12:36:50 +0200 Subject: [PATCH 14/79] ci: split spearheads so they ask for environment when only needed --- .../workflows/publish-insider-spearhead.yml | 75 ++++++++++++------- .../workflows/publish-stable-spearhead.yml | 75 ++++++++++++------- 2 files changed, 94 insertions(+), 56 deletions(-) diff --git a/.github/workflows/publish-insider-spearhead.yml b/.github/workflows/publish-insider-spearhead.yml index e8230830488..bc7846e346b 100644 --- a/.github/workflows/publish-insider-spearhead.yml +++ b/.github/workflows/publish-insider-spearhead.yml @@ -6,9 +6,6 @@ on: new_release: type: boolean description: Force new Release - force_dispatch: - type: boolean - description: Force dispatch dont_update: type: boolean description: Don't update VSCode @@ -18,24 +15,57 @@ on: schedule: - cron: 0 7 * * * +env: + APP_NAME: VSCodium + ASSETS_REPOSITORY: ${{ github.repository }}-insiders + BINARY_NAME: codium-insiders + GH_REPO_PATH: ${{ github.repository }} + ORG_NAME: ${{ github.repository_owner }} + OS_NAME: osx + SOURCEMAPS_REPOSITORY: ${{ github.repository_owner }}/sourcemaps + VERSIONS_REPOSITORY: ${{ github.repository_owner }}/versions + VSCODE_ARCH: arm64 + VSCODE_LATEST: ${{ github.event.inputs.dont_update == 'true' && 'no' || 'yes' }} + VSCODE_QUALITY: insider + jobs: + check: + runs-on: macos-15 + outputs: + MS_COMMIT: ${{ env.MS_COMMIT }} + MS_TAG: ${{ env.MS_TAG }} + RELEASE_VERSION: ${{ env.RELEASE_VERSION }} + SHOULD_BUILD: ${{ env.SHOULD_BUILD }} + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: insider + persist-credentials: false + + - name: Clone VSCode repo + run: . get_repo.sh + + - name: Check existing VSCodium tags/releases + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NEW_RELEASE: ${{ github.event.inputs.new_release }} + IS_SPEARHEAD: 'yes' + run: . check_tags.sh + build: + needs: + - check runs-on: macos-15 environment: publish permissions: contents: write # Release env: - APP_NAME: VSCodium - ASSETS_REPOSITORY: ${{ github.repository }}-insiders - BINARY_NAME: codium-insiders - GH_REPO_PATH: ${{ github.repository }} - ORG_NAME: ${{ github.repository_owner }} - OS_NAME: osx - SOURCEMAPS_REPOSITORY: ${{ github.repository_owner }}/sourcemaps - VERSIONS_REPOSITORY: ${{ github.repository_owner }}/versions - VSCODE_ARCH: arm64 - VSCODE_LATEST: ${{ github.event.inputs.dont_update == 'true' && 'no' || 'yes' }} - VSCODE_QUALITY: insider + MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} + MS_TAG: ${{ needs.check.outputs.MS_TAG }} + RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} + SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} + if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -51,18 +81,10 @@ jobs: - name: Clone VSCode repo run: . get_repo.sh - - name: Check existing VSCodium tags/releases - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NEW_RELEASE: ${{ github.event.inputs.new_release }} - IS_SPEARHEAD: 'yes' - run: . check_tags.sh - - name: Build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ./build.sh - if: env.SHOULD_BUILD == 'yes' - name: Import GPG key uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0 @@ -71,32 +93,29 @@ jobs: passphrase: ${{ secrets.GPG_PASSPHRASE }} git_user_signingkey: true git_commit_gpgsign: true - if: env.SHOULD_BUILD == 'yes' && github.event.inputs.dont_update != 'true' + if: github.event.inputs.dont_update != 'true' - name: Update upstream version run: ./update_upstream.sh - if: env.SHOULD_BUILD == 'yes' && github.event.inputs.dont_update != 'true' + if: github.event.inputs.dont_update != 'true' - name: Prepare source run: ./prepare_src.sh - if: env.SHOULD_BUILD == 'yes' - name: Upload sourcemaps env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./upload_sourcemaps.sh - if: env.SHOULD_BUILD == 'yes' - name: Release source env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh - if: env.SHOULD_BUILD == 'yes' - name: Dispatch builds uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 with: event-type: publish-insider - if: github.event.inputs.dont_dispatch != 'true' && (env.SHOULD_BUILD == 'yes' || github.event.inputs.force_dispatch == 'true') + if: github.event.inputs.dont_dispatch != 'true' diff --git a/.github/workflows/publish-stable-spearhead.yml b/.github/workflows/publish-stable-spearhead.yml index ba2b44153a7..03976b3b86e 100644 --- a/.github/workflows/publish-stable-spearhead.yml +++ b/.github/workflows/publish-stable-spearhead.yml @@ -6,9 +6,6 @@ on: new_release: type: boolean description: Force new Release - force_dispatch: - type: boolean - description: Force dispatch dont_update: type: boolean description: Don't update VSCode @@ -18,24 +15,57 @@ on: schedule: - cron: 0 18 * * * +env: + APP_NAME: VSCodium + ASSETS_REPOSITORY: ${{ github.repository }} + BINARY_NAME: codium + GH_REPO_PATH: ${{ github.repository }} + ORG_NAME: ${{ github.repository_owner }} + OS_NAME: osx + SOURCEMAPS_REPOSITORY: ${{ github.repository_owner }}/sourcemaps + VERSIONS_REPOSITORY: ${{ github.repository_owner }}/versions + VSCODE_ARCH: arm64 + VSCODE_LATEST: ${{ github.event.inputs.dont_update == 'true' && 'no' || 'yes' }} + VSCODE_QUALITY: stable + jobs: + check: + runs-on: macos-15 + outputs: + MS_COMMIT: ${{ env.MS_COMMIT }} + MS_TAG: ${{ env.MS_TAG }} + RELEASE_VERSION: ${{ env.RELEASE_VERSION }} + SHOULD_BUILD: ${{ env.SHOULD_BUILD }} + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: master + persist-credentials: false + + - name: Clone VSCode repo + run: . get_repo.sh + + - name: Check existing VSCodium tags/releases + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NEW_RELEASE: ${{ github.event.inputs.new_release }} + IS_SPEARHEAD: 'yes' + run: . check_tags.sh + build: + needs: + - check runs-on: macos-15 environment: publish permissions: contents: write # Release env: - APP_NAME: VSCodium - ASSETS_REPOSITORY: ${{ github.repository }} - BINARY_NAME: codium - GH_REPO_PATH: ${{ github.repository }} - ORG_NAME: ${{ github.repository_owner }} - OS_NAME: osx - SOURCEMAPS_REPOSITORY: ${{ github.repository_owner }}/sourcemaps - VERSIONS_REPOSITORY: ${{ github.repository_owner }}/versions - VSCODE_ARCH: arm64 - VSCODE_LATEST: ${{ github.event.inputs.dont_update == 'true' && 'no' || 'yes' }} - VSCODE_QUALITY: stable + MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} + MS_TAG: ${{ needs.check.outputs.MS_TAG }} + RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} + SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} + if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -51,18 +81,10 @@ jobs: - name: Clone VSCode repo run: . get_repo.sh - - name: Check existing VSCodium tags/releases - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NEW_RELEASE: ${{ github.event.inputs.new_release }} - IS_SPEARHEAD: 'yes' - run: . check_tags.sh - - name: Build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ./build.sh - if: env.SHOULD_BUILD == 'yes' - name: Import GPG key uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0 @@ -71,32 +93,29 @@ jobs: passphrase: ${{ secrets.GPG_PASSPHRASE }} git_user_signingkey: true git_commit_gpgsign: true - if: env.SHOULD_BUILD == 'yes' && github.event.inputs.dont_update != 'true' + if: github.event.inputs.dont_update != 'true' - name: Update upstream version run: ./update_upstream.sh - if: env.SHOULD_BUILD == 'yes' && github.event.inputs.dont_update != 'true' + if: github.event.inputs.dont_update != 'true' - name: Prepare source run: ./prepare_src.sh - if: env.SHOULD_BUILD == 'yes' - name: Upload sourcemaps env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./upload_sourcemaps.sh - if: env.SHOULD_BUILD == 'yes' - name: Release source env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh - if: env.SHOULD_BUILD == 'yes' - name: Dispatch builds uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 with: event-type: publish-stable - if: github.event.inputs.dont_dispatch != 'true' && (env.SHOULD_BUILD == 'yes' || github.event.inputs.force_dispatch == 'true') + if: github.event.inputs.dont_dispatch != 'true' From c04ffc4585d79210bace36162d77b111b580c2de Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 18 Apr 2026 12:38:01 +0200 Subject: [PATCH 15/79] ci: use existing release version number --- .github/workflows/publish-insider-linux.yml | 3 --- .github/workflows/publish-insider-macos.yml | 2 -- .github/workflows/publish-insider-windows.yml | 3 --- .github/workflows/publish-stable-linux.yml | 3 --- .github/workflows/publish-stable-macos.yml | 2 -- .github/workflows/publish-stable-windows.yml | 3 --- 6 files changed, 16 deletions(-) diff --git a/.github/workflows/publish-insider-linux.yml b/.github/workflows/publish-insider-linux.yml index 076b039080e..b5c09a2ce54 100644 --- a/.github/workflows/publish-insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -23,9 +23,6 @@ jobs: check: runs-on: ubuntu-latest permissions: {} - env: - SHOULD_BUILD: yes - SHOULD_DEPLOY: yes outputs: MS_COMMIT: ${{ env.MS_COMMIT }} MS_TAG: ${{ env.MS_TAG }} diff --git a/.github/workflows/publish-insider-macos.yml b/.github/workflows/publish-insider-macos.yml index ccd6a6c8c46..91154142d44 100644 --- a/.github/workflows/publish-insider-macos.yml +++ b/.github/workflows/publish-insider-macos.yml @@ -24,8 +24,6 @@ jobs: permissions: contents: write env: - SHOULD_BUILD: yes - SHOULD_DEPLOY: yes VSCODE_ARCH: ${{ matrix.vscode_arch }} strategy: fail-fast: false diff --git a/.github/workflows/publish-insider-windows.yml b/.github/workflows/publish-insider-windows.yml index a5adcd86e55..1b695cfd6f2 100644 --- a/.github/workflows/publish-insider-windows.yml +++ b/.github/workflows/publish-insider-windows.yml @@ -21,9 +21,6 @@ jobs: check: runs-on: ubuntu-latest permissions: {} - env: - SHOULD_BUILD: yes - SHOULD_DEPLOY: yes outputs: MS_COMMIT: ${{ env.MS_COMMIT }} MS_TAG: ${{ env.MS_TAG }} diff --git a/.github/workflows/publish-stable-linux.yml b/.github/workflows/publish-stable-linux.yml index b2cc47a1e91..a371b3449ae 100644 --- a/.github/workflows/publish-stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -23,9 +23,6 @@ jobs: check: runs-on: ubuntu-latest permissions: {} - env: - SHOULD_BUILD: yes - SHOULD_DEPLOY: yes outputs: MS_COMMIT: ${{ env.MS_COMMIT }} MS_TAG: ${{ env.MS_TAG }} diff --git a/.github/workflows/publish-stable-macos.yml b/.github/workflows/publish-stable-macos.yml index f894bf473a5..0fdacaa3da1 100644 --- a/.github/workflows/publish-stable-macos.yml +++ b/.github/workflows/publish-stable-macos.yml @@ -24,8 +24,6 @@ jobs: permissions: contents: write env: - SHOULD_BUILD: yes - SHOULD_DEPLOY: yes VSCODE_ARCH: ${{ matrix.vscode_arch }} strategy: fail-fast: false diff --git a/.github/workflows/publish-stable-windows.yml b/.github/workflows/publish-stable-windows.yml index d287e2c3672..966ceab48c5 100644 --- a/.github/workflows/publish-stable-windows.yml +++ b/.github/workflows/publish-stable-windows.yml @@ -21,9 +21,6 @@ jobs: check: runs-on: ubuntu-latest permissions: {} - env: - SHOULD_BUILD: yes - SHOULD_DEPLOY: yes outputs: MS_COMMIT: ${{ env.MS_COMMIT }} MS_TAG: ${{ env.MS_TAG }} From 54162c187ae87ae65c1c9eba23995ff83f3251c4 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 18 Apr 2026 12:39:40 +0200 Subject: [PATCH 16/79] ci: fix permissions --- .github/workflows/publish-insider-spearhead.yml | 1 + .github/workflows/publish-stable-spearhead.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/publish-insider-spearhead.yml b/.github/workflows/publish-insider-spearhead.yml index bc7846e346b..fb6c91c7c14 100644 --- a/.github/workflows/publish-insider-spearhead.yml +++ b/.github/workflows/publish-insider-spearhead.yml @@ -31,6 +31,7 @@ env: jobs: check: runs-on: macos-15 + permissions: {} outputs: MS_COMMIT: ${{ env.MS_COMMIT }} MS_TAG: ${{ env.MS_TAG }} diff --git a/.github/workflows/publish-stable-spearhead.yml b/.github/workflows/publish-stable-spearhead.yml index 03976b3b86e..792f7db5939 100644 --- a/.github/workflows/publish-stable-spearhead.yml +++ b/.github/workflows/publish-stable-spearhead.yml @@ -31,6 +31,7 @@ env: jobs: check: runs-on: macos-15 + permissions: {} outputs: MS_COMMIT: ${{ env.MS_COMMIT }} MS_TAG: ${{ env.MS_TAG }} From 3db4d595d6f22fef3f19f22fc7ab137e741c1266 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 18 Apr 2026 14:31:22 +0200 Subject: [PATCH 17/79] ci: publish workflows are always deploying --- .github/workflows/publish-insider-linux.yml | 26 ++++++----------- .github/workflows/publish-insider-macos.yml | 7 ++--- .github/workflows/publish-insider-windows.yml | 18 +++++------- .github/workflows/publish-stable-linux.yml | 29 ++++++------------- .github/workflows/publish-stable-macos.yml | 7 ++--- .github/workflows/publish-stable-windows.yml | 18 +++++------- 6 files changed, 38 insertions(+), 67 deletions(-) diff --git a/.github/workflows/publish-insider-linux.yml b/.github/workflows/publish-insider-linux.yml index b5c09a2ce54..06df5d07384 100644 --- a/.github/workflows/publish-insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -28,7 +28,6 @@ jobs: MS_TAG: ${{ env.MS_TAG }} RELEASE_VERSION: ${{ env.RELEASE_VERSION }} SHOULD_BUILD: ${{ env.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -113,7 +112,7 @@ jobs: with: name: vscode path: ./vscode.tar.gz - retention-days: ${{ needs.check.outputs.SHOULD_DEPLOY == 'yes' && 30 || 1 }} + retention-days: 30 if: env.SHOULD_BUILD == 'yes' build: @@ -161,12 +160,10 @@ jobs: MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} outputs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} SHOULD_BUILD: ${{ env.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -177,7 +174,7 @@ jobs: - name: Install GH run: ./build/linux/install_gh.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Check existing VSCodium tags/releases env: @@ -216,21 +213,21 @@ jobs: VSCODE_SYSROOT_VERSION: ${{ steps.build.outputs.VSCODE_SYSROOT_VERSION }} VSCODE_SYSROOT_PREFIX: ${{ steps.build.outputs.VSCODE_SYSROOT_PREFIX }} run: ./prepare_assets.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Release env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Update versions repo env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' reh_linux: needs: @@ -272,7 +269,6 @@ jobs: MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} if: needs.check.outputs.SHOULD_BUILD == 'yes' @@ -308,7 +304,7 @@ jobs: - name: Install GH run: ./build/linux/install_gh.sh - if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.DISABLED != 'yes' - name: Check existing VSCodium tags/releases env: @@ -335,7 +331,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh - if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') reh_alpine: needs: @@ -363,7 +359,6 @@ jobs: OS_NAME: alpine RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} if: needs.check.outputs.SHOULD_BUILD == 'yes' @@ -386,7 +381,6 @@ jobs: - name: Install GH run: ./build/linux/install_gh.sh - if: env.SHOULD_DEPLOY == 'yes' - name: Check existing VSCodium tags/releases env: @@ -416,7 +410,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh - if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') aur: needs: @@ -431,7 +425,6 @@ jobs: include: - package_name: vscodium-insiders-bin - package_name: vscodium-insiders - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' steps: - name: Get version @@ -465,7 +458,7 @@ jobs: platform: - amd64 - arm64 - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' && needs.check.outputs.SHOULD_BUILD_SNAP != 'no' && vars.DISABLE_INSIDER_SNAP != 'yes' + if: needs.check.outputs.SHOULD_BUILD_SNAP != 'no' && vars.DISABLE_INSIDER_SNAP != 'yes' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -500,7 +493,6 @@ jobs: runs-on: ubuntu-latest environment: publish permissions: {} - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' steps: - name: Trigger repository rebuild diff --git a/.github/workflows/publish-insider-macos.yml b/.github/workflows/publish-insider-macos.yml index 91154142d44..9df872c9ca1 100644 --- a/.github/workflows/publish-insider-macos.yml +++ b/.github/workflows/publish-insider-macos.yml @@ -58,7 +58,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: . check_tags.sh - if: env.SHOULD_DEPLOY == 'yes' - name: Build env: @@ -74,21 +73,21 @@ jobs: CERTIFICATE_OSX_P12_PASSWORD: ${{ secrets.CERTIFICATE_OSX_NEW_P12_PASSWORD }} CERTIFICATE_OSX_TEAM_ID: ${{ secrets.CERTIFICATE_OSX_NEW_TEAM_ID }} run: ./prepare_assets.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Release env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Update versions repo env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Clean up keychain if: always() diff --git a/.github/workflows/publish-insider-windows.yml b/.github/workflows/publish-insider-windows.yml index 1b695cfd6f2..ef5faf8ee47 100644 --- a/.github/workflows/publish-insider-windows.yml +++ b/.github/workflows/publish-insider-windows.yml @@ -26,7 +26,6 @@ jobs: MS_TAG: ${{ env.MS_TAG }} RELEASE_VERSION: ${{ env.RELEASE_VERSION }} SHOULD_BUILD: ${{ env.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -103,7 +102,7 @@ jobs: with: name: vscode path: ./vscode.tar.gz - retention-days: ${{ needs.check.outputs.SHOULD_DEPLOY == 'yes' && 30 || 1 }} + retention-days: 30 if: env.SHOULD_BUILD == 'yes' build: @@ -129,11 +128,9 @@ jobs: MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} outputs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} - SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -177,7 +174,7 @@ jobs: - name: Prepare assets run: ./prepare_assets.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Upload unsigned artifacts id: upload-unsigned-artifacts @@ -188,7 +185,7 @@ jobs: assets/*.exe assets/*.msi retention-days: 1 - if: env.SHOULD_BUILD == 'yes' && vars.DISABLE_INSIDER_WINDOWS_SIGNING != 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' && vars.DISABLE_INSIDER_WINDOWS_SIGNING != 'yes' - name: Signing uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2.2 @@ -203,25 +200,25 @@ jobs: # 3h to manually approve the request wait-for-completion-timeout-in-seconds: 28800 output-artifact-directory: assets/ - if: env.SHOULD_BUILD == 'yes' && vars.DISABLE_INSIDER_WINDOWS_SIGNING != 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' && vars.DISABLE_INSIDER_WINDOWS_SIGNING != 'yes' - name: Prepare checksums run: ./prepare_checksums.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Release env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Update versions repo env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' winget: needs: build @@ -235,7 +232,6 @@ jobs: env: APP_IDENTIFIER: VSCodium.VSCodium.Insiders ASSETS_REPOSITORY: vscodium-insiders - if: needs.build.outputs.SHOULD_DEPLOY == 'yes' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/publish-stable-linux.yml b/.github/workflows/publish-stable-linux.yml index a371b3449ae..ab18a63a6f1 100644 --- a/.github/workflows/publish-stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -28,7 +28,6 @@ jobs: MS_TAG: ${{ env.MS_TAG }} RELEASE_VERSION: ${{ env.RELEASE_VERSION }} SHOULD_BUILD: ${{ env.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -114,7 +113,7 @@ jobs: with: name: vscode path: ./vscode.tar.gz - retention-days: ${{ needs.check.outputs.SHOULD_DEPLOY == 'yes' && 30 || 1 }} + retention-days: 30 if: env.SHOULD_BUILD == 'yes' build: @@ -162,12 +161,10 @@ jobs: MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} outputs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} SHOULD_BUILD: ${{ env.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -178,7 +175,7 @@ jobs: - name: Install GH run: ./build/linux/install_gh.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Check existing VSCodium tags/releases env: @@ -217,21 +214,21 @@ jobs: VSCODE_SYSROOT_VERSION: ${{ steps.build.outputs.VSCODE_SYSROOT_VERSION }} VSCODE_SYSROOT_PREFIX: ${{ steps.build.outputs.VSCODE_SYSROOT_PREFIX }} run: ./prepare_assets.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Release env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Update versions repo env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' reh_linux: needs: @@ -273,7 +270,6 @@ jobs: MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} if: needs.check.outputs.SHOULD_BUILD == 'yes' @@ -309,7 +305,7 @@ jobs: - name: Install GH run: ./build/linux/install_gh.sh - if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.DISABLED != 'yes' - name: Check existing VSCodium tags/releases env: @@ -336,7 +332,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh - if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') reh_alpine: needs: @@ -364,7 +360,6 @@ jobs: OS_NAME: alpine RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} if: needs.check.outputs.SHOULD_BUILD == 'yes' @@ -387,7 +382,6 @@ jobs: - name: Install GH run: ./build/linux/install_gh.sh - if: env.SHOULD_DEPLOY == 'yes' - name: Check existing VSCodium tags/releases env: @@ -417,7 +411,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./release.sh - if: env.DISABLED != 'yes' && env.SHOULD_DEPLOY == 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') + if: env.DISABLED != 'yes' && (env.SHOULD_BUILD_REH != 'no' || env.SHOULD_BUILD_REH_WEB != 'no') aur: needs: @@ -432,7 +426,6 @@ jobs: include: - package_name: vscodium package_type: stable - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' steps: - name: Publish ${{ matrix.package_name }} @@ -461,7 +454,7 @@ jobs: platform: - amd64 - arm64 - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' && needs.check.outputs.SHOULD_BUILD_SNAP != 'no' && vars.DISABLE_STABLE_SNAP != 'yes' + if: needs.check.outputs.SHOULD_BUILD_SNAP != 'no' && vars.DISABLE_STABLE_SNAP != 'yes' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -478,14 +471,12 @@ jobs: run: ./stores/snapcraft/check_version.sh - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 - if: env.SHOULD_BUILD == 'yes' - uses: diddlesnaps/snapcraft-multiarch-action@cfd7a246fad6bea65bb92f69a1c8d07898c231e5 # v1.9.0 with: path: stores/snapcraft/stable architecture: ${{ matrix.platform }} id: build - if: env.SHOULD_BUILD == 'yes' - uses: diddlesnaps/snapcraft-review-action@40554b42331cf84dab19ef98c382620427f13482 # v1.3.1 with: @@ -516,7 +507,6 @@ jobs: runs-on: ubuntu-latest environment: publish permissions: {} - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' steps: - name: Trigger repository rebuild @@ -531,7 +521,6 @@ jobs: runs-on: ubuntu-latest environment: publish permissions: {} - if: needs.check.outputs.SHOULD_DEPLOY == 'yes' steps: - name: Trigger repository rebuild diff --git a/.github/workflows/publish-stable-macos.yml b/.github/workflows/publish-stable-macos.yml index 0fdacaa3da1..8126cec0bd2 100644 --- a/.github/workflows/publish-stable-macos.yml +++ b/.github/workflows/publish-stable-macos.yml @@ -58,7 +58,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: . check_tags.sh - if: env.SHOULD_DEPLOY == 'yes' - name: Build env: @@ -74,20 +73,20 @@ jobs: CERTIFICATE_OSX_P12_PASSWORD: ${{ secrets.CERTIFICATE_OSX_NEW_P12_PASSWORD }} CERTIFICATE_OSX_TEAM_ID: ${{ secrets.CERTIFICATE_OSX_NEW_TEAM_ID }} run: ./prepare_assets.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ./release.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Update versions repo env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Clean up keychain if: always() diff --git a/.github/workflows/publish-stable-windows.yml b/.github/workflows/publish-stable-windows.yml index 966ceab48c5..971516e6c9a 100644 --- a/.github/workflows/publish-stable-windows.yml +++ b/.github/workflows/publish-stable-windows.yml @@ -26,7 +26,6 @@ jobs: MS_TAG: ${{ env.MS_TAG }} RELEASE_VERSION: ${{ env.RELEASE_VERSION }} SHOULD_BUILD: ${{ env.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -103,7 +102,7 @@ jobs: with: name: vscode path: ./vscode.tar.gz - retention-days: ${{ needs.check.outputs.SHOULD_DEPLOY == 'yes' && 30 || 1 }} + retention-days: 30 if: env.SHOULD_BUILD == 'yes' build: @@ -129,11 +128,9 @@ jobs: MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} SHOULD_BUILD: ${{ needs.check.outputs.SHOULD_BUILD }} - SHOULD_DEPLOY: ${{ needs.check.outputs.SHOULD_DEPLOY }} VSCODE_ARCH: ${{ matrix.vscode_arch }} outputs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} - SHOULD_DEPLOY: ${{ env.SHOULD_DEPLOY }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -177,7 +174,7 @@ jobs: - name: Prepare assets run: ./prepare_assets.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Upload unsigned artifacts id: upload-unsigned-artifacts @@ -188,7 +185,7 @@ jobs: assets/*.exe assets/*.msi retention-days: 1 - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Signing uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2.2 @@ -203,24 +200,24 @@ jobs: # 8h to manually approve the request wait-for-completion-timeout-in-seconds: 28800 output-artifact-directory: assets/ - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Prepare checksums run: ./prepare_checksums.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ./release.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' - name: Update versions repo env: GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.repository_owner }} run: ./update_version.sh - if: env.SHOULD_BUILD == 'yes' && env.SHOULD_DEPLOY == 'yes' + if: env.SHOULD_BUILD == 'yes' winget: needs: build @@ -233,7 +230,6 @@ jobs: shell: bash env: APP_IDENTIFIER: VSCodium.VSCodium - if: needs.build.outputs.SHOULD_DEPLOY == 'yes' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 From 18fac11bcf11e1be974ab2074196b826bbf405f6 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 19 Apr 2026 09:09:49 +0200 Subject: [PATCH 18/79] ci: re-set 180 days-before-stale --- .github/workflows/mod-stale-issue-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mod-stale-issue-pr.yml b/.github/workflows/mod-stale-issue-pr.yml index a2bb6b530aa..c8d8ac02abf 100644 --- a/.github/workflows/mod-stale-issue-pr.yml +++ b/.github/workflows/mod-stale-issue-pr.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 with: - days-before-stale: 90 + days-before-stale: 180 days-before-close: 30 operations-per-run: 1024 stale-issue-message: This issue has been automatically marked as stale. **If this issue is still affecting you, please leave any comment**, and we'll keep it open. If you have any new additional information, please include it with your comment! From caebfcd98691f6b1d05d01b44e8b80da72a121b8 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Wed, 22 Apr 2026 01:54:37 +0200 Subject: [PATCH 19/79] 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 | 169 +- 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, 6018 insertions(+), 599 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 85e502778f6..32a2d7bd80d 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.22.0 +22.22.1 diff --git a/build.sh b/build.sh index bd78e0f157b..84a75bc1711 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 9258eae7c73..8db1c93e9b9 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 4afa57f8518..a7204c14703 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 9417bf35284..23adf9e88f6 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 7e81ed88dfd..4e2d4caff9b 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 9211b7fdae4..293bbfc97c4 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.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.hidden.negate(), +@@ -385,2 +387,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(), + 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 2352c04383d..2b68450a0fa 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 00000000000..62cf4a41c08 --- /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 33a96a5f23b..8a7fce4c7e3 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 00000000000..8843a668ef1 --- /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 8e1d9654206..a67374162c4 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 f2fadf67667..35bf3732c2a 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 1f95eb5b905..ec3712d76cd 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 f7479d157f3..3aafb3a6645 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 8cb15a32c66..48b9c078be5 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 28042b81739..af2df0f8456 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 60e992de821..b3cdf4afd8d 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 1761c7f24aa..eac05593e58 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 0706b1615f3..1bdd0111382 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 7b0d84b78bd..0909ccb60e9 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" } From 148fdb46290db3ee01d597a2773463371d10beb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 14:59:00 +0200 Subject: [PATCH 20/79] build(deps): bump zizmorcore/zizmor-action from 0.5.2 to 0.5.3 (#2798) --- .github/workflows/lint-zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-zizmor.yml b/.github/workflows/lint-zizmor.yml index d26de7cc0e5..a9c484a431c 100644 --- a/.github/workflows/lint-zizmor.yml +++ b/.github/workflows/lint-zizmor.yml @@ -27,4 +27,4 @@ jobs: persist-credentials: false - name: Run zizmor - uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 + uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 From 7ff99c23b54cfc017e536d6e2199067efd4a3cb3 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Wed, 22 Apr 2026 16:05:34 +0200 Subject: [PATCH 21/79] refactor: patches (#2797) --- dev/patch.sh | 60 +++++++++++++--- dev/update_patches.sh | 70 ++++++++++++++----- ...ry-name.patch => 00-binary-fix-name.patch} | 0 ...d.patch => 00-brand-remove-branding.patch} | 0 ...d.patch => 00-build-disable-esbuild.patch} | 0 ...le.patch => 00-build-disable-mangle.patch} | 0 ...0-build-download-extensions-from-gh.patch} | 0 ...atch => 00-build-fix-npm-preinstall.patch} | 0 ...h.no => 00-build-update-electron.patch.no} | 0 ...ch => 00-build-update-sourcemap-url.patch} | 0 ...able-cloud.patch => 00-cloud-remove.patch} | 0 ...h => 00-community-add-announcements.patch} | 0 ...00-copilot-disable-terminal-suggest.patch} | 0 ... => 00-copilot-fix-action-condition.patch} | 0 ...0-ext-github-authentication-use-pat.patch} | 0 ...h => 00-ext-github-remove-vscodedev.patch} | 0 ...sion-disable-signature-verification.patch} | 0 ... 00-remote-add-missing-dependencies.patch} | 0 ...mote-url.patch => 00-remote-add-url.patch} | 0 ...ch => 00-remote-remove-missing-vsda.patch} | 0 ...h => 00-security-add-command-filter.patch} | 0 ...allery.patch => 00-settings-gallery.patch} | 0 ...t.patch => 00-settings-user-product.patch} | 0 ...metry.patch => 00-telemetry-disable.patch} | 0 ...=> 00-tunnel-disable-recommendation.patch} | 0 ...tal-font.patch => 00-ui-custom-font.patch} | 0 ...r.patch => 00-ui-improve-eol-banner.patch} | 0 ...t-issue.patch => 00-ui-report-issue.patch} | 0 ....patch.yet => 00-update-disable.patch.yet} | 0 ...atch => 00-update-rename-cache-path.patch} | 0 ...sce.patch => 00-vsce-use-custom-lib.patch} | 0 ...ase.patch => 10-version-add-release.patch} | 0 ...tch => 11-update-use-github-release.patch} | 0 ...p.patch => 20-keymap-use-custom-lib.patch} | 0 ...s.patch => 21-policy-use-custom-lib.patch} | 0 ...> 30-build-add-missing-dependencies.patch} | 0 ...cli.patch => 40-cli-use-reh-archive.patch} | 0 ...atch => 50-build-improve-gulp-tasks.patch} | 0 ...t.patch => 51-ext-copilot-remove-it.patch} | 0 ...curity-add-option-for-malicious-ext.patch} | 0 ...ch => 61-extension-close-connection.patch} | 0 ...ode-docker.patch => 00-build-docker.patch} | 0 ...m.patch => 00-asset-fix-rpm-version.patch} | 0 ...tall.patch => 00-build-improve-qemu.patch} | 0 .../{cli.patch => 00-cli-fix-unused.patch} | 0 ...th.patch => 00-env-improve-xdg-path.patch} | 0 ...home.patch => 00-log-use-state-home.patch} | 0 ....patch => 00-policy-use-custom-path.patch} | 0 ...ap.patch => 00-remote-fix-bootstrap.patch} | 0 ....patch => 31-build-fix-dependencies.patch} | 0 ...upport.patch => 41-arch-add-support.patch} | 0 ...pc64le.patch => 42-arch-add-ppc64le.patch} | 0 ...iscv64.patch => 43-arch-add-riscv64.patch} | 0 ...oong64.patch => 44-arch-add-loong64.patch} | 0 ...-4-s390x.patch => 45-arch-add-s390x.patch} | 0 ...te.patch => 00-build-disable-remote.patch} | 0 ... => 00-system-support-16k-page-size.patch} | 0 ...tch => 00-build-override-dependency.patch} | 0 ...patch => 00-ui-update-emulated-urls.patch} | 0 patches/osx/fix-codesign.patch.no | 30 -------- ...appx.patch => 00-build-disable-appx.patch} | 0 ....patch => 00-build-min-version-win7.patch} | 0 ...{cli.patch => 41-cli-fix-update-url.patch} | 0 prepare_vscode.sh | 2 +- 64 files changed, 107 insertions(+), 55 deletions(-) rename patches/{binary-name.patch => 00-binary-fix-name.patch} (100%) rename patches/{brand.patch => 00-brand-remove-branding.patch} (100%) rename patches/{fix-disable-esbuild.patch => 00-build-disable-esbuild.patch} (100%) rename patches/{remove-mangle.patch => 00-build-disable-mangle.patch} (100%) rename patches/{ext-from-gh.patch => 00-build-download-extensions-from-gh.patch} (100%) rename patches/{fix-npm-preinstall.patch => 00-build-fix-npm-preinstall.patch} (100%) rename patches/{update-electron.patch.no => 00-build-update-electron.patch.no} (100%) rename patches/{sourcemaps.patch => 00-build-update-sourcemap-url.patch} (100%) rename patches/{disable-cloud.patch => 00-cloud-remove.patch} (100%) rename patches/{feat-announcements.patch => 00-community-add-announcements.patch} (100%) rename patches/{terminal-suggest.patch => 00-copilot-disable-terminal-suggest.patch} (100%) rename patches/{disable-copilot.patch => 00-copilot-fix-action-condition.patch} (100%) rename patches/{use-github-pat.patch => 00-ext-github-authentication-use-pat.patch} (100%) rename patches/{disable-vscodedev.patch => 00-ext-github-remove-vscodedev.patch} (100%) rename patches/{disable-signature-verification.patch => 00-extension-disable-signature-verification.patch} (100%) rename patches/{fix-remote-libs.patch => 00-remote-add-missing-dependencies.patch} (100%) rename patches/{add-remote-url.patch => 00-remote-add-url.patch} (100%) rename patches/{disable-missing-vsda.patch => 00-remote-remove-missing-vsda.patch} (100%) rename patches/{feat-command-filter.patch => 00-security-add-command-filter.patch} (100%) rename patches/{fix-gallery.patch => 00-settings-gallery.patch} (100%) rename patches/{feat-user-product.patch => 00-settings-user-product.patch} (100%) rename patches/{telemetry.patch => 00-telemetry-disable.patch} (100%) rename patches/{fix-tunnel-extension-recommendation.patch => 00-tunnel-disable-recommendation.patch} (100%) rename patches/{feat-experimental-font.patch => 00-ui-custom-font.patch} (100%) rename patches/{fix-eol-banner.patch => 00-ui-improve-eol-banner.patch} (100%) rename patches/{report-issue.patch => 00-ui-report-issue.patch} (100%) rename patches/{disable-update.patch.yet => 00-update-disable.patch.yet} (100%) rename patches/{update-cache-path.patch => 00-update-rename-cache-path.patch} (100%) rename patches/{fix-build-vsce.patch => 00-vsce-use-custom-lib.patch} (100%) rename patches/{version-0-release.patch => 10-version-add-release.patch} (100%) rename patches/{version-1-update.patch => 11-update-use-github-release.patch} (100%) rename patches/{fix-keymap.patch => 20-keymap-use-custom-lib.patch} (100%) rename patches/{fix-policies.patch => 21-policy-use-custom-lib.patch} (100%) rename patches/{optional-tree-sitter.patch => 30-build-add-missing-dependencies.patch} (100%) rename patches/{cli.patch => 40-cli-use-reh-archive.patch} (100%) rename patches/{feat-gulp-build-tasks.patch => 50-build-improve-gulp-tasks.patch} (100%) rename patches/{feat-remove-copilot.patch => 51-ext-copilot-remove-it.patch} (100%) rename patches/{feat-ext-unsafe.patch => 60-security-add-option-for-malicious-ext.patch} (100%) rename patches/{fix-extensions-control-connection.patch => 61-extension-close-connection.patch} (100%) rename patches/alpine/reh/{fix-node-docker.patch => 00-build-docker.patch} (100%) rename patches/linux/{rpm.patch => 00-asset-fix-rpm-version.patch} (100%) rename patches/linux/{fix-npm-postinstall.patch => 00-build-improve-qemu.patch} (100%) rename patches/linux/{cli.patch => 00-cli-fix-unused.patch} (100%) rename patches/linux/{update-xdg-path.patch => 00-env-improve-xdg-path.patch} (100%) rename patches/linux/{feat-logs-home.patch => 00-log-use-state-home.patch} (100%) rename patches/linux/{fix-global-policy.patch => 00-policy-use-custom-path.patch} (100%) rename patches/linux/{fix-reh-bootstrap.patch => 00-remote-fix-bootstrap.patch} (100%) rename patches/linux/{fix-build.patch => 31-build-fix-dependencies.patch} (100%) rename patches/linux/{arch-0-support.patch => 41-arch-add-support.patch} (100%) rename patches/linux/{arch-1-ppc64le.patch => 42-arch-add-ppc64le.patch} (100%) rename patches/linux/{arch-2-riscv64.patch => 43-arch-add-riscv64.patch} (100%) rename patches/linux/{arch-3-loong64.patch => 44-arch-add-loong64.patch} (100%) rename patches/linux/{arch-4-s390x.patch => 45-arch-add-s390x.patch} (100%) rename patches/linux/client/{disable-remote.patch => 00-build-disable-remote.patch} (100%) rename patches/linux/client/{avoid-crash-16k-page-size.patch => 00-system-support-16k-page-size.patch} (100%) rename patches/linux/reh/s390x/{package.json.patch => 00-build-override-dependency.patch} (100%) rename patches/osx/{fix-emulated-urls.patch => 00-ui-update-emulated-urls.patch} (100%) delete mode 100644 patches/osx/fix-codesign.patch.no rename patches/windows/{appx.patch => 00-build-disable-appx.patch} (100%) rename patches/windows/{win7.patch => 00-build-min-version-win7.patch} (100%) rename patches/windows/{cli.patch => 41-cli-fix-update-url.patch} (100%) diff --git a/dev/patch.sh b/dev/patch.sh index 63d5bf5f3e7..8ce692fbd92 100755 --- a/dev/patch.sh +++ b/dev/patch.sh @@ -30,19 +30,63 @@ normalize_file "${1}" if [[ "${FILE}" != "../patches/helper/settings.patch" ]]; then git apply --reject "../patches/helper/settings.patch" - while [ $# -gt 1 ]; do - echo "Parameter: $1" + if [[ $# -gt 1 ]]; then + while [ $# -gt 1 ]; do + echo "Parameter: $1" + normalize_file "${1}" + + git apply --reject "${FILE}" + + shift + done + + git add . + git commit --no-verify -q -m "VSCODIUM HELPER" + normalize_file "${1}" + else + normalize_file "${1}" + + BASENAME=$(basename "${FILE}") + DIRNAME=$(dirname "${FILE}") - git apply --reject "${FILE}" + if [[ "${BASENAME}" =~ ^([0-9])([1-9])(-.*)\.patch$ ]]; then + GROUP_ID="${BASH_REMATCH[1]}" + INDEX="${BASH_REMATCH[2]}" + ENDNAME="${BASH_REMATCH[3]}" - shift - done + for ((I = 0; I < INDEX; I++)); do + NOT_FOUND=1 - git add . - git commit --no-verify -q -m "VSCODIUM HELPER" + for CANDIDATE in "${DIRNAME}/${GROUP_ID}${I}-"*.patch; do + if [[ -f "$CANDIDATE" ]]; then + echo "Candidate: ${CANDIDATE}" + normalize_file "${CANDIDATE}" - normalize_file "${1}" + git apply --reject "${FILE}" + + NOT_FOUND=0 + fi + done + + if (( $NOT_FOUND )); then + for CANDIDATE in "${DIRNAME}/../${GROUP_ID}${I}-"*.patch; do + if [[ -f "$CANDIDATE" ]]; then + echo "Candidate: ${CANDIDATE}" + normalize_file "${CANDIDATE}" + + git apply --reject "${FILE}" + fi + done + fi + done + + git add . + git commit --no-verify -q -m "VSCODIUM HELPER" + + normalize_file "${1}" + fi + fi fi echo "FILE: ${FILE}" diff --git a/dev/update_patches.sh b/dev/update_patches.sh index 8fcbf8e5cf5..32f5e5bb176 100755 --- a/dev/update_patches.sh +++ b/dev/update_patches.sh @@ -119,6 +119,7 @@ check_file() { fi while [[ -n "$( find . -name '*.rej' -print )" ]]; do + echo "patch: ${1}" find . -name '*.rej' -print read -rp "Press any key when the conflict have been resolved..." -n1 -s echo @@ -144,8 +145,26 @@ while [[ -n "$( git log -1 | grep "VSCODIUM HELPER" )" ]]; do done for FILE in ../patches/*.patch; do - if [[ "${FILE}" == *"/fix-policies.patch" ]]; then - check_file "../patches/fix-keymap.patch" "../patches/fix-policies.patch" + ADDITIONAL_FILES=() + BASENAME=$(basename "${FILE}") + DIRNAME=$(dirname "${FILE}") + + if [[ "${BASENAME}" =~ ^([0-9])([1-9])(-.*)\.patch$ ]]; then + GROUP_ID="${BASH_REMATCH[1]}" + INDEX="${BASH_REMATCH[2]}" + ENDNAME="${BASH_REMATCH[3]}" + + for ((I = 0; I < INDEX; I++)); do + for CANDIDATE in "${DIRNAME}/${GROUP_ID}${I}-"*.patch; do + if [[ -f "$CANDIDATE" ]]; then + ADDITIONAL_FILES+=("$CANDIDATE") + fi + done + done + fi + + if [[ ${#ADDITIONAL_FILES[@]} -gt 0 ]]; then + check_file ${ADDITIONAL_FILES[@]} "${FILE}" else check_file "${FILE}" fi @@ -159,25 +178,44 @@ fi for ARCH in alpine linux osx windows; do for FILE in "../patches/${ARCH}/"*.patch; do - if [[ "${ARCH}" == "linux" && "${FILE}" == *"/arch-"* ]] || [[ "${ARCH}" == "linux" && "${FILE}" == *"/fix-dependencies.patch" ]] || [[ "${ARCH}" == "windows" && "${FILE}" == *"/cli"* ]]; then - echo "skip ${FILE}" + ADDITIONAL_FILES=() + BASENAME=$(basename "${FILE}") + DIRNAME=$(dirname "${FILE}") + + if [[ "${BASENAME}" =~ ^([0-9])([1-9])(-.*)\.patch$ ]]; then + GROUP_ID="${BASH_REMATCH[1]}" + INDEX="${BASH_REMATCH[2]}" + ENDNAME="${BASH_REMATCH[3]}" + + for ((I = 0; I < INDEX; I++)); do + NOT_FOUND=1 + + for CANDIDATE in "${DIRNAME}/${GROUP_ID}${I}-"*.patch; do + if [[ -f "$CANDIDATE" ]]; then + ADDITIONAL_FILES+=("$CANDIDATE") + NOT_FOUND=0 + fi + done + + if (( $NOT_FOUND )); then + for CANDIDATE in "${DIRNAME}/../${GROUP_ID}${I}-"*.patch; do + if [[ -f "$CANDIDATE" ]]; then + ADDITIONAL_FILES+=("$CANDIDATE") + fi + done + fi + done + + if [[ ${#ADDITIONAL_FILES[@]} -gt 0 ]]; then + check_file ${ADDITIONAL_FILES[@]} "${FILE}" + else + check_file "${FILE}" + fi else check_file "${FILE}" fi done - if [[ "${ARCH}" == "linux" ]]; then - check_file "../patches/optional-tree-sitter.patch" "../patches/linux/fix-dependencies.patch" - - check_file "../patches/cli.patch" "../patches/linux/arch-0-support.patch" - check_file "../patches/cli.patch" "../patches/linux/arch-0-support.patch" "../patches/linux/arch-1-ppc64le.patch" - check_file "../patches/cli.patch" "../patches/linux/arch-0-support.patch" "../patches/linux/arch-1-ppc64le.patch" "../patches/linux/arch-2-riscv64.patch" - check_file "../patches/cli.patch" "../patches/linux/arch-0-support.patch" "../patches/linux/arch-1-ppc64le.patch" "../patches/linux/arch-2-riscv64.patch" "../patches/linux/arch-3-loong64.patch" - check_file "../patches/cli.patch" "../patches/linux/arch-0-support.patch" "../patches/linux/arch-1-ppc64le.patch" "../patches/linux/arch-2-riscv64.patch" "../patches/linux/arch-3-loong64.patch" "../patches/linux/arch-4-s390x.patch" - elif [[ "${ARCH}" == "windows" ]]; then - check_file "../patches/cli.patch" "../patches/windows/cli.patch" - fi - for TARGET in client reh; do for FILE in "../patches/${ARCH}/${TARGET}/"*.patch; do check_file "${FILE}" diff --git a/patches/binary-name.patch b/patches/00-binary-fix-name.patch similarity index 100% rename from patches/binary-name.patch rename to patches/00-binary-fix-name.patch diff --git a/patches/brand.patch b/patches/00-brand-remove-branding.patch similarity index 100% rename from patches/brand.patch rename to patches/00-brand-remove-branding.patch diff --git a/patches/fix-disable-esbuild.patch b/patches/00-build-disable-esbuild.patch similarity index 100% rename from patches/fix-disable-esbuild.patch rename to patches/00-build-disable-esbuild.patch diff --git a/patches/remove-mangle.patch b/patches/00-build-disable-mangle.patch similarity index 100% rename from patches/remove-mangle.patch rename to patches/00-build-disable-mangle.patch diff --git a/patches/ext-from-gh.patch b/patches/00-build-download-extensions-from-gh.patch similarity index 100% rename from patches/ext-from-gh.patch rename to patches/00-build-download-extensions-from-gh.patch diff --git a/patches/fix-npm-preinstall.patch b/patches/00-build-fix-npm-preinstall.patch similarity index 100% rename from patches/fix-npm-preinstall.patch rename to patches/00-build-fix-npm-preinstall.patch diff --git a/patches/update-electron.patch.no b/patches/00-build-update-electron.patch.no similarity index 100% rename from patches/update-electron.patch.no rename to patches/00-build-update-electron.patch.no diff --git a/patches/sourcemaps.patch b/patches/00-build-update-sourcemap-url.patch similarity index 100% rename from patches/sourcemaps.patch rename to patches/00-build-update-sourcemap-url.patch diff --git a/patches/disable-cloud.patch b/patches/00-cloud-remove.patch similarity index 100% rename from patches/disable-cloud.patch rename to patches/00-cloud-remove.patch diff --git a/patches/feat-announcements.patch b/patches/00-community-add-announcements.patch similarity index 100% rename from patches/feat-announcements.patch rename to patches/00-community-add-announcements.patch diff --git a/patches/terminal-suggest.patch b/patches/00-copilot-disable-terminal-suggest.patch similarity index 100% rename from patches/terminal-suggest.patch rename to patches/00-copilot-disable-terminal-suggest.patch diff --git a/patches/disable-copilot.patch b/patches/00-copilot-fix-action-condition.patch similarity index 100% rename from patches/disable-copilot.patch rename to patches/00-copilot-fix-action-condition.patch diff --git a/patches/use-github-pat.patch b/patches/00-ext-github-authentication-use-pat.patch similarity index 100% rename from patches/use-github-pat.patch rename to patches/00-ext-github-authentication-use-pat.patch diff --git a/patches/disable-vscodedev.patch b/patches/00-ext-github-remove-vscodedev.patch similarity index 100% rename from patches/disable-vscodedev.patch rename to patches/00-ext-github-remove-vscodedev.patch diff --git a/patches/disable-signature-verification.patch b/patches/00-extension-disable-signature-verification.patch similarity index 100% rename from patches/disable-signature-verification.patch rename to patches/00-extension-disable-signature-verification.patch diff --git a/patches/fix-remote-libs.patch b/patches/00-remote-add-missing-dependencies.patch similarity index 100% rename from patches/fix-remote-libs.patch rename to patches/00-remote-add-missing-dependencies.patch diff --git a/patches/add-remote-url.patch b/patches/00-remote-add-url.patch similarity index 100% rename from patches/add-remote-url.patch rename to patches/00-remote-add-url.patch diff --git a/patches/disable-missing-vsda.patch b/patches/00-remote-remove-missing-vsda.patch similarity index 100% rename from patches/disable-missing-vsda.patch rename to patches/00-remote-remove-missing-vsda.patch diff --git a/patches/feat-command-filter.patch b/patches/00-security-add-command-filter.patch similarity index 100% rename from patches/feat-command-filter.patch rename to patches/00-security-add-command-filter.patch diff --git a/patches/fix-gallery.patch b/patches/00-settings-gallery.patch similarity index 100% rename from patches/fix-gallery.patch rename to patches/00-settings-gallery.patch diff --git a/patches/feat-user-product.patch b/patches/00-settings-user-product.patch similarity index 100% rename from patches/feat-user-product.patch rename to patches/00-settings-user-product.patch diff --git a/patches/telemetry.patch b/patches/00-telemetry-disable.patch similarity index 100% rename from patches/telemetry.patch rename to patches/00-telemetry-disable.patch diff --git a/patches/fix-tunnel-extension-recommendation.patch b/patches/00-tunnel-disable-recommendation.patch similarity index 100% rename from patches/fix-tunnel-extension-recommendation.patch rename to patches/00-tunnel-disable-recommendation.patch diff --git a/patches/feat-experimental-font.patch b/patches/00-ui-custom-font.patch similarity index 100% rename from patches/feat-experimental-font.patch rename to patches/00-ui-custom-font.patch diff --git a/patches/fix-eol-banner.patch b/patches/00-ui-improve-eol-banner.patch similarity index 100% rename from patches/fix-eol-banner.patch rename to patches/00-ui-improve-eol-banner.patch diff --git a/patches/report-issue.patch b/patches/00-ui-report-issue.patch similarity index 100% rename from patches/report-issue.patch rename to patches/00-ui-report-issue.patch diff --git a/patches/disable-update.patch.yet b/patches/00-update-disable.patch.yet similarity index 100% rename from patches/disable-update.patch.yet rename to patches/00-update-disable.patch.yet diff --git a/patches/update-cache-path.patch b/patches/00-update-rename-cache-path.patch similarity index 100% rename from patches/update-cache-path.patch rename to patches/00-update-rename-cache-path.patch diff --git a/patches/fix-build-vsce.patch b/patches/00-vsce-use-custom-lib.patch similarity index 100% rename from patches/fix-build-vsce.patch rename to patches/00-vsce-use-custom-lib.patch diff --git a/patches/version-0-release.patch b/patches/10-version-add-release.patch similarity index 100% rename from patches/version-0-release.patch rename to patches/10-version-add-release.patch diff --git a/patches/version-1-update.patch b/patches/11-update-use-github-release.patch similarity index 100% rename from patches/version-1-update.patch rename to patches/11-update-use-github-release.patch diff --git a/patches/fix-keymap.patch b/patches/20-keymap-use-custom-lib.patch similarity index 100% rename from patches/fix-keymap.patch rename to patches/20-keymap-use-custom-lib.patch diff --git a/patches/fix-policies.patch b/patches/21-policy-use-custom-lib.patch similarity index 100% rename from patches/fix-policies.patch rename to patches/21-policy-use-custom-lib.patch diff --git a/patches/optional-tree-sitter.patch b/patches/30-build-add-missing-dependencies.patch similarity index 100% rename from patches/optional-tree-sitter.patch rename to patches/30-build-add-missing-dependencies.patch diff --git a/patches/cli.patch b/patches/40-cli-use-reh-archive.patch similarity index 100% rename from patches/cli.patch rename to patches/40-cli-use-reh-archive.patch diff --git a/patches/feat-gulp-build-tasks.patch b/patches/50-build-improve-gulp-tasks.patch similarity index 100% rename from patches/feat-gulp-build-tasks.patch rename to patches/50-build-improve-gulp-tasks.patch diff --git a/patches/feat-remove-copilot.patch b/patches/51-ext-copilot-remove-it.patch similarity index 100% rename from patches/feat-remove-copilot.patch rename to patches/51-ext-copilot-remove-it.patch diff --git a/patches/feat-ext-unsafe.patch b/patches/60-security-add-option-for-malicious-ext.patch similarity index 100% rename from patches/feat-ext-unsafe.patch rename to patches/60-security-add-option-for-malicious-ext.patch diff --git a/patches/fix-extensions-control-connection.patch b/patches/61-extension-close-connection.patch similarity index 100% rename from patches/fix-extensions-control-connection.patch rename to patches/61-extension-close-connection.patch diff --git a/patches/alpine/reh/fix-node-docker.patch b/patches/alpine/reh/00-build-docker.patch similarity index 100% rename from patches/alpine/reh/fix-node-docker.patch rename to patches/alpine/reh/00-build-docker.patch diff --git a/patches/linux/rpm.patch b/patches/linux/00-asset-fix-rpm-version.patch similarity index 100% rename from patches/linux/rpm.patch rename to patches/linux/00-asset-fix-rpm-version.patch diff --git a/patches/linux/fix-npm-postinstall.patch b/patches/linux/00-build-improve-qemu.patch similarity index 100% rename from patches/linux/fix-npm-postinstall.patch rename to patches/linux/00-build-improve-qemu.patch diff --git a/patches/linux/cli.patch b/patches/linux/00-cli-fix-unused.patch similarity index 100% rename from patches/linux/cli.patch rename to patches/linux/00-cli-fix-unused.patch diff --git a/patches/linux/update-xdg-path.patch b/patches/linux/00-env-improve-xdg-path.patch similarity index 100% rename from patches/linux/update-xdg-path.patch rename to patches/linux/00-env-improve-xdg-path.patch diff --git a/patches/linux/feat-logs-home.patch b/patches/linux/00-log-use-state-home.patch similarity index 100% rename from patches/linux/feat-logs-home.patch rename to patches/linux/00-log-use-state-home.patch diff --git a/patches/linux/fix-global-policy.patch b/patches/linux/00-policy-use-custom-path.patch similarity index 100% rename from patches/linux/fix-global-policy.patch rename to patches/linux/00-policy-use-custom-path.patch diff --git a/patches/linux/fix-reh-bootstrap.patch b/patches/linux/00-remote-fix-bootstrap.patch similarity index 100% rename from patches/linux/fix-reh-bootstrap.patch rename to patches/linux/00-remote-fix-bootstrap.patch diff --git a/patches/linux/fix-build.patch b/patches/linux/31-build-fix-dependencies.patch similarity index 100% rename from patches/linux/fix-build.patch rename to patches/linux/31-build-fix-dependencies.patch diff --git a/patches/linux/arch-0-support.patch b/patches/linux/41-arch-add-support.patch similarity index 100% rename from patches/linux/arch-0-support.patch rename to patches/linux/41-arch-add-support.patch diff --git a/patches/linux/arch-1-ppc64le.patch b/patches/linux/42-arch-add-ppc64le.patch similarity index 100% rename from patches/linux/arch-1-ppc64le.patch rename to patches/linux/42-arch-add-ppc64le.patch diff --git a/patches/linux/arch-2-riscv64.patch b/patches/linux/43-arch-add-riscv64.patch similarity index 100% rename from patches/linux/arch-2-riscv64.patch rename to patches/linux/43-arch-add-riscv64.patch diff --git a/patches/linux/arch-3-loong64.patch b/patches/linux/44-arch-add-loong64.patch similarity index 100% rename from patches/linux/arch-3-loong64.patch rename to patches/linux/44-arch-add-loong64.patch diff --git a/patches/linux/arch-4-s390x.patch b/patches/linux/45-arch-add-s390x.patch similarity index 100% rename from patches/linux/arch-4-s390x.patch rename to patches/linux/45-arch-add-s390x.patch diff --git a/patches/linux/client/disable-remote.patch b/patches/linux/client/00-build-disable-remote.patch similarity index 100% rename from patches/linux/client/disable-remote.patch rename to patches/linux/client/00-build-disable-remote.patch diff --git a/patches/linux/client/avoid-crash-16k-page-size.patch b/patches/linux/client/00-system-support-16k-page-size.patch similarity index 100% rename from patches/linux/client/avoid-crash-16k-page-size.patch rename to patches/linux/client/00-system-support-16k-page-size.patch diff --git a/patches/linux/reh/s390x/package.json.patch b/patches/linux/reh/s390x/00-build-override-dependency.patch similarity index 100% rename from patches/linux/reh/s390x/package.json.patch rename to patches/linux/reh/s390x/00-build-override-dependency.patch diff --git a/patches/osx/fix-emulated-urls.patch b/patches/osx/00-ui-update-emulated-urls.patch similarity index 100% rename from patches/osx/fix-emulated-urls.patch rename to patches/osx/00-ui-update-emulated-urls.patch diff --git a/patches/osx/fix-codesign.patch.no b/patches/osx/fix-codesign.patch.no deleted file mode 100644 index f5fdd145dc1..00000000000 --- a/patches/osx/fix-codesign.patch.no +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/build/darwin/sign.js b/build/darwin/sign.js -index dff30fd..df48bee 100644 ---- a/build/darwin/sign.js -+++ b/build/darwin/sign.js -@@ -56,5 +56,7 @@ async function main(buildDir) { - ignore: (filePath) => { -+ const ext = path_1.default.extname(filePath); - return filePath.includes(gpuHelperAppName) || - filePath.includes(rendererHelperAppName) || -- filePath.includes(pluginHelperAppName); -+ filePath.includes(pluginHelperAppName) || -+ ext == '.asar' || ext == '.dat' || ext == '.gif' || ext == '.icns' || ext == '.ico' || ext == '.json' || ext == '.mp3' || ext == '.nib' || ext == '.pak' || ext == '.png' || ext == '.scpt' || ext == '.ttf' || ext == '.wasm' || ext == '.woff' || ext == '.woff2'; - } -diff --git a/build/darwin/sign.ts b/build/darwin/sign.ts -index ecf1627..a414032 100644 ---- a/build/darwin/sign.ts -+++ b/build/darwin/sign.ts -@@ -60,6 +60,9 @@ async function main(buildDir?: string): Promise { - ignore: (filePath: string) => { -- return filePath.includes(gpuHelperAppName) || -- filePath.includes(rendererHelperAppName) || -- filePath.includes(pluginHelperAppName); -+ const ext = path.extname(filePath); -+ return filePath.includes(gpuHelperAppName) || -+ filePath.includes(rendererHelperAppName) || -+ filePath.includes(pluginHelperAppName) || -+ ext == '.asar' || ext == '.dat' || ext == '.gif' || ext == '.icns' || ext == '.ico' || ext == '.json' || ext == '.mp3' || ext == '.nib' || ext == '.pak' || ext == '.png' || ext == '.scpt' || ext == '.ttf' || ext == '.wasm' || ext == '.woff' || ext == '.woff2'; - } -+ - }; diff --git a/patches/windows/appx.patch b/patches/windows/00-build-disable-appx.patch similarity index 100% rename from patches/windows/appx.patch rename to patches/windows/00-build-disable-appx.patch diff --git a/patches/windows/win7.patch b/patches/windows/00-build-min-version-win7.patch similarity index 100% rename from patches/windows/win7.patch rename to patches/windows/00-build-min-version-win7.patch diff --git a/patches/windows/cli.patch b/patches/windows/41-cli-fix-update-url.patch similarity index 100% rename from patches/windows/cli.patch rename to patches/windows/41-cli-fix-update-url.patch diff --git a/prepare_vscode.sh b/prepare_vscode.sh index eac05593e58..6dc2973e3e4 100755 --- a/prepare_vscode.sh +++ b/prepare_vscode.sh @@ -146,7 +146,7 @@ echo "ORG_NAME=\"${ORG_NAME}\"" echo "TUNNEL_APP_NAME=\"${TUNNEL_APP_NAME}\"" if [[ "${DISABLE_UPDATE}" == "yes" ]]; then - mv ../patches/disable-update.patch.yet ../patches/disable-update.patch + mv ../patches/00-update-disable.patch.yet ../patches/00-update-disable.patch fi for file in ../patches/*.patch; do From f1c093d6ec462eb66ad69f4c3bd78cb6c25675a6 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Wed, 22 Apr 2026 19:02:12 +0200 Subject: [PATCH 22/79] feat: add cooldown setting to update (#2799) --- patches/12-update-add-cooldown.patch | 52 ++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 patches/12-update-add-cooldown.patch diff --git a/patches/12-update-add-cooldown.patch b/patches/12-update-add-cooldown.patch new file mode 100644 index 00000000000..c52a18a69da --- /dev/null +++ b/patches/12-update-add-cooldown.patch @@ -0,0 +1,52 @@ +diff --git a/src/vs/platform/update/common/update.config.contribution.ts b/src/vs/platform/update/common/update.config.contribution.ts +index 6061e15b..84246033 100644 +--- a/src/vs/platform/update/common/update.config.contribution.ts ++++ b/src/vs/platform/update/common/update.config.contribution.ts +@@ -65,2 +65,8 @@ configurationRegistry.registerConfiguration({ + }, ++ 'update.minReleaseAge': { ++ type: 'integer', ++ default: 7, ++ scope: ConfigurationScope.APPLICATION, ++ description: localize('update.cooldown', "Configure how old an update need to be before installing it (in days)."), ++ }, + 'update.enableWindowsBackgroundUpdates': { +diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts +index 3767c907..9b3c17bf 100644 +--- a/src/vs/platform/update/electron-main/abstractUpdateService.ts ++++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts +@@ -470,3 +470,3 @@ export abstract class AbstractUpdateService implements IUpdateService { + +- this.logService.info('update#isLatestVersion() - found version', fetchedVersion, currentVersion); ++ this.logService.info(`update#isLatestVersion() - found: ${fetchedVersion}, current: ${currentVersion}`); + +@@ -474,3 +474,28 @@ export abstract class AbstractUpdateService implements IUpdateService { + +- return Promise.resolve({ lastest, update }); ++ const minReleaseAge = this.configurationService.getValue('update.minReleaseAge'); ++ ++ if(minReleaseAge === 0) { ++ return Promise.resolve({ lastest, update }); ++ } ++ ++ this.logService.info(`update#isLatestVersion() - ${update.timestamp} ${typeof update.timestamp}`); ++ ++ const releaseDate = update.timestamp ? new Date(Number.parseInt(String(update.timestamp), 10)) : null; ++ ++ this.logService.info(`update#isLatestVersion() - releaseDate: ${releaseDate}`); ++ ++ if(!releaseDate || isNaN(releaseDate.getTime())) { ++ return Promise.resolve(undefined); ++ } ++ ++ const age = Math.round(Math.abs(Date.now() - releaseDate.getTime()) / (1000 * 60 * 60 * 24)); ++ ++ this.logService.info(`update#isLatestVersion() - releaseAge: ${age}, minReleaseAge: ${minReleaseAge}`); ++ ++ if(age >= minReleaseAge) { ++ return Promise.resolve({ lastest, update }); ++ } ++ else { ++ return Promise.resolve(undefined); ++ } + }) From cfc61411834944c5f413f972fef84cfc6f61982d Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 25 Apr 2026 18:00:14 +0200 Subject: [PATCH 23/79] docs: replace old url with archived one (#2800) --- docs/extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extensions.md b/docs/extensions.md index 930ff8d603b..6a34b75fddd 100644 --- a/docs/extensions.md +++ b/docs/extensions.md @@ -28,7 +28,7 @@ By default, the `product.json` file is set up to use [open-vsx.org](https://open ## How to use the Open VSX Registry As noted above, the [Open VSX Registry](https://open-vsx.org/) is the pre-set extension gallery in VSCodium. Using the extension view in VSCodium will therefore by default use it. -See [this article](https://www.gitpod.io/blog/open-vsx/) for more information on the motivation behind Open VSX. +See [this article](https://web.archive.org/web/20200423131829/https://www.gitpod.io/blog/open-vsx/) for more information on the motivation behind Open VSX. ## How to use a different extension gallery From e34f5aa1fd0f9a63f131de65f797d063adfd0778 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 25 Apr 2026 18:48:04 +0200 Subject: [PATCH 24/79] build: exclude patch helper when working on patch --- dev/patch.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/patch.sh b/dev/patch.sh index 8ce692fbd92..e6965c33ef0 100755 --- a/dev/patch.sh +++ b/dev/patch.sh @@ -80,12 +80,12 @@ if [[ "${FILE}" != "../patches/helper/settings.patch" ]]; then done fi done + fi - git add . - git commit --no-verify -q -m "VSCODIUM HELPER" + git add . + git commit --no-verify -q -m "VSCODIUM HELPER" - normalize_file "${1}" - fi + normalize_file "${1}" fi fi From 5e5b2ffb2e7524d437c610a4abdd0ef42aba95c9 Mon Sep 17 00:00:00 2001 From: timkenhan <33104842+timkenhan@users.noreply.github.com> Date: Sun, 26 Apr 2026 00:20:04 +0700 Subject: [PATCH 25/79] enhance(linux): external terminal launch command in shell (#2790) --- patches/linux/00-terminal-cmd-in-shell.patch | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 patches/linux/00-terminal-cmd-in-shell.patch diff --git a/patches/linux/00-terminal-cmd-in-shell.patch b/patches/linux/00-terminal-cmd-in-shell.patch new file mode 100644 index 00000000000..9bf4bc56cc6 --- /dev/null +++ b/patches/linux/00-terminal-cmd-in-shell.patch @@ -0,0 +1,13 @@ +diff --git a/src/vs/platform/externalTerminal/node/externalTerminalService.ts b/src/vs/platform/externalTerminal/node/externalTerminalService.ts +index e7cf3f54588..80075787abf 100644 +--- a/src/vs/platform/externalTerminal/node/externalTerminalService.ts ++++ b/src/vs/platform/externalTerminal/node/externalTerminalService.ts +@@ -327,7 +327,7 @@ export class LinuxExternalTerminalService extends ExternalTerminalService implem + const env = getSanitizedEnvironment(process); + const basename = path.basename(exec).toLowerCase(); + const args = basename === 'ghostty' && cwd ? [`--working-directory=${cwd}`] : []; +- const child = spawner.spawn(exec, args, { cwd, env }); ++ const child = spawner.spawn(exec, args, { cwd, env, shell: true }); + child.on('error', e); + child.on('exit', () => c()); + }); From e78f14dea2836e3262b2b2f171c7c03f9aa7a1fd Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 26 Apr 2026 12:26:21 +0200 Subject: [PATCH 26/79] build(font-size): update and lock dependencies (#2803) --- font-size/.artifactrc.yml | 5 +- font-size/.editorconfig | 5 + font-size/.fixpackrc | 10 +- font-size/.nvmrc | 2 +- font-size/.tazerc.json | 6 + font-size/.xo-config.json | 25 ++- font-size/package-lock.json | 403 +++++++++++++++++++++++++++++++++--- font-size/package.json | 23 +- 8 files changed, 436 insertions(+), 43 deletions(-) create mode 100644 font-size/.tazerc.json diff --git a/font-size/.artifactrc.yml b/font-size/.artifactrc.yml index 007a8d3be61..b2069690dc5 100644 --- a/font-size/.artifactrc.yml +++ b/font-size/.artifactrc.yml @@ -1,7 +1,8 @@ +$schema: https://raw.githubusercontent.com/zokugun/artifact/v0.6.0/schemas/v1/install.json artifacts: "@daiyam/artifact-lang-js": - version: 0.9.3 + version: 0.11.3 requires: - "22" "@daiyam/artifact-lang-ts": - version: 0.6.3 + version: 0.8.0 diff --git a/font-size/.editorconfig b/font-size/.editorconfig index 255114eb70e..3d01fcbfe94 100644 --- a/font-size/.editorconfig +++ b/font-size/.editorconfig @@ -15,3 +15,8 @@ indent_size = 2 [*.md] indent_style = space indent_size = 4 + +[*.patch] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false diff --git a/font-size/.fixpackrc b/font-size/.fixpackrc index b871313d2ef..0707a38391d 100644 --- a/font-size/.fixpackrc +++ b/font-size/.fixpackrc @@ -1,9 +1,14 @@ { + "finalNewLine": true, + "required": [ + "dependencies" + ], "sortToTop": [ "name", "displayName", "description", "version", + "since", "private", "author", "publisher", @@ -23,7 +28,8 @@ "dependencies", "devDependencies", "optionalDependencies", + "peerDependencies", + "overrides", "keywords" - ], - "finalNewLine": true + ] } diff --git a/font-size/.nvmrc b/font-size/.nvmrc index c6a66a6e6a6..a3745049aad 100644 --- a/font-size/.nvmrc +++ b/font-size/.nvmrc @@ -1 +1 @@ -v22.21.1 +v22.22.2 diff --git a/font-size/.tazerc.json b/font-size/.tazerc.json new file mode 100644 index 00000000000..52b7a03b709 --- /dev/null +++ b/font-size/.tazerc.json @@ -0,0 +1,6 @@ +{ + "force": true, + "includeLocked": true, + "maturityPeriod": 7, + "write": true +} diff --git a/font-size/.xo-config.json b/font-size/.xo-config.json index 7c4c8c89360..e2f442786f9 100644 --- a/font-size/.xo-config.json +++ b/font-size/.xo-config.json @@ -48,8 +48,14 @@ "error", { "selector": "variable", - "modifiers": ["const", "global"], - "format": ["camelCase", "UPPER_CASE"], + "modifiers": [ + "const", + "global" + ], + "format": [ + "camelCase", + "UPPER_CASE" + ], "filter": { "regex": "^(__filename|__dirname)$", "match": false @@ -57,7 +63,9 @@ }, { "selector": "variable", - "format": ["camelCase"], + "format": [ + "camelCase" + ], "filter": { "regex": "^(__filename|__dirname)$", "match": false @@ -66,6 +74,14 @@ ], "@typescript-eslint/no-confusing-void-expression": "off", "@typescript-eslint/no-dynamic-delete": "off", + "@typescript-eslint/no-empty-function": [ + "error", + { + "allow": [ + "arrowFunctions" + ] + } + ], "@typescript-eslint/no-inferrable-types": "off", "@typescript-eslint/no-namespace": "off", "@typescript-eslint/object-curly-spacing": [ @@ -82,7 +98,7 @@ "capitalized-comments": "off", "complexity": "off", "default-case": "off", - "import/extensions": [ + "import/extensions": [ "error", "never" ], @@ -117,6 +133,7 @@ "no-lonely-if": "off", "no-negated-condition": "off", "object-curly-newline": "off", + "object-shorthand": "off", "one-var": [ "error", "never" diff --git a/font-size/package-lock.json b/font-size/package-lock.json index cf7f5031815..097f1ad72cf 100644 --- a/font-size/package-lock.json +++ b/font-size/package-lock.json @@ -9,17 +9,45 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@zokugun/fs-extra-plus": "^0.3.3", - "@zokugun/xtry": "^0.10.1", - "fast-glob": "^3.3.3", - "postcss": "^8.5.6" + "@zokugun/fs-extra-plus": "0.3.8", + "@zokugun/xtry": "0.10.1", + "fast-glob": "3.3.3", + "postcss": "8.5.10" }, "devDependencies": { - "@types/node": "^22.9.0", - "fixpack": "^4.0.0", + "@types/node": "22.19.17", + "fixpack": "4.0.0", + "taze": "19.11.0", + "typescript": "5.9.3", "xo": "0.60.0" } }, + "node_modules/@antfu/ni": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@antfu/ni/-/ni-30.1.0.tgz", + "integrity": "sha512-3VuAbPjgY52rQNn4wABaXMhBU2Oq91uy6L8nX49eJ35OLI68CyckGU+HZxcaHix4ymuGM2nFL1D6sLpgODK5xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fzf": "^0.5.2", + "package-manager-detector": "^1.6.0", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15" + }, + "bin": { + "na": "bin/na.mjs", + "nci": "bin/nci.mjs", + "nd": "bin/nd.mjs", + "ni": "bin/ni.mjs", + "nlx": "bin/nlx.mjs", + "nr": "bin/nr.mjs", + "nun": "bin/nun.mjs", + "nup": "bin/nup.mjs" + }, + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -145,6 +173,13 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@henrygd/queue": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@henrygd/queue/-/queue-1.2.0.tgz", + "integrity": "sha512-jW/BLSTpcvExDhqJGxtIPgGr2O0IFF8XUNDwEbfCfhrXT8a4xztQ9Lv6U/vbYzYC0xVWn+3zv6YnLUh3bEFUKA==", + "dev": true, + "license": "MIT" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -286,6 +321,19 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@quansync/fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@quansync/fs/-/fs-1.0.0.tgz", + "integrity": "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -358,9 +406,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", - "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", + "version": "22.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", + "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -527,9 +575,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "dev": true, "license": "MIT", "dependencies": { @@ -824,22 +872,28 @@ "peer": true }, "node_modules/@zokugun/fs-extra-plus": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@zokugun/fs-extra-plus/-/fs-extra-plus-0.3.3.tgz", - "integrity": "sha512-GzLdcuSttjzcWL3dkdoxKPm9MZVVOVv5q2tWjCn4KUlIu27FBTx3pyvZMmpJf8u6ZNw0bqafWVYYppBiFqJ23g==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@zokugun/fs-extra-plus/-/fs-extra-plus-0.3.8.tgz", + "integrity": "sha512-EijO0TJdupzTQN8a2ZsoWZNG7PuuKsyuYwr0XJDpgfWCVofuabxLSJPRiT5uzdTrTpsyEC4tk4RTXp9A5ZPeXg==", "license": "MIT", "dependencies": { - "@zokugun/is-it-type": "^0.5.2", - "@zokugun/xtry": "^0.10.1" + "@zokugun/is-it-type": "0.7.0", + "@zokugun/xtry": "0.11.4" }, "optionalDependencies": { "mime-types": "*" } }, + "node_modules/@zokugun/fs-extra-plus/node_modules/@zokugun/xtry": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/@zokugun/xtry/-/xtry-0.11.4.tgz", + "integrity": "sha512-Skkb/roP7cLWOo2uei66C63B39FeGqaaR/HU68at46X6ObTvYVMvvqEDr2AhmETVQF74wIOxSTMY6NPPhOCzEA==", + "license": "MIT" + }, "node_modules/@zokugun/is-it-type": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@zokugun/is-it-type/-/is-it-type-0.5.2.tgz", - "integrity": "sha512-Gj6H+nvhwJt4Q5UeIntwRQY4/JTs9ZVaJ/Ac2na3LUdN//dgSyH4gZrXzGqzDu31ZyaEySppvF+frN7L2Rg7Pg==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@zokugun/is-it-type/-/is-it-type-0.7.0.tgz", + "integrity": "sha512-6r7JJKo9+KkLw4aOIFkAAI5ZpcRXlGPogjENwbzqPOk37WREUKLpBTfxMxmS/dCBELAvyvBWrOatm0clEsHjdA==", "license": "MIT" }, "node_modules/@zokugun/xtry": { @@ -1162,9 +1216,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -1255,6 +1309,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cac": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cac/-/cac-7.0.0.tgz", + "integrity": "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -1664,6 +1728,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true, + "license": "MIT" + }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -3480,6 +3558,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fzf": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fzf/-/fzf-0.5.2.tgz", + "integrity": "sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", @@ -4671,6 +4756,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4959,6 +5054,19 @@ "node": ">= 0.6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -5061,6 +5169,13 @@ "semver": "bin/semver.js" } }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "dev": true, + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -5278,6 +5393,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5288,6 +5415,22 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/open": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", @@ -5404,6 +5547,13 @@ "node": ">=6" } }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "dev": true, + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5499,6 +5649,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5644,6 +5801,26 @@ "node": ">=4" } }, + "node_modules/pnpm-workspace-yaml": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pnpm-workspace-yaml/-/pnpm-workspace-yaml-1.6.0.tgz", + "integrity": "sha512-uUy4dK3E11sp7nK+hnT7uAWfkBMe00KaUw8OG3NuNlYQoTk4sc9pcdIy1+XIP85v9Tvr02mK3JPaNNrP0QyRaw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT", + "dependencies": { + "yaml": "^2.8.2" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -5655,9 +5832,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", "funding": [ { "type": "opencollective", @@ -5757,6 +5934,23 @@ "node": ">=6" } }, + "node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6051,6 +6245,23 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -6649,6 +6860,34 @@ "node": ">=0.6" } }, + "node_modules/taze": { + "version": "19.11.0", + "resolved": "https://registry.npmjs.org/taze/-/taze-19.11.0.tgz", + "integrity": "sha512-BlfH8Z6JdoIsrUptnz4P4YuEqdYsa/bSNNDOMhTlsHZ7Bbg1/0NyYh6uPkoRREjrt/kVovV+HYdi1ilHxvChfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/ni": "^30.0.0", + "@henrygd/queue": "^1.2.0", + "cac": "^7.0.0", + "find-up-simple": "^1.0.1", + "ofetch": "^1.5.1", + "package-manager-detector": "^1.6.0", + "pathe": "^2.0.3", + "pnpm-workspace-yaml": "^1.6.0", + "restore-cursor": "^5.1.0", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15", + "unconfig": "^7.5.0", + "yaml": "^2.8.3" + }, + "bin": { + "taze": "bin/taze.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/terser": { "version": "5.46.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", @@ -6719,6 +6958,64 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyexec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", + "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-absolute-glob": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-3.0.0.tgz", @@ -6925,6 +7222,13 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true, + "license": "MIT" + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -6954,6 +7258,37 @@ "node": ">=0.10.0" } }, + "node_modules/unconfig": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/unconfig/-/unconfig-7.5.0.tgz", + "integrity": "sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "defu": "^6.1.4", + "jiti": "^2.6.1", + "quansync": "^1.0.0", + "unconfig-core": "7.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unconfig-core": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/unconfig-core/-/unconfig-core-7.5.0.tgz", + "integrity": "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -7361,6 +7696,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yocto-queue": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", diff --git a/font-size/package.json b/font-size/package.json index 2f3a8e686f3..4e223b9fa3d 100644 --- a/font-size/package.json +++ b/font-size/package.json @@ -10,20 +10,27 @@ "type": "module", "main": "generate-css.ts", "scripts": { - "clean": "rimraf lib", + "audit:fix": "npm audit fix --min-release-age=0", + "ci:lint": "zizmor .", + "ci:lint:fix": "zizmor . --fix=all", "lint": "xo", + "lint:all": "fixpack && npm audit && npm run ci:lint && npm run lint", "lint:fix": "xo --fix", - "prepare": "fixpack || true" + "prepare": "fixpack || true", + "update:artifacts": "artifact update", + "update:deps": "taze" }, "dependencies": { - "@zokugun/fs-extra-plus": "^0.3.3", - "@zokugun/xtry": "^0.10.1", - "fast-glob": "^3.3.3", - "postcss": "^8.5.6" + "@zokugun/fs-extra-plus": "0.3.8", + "@zokugun/xtry": "0.10.1", + "fast-glob": "3.3.3", + "postcss": "8.5.10" }, "devDependencies": { - "@types/node": "^22.9.0", - "fixpack": "^4.0.0", + "@types/node": "22.19.17", + "fixpack": "4.0.0", + "taze": "19.11.0", + "typescript": "5.9.3", "xo": "0.60.0" }, "keywords": [] From e60052e2834afe1839b4d018ce83e539f2eab0f9 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 26 Apr 2026 12:26:50 +0200 Subject: [PATCH 27/79] build(win32): appx contextmenu (#2801) --- .github/workflows/ci-build-windows.yml | 2 + build/windows/appx/build.sh | 4 +- build/windows/package.sh | 2 + build/windows/prepare_assets.sh | 4 +- patches/00-brand-remove-branding.patch | 83 ++++++++++++++++++- .../windows/00-build-contextmenu-appx.patch | 37 +++++++++ ...x.patch => 00-build-disable-appx.patch.no} | 0 7 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 patches/windows/00-build-contextmenu-appx.patch rename patches/windows/{00-build-disable-appx.patch => 00-build-disable-appx.patch.no} (100%) diff --git a/.github/workflows/ci-build-windows.yml b/.github/workflows/ci-build-windows.yml index b620a5a16cf..c49eb3f9976 100644 --- a/.github/workflows/ci-build-windows.yml +++ b/.github/workflows/ci-build-windows.yml @@ -109,6 +109,8 @@ jobs: MS_COMMIT: ${{ needs.check.outputs.MS_COMMIT }} MS_TAG: ${{ needs.check.outputs.MS_TAG }} RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} + SHOULD_BUILD_REH: no + SHOULD_BUILD_REH_WEB: no VSCODE_ARCH: ${{ matrix.vscode_arch }} outputs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} diff --git a/build/windows/appx/build.sh b/build/windows/appx/build.sh index 1cd450f3742..19a75156f89 100755 --- a/build/windows/appx/build.sh +++ b/build/windows/appx/build.sh @@ -8,7 +8,7 @@ export PATH="${SDK}:${PATH}" APPX_NAME="${BINARY_NAME//-/_}" -makeappx pack /d "../../../VSCode-win32-${VSCODE_ARCH}/appx/manifest" /p "../../../VSCode-win32-${VSCODE_ARCH}/appx/${APPX_NAME}_${VSCODE_ARCH}.appx" /nv +powershell "makeappx pack /d ..\\VSCode-win32-${VSCODE_ARCH}\\appx\\manifest /p ..\\VSCode-win32-${VSCODE_ARCH}\\appx\\${APPX_NAME}_${VSCODE_ARCH}.appx /nv" # Remove the raw manifest folder -rm -rf "../../../VSCode-win32-${VSCODE_ARCH}/appx/manifest" +rm -rf "../VSCode-win32-${VSCODE_ARCH}/appx/manifest" diff --git a/build/windows/package.sh b/build/windows/package.sh index a7204c14703..9e051ae7eda 100755 --- a/build/windows/package.sh +++ b/build/windows/package.sh @@ -31,6 +31,8 @@ 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 +node build/win32/explorer-dll-fetcher.ts .build/win32/appx + npm run gulp "vscode-win32-${VSCODE_ARCH}-min-packing" . ../build_cli.sh diff --git a/build/windows/prepare_assets.sh b/build/windows/prepare_assets.sh index 346c220a12e..1423005c66b 100644 --- a/build/windows/prepare_assets.sh +++ b/build/windows/prepare_assets.sh @@ -4,12 +4,12 @@ cd vscode || { echo "'vscode' dir not found"; exit 1; } npm run gulp "vscode-win32-${VSCODE_ARCH}-inno-updater" +. ../build/windows/appx/build.sh + if [[ "${SHOULD_BUILD_ZIP}" != "no" ]]; then 7z.exe a -tzip "../assets/${APP_NAME}-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" -x!CodeSignSummary*.md -x!tools "../VSCode-win32-${VSCODE_ARCH}/*" -r fi -# . ../build/windows/appx/build.sh - if [[ "${SHOULD_BUILD_EXE_SYS}" != "no" ]]; then npm run gulp "vscode-win32-${VSCODE_ARCH}-system-setup" fi diff --git a/patches/00-brand-remove-branding.patch b/patches/00-brand-remove-branding.patch index 23adf9e88f6..e0b960b6fb9 100644 --- a/patches/00-brand-remove-branding.patch +++ b/patches/00-brand-remove-branding.patch @@ -1,3 +1,41 @@ +diff --git a/build/win32/i18n/messages.en.isl b/build/win32/i18n/messages.en.isl +index a5cc5820..57b03db1 100644 +--- a/build/win32/i18n/messages.en.isl ++++ b/build/win32/i18n/messages.en.isl +@@ -16,2 +16,2 @@ SourceFile=%1 Source File + OpenWithCodeContextMenu=Open w&ith %1 +-UpdatingVisualStudioCode=Updating Visual Studio Code... ++UpdatingVisualStudioCode=Updating !!APP_NAME!!... +diff --git a/build/win32/i18n/messages.es.isl b/build/win32/i18n/messages.es.isl +index 66b7534a..4d4d360e 100644 +--- a/build/win32/i18n/messages.es.isl ++++ b/build/win32/i18n/messages.es.isl +@@ -9,2 +9,2 @@ SourceFile=Archivo de origen %1 + OpenWithCodeContextMenu=Abrir &con %1 +-UpdatingVisualStudioCode=Actualizando Visual Studio Code... +\ No newline at end of file ++UpdatingVisualStudioCode=Actualizando !!APP_NAME!!... +\ No newline at end of file +diff --git a/build/win32/i18n/messages.it.isl b/build/win32/i18n/messages.it.isl +index bc238258..a94d91cb 100644 +--- a/build/win32/i18n/messages.it.isl ++++ b/build/win32/i18n/messages.it.isl +@@ -9,2 +9,2 @@ SourceFile=File di origine %1 + OpenWithCodeContextMenu=Apri con %1 +-UpdatingVisualStudioCode=Aggiornamento di Visual Studio Code... +\ No newline at end of file ++UpdatingVisualStudioCode=Aggiornamento di !!APP_NAME!!... +\ No newline at end of file +diff --git a/build/win32/i18n/messages.pt-br.isl b/build/win32/i18n/messages.pt-br.isl +index e85aede3..76eda20e 100644 +--- a/build/win32/i18n/messages.pt-br.isl ++++ b/build/win32/i18n/messages.pt-br.isl +@@ -9,2 +9,2 @@ SourceFile=Arquivo Fonte %1 + OpenWithCodeContextMenu=Abrir com %1 +-UpdatingVisualStudioCode=Atualizando o Visual Studio Code... +\ No newline at end of file ++UpdatingVisualStudioCode=Atualizando o !!APP_NAME!!... +\ No newline at end of file diff --git a/extensions/configuration-editing/src/configurationEditingMain.ts b/extensions/configuration-editing/src/configurationEditingMain.ts index 2578270c..99c8ca5b 100644 --- a/extensions/configuration-editing/src/configurationEditingMain.ts @@ -651,9 +689,14 @@ index e13d1ba5..7c411624 100644 + 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 58ff362b..9bf7ed90 100644 +index 58ff362b..54815f3c 100644 --- a/src/vs/server/node/server.cli.ts +++ b/src/vs/server/node/server.cli.ts +@@ -93,3 +93,3 @@ export async function main(desc: ProductDescription, args: string[]): Promise v instanceof TestItemImpl, extHostTypeConverter.TestItem.from); + 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/node/loopbackServer.ts b/src/vs/workbench/api/node/loopbackServer.ts +index 1d32a486..ae5c4c7e 100644 +--- a/src/vs/workbench/api/node/loopbackServer.ts ++++ b/src/vs/workbench/api/node/loopbackServer.ts +@@ -190,7 +190,2 @@ export class LoopbackAuthServer implements ILoopbackServer { + let backgroundImage = 'url(\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAQABAMAAACNMzawAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAJ1BMVEUAAAD///9Qm8+ozed8tNsWer+Lvd9trNfF3u9Ck8slgsMzi8eZxeM/Qa6mAAAAAXRSTlMAQObYZgAAAAFiS0dEAf8CLd4AAAAHdElNRQfiCwYULRt0g+ZLAAAJRUlEQVR42u3SUY0CQRREUSy0hSZtBA+wfOwv4wAPYwAJSMAfAthkB6YD79HnKqikzmYjSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIk6cmKhg4AAASAABAAAkAACAABIAAEgAAQAAJAAAgAAaBvBVAjtV2wfFe1ogcA+0gdFgBoe60IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAjAP/1MkWoAvBvAsUTqBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAAgAASAABIAAEAACQAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKA7gDlbFwC6AijZagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQDM2boA0BXAqAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIATARAAAkAAvF7N1hWArgBKthoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfBrAlK0bAF0BjBoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4EQABIAAEwOtN2boB0BVAyVYDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgE8DqNm6AtAVwKgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAEwEQAAJAAPzd7xypMwDvBnAskToBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAIAAkAACAABIAAEgAAQAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAkAACAABIAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBKBGarsAwK5qRQ8ANGYAACAABIAAEAACQAAIAAEgAASAABAAAkDfCUCSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJC3uDtO80OSql+i8AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE4LTExLTA2VDIwOjQ1OjI3KzAwOjAwEjLurQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOC0xMS0wNlQyMDo0NToyNyswMDowMGNvVhEAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAAAAElFTkSuQmCC\')'; +- if (this._appName === 'Visual Studio Code') { +- backgroundImage = 'url(\'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjU2IiBoZWlnaHQ9IjI1NiIgdmlld0JveD0iMCAwIDI1NiAyNTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxtYXNrIGlkPSJtYXNrMCIgbWFzay10eXBlPSJhbHBoYSIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeD0iMCIgeT0iMCIgd2lkdGg9IjI1NiIgaGVpZ2h0PSIyNTYiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE4MS41MzQgMjU0LjI1MkMxODUuNTY2IDI1NS44MjMgMTkwLjE2NCAyNTUuNzIyIDE5NC4yMzQgMjUzLjc2NEwyNDYuOTQgMjI4LjQwM0MyNTIuNDc4IDIyNS43MzggMjU2IDIyMC4xMzIgMjU2IDIxMy45ODNWNDIuMDE4MUMyNTYgMzUuODY4OSAyNTIuNDc4IDMwLjI2MzggMjQ2Ljk0IDI3LjU5ODhMMTk0LjIzNCAyLjIzNjgxQzE4OC44OTMgLTAuMzMzMTMyIDE4Mi42NDIgMC4yOTYzNDQgMTc3Ljk1NSAzLjcwNDE4QzE3Ny4yODUgNC4xOTEgMTc2LjY0NyA0LjczNDU0IDE3Ni4wNDkgNS4zMzM1NEw3NS4xNDkgOTcuMzg2MkwzMS4xOTkyIDY0LjAyNDdDMjcuMTA3OSA2MC45MTkxIDIxLjM4NTMgNjEuMTczNSAxNy41ODU1IDY0LjYzTDMuNDg5MzYgNzcuNDUyNUMtMS4xNTg1MyA4MS42ODA1IC0xLjE2Mzg2IDg4Ljk5MjYgMy40Nzc4NSA5My4yMjc0TDQxLjU5MjYgMTI4TDMuNDc3ODUgMTYyLjc3M0MtMS4xNjM4NiAxNjcuMDA4IC0xLjE1ODUzIDE3NC4zMiAzLjQ4OTM2IDE3OC41NDhMMTcuNTg1NSAxOTEuMzdDMjEuMzg1MyAxOTQuODI3IDI3LjEwNzkgMTk1LjA4MSAzMS4xOTkyIDE5MS45NzZMNzUuMTQ5IDE1OC42MTRMMTc2LjA0OSAyNTAuNjY3QzE3Ny42NDUgMjUyLjI2NCAxNzkuNTE5IDI1My40NjcgMTgxLjUzNCAyNTQuMjUyWk0xOTIuMDM5IDY5Ljg4NTNMMTE1LjQ3OSAxMjhMMTkyLjAzOSAxODYuMTE1VjY5Ljg4NTNaIiBmaWxsPSJ3aGl0ZSIvPgo8L21hc2s+CjxnIG1hc2s9InVybCgjbWFzazApIj4KPHBhdGggZD0iTTI0Ni45NCAyNy42MzgzTDE5NC4xOTMgMi4yNDEzOEMxODguMDg4IC0wLjY5ODMwMiAxODAuNzkxIDAuNTQxNzIxIDE3NS45OTkgNS4zMzMzMkwzLjMyMzcxIDE2Mi43NzNDLTEuMzIwODIgMTY3LjAwOCAtMS4zMTU0OCAxNzQuMzIgMy4zMzUyMyAxNzguNTQ4TDE3LjQzOTkgMTkxLjM3QzIxLjI0MjEgMTk0LjgyNyAyNi45NjgyIDE5NS4wODEgMzEuMDYxOSAxOTEuOTc2TDIzOS4wMDMgMzQuMjI2OUMyNDUuOTc5IDI4LjkzNDcgMjU1Ljk5OSAzMy45MTAzIDI1NS45OTkgNDIuNjY2N1Y0Mi4wNTQzQzI1NS45OTkgMzUuOTA3OCAyNTIuNDc4IDMwLjMwNDcgMjQ2Ljk0IDI3LjYzODNaIiBmaWxsPSIjMDA2NUE5Ii8+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2QpIj4KPHBhdGggZD0iTTI0Ni45NCAyMjguMzYyTDE5NC4xOTMgMjUzLjc1OUMxODguMDg4IDI1Ni42OTggMTgwLjc5MSAyNTUuNDU4IDE3NS45OTkgMjUwLjY2N0wzLjMyMzcxIDkzLjIyNzJDLTEuMzIwODIgODguOTkyNSAtMS4zMTU0OCA4MS42ODAyIDMuMzM1MjMgNzcuNDUyM0wxNy40Mzk5IDY0LjYyOThDMjEuMjQyMSA2MS4xNzMzIDI2Ljk2ODIgNjAuOTE4OCAzMS4wNjE5IDY0LjAyNDVMMjM5LjAwMyAyMjEuNzczQzI0NS45NzkgMjI3LjA2NSAyNTUuOTk5IDIyMi4wOSAyNTUuOTk5IDIxMy4zMzNWMjEzLjk0NkMyNTUuOTk5IDIyMC4wOTIgMjUyLjQ3OCAyMjUuNjk1IDI0Ni45NCAyMjguMzYyWiIgZmlsbD0iIzAwN0FDQyIvPgo8L2c+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIxX2QpIj4KPHBhdGggZD0iTTE5NC4xOTYgMjUzLjc2M0MxODguMDg5IDI1Ni43IDE4MC43OTIgMjU1LjQ1OSAxNzYgMjUwLjY2N0MxODEuOTA0IDI1Ni41NzEgMTkyIDI1Mi4zODkgMTkyIDI0NC4wMzlWMTEuOTYwNkMxOTIgMy42MTA1NyAxODEuOTA0IC0wLjU3MTE3NSAxNzYgNS4zMzMyMUMxODAuNzkyIDAuNTQxMTY2IDE4OC4wODkgLTAuNzAwNjA3IDE5NC4xOTYgMi4yMzY0OEwyNDYuOTM0IDI3LjU5ODVDMjUyLjQ3NiAzMC4yNjM1IDI1NiAzNS44Njg2IDI1NiA0Mi4wMTc4VjIxMy45ODNDMjU2IDIyMC4xMzIgMjUyLjQ3NiAyMjUuNzM3IDI0Ni45MzQgMjI4LjQwMkwxOTQuMTk2IDI1My43NjNaIiBmaWxsPSIjMUY5Q0YwIi8+CjwvZz4KPGcgc3R5bGU9Im1peC1ibGVuZC1tb2RlOm92ZXJsYXkiIG9wYWNpdHk9IjAuMjUiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE4MS4zNzggMjU0LjI1MkMxODUuNDEgMjU1LjgyMiAxOTAuMDA4IDI1NS43MjIgMTk0LjA3NyAyNTMuNzY0TDI0Ni43ODMgMjI4LjQwMkMyNTIuMzIyIDIyNS43MzcgMjU1Ljg0NCAyMjAuMTMyIDI1NS44NDQgMjEzLjk4M1Y0Mi4wMTc5QzI1NS44NDQgMzUuODY4NyAyNTIuMzIyIDMwLjI2MzYgMjQ2Ljc4NCAyNy41OTg2TDE5NC4wNzcgMi4yMzY2NUMxODguNzM3IC0wLjMzMzI5OSAxODIuNDg2IDAuMjk2MTc3IDE3Ny43OTggMy43MDQwMUMxNzcuMTI5IDQuMTkwODMgMTc2LjQ5MSA0LjczNDM3IDE3NS44OTIgNS4zMzMzN0w3NC45OTI3IDk3LjM4NkwzMS4wNDI5IDY0LjAyNDVDMjYuOTUxNyA2MC45MTg5IDIxLjIyOSA2MS4xNzM0IDE3LjQyOTIgNjQuNjI5OEwzLjMzMzExIDc3LjQ1MjNDLTEuMzE0NzggODEuNjgwMyAtMS4zMjAxMSA4OC45OTI1IDMuMzIxNiA5My4yMjczTDQxLjQzNjQgMTI4TDMuMzIxNiAxNjIuNzczQy0xLjMyMDExIDE2Ny4wMDggLTEuMzE0NzggMTc0LjMyIDMuMzMzMTEgMTc4LjU0OEwxNy40MjkyIDE5MS4zN0MyMS4yMjkgMTk0LjgyNyAyNi45NTE3IDE5NS4wODEgMzEuMDQyOSAxOTEuOTc2TDc0Ljk5MjcgMTU4LjYxNEwxNzUuODkyIDI1MC42NjdDMTc3LjQ4OCAyNTIuMjY0IDE3OS4zNjMgMjUzLjQ2NyAxODEuMzc4IDI1NC4yNTJaTTE5MS44ODMgNjkuODg1MUwxMTUuMzIzIDEyOEwxOTEuODgzIDE4Ni4xMTVWNjkuODg1MVoiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcikiLz4KPC9nPgo8L2c+CjxkZWZzPgo8ZmlsdGVyIGlkPSJmaWx0ZXIwX2QiIHg9Ii0yMS40ODk2IiB5PSI0MC41MjI1IiB3aWR0aD0iMjk4LjgyMiIgaGVpZ2h0PSIyMzYuMTQ5IiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+CjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIi8+CjxmZU9mZnNldC8+CjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjEwLjY2NjciLz4KPGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAuMjUgMCIvPgo8ZmVCbGVuZCBtb2RlPSJvdmVybGF5IiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3ciLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3ciIHJlc3VsdD0ic2hhcGUiLz4KPC9maWx0ZXI+CjxmaWx0ZXIgaWQ9ImZpbHRlcjFfZCIgeD0iMTU0LjY2NyIgeT0iLTIwLjY3MzUiIHdpZHRoPSIxMjIuNjY3IiBoZWlnaHQ9IjI5Ny4zNDciIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KPGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz4KPGZlQ29sb3JNYXRyaXggaW49IlNvdXJjZUFscGhhIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMTI3IDAiLz4KPGZlT2Zmc2V0Lz4KPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMTAuNjY2NyIvPgo8ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMC4yNSAwIi8+CjxmZUJsZW5kIG1vZGU9Im92ZXJsYXkiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvdyIvPgo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvdyIgcmVzdWx0PSJzaGFwZSIvPgo8L2ZpbHRlcj4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyIiB4MT0iMTI3Ljg0NCIgeTE9IjAuNjU5OTg4IiB4Mj0iMTI3Ljg0NCIgeTI9IjI1NS4zNCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSJ3aGl0ZSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IndoaXRlIiBzdG9wLW9wYWNpdHk9IjAiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K\')'; +- } else if (this._appName === 'Visual Studio Code - Insiders') { +- backgroundImage = 'url(\'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjU2IiBoZWlnaHQ9IjI1NiIgdmlld0JveD0iMCAwIDI1NiAyNTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxtYXNrIGlkPSJtYXNrMCIgbWFzay10eXBlPSJhbHBoYSIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeD0iMCIgeT0iMCIgd2lkdGg9IjI1NiIgaGVpZ2h0PSIyNTYiPgo8cGF0aCBkPSJNMTc2LjA0OSAyNTAuNjY5QzE4MC44MzggMjU1LjQ1OSAxODguMTMgMjU2LjcgMTk0LjIzNCAyNTMuNzY0TDI0Ni45NCAyMjguNDE5QzI1Mi40NzggMjI1Ljc1NSAyNTYgMjIwLjE1NCAyNTYgMjE0LjAwOFY0Mi4xNDc5QzI1NiAzNi4wMDI1IDI1Mi40NzggMzAuNDAwOCAyNDYuOTQgMjcuNzM3NEwxOTQuMjM0IDIuMzkwODlDMTg4LjEzIC0wLjU0NDQxNiAxODAuODM4IDAuNjk2NjA3IDE3Ni4wNDkgNS40ODU3MkMxODEuOTUgLTAuNDE1MDYgMTkyLjAzOSAzLjc2NDEzIDE5Mi4wMzkgMTIuMTA5MVYyNDQuMDQ2QzE5Mi4wMzkgMjUyLjM5MSAxODEuOTUgMjU2LjU3IDE3Ni4wNDkgMjUwLjY2OVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xODEuMzc5IDE4MC42NDZMMTE0LjMzIDEyOC42MzNMMTgxLjM3OSA3NS41MTE0VjE3Ljc5NEMxODEuMzc5IDEwLjg0NzcgMTczLjEyOCA3LjIwNjczIDE2Ny45OTYgMTEuODg2Mkw3NC42NTE0IDk3Ljg1MThMMzEuMTk5NCA2NC4xNDM4QzI3LjEwODEgNjEuMDM5IDIxLjM4NTEgNjEuMjk0IDE3LjU4NTMgNjQuNzQ3NkwzLjQ4OTc0IDc3LjU2MjdDLTEuMTU4NDcgODEuNzg5MyAtMS4xNjM2NyA4OS4wOTQ4IDMuNDc2NzIgOTMuMzI5MkwxNjcuOTggMjQ0LjE4NUMxNzMuMTA3IDI0OC44ODcgMTgxLjM3OSAyNDUuMjQ5IDE4MS4zNzkgMjM4LjI5MlYxODAuNjQ2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTM2LjY5MzcgMTM0LjE5NUwzLjQ3NjcyIDE2Mi44MjhDLTEuMTYzNjcgMTY3LjA2MiAtMS4xNTg0NyAxNzQuMzcgMy40ODk3NCAxNzguNTk0TDE3LjU4NTMgMTkxLjQwOUMyMS4zODUxIDE5NC44NjMgMjcuMTA4MSAxOTUuMTE4IDMxLjE5OTQgMTkyLjAxM0w2OS40NDcyIDE2NC4wNTdMMzYuNjkzNyAxMzQuMTk1WiIgZmlsbD0id2hpdGUiLz4KPC9tYXNrPgo8ZyBtYXNrPSJ1cmwoI21hc2swKSI+CjxwYXRoIGQ9Ik0xNjcuOTk2IDExLjg4NTdDMTczLjEyOCA3LjIwNjI3IDE4MS4zNzkgMTAuODQ3MyAxODEuMzc5IDE3Ljc5MzZWNzUuNTEwOUwxMDQuOTM4IDEzNi4wNzNMNjUuNTc0MiAxMDYuMjExTDE2Ny45OTYgMTEuODg1N1oiIGZpbGw9IiMwMDlBN0MiLz4KPHBhdGggZD0iTTM2LjY5MzcgMTM0LjE5NEwzLjQ3NjcyIDE2Mi44MjdDLTEuMTYzNjcgMTY3LjA2MiAtMS4xNTg0NyAxNzQuMzcgMy40ODk3NCAxNzguNTk0TDE3LjU4NTMgMTkxLjQwOUMyMS4zODUxIDE5NC44NjMgMjcuMTA4MSAxOTUuMTE4IDMxLjE5OTQgMTkyLjAxM0w2OS40NDcyIDE2NC4wNTZMMzYuNjkzNyAxMzQuMTk0WiIgZmlsbD0iIzAwOUE3QyIvPgo8ZyBmaWx0ZXI9InVybCgjZmlsdGVyMF9kKSI+CjxwYXRoIGQ9Ik0xODEuMzc5IDE4MC42NDVMMzEuMTk5NCA2NC4xNDI3QzI3LjEwODEgNjEuMDM3OSAyMS4zODUxIDYxLjI5MjkgMTcuNTg1MyA2NC43NDY1TDMuNDg5NzQgNzcuNTYxNkMtMS4xNTg0NyA4MS43ODgyIC0xLjE2MzY3IDg5LjA5MzcgMy40NzY3MiA5My4zMjgxTDE2Ny45NzIgMjQ0LjE3NkMxNzMuMTAyIDI0OC44ODEgMTgxLjM3OSAyNDUuMjQxIDE4MS4zNzkgMjM4LjI4VjE4MC42NDVaIiBmaWxsPSIjMDBCMjk0Ii8+CjwvZz4KPGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjFfZCkiPgo8cGF0aCBkPSJNMTk0LjIzMyAyNTMuNzY2QzE4OC4xMyAyNTYuNzAxIDE4MC44MzcgMjU1LjQ2IDE3Ni4wNDggMjUwLjY3MUMxODEuOTQ5IDI1Ni41NzEgMTkyLjAzOSAyNTIuMzkyIDE5Mi4wMzkgMjQ0LjA0N1YxMi4xMTAzQzE5Mi4wMzkgMy43NjUzNSAxODEuOTQ5IC0wLjQxMzgzOSAxNzYuMDQ4IDUuNDg2OTRDMTgwLjgzNyAwLjY5NzgyNCAxODguMTI5IC0wLjU0MzE5MSAxOTQuMjMzIDIuMzkyMUwyNDYuOTM5IDI3LjczODZDMjUyLjQ3OCAzMC40MDIgMjU2IDM2LjAwMzcgMjU2IDQyLjE0OTFWMjE0LjAwOUMyNTYgMjIwLjE1NSAyNTIuNDc4IDIyNS43NTcgMjQ2LjkzOSAyMjguNDJMMTk0LjIzMyAyNTMuNzY2WiIgZmlsbD0iIzI0QkZBNSIvPgo8L2c+CjwvZz4KPGRlZnM+CjxmaWx0ZXIgaWQ9ImZpbHRlcjBfZCIgeD0iLTIxLjMzMzMiIHk9IjQwLjY0MTMiIHdpZHRoPSIyMjQuMDQ1IiBoZWlnaHQ9IjIyNi45ODgiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KPGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz4KPGZlQ29sb3JNYXRyaXggaW49IlNvdXJjZUFscGhhIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMTI3IDAiLz4KPGZlT2Zmc2V0Lz4KPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMTAuNjY2NyIvPgo8ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMC4xNSAwIi8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0iZWZmZWN0MV9kcm9wU2hhZG93Ii8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9kcm9wU2hhZG93IiByZXN1bHQ9InNoYXBlIi8+CjwvZmlsdGVyPgo8ZmlsdGVyIGlkPSJmaWx0ZXIxX2QiIHg9IjE1NC43MTUiIHk9Ii0yMC41MTY5IiB3aWR0aD0iMTIyLjYxOCIgaGVpZ2h0PSIyOTcuMTkxIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+CjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIi8+CjxmZU9mZnNldC8+CjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjEwLjY2NjciLz4KPGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAuMjUgMCIvPgo8ZmVCbGVuZCBtb2RlPSJvdmVybGF5IiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3ciLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3ciIHJlc3VsdD0ic2hhcGUiLz4KPC9maWx0ZXI+CjwvZGVmcz4KPC9zdmc+Cg==\')'; +- } + return ` diff --git a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts index 0d713847..ae8d169b 100644 --- a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts @@ -900,13 +955,24 @@ index 818e6628..2d2ead7a 100644 - '\t// List of extensions recommended by VS Code that should not be recommended for users of this workspace.', + '\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/installExtensionsTool.ts b/src/vs/workbench/contrib/extensions/common/installExtensionsTool.ts +index d174c8c9..a7160f60 100644 +--- a/src/vs/workbench/contrib/extensions/common/installExtensionsTool.ts ++++ b/src/vs/workbench/contrib/extensions/common/installExtensionsTool.ts +@@ -19,3 +19,3 @@ export const InstallExtensionsToolData: IToolData = { + displayName: localize('installExtensionsTool.displayName', 'Install Extensions'), +- modelDescription: 'This is a tool for installing extensions in Visual Studio Code. You should provide the list of extension ids to install. The identifier of an extension is \'\${ publisher }.\${ name }\' for example: \'vscode.csharp\'.', ++ modelDescription: 'This is a tool for installing extensions in !!APP_NAME!!. You should provide the list of extension ids to install. The identifier of an extension is \'\${ publisher }.\${ name }\' for example: \'vscode.csharp\'.', + userDescription: localize('installExtensionsTool.userDescription', 'Tool for installing extensions'), diff --git a/src/vs/workbench/contrib/extensions/common/searchExtensionsTool.ts b/src/vs/workbench/contrib/extensions/common/searchExtensionsTool.ts -index 91541b6a..ccb94143 100644 +index 91541b6a..6e2e8207 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 = { - modelDescription: 'This is a tool for browsing Visual Studio Code Extensions Marketplace. It allows the model to search for extensions and retrieve detailed information about them. The model should use this tool whenever it needs to discover extensions or resolve information about known ones. To use the tool, the model has to provide the category of the extensions, relevant search keywords, or known extension IDs. Note that search results may include false positives, so reviewing and filtering is recommended.', +@@ -22,4 +22,4 @@ export const SearchExtensionsToolData: IToolData = { + displayName: localize('searchExtensionsTool.displayName', 'Search Extensions'), +- modelDescription: 'This is a tool for browsing Visual Studio Code Extensions Marketplace. It allows the model to search for extensions and retrieve detailed information about them. The model should use this tool whenever it needs to discover extensions or resolve information about known ones. To use the tool, the model has to provide the category of the extensions, relevant search keywords, or known extension IDs. Note that search results may include false positives, so reviewing and filtering is recommended.', - userDescription: localize('searchExtensionsTool.userDescription', 'Search for VS Code extensions'), ++ modelDescription: 'This is a tool for browsing OpenVSX. It allows the model to search for extensions and retrieve detailed information about them. The model should use this tool whenever it needs to discover extensions or resolve information about known ones. To use the tool, the model has to provide the category of the extensions, relevant search keywords, or known extension IDs. Note that search results may include false positives, so reviewing and filtering is recommended.', + 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 @@ -1335,6 +1401,15 @@ index 40e38cd8..7c0ff9dd 100644 - type: 'svg', altText: 'VS Code extension marketplace with featured language extensions', path: 'extensions.svg' + type: 'svg', altText: '!!APP_NAME!! extension marketplace with featured language extensions', path: 'extensions.svg' }, +diff --git a/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts b/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts +index c9d06f40..eb2117e4 100644 +--- a/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts ++++ b/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts +@@ -173,3 +173,3 @@ export class OnboardingVariationA extends Disposable implements IOnboardingServi + this.overlay.setAttribute('aria-modal', 'true'); +- this.overlay.setAttribute('aria-label', localize('onboarding.a.aria', "Welcome to Visual Studio Code")); ++ this.overlay.setAttribute('aria-label', localize('onboarding.a.aria', "Welcome to !!APP_NAME!!")); + 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 bdd30bf2..317d11c8 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/editor/vs_code_editor_walkthrough.ts diff --git a/patches/windows/00-build-contextmenu-appx.patch b/patches/windows/00-build-contextmenu-appx.patch new file mode 100644 index 00000000000..4e5545ad305 --- /dev/null +++ b/patches/windows/00-build-contextmenu-appx.patch @@ -0,0 +1,37 @@ +diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts +index 080d97f3..b01d710a 100644 +--- a/build/gulpfile.vscode.ts ++++ b/build/gulpfile.vscode.ts +@@ -624,3 +624,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d + const rawVersion = version.replace(/-\w+$/, '').split('.'); +- const appxVersion = `${rawVersion[0]}.0.${rawVersion[1]}.${rawVersion[2]}`; ++ const appxVersion = `${rawVersion[0]}.0.${rawVersion[1]}.${rawVersion[2].slice(1)}`; + result = es.merge(result, gulp.src('resources/win32/appx/AppxManifest.xml', { base: '.' }) +diff --git a/build/gulpfile.vscode.win32.ts b/build/gulpfile.vscode.win32.ts +index 7a711276..2675bd85 100644 +--- a/build/gulpfile.vscode.win32.ts ++++ b/build/gulpfile.vscode.win32.ts +@@ -134,4 +134,4 @@ function buildWin32Setup(arch: string, target: string): task.CallbackTask { + if (quality === 'stable' || quality === 'insider') { +- definitions['AppxPackage'] = `${quality === 'stable' ? 'code' : 'code_insider'}_${arch}.appx`; +- definitions['AppxPackageDll'] = `${quality === 'stable' ? 'code' : 'code_insider'}_explorer_command_${arch}.dll`; ++ definitions['AppxPackage'] = `${product.applicationName.replaceAll('-', '_')}_${arch}.appx`; ++ definitions['AppxPackageDll'] = `${product.applicationName.replaceAll('-', '_')}_explorer_command_${arch}.dll`; + definitions['AppxPackageName'] = `${product.win32AppUserModelId}`; +diff --git a/build/win32/explorer-dll-fetcher.ts b/build/win32/explorer-dll-fetcher.ts +index 33603ef9..ead89fe4 100644 +--- a/build/win32/explorer-dll-fetcher.ts ++++ b/build/win32/explorer-dll-fetcher.ts +@@ -11,2 +11,3 @@ import productJson from '../../product.json' with { type: 'json' }; + interface ProductConfiguration { ++ applicationName: string; + quality?: string; +@@ -55,4 +56,6 @@ export async function downloadExplorerDll(outDir: string, quality: string = 'sta + +- d(`moving ${artifact} to ${outDir}`); +- await fs.copyFileSync(artifact, path.join(outDir, fileName)); ++ const outFilePath = path.join(outDir, `${product.applicationName.replaceAll('-', '_')}_explorer_command_${targetArch}.dll`); ++ ++ d(`moving ${artifact} as ${outFilePath}`); ++ await fs.copyFileSync(artifact, outFilePath); + } diff --git a/patches/windows/00-build-disable-appx.patch b/patches/windows/00-build-disable-appx.patch.no similarity index 100% rename from patches/windows/00-build-disable-appx.patch rename to patches/windows/00-build-disable-appx.patch.no From f66ae9798a768b0c57e2b941fc964b7564b676eb Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 26 Apr 2026 23:16:29 +0200 Subject: [PATCH 28/79] feat: disable onboarding screen (#2804) --- patches/00-ui-disable-onboarding.patch | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 patches/00-ui-disable-onboarding.patch diff --git a/patches/00-ui-disable-onboarding.patch b/patches/00-ui-disable-onboarding.patch new file mode 100644 index 00000000000..42807172ae2 --- /dev/null +++ b/patches/00-ui-disable-onboarding.patch @@ -0,0 +1,35 @@ +diff --git a/src/vs/workbench/contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.ts b/src/vs/workbench/contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.ts +index d725507e..a4a092d8 100644 +--- a/src/vs/workbench/contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.ts ++++ b/src/vs/workbench/contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.ts +@@ -4,30 +4 @@ + *--------------------------------------------------------------------------------------------*/ +- +-// Load styles for the remaining onboarding variant. +-import './media/variationA.css'; +- +-import { localize2 } from '../../../../nls.js'; +-import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; +-import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; +-import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +-import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +-import { IOnboardingService } from '../common/onboardingService.js'; +-import { OnboardingVariationA } from './onboardingVariationA.js'; +- +-registerSingleton(IOnboardingService, OnboardingVariationA, InstantiationType.Delayed); +- +-registerAction2(class extends Action2 { +- constructor() { +- super({ +- id: 'workbench.action.welcomeOnboarding2026', +- title: localize2('welcomeOnboarding2026', "Welcome Onboarding 2026"), +- category: Categories.Developer, +- f1: true, +- }); +- } +- +- run(accessor: ServicesAccessor): void { +- const onboardingService = accessor.get(IOnboardingService); +- onboardingService.show(); +- } +-}); From 8b760ef6a783d03542eabd57dbedde968f3bd5c2 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 27 Apr 2026 10:09:52 +0200 Subject: [PATCH 29/79] ci: update actions (#2805) --- .github/workflows/ci-build-linux.yml | 6 +++--- .github/workflows/ci-build-macos.yml | 2 +- .github/workflows/ci-build-windows.yml | 4 ++-- .github/workflows/publish-insider-linux.yml | 6 +++--- .github/workflows/publish-insider-macos.yml | 2 +- .github/workflows/publish-insider-spearhead.yml | 2 +- .github/workflows/publish-insider-windows.yml | 6 +++--- .github/workflows/publish-stable-linux.yml | 6 +++--- .github/workflows/publish-stable-macos.yml | 2 +- .github/workflows/publish-stable-spearhead.yml | 2 +- .github/workflows/publish-stable-windows.yml | 6 +++--- justfile | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci-build-linux.yml b/.github/workflows/ci-build-linux.yml index 0fc3d3bbd55..62240e13687 100644 --- a/.github/workflows/ci-build-linux.yml +++ b/.github/workflows/ci-build-linux.yml @@ -63,7 +63,7 @@ jobs: platform: x64 - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc @@ -258,7 +258,7 @@ jobs: if: env.DISABLED != 'yes' - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' if: env.DISABLED != 'yes' @@ -339,7 +339,7 @@ jobs: platform: x64 - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/ci-build-macos.yml b/.github/workflows/ci-build-macos.yml index 16967a07020..37af0951217 100644 --- a/.github/workflows/ci-build-macos.yml +++ b/.github/workflows/ci-build-macos.yml @@ -58,7 +58,7 @@ jobs: run: . get_pr.sh - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/ci-build-windows.yml b/.github/workflows/ci-build-windows.yml index c49eb3f9976..e806897105c 100644 --- a/.github/workflows/ci-build-windows.yml +++ b/.github/workflows/ci-build-windows.yml @@ -58,7 +58,7 @@ jobs: run: ./get_pr.sh - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' @@ -127,7 +127,7 @@ jobs: run: ./get_pr.sh - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/publish-insider-linux.yml b/.github/workflows/publish-insider-linux.yml index 06df5d07384..a8fdfa9a908 100644 --- a/.github/workflows/publish-insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -73,7 +73,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc if: env.SHOULD_BUILD == 'yes' @@ -287,7 +287,7 @@ jobs: if: env.DISABLED != 'yes' - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' if: env.DISABLED != 'yes' @@ -375,7 +375,7 @@ jobs: platform: x64 - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/publish-insider-macos.yml b/.github/workflows/publish-insider-macos.yml index 9df872c9ca1..04eecfaa4f6 100644 --- a/.github/workflows/publish-insider-macos.yml +++ b/.github/workflows/publish-insider-macos.yml @@ -41,7 +41,7 @@ jobs: persist-credentials: false - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc diff --git a/.github/workflows/publish-insider-spearhead.yml b/.github/workflows/publish-insider-spearhead.yml index fb6c91c7c14..dbe4fb2dd3b 100644 --- a/.github/workflows/publish-insider-spearhead.yml +++ b/.github/workflows/publish-insider-spearhead.yml @@ -75,7 +75,7 @@ jobs: persist-credentials: false - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/publish-insider-windows.yml b/.github/workflows/publish-insider-windows.yml index ef5faf8ee47..bd0e1aeaf49 100644 --- a/.github/workflows/publish-insider-windows.yml +++ b/.github/workflows/publish-insider-windows.yml @@ -67,7 +67,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc if: env.SHOULD_BUILD == 'yes' @@ -140,7 +140,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' if: env.SHOULD_BUILD == 'yes' @@ -188,7 +188,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' && vars.DISABLE_INSIDER_WINDOWS_SIGNING != 'yes' - name: Signing - uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2.2 + uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }} diff --git a/.github/workflows/publish-stable-linux.yml b/.github/workflows/publish-stable-linux.yml index ab18a63a6f1..da325131bc0 100644 --- a/.github/workflows/publish-stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -74,7 +74,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc if: env.SHOULD_BUILD == 'yes' @@ -288,7 +288,7 @@ jobs: if: env.DISABLED != 'yes' - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' if: env.DISABLED != 'yes' @@ -376,7 +376,7 @@ jobs: platform: x64 - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/publish-stable-macos.yml b/.github/workflows/publish-stable-macos.yml index 8126cec0bd2..3ebf93fab8a 100644 --- a/.github/workflows/publish-stable-macos.yml +++ b/.github/workflows/publish-stable-macos.yml @@ -41,7 +41,7 @@ jobs: persist-credentials: false - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc diff --git a/.github/workflows/publish-stable-spearhead.yml b/.github/workflows/publish-stable-spearhead.yml index 792f7db5939..b98da32d9e7 100644 --- a/.github/workflows/publish-stable-spearhead.yml +++ b/.github/workflows/publish-stable-spearhead.yml @@ -75,7 +75,7 @@ jobs: persist-credentials: false - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/publish-stable-windows.yml b/.github/workflows/publish-stable-windows.yml index 971516e6c9a..96ccab1645b 100644 --- a/.github/workflows/publish-stable-windows.yml +++ b/.github/workflows/publish-stable-windows.yml @@ -67,7 +67,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: .nvmrc if: env.SHOULD_BUILD == 'yes' @@ -140,7 +140,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Node.js environment - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' if: env.SHOULD_BUILD == 'yes' @@ -188,7 +188,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Signing - uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2.2 + uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }} diff --git a/justfile b/justfile index 2d6300889cd..b0cf4a70fd4 100644 --- a/justfile +++ b/justfile @@ -7,4 +7,4 @@ ci-lint-fix: zizmor . --fix=all ci-update: - PINACT_MIN_AGE=7 pinact run + PINACT_MIN_AGE=7 pinact run --update From 3ab1aeedbfc9b3acfb7d736ad3ba560320a79d7a Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 27 Apr 2026 11:52:11 +0200 Subject: [PATCH 30/79] fix: disable wip appx (#2807) --- build/windows/package.sh | 2 +- build/windows/prepare_assets.sh | 2 +- .../windows/00-build-disable-appx.patch.no | 37 ------------ ....patch => 70-build-contextmenu-appx.patch} | 0 patches/windows/71-build-disable-appx.patch | 56 +++++++++++++++++++ 5 files changed, 58 insertions(+), 39 deletions(-) delete mode 100644 patches/windows/00-build-disable-appx.patch.no rename patches/windows/{00-build-contextmenu-appx.patch => 70-build-contextmenu-appx.patch} (100%) create mode 100644 patches/windows/71-build-disable-appx.patch diff --git a/build/windows/package.sh b/build/windows/package.sh index 9e051ae7eda..560c132c7b0 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 -node build/win32/explorer-dll-fetcher.ts .build/win32/appx +# node build/win32/explorer-dll-fetcher.ts .build/win32/appx npm run gulp "vscode-win32-${VSCODE_ARCH}-min-packing" diff --git a/build/windows/prepare_assets.sh b/build/windows/prepare_assets.sh index 1423005c66b..82ee0308bb5 100644 --- a/build/windows/prepare_assets.sh +++ b/build/windows/prepare_assets.sh @@ -4,7 +4,7 @@ cd vscode || { echo "'vscode' dir not found"; exit 1; } npm run gulp "vscode-win32-${VSCODE_ARCH}-inno-updater" -. ../build/windows/appx/build.sh +# . ../build/windows/appx/build.sh if [[ "${SHOULD_BUILD_ZIP}" != "no" ]]; then 7z.exe a -tzip "../assets/${APP_NAME}-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" -x!CodeSignSummary*.md -x!tools "../VSCode-win32-${VSCODE_ARCH}/*" -r diff --git a/patches/windows/00-build-disable-appx.patch.no b/patches/windows/00-build-disable-appx.patch.no deleted file mode 100644 index b3cdf4afd8d..00000000000 --- a/patches/windows/00-build-disable-appx.patch.no +++ /dev/null @@ -1,37 +0,0 @@ -diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index 080d97f3..6b1b4032 100644 ---- a/build/gulpfile.vscode.ts -+++ b/build/gulpfile.vscode.ts -@@ -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') { -- result = es.merge(result, gulp.src('.build/win32/appx/**', { base: '.build/win32' })); -- const rawVersion = version.replace(/-\w+$/, '').split('.'); -- const appxVersion = `${rawVersion[0]}.0.${rawVersion[1]}.${rawVersion[2]}`; -- result = es.merge(result, gulp.src('resources/win32/appx/AppxManifest.xml', { base: '.' }) -- .pipe(replace('@@AppxPackageName@@', product.win32AppUserModelId)) -- .pipe(replace('@@AppxPackageVersion@@', appxVersion)) -- .pipe(replace('@@AppxPackageDisplayName@@', product.nameLong)) -- .pipe(replace('@@AppxPackageDescription@@', product.win32NameVersion)) -- .pipe(replace('@@ApplicationIdShort@@', product.win32RegValueName)) -- .pipe(replace('@@ApplicationExe@@', product.nameShort + '.exe')) -- .pipe(replace('@@FileExplorerContextMenuID@@', quality === 'stable' ? 'OpenWithCode' : 'OpenWithCodeInsiders')) -- .pipe(replace('@@FileExplorerContextMenuCLSID@@', (product as { win32ContextMenu?: Record }).win32ContextMenu![arch].clsid)) -- .pipe(replace('@@FileExplorerContextMenuDLL@@', `${quality === 'stable' ? 'code' : 'code_insider'}_explorer_command_${arch}.dll`)) -- .pipe(rename(f => f.dirname = `appx/manifest`))); -- } - } else if (platform === 'linux') { -diff --git a/build/gulpfile.vscode.win32.ts b/build/gulpfile.vscode.win32.ts -index 7a711276..e6a838e2 100644 ---- a/build/gulpfile.vscode.win32.ts -+++ b/build/gulpfile.vscode.win32.ts -@@ -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`; -- definitions['AppxPackageDll'] = `${quality === 'stable' ? 'code' : 'code_insider'}_explorer_command_${arch}.dll`; -- definitions['AppxPackageName'] = `${product.win32AppUserModelId}`; -- } -- - fs.writeFileSync(productJsonPath, JSON.stringify(productJson, undefined, '\t')); diff --git a/patches/windows/00-build-contextmenu-appx.patch b/patches/windows/70-build-contextmenu-appx.patch similarity index 100% rename from patches/windows/00-build-contextmenu-appx.patch rename to patches/windows/70-build-contextmenu-appx.patch diff --git a/patches/windows/71-build-disable-appx.patch b/patches/windows/71-build-disable-appx.patch new file mode 100644 index 00000000000..bc342fd0e5d --- /dev/null +++ b/patches/windows/71-build-disable-appx.patch @@ -0,0 +1,56 @@ +diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts +index b01d710a..303d2f68 100644 +--- a/build/gulpfile.vscode.ts ++++ b/build/gulpfile.vscode.ts +@@ -621,18 +621,18 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d + +- if (quality === 'stable' || quality === 'insider') { +- result = es.merge(result, gulp.src('.build/win32/appx/**', { base: '.build/win32' })); +- const rawVersion = version.replace(/-\w+$/, '').split('.'); +- const appxVersion = `${rawVersion[0]}.0.${rawVersion[1]}.${rawVersion[2].slice(1)}`; +- result = es.merge(result, gulp.src('resources/win32/appx/AppxManifest.xml', { base: '.' }) +- .pipe(replace('@@AppxPackageName@@', product.win32AppUserModelId)) +- .pipe(replace('@@AppxPackageVersion@@', appxVersion)) +- .pipe(replace('@@AppxPackageDisplayName@@', product.nameLong)) +- .pipe(replace('@@AppxPackageDescription@@', product.win32NameVersion)) +- .pipe(replace('@@ApplicationIdShort@@', product.win32RegValueName)) +- .pipe(replace('@@ApplicationExe@@', product.nameShort + '.exe')) +- .pipe(replace('@@FileExplorerContextMenuID@@', quality === 'stable' ? 'OpenWithCode' : 'OpenWithCodeInsiders')) +- .pipe(replace('@@FileExplorerContextMenuCLSID@@', (product as { win32ContextMenu?: Record }).win32ContextMenu![arch].clsid)) +- .pipe(replace('@@FileExplorerContextMenuDLL@@', `${quality === 'stable' ? 'code' : 'code_insider'}_explorer_command_${arch}.dll`)) +- .pipe(rename(f => f.dirname = `appx/manifest`))); +- } ++ // if (quality === 'stable' || quality === 'insider') { ++ // result = es.merge(result, gulp.src('.build/win32/appx/**', { base: '.build/win32' })); ++ // const rawVersion = version.replace(/-\w+$/, '').split('.'); ++ // const appxVersion = `${rawVersion[0]}.0.${rawVersion[1]}.${rawVersion[2].slice(1)}`; ++ // result = es.merge(result, gulp.src('resources/win32/appx/AppxManifest.xml', { base: '.' }) ++ // .pipe(replace('@@AppxPackageName@@', product.win32AppUserModelId)) ++ // .pipe(replace('@@AppxPackageVersion@@', appxVersion)) ++ // .pipe(replace('@@AppxPackageDisplayName@@', product.nameLong)) ++ // .pipe(replace('@@AppxPackageDescription@@', product.win32NameVersion)) ++ // .pipe(replace('@@ApplicationIdShort@@', product.win32RegValueName)) ++ // .pipe(replace('@@ApplicationExe@@', product.nameShort + '.exe')) ++ // .pipe(replace('@@FileExplorerContextMenuID@@', quality === 'stable' ? 'OpenWithCode' : 'OpenWithCodeInsiders')) ++ // .pipe(replace('@@FileExplorerContextMenuCLSID@@', (product as { win32ContextMenu?: Record }).win32ContextMenu![arch].clsid)) ++ // .pipe(replace('@@FileExplorerContextMenuDLL@@', `${quality === 'stable' ? 'code' : 'code_insider'}_explorer_command_${arch}.dll`)) ++ // .pipe(rename(f => f.dirname = `appx/manifest`))); ++ // } + } else if (platform === 'linux') { +diff --git a/build/gulpfile.vscode.win32.ts b/build/gulpfile.vscode.win32.ts +index 2675bd85..51723721 100644 +--- a/build/gulpfile.vscode.win32.ts ++++ b/build/gulpfile.vscode.win32.ts +@@ -133,7 +133,7 @@ function buildWin32Setup(arch: string, target: string): task.CallbackTask { + +- if (quality === 'stable' || quality === 'insider') { +- definitions['AppxPackage'] = `${product.applicationName.replaceAll('-', '_')}_${arch}.appx`; +- definitions['AppxPackageDll'] = `${product.applicationName.replaceAll('-', '_')}_explorer_command_${arch}.dll`; +- definitions['AppxPackageName'] = `${product.win32AppUserModelId}`; +- } ++ // if (quality === 'stable' || quality === 'insider') { ++ // definitions['AppxPackage'] = `${product.applicationName.replaceAll('-', '_')}_${arch}.appx`; ++ // definitions['AppxPackageDll'] = `${product.applicationName.replaceAll('-', '_')}_explorer_command_${arch}.dll`; ++ // definitions['AppxPackageName'] = `${product.win32AppUserModelId}`; ++ // } + From f4d12efa852f25f6e055ac0b4c79f57a882af4e7 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 27 Apr 2026 14:23:46 +0200 Subject: [PATCH 31/79] feat(1.116): update apis (#2808) --- product.json | 13 +++++-------- upstream/stable.json | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/product.json b/product.json index 1bdd0111382..efeaa2c0a85 100644 --- a/product.json +++ b/product.json @@ -49,6 +49,7 @@ "extensionsEnabledWithApiProposalVersion": [ "GitHub.copilot-chat", "ms-vscode.vscode-commander", + "ms-vscode.vscode-copilot-vision", "GitHub.vscode-pull-request-github" ], "extensionEnabledApiProposals": { @@ -245,14 +246,10 @@ ], "GitHub.copilot": [ "inlineCompletionsAdditions", - "interactive", - "terminalDataWriteEvent", "devDeviceId" ], "GitHub.copilot-nightly": [ "inlineCompletionsAdditions", - "interactive", - "terminalDataWriteEvent", "devDeviceId" ], "GitHub.copilot-chat": [ @@ -384,6 +381,7 @@ "terminalDataWriteEvent", "chatParticipantAdditions" ], + "vscjava.vscode-java-pack": [], "ms-dotnettools.csdevkit": [ "inlineCompletionsAdditions" ], @@ -407,6 +405,9 @@ "languageModelSystem", "mcpServerDefinitions" ], + "ms-toolsai.datawrangler": [], + "ms-vscode.vscode-commander": [], + "ms-vscode.vscode-websearchforcopilot": [], "ms-vscode.vscode-copilot-vision": [ "chatReferenceBinaryData", "codeActionAI" @@ -414,10 +415,6 @@ "ms-autodev.vscode-autodev": [ "chatParticipantAdditions" ], - "codetrek.haystack-search": [ - "fileSearchProvider", - "textSearchProvider2" - ], "vscjava.migrate-java-to-azure": [ "chatParticipantAdditions", "chatParticipantPrivate" diff --git a/upstream/stable.json b/upstream/stable.json index 7b0d84b78bd..0909ccb60e9 100644 --- a/upstream/stable.json +++ b/upstream/stable.json @@ -1,4 +1,4 @@ { - "tag": "1.112.0", - "commit": "07ff9d6178ede9a1bd12ad3399074d726ebe6e43" + "tag": "1.116.0", + "commit": "560a9dba96f961efea7b1612916f89e5d5d4d679" } From 83452e4c588142ee4868185ea535738108e428bf Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 28 Apr 2026 12:38:13 +0200 Subject: [PATCH 32/79] ci: disable automated spearheads since, now, they always need to be manually validated --- .github/workflows/publish-insider-spearhead.yml | 2 -- .github/workflows/publish-stable-spearhead.yml | 2 -- .github/zizmor.yml | 6 ++++++ 3 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 .github/zizmor.yml diff --git a/.github/workflows/publish-insider-spearhead.yml b/.github/workflows/publish-insider-spearhead.yml index dbe4fb2dd3b..f2ec331c421 100644 --- a/.github/workflows/publish-insider-spearhead.yml +++ b/.github/workflows/publish-insider-spearhead.yml @@ -12,8 +12,6 @@ on: dont_dispatch: type: boolean description: Disable dispatch - schedule: - - cron: 0 7 * * * env: APP_NAME: VSCodium diff --git a/.github/workflows/publish-stable-spearhead.yml b/.github/workflows/publish-stable-spearhead.yml index b98da32d9e7..532dd4322a0 100644 --- a/.github/workflows/publish-stable-spearhead.yml +++ b/.github/workflows/publish-stable-spearhead.yml @@ -12,8 +12,6 @@ on: dont_dispatch: type: boolean description: Disable dispatch - schedule: - - cron: 0 18 * * * env: APP_NAME: VSCodium diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 00000000000..f86b55dc7bd --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,6 @@ +rules: + superfluous-actions: + ignore: + # allows `svenstaro/upload-release-action` action + - publish-insider-linux.yml:482 + - publish-stable-linux.yml:487 From f20e075505ce12315a226b6a6886dd390ab86af8 Mon Sep 17 00:00:00 2001 From: DetachHead <57028336+DetachHead@users.noreply.github.com> Date: Fri, 1 May 2026 01:09:30 +1000 Subject: [PATCH 33/79] fix(appimage): remove bad LD_LIBRARY_PATH (#2740) --- build/linux/appimage/recipe.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/build/linux/appimage/recipe.yml b/build/linux/appimage/recipe.yml index 59c273703dd..6dad8bf221d 100644 --- a/build/linux/appimage/recipe.yml +++ b/build/linux/appimage/recipe.yml @@ -36,7 +36,6 @@ script: - #!/bin/sh - HERE="$(dirname "$(readlink -f "${0}")")" - export PATH="${HERE}"/usr/bin/:"${HERE}"/usr/sbin/:"${HERE}"/usr/games/:"${HERE}"/bin/:"${HERE}"/sbin/:"${PATH}" - - export LD_LIBRARY_PATH="${HERE}"/usr/lib/:"${HERE}"/usr/lib32/:"${HERE}"/usr/lib64/:"${HERE}"/lib/:"${HERE}"/lib/i386-linux-gnu/:"${HERE}"/lib/x86_64-linux-gnu/:"${HERE}"/lib32/:"${HERE}"/lib64/:"${LD_LIBRARY_PATH}" - export XDG_DATA_DIRS="${HERE}"/usr/share/:"${XDG_DATA_DIRS}" - export PERLLIB="${HERE}"/usr/share/perl5/:"${HERE}"/usr/lib/perl5/:"${PERLLIB}" - export GSETTINGS_SCHEMA_DIR="${HERE}"/usr/share/glib-2.0/schemas/:"${GSETTINGS_SCHEMA_DIR}" From 5e7d0e5540b6648ace22592684aab416a2f73b22 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 2 May 2026 19:56:39 +0200 Subject: [PATCH 34/79] feat(1.118): update patches (#2820) --- patches/00-copilot-fix-action-condition.patch | 48 +- patches/00-remote-add-url.patch | 15 +- patches/00-ui-custom-font.patch | 62 +- patches/40-cli-use-reh-archive.patch | 6 +- patches/51-ext-copilot-remove-it.patch | 9342 ++++++++++++----- patches/linux/00-build-improve-qemu.patch | 4 +- product.json | 4 + upstream/insider.json | 4 +- 8 files changed, 6630 insertions(+), 2855 deletions(-) diff --git a/patches/00-copilot-fix-action-condition.patch b/patches/00-copilot-fix-action-condition.patch index 293bbfc97c4..d4d56fbf1ad 100644 --- a/patches/00-copilot-fix-action-condition.patch +++ b/patches/00-copilot-fix-action-condition.patch @@ -1,44 +1,44 @@ diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts -index f7d4917d..b62a8a43 100644 +index 27d6be94..ddb504b4 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts -@@ -1144,3 +1144,3 @@ export function registerChatActions() { +@@ -1168,3 +1168,3 @@ export function registerChatActions() { precondition: ContextKeyExpr.and( - ChatContextKeys.Setup.installed, + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.disabled.negate(), diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts -index 04243ca9..5241910f 100644 +index d2fccfb3..cd0bb779 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts -@@ -316,3 +316,4 @@ class AttachSelectionToChatAction extends Action2 { +@@ -318,3 +318,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 7e9c73f6..03da6f20 100644 +index 883cd676..dc50ad73 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts -@@ -1420,3 +1420,3 @@ configurationRegistry.registerConfiguration({ +@@ -1491,3 +1491,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 + 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 8ac8f531..f56d3e96 100644 +index 8fb472b3..7b26c605 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts -@@ -72,2 +72,3 @@ const chatViewDescriptor: IViewDescriptor = { - ContextKeyExpr.and( -+ ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), - ChatContextKeys.Setup.hidden.negate(), +@@ -74,2 +74,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 3fa75da3..b1832fcb 100644 +index 6df31b5e..3cdad208 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupContributions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupContributions.ts -@@ -232,8 +232,9 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr +@@ -231,8 +231,9 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr f1: true, - precondition: ContextKeyExpr.or( - ChatContextKeys.Setup.hidden, @@ -54,27 +54,27 @@ index 3fa75da3..b1832fcb 100644 + ChatContextKeys.Setup.completed, + ChatContextKeys.Entitlement.canSignUp.negate() ) -@@ -353,2 +354,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr +@@ -352,2 +353,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr when: ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.hidden.negate(), -@@ -385,2 +387,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr +@@ -384,2 +386,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 +@@ -414,2 +417,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 +@@ -471,2 +475,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 +@@ -563,2 +568,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr const internalGenerateCodeContext = ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.hidden.negate(), -@@ -801,3 +807,7 @@ export class ChatTeardownContribution extends Disposable implements IWorkbenchCo +@@ -806,3 +812,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( @@ -84,10 +84,10 @@ index 3fa75da3..b1832fcb 100644 + ), menu: { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts -index 2c244736..2f5023d8 100644 +index 86516c54..2ac1702a 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts -@@ -43,3 +43,4 @@ const inlineChatContextKey = ContextKeyExpr.and( +@@ -42,3 +42,4 @@ const inlineChatContextKey = ContextKeyExpr.and( EditorContextKeys.writable, - EditorContextKeys.editorSimpleInput.negate() + EditorContextKeys.editorSimpleInput.negate(), @@ -123,10 +123,10 @@ index dc231e44..633de730 100644 + 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 6bdf7f36..3bfc7d19 100644 +index 6ee2a32c..c5cdc792 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts -@@ -703,2 +703,3 @@ registerAction2(class extends Action2 { +@@ -680,2 +680,3 @@ registerAction2(class extends Action2 { when: ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.hidden.negate(), diff --git a/patches/00-remote-add-url.patch b/patches/00-remote-add-url.patch index 21f52b57430..d0e287d72fc 100644 --- a/patches/00-remote-add-url.patch +++ b/patches/00-remote-add-url.patch @@ -1,18 +1,17 @@ diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts -index b935764..68067db 100644 +index 3c0e3a16..cb47f50b 100644 --- a/build/gulpfile.reh.ts +++ b/build/gulpfile.reh.ts -@@ -323,3 +323,3 @@ function packageTask(type: string, platform: string, arch: string, sourceFolderN +@@ -329,3 +329,3 @@ function packageTask(type: string, platform: string, arch: string, sourceFolderN const productJsonStream = gulp.src(['product.json'], { base: '.' }) - .pipe(jsonEditor({ commit, date: readISODate(sourceFolderName), version })) + .pipe(jsonEditor({ commit, date: readISODate(sourceFolderName), version, serverDownloadUrlTemplate: 'https://github.com/!!ASSETS_REPOSITORY!!/releases/download/!!RELEASE_VERSION!!/!!APP_NAME_LC!!-reh-${os}-${arch}-!!RELEASE_VERSION!!.tar.gz' })) .pipe(es.through(function (file) { diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index a103f11..82142a1 100644 +index 028938bd..765da3f4 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -374,3 +374,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d - const productJsonStream = gulp.src(['product.json'], { base: '.' }) -- .pipe(jsonEditor({ commit, date: readISODate(out), checksums, version })) -+ .pipe(jsonEditor({ commit, date: readISODate(out), checksums, version, serverDownloadUrlTemplate: 'https://github.com/!!ASSETS_REPOSITORY!!/releases/download/!!RELEASE_VERSION!!/!!APP_NAME_LC!!-reh-${os}-${arch}-!!RELEASE_VERSION!!.tar.gz' })) - .pipe(es.through(function (file) { +@@ -380,2 +380,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d + json.version = version; ++ json.serverDownloadUrlTemplate = 'https://github.com/!!ASSETS_REPOSITORY!!/releases/download/!!RELEASE_VERSION!!/!!APP_NAME_LC!!-reh-${os}-${arch}-!!RELEASE_VERSION!!.tar.gz'; + if (embedded) { diff --git a/patches/00-ui-custom-font.patch b/patches/00-ui-custom-font.patch index 2b68450a0fa..adedf118e28 100644 --- a/patches/00-ui-custom-font.patch +++ b/patches/00-ui-custom-font.patch @@ -1306,7 +1306,7 @@ index 568a7212..b3d7e506 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 d32082b4..ad7e524c 100644 +index c89c0e4e..88c43a91 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 @@ -1314,7 +1314,7 @@ index d32082b4..ad7e524c 100644 +import { FONT, getFontSize, updateSidebarSize } from '../../../../base/common/font.js'; +import { SidebarPart } from '../sidebar/sidebarPart.js'; -@@ -147,2 +149,8 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { +@@ -146,2 +148,8 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { } + if (e.affectsConfiguration(SidebarPart.fontSizeSettingsKey)) { + this.applyAuxiliaryBarFontSize(); @@ -1323,7 +1323,7 @@ index d32082b4..ad7e524c 100644 + this.applyAuxiliaryBarFontFamily(); + } })); -@@ -198,2 +206,37 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { +@@ -197,2 +205,37 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : '0px'; + + this.applyAuxiliaryBarFontSize(container); @@ -1375,7 +1375,7 @@ index aec3de2d..b0e1fd8f 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 b0a44e2c..e711a809 100644 +index 2ba6b32b..6eb3ee83 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'; @@ -1396,11 +1396,11 @@ index b0a44e2c..e711a809 100644 + }; + } -@@ -142,2 +146,3 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC +@@ -148,2 +152,3 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC @IHostService private readonly hostService: IHostService, + @IConfigurationService protected readonly configurationService: IConfigurationService, ) { -@@ -149,2 +154,13 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC +@@ -155,2 +160,13 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC + this._register(configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('workbench.tabs.experimental.fontFamily')) { @@ -1414,12 +1414,12 @@ index b0a44e2c..e711a809 100644 + })); + // Context Keys -@@ -170,2 +186,4 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC +@@ -176,2 +192,4 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC protected create(parent: HTMLElement): HTMLElement { + this.applyTabsFontSize(parent); + this.applyTabsFontFamily(parent); this.updateTabHeight(); -@@ -174,2 +192,30 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC +@@ -180,2 +198,30 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC + private applyTabsFontFamily(container?: HTMLElement): void { + const target = container ?? this.parent; @@ -1532,7 +1532,7 @@ index a24f7613..6b15b9c2 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 4f9477d0..b47fd85b 100644 +index d57abaaf..2cdf3103 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 @@ @@ -1547,7 +1547,7 @@ index 4f9477d0..b47fd85b 100644 - min-width: calc(var(--tab-sizing-current-width, var(--tab-sizing-fixed-min-width, 50px)) - 1px); + min-width: 50px - 1px; } -@@ -568 +568,113 @@ +@@ -576 +576,113 @@ } + + @@ -1663,7 +1663,7 @@ index 4f9477d0..b47fd85b 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 b56047d6..d6e6c7ed 100644 +index 1fe80afb..d792a8c5 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -6,2 +6,3 @@ @@ -1887,14 +1887,14 @@ index e1c147d8..63d0e109 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 9afcf596..a8d4c2e6 100644 +index 235bad74..cdd90d95 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 import { Extensions } from '../../panecomposite.js'; +import { FONT, getFontSize, updatePanelSize as updateBottomPaneSize } from '../../../../base/common/font.js'; -@@ -117,2 +118,8 @@ export class PanelPart extends AbstractPaneCompositePart { +@@ -116,2 +117,8 @@ export class PanelPart extends AbstractPaneCompositePart { } + if (e.affectsConfiguration('workbench.bottomPane.experimental.fontFamily')) { + this.applyPanelFontFamily(); @@ -1903,7 +1903,7 @@ index 9afcf596..a8d4c2e6 100644 + this.applyPanelFontSize(); + } })); -@@ -133,2 +140,37 @@ export class PanelPart extends AbstractPaneCompositePart { +@@ -132,2 +139,37 @@ export class PanelPart extends AbstractPaneCompositePart { } + + this.applyPanelFontFamily(container); @@ -1999,7 +1999,7 @@ index decb51ab..d0db4363 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 101b9c6c..970cdaae 100644 +index f269e758..f87d5781 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 @@ -2010,7 +2010,7 @@ index 101b9c6c..970cdaae 100644 static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid'; + static readonly fontSizeSettingsKey = 'workbench.sideBar.experimental.fontSize'; -@@ -126,2 +128,8 @@ export class SidebarPart extends AbstractPaneCompositePart { +@@ -125,2 +127,8 @@ export class SidebarPart extends AbstractPaneCompositePart { } + if (e.affectsConfiguration(SidebarPart.fontSizeSettingsKey)) { + this.applySidebarFontSize(); @@ -2019,13 +2019,13 @@ index 101b9c6c..970cdaae 100644 + this.applySidebarFontFamily(); + } })); -@@ -178,2 +186,5 @@ export class SidebarPart extends AbstractPaneCompositePart { +@@ -177,2 +185,5 @@ export class SidebarPart extends AbstractPaneCompositePart { container.style.outlineColor = this.getColor(SIDE_BAR_DRAG_AND_DROP_BACKGROUND) ?? ''; + + this.applySidebarFontSize(container); + this.applySidebarFontFamily(container); } -@@ -320,2 +331,34 @@ export class SidebarPart extends AbstractPaneCompositePart { +@@ -319,2 +330,34 @@ export class SidebarPart extends AbstractPaneCompositePart { + private applySidebarFontFamily(container?: HTMLElement): void { + const target = container ?? this.getContainer(); @@ -2473,25 +2473,25 @@ index 6f588651..3ab0ee4e 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 d4f277de..4f386424 100644 +index 01aa7ab5..ca43c1e0 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts -@@ -53,2 +53,3 @@ import { BugIndicatingError } from '../../../../../base/common/errors.js'; - import { compareIgnoreCase } from '../../../../../base/common/strings.js'; +@@ -55,2 +55,3 @@ import { CancellationTokenSource } from '../../../../../base/common/cancellation + import { IChatSessionsService } from '../../common/chatSessionsService.js'; +import { FONT } from '../../../../../base/common/font.js'; -@@ -838,5 +839,2 @@ export class AgentSessionsListDelegate implements IListVirtualDelegate ServerBuilder<'a> { @@ -85,14 +85,14 @@ index 3d7d718a..98529bc6 100644 + .join(self.quality.server_entrypoint().unwrap()) }, diff --git a/cli/src/update_service.rs b/cli/src/update_service.rs -index 55f1dadc..3b7ef5c4 100644 +index de977b73..97b8b5fa 100644 --- a/cli/src/update_service.rs +++ b/cli/src/update_service.rs @@ -10,3 +10,3 @@ use serde::{Deserialize, Serialize}; use crate::{ - constants::VSCODE_CLI_UPDATE_ENDPOINT, + constants::{VSCODE_CLI_APP_NAME, VSCODE_CLI_DOWNLOAD_ENDPOINT, VSCODE_CLI_UPDATE_ENDPOINT}, - debug, log, options, spanf, + log, options, @@ -18,3 +18,3 @@ use crate::{ zipper, - }, diff --git a/patches/51-ext-copilot-remove-it.patch b/patches/51-ext-copilot-remove-it.patch index 62cf4a41c08..b61d395d22d 100644 --- a/patches/51-ext-copilot-remove-it.patch +++ b/patches/51-ext-copilot-remove-it.patch @@ -1,5 +1,5 @@ diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts -index 2c44fc94..d85dadfe 100644 +index 3c0e3a16..d85dadfe 100644 --- a/build/gulpfile.reh.ts +++ b/build/gulpfile.reh.ts @@ -30,3 +30,3 @@ import rceditCallback from 'rcedit'; @@ -9,36 +9,35 @@ index 2c44fc94..d85dadfe 100644 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 { getCopilotExcludeFilter, prepareBuiltInCopilotRipgrepShim } 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) { +@@ -465,12 +463,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { --function copyCopilotNativeDepsTaskREH(platform: string, arch: string, destinationFolderName: string) { +-function prepareCopilotRipgrepShimTaskREH(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); +- prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, nodeModulesDir); - }; -} - /** -@@ -525,3 +512,2 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { +@@ -524,3 +512,2 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { packageTask(type, platform, arch, sourceFolderName, destinationFolderName), -- copyCopilotNativeDepsTaskREH(platform, arch, destinationFolderName) +- prepareCopilotRipgrepShimTaskREH(platform, arch, destinationFolderName) ]; -@@ -539,3 +525,2 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { +@@ -538,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 +index 6b366a16..1984fce5 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts @@ -31,5 +31,4 @@ import minimist from 'minimist'; @@ -46,19 +45,19 @@ index 93b5f711..d8084ab1 100644 -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 { getCopilotExcludeFilter, prepareBuiltInCopilotRipgrepShim } from './lib/copilot.ts'; import type { EmbeddedProductInfo } from './lib/embeddedType.ts'; -@@ -446,3 +445,2 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d +@@ -438,3 +437,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 +@@ -445,3 +443,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) { +@@ -683,19 +680,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { --function copyCopilotNativeDepsTask(platform: string, arch: string, destinationFolderName: string) { +-function prepareCopilotRipgrepShimTask(platform: string, arch: string, destinationFolderName: string) { - const outputDir = path.join(path.dirname(root), destinationFolderName); - - return async () => { @@ -69,23 +68,22 @@ index 93b5f711..d8084ab1 100644 - ? 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); +- prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, appNodeModulesDir); - }; -} - const buildRoot = path.dirname(root); -@@ -734,3 +713,2 @@ BUILD_TARGETS.forEach(buildTarget => { +@@ -725,3 +705,2 @@ BUILD_TARGETS.forEach(buildTarget => { packageTask(platform, arch, sourceFolderName, destinationFolderName, opts), -- copyCopilotNativeDepsTask(platform, arch, destinationFolderName) +- prepareCopilotRipgrepShimTask(platform, arch, destinationFolderName) ]; -@@ -761,3 +739,2 @@ BUILD_TARGETS.forEach(buildTarget => { +@@ -752,3 +731,2 @@ BUILD_TARGETS.forEach(buildTarget => { compileNonNativeExtensionsBuildTask, - compileCopilotExtensionBuildTask, compileExtensionMediaBuildTask, -@@ -782,3 +759,2 @@ BUILD_TARGETS.forEach(buildTarget => { +@@ -773,3 +751,2 @@ BUILD_TARGETS.forEach(buildTarget => { compileNonNativeExtensionsBuildTask, - compileCopilotExtensionBuildTask, compileExtensionMediaBuildTask, @@ -98,7 +96,7 @@ index 74b5422b..db8df90a 100644 - 'extensions/copilot', 'extensions/css-language-features', diff --git a/build/npm/postinstall.ts b/build/npm/postinstall.ts -index db659fa7..ee169a6f 100644 +index dc154d4b..834a4a78 100644 --- a/build/npm/postinstall.ts +++ b/build/npm/postinstall.ts @@ -316,33 +316,2 @@ async function main() { @@ -136,187 +134,10 @@ index db659fa7..ee169a6f 100644 - } } diff --git a/package-lock.json b/package-lock.json -index 75b98719..86572201 100644 +index 69695f7e..df0fe9d4 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 @@ +@@ -2431,8 +2431,2 @@ }, - "node_modules/@pondwader/socks5-server": { - "version": "1.0.10", @@ -325,38 +146,11 @@ index 75b98719..86572201 100644 - "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 @@ +@@ -17227,2 +17221,3 @@ "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, "license": "MIT", -@@ -19610,11 +19416,2 @@ +@@ -19604,11 +19599,2 @@ }, - "node_modules/vscode-jsonrpc": { - "version": "8.2.1", @@ -368,12 +162,12 @@ index 75b98719..86572201 100644 - } - }, "node_modules/vscode-oniguruma": { -@@ -20328,2 +20125,3 @@ +@@ -20322,2 +20308,3 @@ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, "license": "MIT", diff --git a/package.json b/package.json -index b925a690..86fdd1f6 100644 +index e5549c39..c4eee403 100644 --- a/package.json +++ b/package.json @@ -23,3 +23,3 @@ @@ -387,140 +181,259 @@ index b925a690..86fdd1f6 100644 - "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", +@@ -83,4 +80,2 @@ + "perf:chat-leak": "node scripts/chat-simulation/test-chat-mem-leaks.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 @@ +@@ -91,5 +86,2 @@ "dependencies": { -- "@anthropic-ai/sandbox-runtime": "0.0.42", -- "@github/copilot": "^1.0.24", +- "@anthropic-ai/sandbox-runtime": "0.0.49", +- "@github/copilot": "^1.0.34", - "@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 +index e40bbb51..89931c4d 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'; +@@ -19,3 +19,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 { +@@ -108,3 +107,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 +index 6bf63cdb..e307f639 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'; +@@ -32,3 +32,2 @@ import { InstantiationService } from '../../instantiation/common/instantiationSe 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); +@@ -189,5 +188,2 @@ async function main(): Promise { + diServices.set(IAgentHostGitService, gitService); - 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 +index 14b27128..00000000 --- a/src/vs/platform/agentHost/node/copilot/copilotAgent.ts +++ /dev/null -@@ -1,696 +0,0 @@ +@@ -1,1288 +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 { CopilotClient, ResumeSessionConfig, type CopilotClientOptions, type SessionConfig } from '@github/copilot-sdk'; -import { rgPath } from '@vscode/ripgrep'; +-import * as fs from 'fs/promises'; -import { Limiter, SequencerByKey } from '../../../../base/common/async.js'; -import { Emitter } from '../../../../base/common/event.js'; +-import { appendEscapedMarkdownInlineCode } from '../../../../base/common/htmlContent.js'; -import { Disposable, DisposableMap } from '../../../../base/common/lifecycle.js'; +-import { ResourceMap } from '../../../../base/common/map.js'; -import { FileAccess } from '../../../../base/common/network.js'; --import { delimiter, dirname } from '../../../../base/common/path.js'; +-import { equals } from '../../../../base/common/objects.js'; +-import { observableValue } from '../../../../base/common/observable.js'; +-import { basename, delimiter, dirname } from '../../../../base/common/path.js'; -import { URI } from '../../../../base/common/uri.js'; -import { generateUuid } from '../../../../base/common/uuid.js'; +-import { localize } from '../../../../nls.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 { AgentSession, IAgent, IAgentAttachment, IAgentCreateSessionConfig, IAgentCreateSessionResult, IAgentDescriptor, IAgentDeltaEvent, IAgentMessageEvent, IAgentModelInfo, IAgentProgressEvent, IAgentResolveSessionConfigParams, IAgentSessionConfigCompletionsParams, IAgentSessionMetadata, IAgentSessionProjectInfo, SessionHistoryEvent } from '../../common/agentService.js'; +-import { AutoApproveLevel, ISchemaProperty, createSchema, platformSessionSchema, schemaProperty } from '../../common/agentHostSchema.js'; +-import { SessionConfigKey } from '../../common/sessionConfigKeys.js'; +-import { ISessionDataService, SESSION_DB_FILENAME } from '../../common/sessionDataService.js'; +-import type { ResolveSessionConfigResult, SessionConfigCompletionsResult } from '../../common/state/protocol/commands.js'; +-import { ProtectedResourceMetadata, type ConfigSchema, type ModelSelection, type ToolDefinition } from '../../common/state/protocol/state.js'; +-import { AHP_AUTH_REQUIRED, ProtocolError } from '../../common/state/sessionProtocol.js'; +-import { CustomizationStatus, CustomizationRef, SessionInputResponseKind, type PendingMessage, type SessionInputAnswer, type ToolCallResult, type PolicyState } from '../../common/state/sessionState.js'; +-import { IAgentHostGitService, META_DIFF_BASE_BRANCH } from '../agentHostGitService.js'; -import { IAgentHostTerminalManager } from '../agentHostTerminalManager.js'; +-import { CopilotAgentSession, SessionWrapperFactory, type IActiveClientSnapshot } from './copilotAgentSession.js'; -import { ICopilotSessionContext, projectFromCopilotContext } from './copilotGitProject.js'; +-import { parsedPluginsEqual, toSdkCustomAgents, toSdkHooks, toSdkMcpServers, toSdkSkillDirectories } from './copilotPluginConverters.js'; +-import { CopilotSessionWrapper } from './copilotSessionWrapper.js'; -import { createShellTools, ShellManager } from './copilotShellTools.js'; - +-interface ICreatedWorktree { +- readonly repositoryRoot: URI; +- readonly worktree: URI; +-} +- +-const ThinkingLevelConfigKey = 'thinkingLevel'; +-const ReasoningEfforts = ['low', 'medium', 'high', 'xhigh'] as const; +-type ReasoningEffort = NonNullable; +- +-interface ISerializedModelSelection { +- id?: unknown; +- config?: unknown; +-} +- +-/** +- * Narrow surface of {@link CopilotClient} used by this provider. The SDK class +- * has private members, so tests use this structural type to inject a fake. +- */ +-export interface ICopilotClient { +- start(): Promise; +- stop: CopilotClient['stop']; +- listSessions: CopilotClient['listSessions']; +- listModels: () => Promise; +- createSession: CopilotClient['createSession']; +- resumeSession: CopilotClient['resumeSession']; +- getSessionMetadata: CopilotClient['getSessionMetadata']; +- readonly rpc: { readonly sessions: { readonly fork: CopilotClient['rpc']['sessions']['fork'] } }; +-} +- +-/** +- * Corrected shape of {@link CopilotClient.listModels} entries. +- * +- * The SDK's `ModelInfo` type declares `capabilities`, `capabilities.limits`, +- * and `capabilities.limits.max_context_window_tokens` as required, but at +- * runtime synthetic entries (e.g. the `auto` router) ship with an empty +- * `capabilities: {}` object. We mirror the SDK fields we consume but mark the +- * unreliable parts as optional so callers must defensively handle them. +- */ +-export interface ICopilotModelInfo { +- readonly id: string; +- readonly name: string; +- readonly capabilities?: { +- readonly supports?: { readonly vision?: boolean }; +- readonly limits?: { readonly max_context_window_tokens?: number }; +- }; +- readonly policy?: { readonly state?: string }; +- readonly supportedReasoningEfforts?: readonly string[]; +- readonly defaultReasoningEffort?: string; +-} +- +-function isReasoningEffort(value: string | undefined): value is ReasoningEffort { +- return ReasoningEfforts.some(reasoningEffort => reasoningEffort === value); +-} +- +-export function getCopilotWorktreesRoot(repositoryRoot: URI): URI { +- return URI.joinPath(repositoryRoot, '..', `${basename(repositoryRoot.fsPath)}.worktrees`); +-} +- +-export function getCopilotWorktreeName(branchName: string): string { +- return branchName.replace(/\//g, '-'); +-} +- +-export function getCopilotWorktreeBranchName(sessionId: string, branchNameHint: string | undefined): string { +- return `agents/${branchNameHint ? `${branchNameHint}-${sessionId.substring(0, 8)}` : sessionId}`; +-} +- +-/** +- * Builds the localized "Created isolated worktree for branch X" markdown +- * shown at the top of the first response in worktree-isolated sessions. +- * The branch name is wrapped as inline code so the localized template +- * doesn't have to embed markdown punctuation. The trailing blank line +- * keeps the announcement visually separated when it gets merged into the +- * same markdown part as the model's reply. +- */ +-function buildWorktreeAnnouncementText(branchName: string): string { +- return localize( +- 'copilotAgent.worktreeCreated', +- "Created isolated worktree for branch {0}", +- appendEscapedMarkdownInlineCode(branchName) +- ) + '\n\n'; +-} +- +-type AgentMessageOrEvent = SessionHistoryEvent; +- +-/** +- * Returns a copy of `messages` where `announcement` has been prepended to +- * the first top-level assistant message's content. Subagent inner messages +- * (those with a `parentToolCallId`) are skipped so the announcement lands +- * on the parent turn. If no assistant message exists yet, returns the +- * messages unchanged — the live announcement path is responsible for the +- * very first turn before any reply has been recorded. +- */ +-function prependAnnouncementToFirstAssistantMessage( +- messages: readonly AgentMessageOrEvent[], +- announcement: string, +-): readonly AgentMessageOrEvent[] { +- const firstAssistantIdx = messages.findIndex(m => m.type === 'message' && m.role === 'assistant' && !m.parentToolCallId); +- if (firstAssistantIdx === -1) { +- return messages; +- } +- const target = messages[firstAssistantIdx] as IAgentMessageEvent; +- const updated: IAgentMessageEvent = { ...target, content: announcement + target.content }; +- return [ +- ...messages.slice(0, firstAssistantIdx), +- updated, +- ...messages.slice(firstAssistantIdx + 1), +- ]; +-} +- -/** - * Agent provider backed by the Copilot SDK {@link CopilotClient}. - */ -export class CopilotAgent extends Disposable implements IAgent { -- readonly id = 'copilot' as const; +- readonly id = 'copilotcli' as const; +- private static readonly _BRANCH_COMPLETION_LIMIT = 25; - - private readonly _onDidSessionProgress = this._register(new Emitter()); - readonly onDidSessionProgress = this._onDidSessionProgress.event; +- private readonly _models = observableValue(this, []); +- readonly models = this._models; - -- private _client: CopilotClient | undefined; -- private _clientStarting: Promise | undefined; +- private _client: ICopilotClient | undefined; +- private _clientStarting: Promise | undefined; - private _githubToken: string | undefined; - private readonly _sessions = this._register(new DisposableMap()); +- private readonly _createdWorktrees = new Map(); +- /** +- * Per-session announcement (markdown string) that should be emitted as +- * a synthetic streaming `delta` event the first time {@link sendMessage} +- * is called for the session. Currently used to surface the "Created +- * isolated worktree for branch X" message live during the first turn. +- * The same announcement is also injected on restore via +- * {@link getSessionMessages} by prepending to the first assistant +- * message's content so it stays visible after the session is reopened. +- */ +- private readonly _pendingFirstTurnAnnouncements = new Map(); - private readonly _sessionSequencer = new SequencerByKey(); +- private _shutdownPromise: Promise | undefined; - private readonly _plugins: PluginController; +- /** Per-session active client state for tools + plugin snapshot tracking. */ +- private readonly _activeClients = new ResourceMap(); - - constructor( - @ILogService private readonly _logService: ILogService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IFileService private readonly _fileService: IFileService, - @ISessionDataService private readonly _sessionDataService: ISessionDataService, +- @IAgentHostGitService private readonly _gitService: IAgentHostGitService, - @IAgentHostTerminalManager private readonly _terminalManager: IAgentHostTerminalManager, - ) { - super(); - this._plugins = this._instantiationService.createInstance(PluginController); - } - +- protected _createCopilotClient(options: CopilotClientOptions): ICopilotClient { +- return new CopilotClient(options); +- } +- - // ---- auth --------------------------------------------------------------- - - getDescriptor(): IAgentDescriptor { - return { -- provider: 'copilot', -- displayName: 'Copilot', +- provider: 'copilotcli', +- displayName: 'Copilot CLI', - description: 'Copilot SDK agent running in a dedicated process', - }; - } - -- getProtectedResources(): IProtectedResourceMetadata[] { +- getProtectedResources(): ProtectedResourceMetadata[] { - return [{ - resource: 'https://api.github.com', - resource_name: 'GitHub Copilot', @@ -539,25 +452,55 @@ index 565e4160..00000000 - 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(); +- await this._stopClient(); +- } +- if (tokenChanged) { +- void this._refreshModels(); - } - return true; - } - +- private async _refreshModels(): Promise { +- const tokenAtRefreshStart = this._githubToken; +- if (!tokenAtRefreshStart) { +- this._models.set([], undefined); +- return; +- } +- try { +- const models = await this._listModels(); +- if (this._githubToken === tokenAtRefreshStart) { +- this._models.set(models, undefined); +- } +- } catch (err) { +- this._logService.error(err, '[Copilot] Failed to refresh models'); +- if (this._githubToken === tokenAtRefreshStart) { +- this._models.set([], undefined); +- } +- } +- } +- +- private async _stopClient(): Promise { +- const client = this._client; +- this._client = undefined; +- this._clientStarting = undefined; +- await client?.stop(); +- } +- - // ---- client lifecycle --------------------------------------------------- - -- private async _ensureClient(): Promise { +- private async _ensureClient(): Promise { +- const tokenAtStartup = this._githubToken; +- if (!tokenAtStartup) { +- throw new ProtocolError(AHP_AUTH_REQUIRED, 'Authentication is required to use Copilot'); +- } - 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)'}`); +- const clientStarting = (async () => { +- this._logService.info('[Copilot] Starting CopilotClient... (with 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. @@ -593,138 +536,355 @@ index 565e4160..00000000 - 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, +- const client = this._createCopilotClient({ +- githubToken: tokenAtStartup, +- useLoggedInUser: false, - useStdio: true, - autoStart: true, - env, - cliPath, - }); - await client.start(); +- if (this._githubToken !== tokenAtStartup) { +- await client.stop(); +- throw new Error('Copilot authentication changed while the client was starting'); +- } - this._logService.info('[Copilot] CopilotClient started successfully'); - this._client = client; - this._clientStarting = undefined; - return client; - })(); -- return this._clientStarting; +- this._clientStarting = clientStarting; +- void clientStarting.catch(() => { +- this._clientStarting = undefined; +- }); +- return clientStarting; - } - - // ---- session management ------------------------------------------------- - +- private _createThinkingLevelConfigSchema(supportedReasoningEfforts: readonly string[] | undefined, defaultReasoningEffort: string | undefined): ConfigSchema | undefined { +- if (!supportedReasoningEfforts?.length) { +- return undefined; +- } +- +- const enumLabels = supportedReasoningEfforts.map(value => { +- switch (value) { +- case 'low': return localize('copilot.modelThinkingLevel.low', "Low"); +- case 'medium': return localize('copilot.modelThinkingLevel.medium', "Medium"); +- case 'high': return localize('copilot.modelThinkingLevel.high', "High"); +- case 'xhigh': return localize('copilot.modelThinkingLevel.xhigh', "Extra High"); +- default: return value; +- } +- }); +- +- return { +- type: 'object', +- properties: { +- [ThinkingLevelConfigKey]: { +- type: 'string', +- title: localize('copilot.modelThinkingLevel.title', "Thinking Level"), +- description: localize('copilot.modelThinkingLevel.description', "Controls how much reasoning effort the model uses."), +- default: defaultReasoningEffort, +- enum: [...supportedReasoningEfforts], +- enumLabels, +- }, +- }, +- }; +- } +- +- private _getReasoningEffort(model: ModelSelection | undefined): SessionConfig['reasoningEffort'] { +- const thinkingLevel = model?.config?.[ThinkingLevelConfigKey]; +- return isReasoningEffort(thinkingLevel) ? thinkingLevel : undefined; +- } +- +- private _serializeModelSelection(model: ModelSelection): string { +- return JSON.stringify(model); +- } +- +- private _parseModelSelection(raw: string | undefined): ModelSelection | undefined { +- if (!raw) { +- return undefined; +- } +- +- try { +- const value: ISerializedModelSelection | string | number | boolean | null = JSON.parse(raw); +- if (value && typeof value === 'object' && typeof value.id === 'string') { +- const modelSelection: ModelSelection = { id: value.id }; +- if (value.config && typeof value.config === 'object') { +- const config: Record = {}; +- for (const [key, configValue] of Object.entries(value.config)) { +- if (typeof configValue === 'string') { +- config[key] = configValue; +- } +- } +- if (Object.keys(config).length > 0) { +- modelSelection.config = config; +- } +- } +- return modelSelection; +- } +- } catch { +- // Older session metadata stored the raw model id as a plain string. +- } +- +- return { id: raw }; +- } +- - 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 mapped = await Promise.all(sessions.map(async s => { - const session = AgentSession.uri(this.id, s.sessionId); -- let { project, resolved } = await this._readSessionProject(session); +- const metadata = await this._readStoredSessionMetadata(session); +- if (!metadata) { +- return undefined; +- } +- let { project, resolved } = metadata; - if (!resolved) { - project = await this._resolveSessionProject(s.context, projectLimiter, projectByContext); -- this._storeSessionProjectResolution(session, project); +- void this._storeSessionProjectResolution(session, project); - } -- return { +- const workingDirectory = metadata.workingDirectory ?? (typeof s.context?.cwd === 'string' ? URI.file(s.context.cwd) : undefined); +- const result: IAgentSessionMetadata = { - session, - startTime: s.startTime.getTime(), - modifiedTime: s.modifiedTime.getTime(), -- ...(project ? { project } : {}), +- project, - summary: s.summary, -- workingDirectory: typeof s.context?.cwd === 'string' ? URI.file(s.context.cwd) : undefined, +- model: metadata.model, +- workingDirectory, - }; +- return result; - })); +- const result = mapped.filter((s): s is IAgentSessionMetadata => s !== undefined); - this._logService.info(`[Copilot] Found ${result.length} sessions`); - return result; - } - -- async listModels(): Promise { +- private async _listModels(): Promise { - this._logService.info('[Copilot] Listing models...'); - const client = await this._ensureClient(); - const models = await client.listModels(); -- const result = models.map(m => ({ +- const result = models.map((m): IAgentModelInfo => ({ - 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, +- // Synthetic SDK entries like `auto` ship with `capabilities: {}` and +- // no fixed context window — surface them with maxContextWindow undefined. +- maxContextWindow: m.capabilities?.limits?.max_context_window_tokens, +- supportsVision: !!m.capabilities?.supports?.vision, +- configSchema: this._createThinkingLevelConfigSchema(m.supportedReasoningEfforts, 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}` : ''}`); +- if (!config?.workingDirectory) { +- throw new Error('workingDirectory is required to create a Copilot session'); +- } +- +- this._logService.info(`[Copilot] Creating session... ${config?.model ? `model=${config.model.id}` : ''}`); - 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. +- // When forking, use the SDK's sessions.fork RPC. - 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. +- // modifications while we read its state. - return this._sessionSequencer.queue(sourceSessionId, async () => { -- this._logService.info(`[Copilot] Forking session ${sourceSessionId} at index ${config.fork!.turnIndex} → ${newSessionId}`); +- this._logService.info(`[Copilot] Forking session ${sourceSessionId} at turnId=${config.fork!.turnId}`); - -- // Ensure the source session is loaded so on-disk data is available -- if (!this._sessions.has(sourceSessionId)) { -- await this._resumeSession(sourceSessionId); -- } +- // Ensure the source session is loaded so we can read its event IDs +- const sourceEntry = this._sessions.get(sourceSessionId) ?? await this._resumeSession(sourceSessionId); +- +- // Look up the SDK event ID for the turn *after* the fork point. +- // toEventId is exclusive — events before it are included. +- // If there's no next turn, omit toEventId to include all events. +- const toEventId = await sourceEntry.getNextTurnEventId(config.fork!.turnId); +- +- const forkResult = await client.rpc.sessions.fork({ +- sessionId: sourceSessionId, +- ...(toEventId ? { toEventId } : {}), +- }); +- const newSessionId = forkResult.sessionId; - -- const copilotDataDir = getCopilotDataDir(); -- await forkCopilotSessionOnDisk(copilotDataDir, sourceSessionId, newSessionId, config.fork!.turnIndex); +- // Copy the source session's database using VACUUM INTO so the +- // forked session inherits turn event IDs and file-edit snapshots. +- // VACUUM INTO is safe even while the source DB is open. +- const targetDbDir = this._sessionDataService.getSessionDataDirById(newSessionId); +- const targetDbPath = URI.joinPath(targetDbDir, SESSION_DB_FILENAME); +- try { +- const sourceDbRef = await this._sessionDataService.tryOpenDatabase(config.fork!.session); +- if (sourceDbRef) { +- try { +- await fs.mkdir(targetDbDir.fsPath, { recursive: true }); +- await sourceDbRef.object.vacuumInto(targetDbPath.fsPath); +- } finally { +- sourceDbRef.dispose(); +- } +- } +- } catch (err) { +- this._logService.warn(`[Copilot] Failed to copy session database for fork: ${err instanceof Error ? err.message : String(err)}`); +- } - - // Resume the forked session so the SDK loads the forked history - const agentSession = await this._resumeSession(newSessionId); +- +- // Remap turn IDs to match the new protocol turn IDs +- if (config.fork!.turnIdMapping) { +- await agentSession.remapTurnIds(config.fork!.turnIdMapping); +- } +- - 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 project = await projectFromCopilotContext({ cwd: config.workingDirectory?.fsPath }, this._gitService); +- await this._storeSessionMetadata(session, config.model, config.workingDirectory, project, true); +- return { session, workingDirectory: config.workingDirectory, ...(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); +- let seededActiveClient = false; +- if (config?.activeClient) { +- const ac = this._getOrCreateActiveClient(sessionUri); +- seededActiveClient = true; +- ac.updateTools(config.activeClient.clientId, config.activeClient.tools); +- if (config.activeClient.customizations !== undefined) { +- await this._plugins.sync(config.activeClient.clientId, config.activeClient.customizations); +- } +- } +- const activeClient = this._activeClients.get(sessionUri); +- const snapshot = activeClient ? await activeClient.snapshot() : undefined; +- const workingDirectory = await this._resolveSessionWorkingDirectory(config, sessionId); +- const shellManager = this._instantiationService.createInstance(ShellManager, sessionUri, workingDirectory); +- const sessionConfig = this._buildSessionConfig(snapshot, shellManager); - - const factory: SessionWrapperFactory = async callbacks => { - const raw = await client.createSession({ -- model: config?.model, +- model: config?.model?.id, +- reasoningEffort: this._getReasoningEffort(config?.model), - sessionId, - streaming: true, -- workingDirectory: config?.workingDirectory?.fsPath, +- workingDirectory: 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(); -- +- let agentSession: CopilotAgentSession; +- try { +- agentSession = this._createAgentSession(factory, sessionId, shellManager, workingDirectory, snapshot); +- await agentSession.initializeSession(); +- } catch (error) { +- if (seededActiveClient) { +- this._activeClients.delete(sessionUri); +- } +- await this._removeCreatedWorktree(sessionId); +- throw error; +- } - const session = agentSession.sessionUri; - this._logService.info(`[Copilot] Session created: ${session.toString()}`); -- const project = await projectFromCopilotContext({ cwd: config?.workingDirectory?.fsPath }); +- const project = await projectFromCopilotContext({ cwd: workingDirectory?.fsPath }, this._gitService); - // 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 } : {}) }; +- await this._storeSessionMetadata(agentSession.sessionUri, config?.model, workingDirectory, project, true); +- return { session, workingDirectory, ...(project ? { project } : {}) }; +- } +- +- async resolveSessionConfig(params: IAgentResolveSessionConfigParams): Promise { +- const gitInfo = params.workingDirectory ? await this._getGitInfo(params.workingDirectory) : undefined; +- +- const isolationProperty = schemaProperty<'folder' | 'worktree'>({ +- type: 'string', +- title: localize('agentHost.sessionConfig.isolation', "Isolation"), +- description: localize('agentHost.sessionConfig.isolationDescription', "Where the agent should make changes"), +- enum: gitInfo ? ['folder', 'worktree'] : ['folder'], +- enumLabels: gitInfo ? [localize('agentHost.sessionConfig.isolation.folder', "Folder"), localize('agentHost.sessionConfig.isolation.worktree', "Worktree")] : [localize('agentHost.sessionConfig.isolation.folder', "Folder")], +- enumDescriptions: gitInfo ? [localize('agentHost.sessionConfig.isolation.folderDescription', "Work directly in the folder"), localize('agentHost.sessionConfig.isolation.worktreeDescription', "Create a Git worktree for isolation")] : [localize('agentHost.sessionConfig.isolation.folderDescription', "Work directly in the folder")], +- default: gitInfo ? 'worktree' : 'folder', +- readOnly: !gitInfo, +- }); +- +- // Resolve isolation first — downstream schema shapes (branch's +- // read-only mode + enum restriction) depend on the effective value. +- const isolationDefault: 'folder' | 'worktree' = gitInfo ? 'worktree' : 'folder'; +- const isolationValue = isolationProperty.validate(params.config?.[SessionConfigKey.Isolation]) +- ? params.config[SessionConfigKey.Isolation] as 'folder' | 'worktree' +- : isolationDefault; +- +- let branchProperty: ISchemaProperty | undefined; +- let branchDefault: string | undefined; +- if (gitInfo) { +- const branchReadOnly = isolationValue === 'folder'; +- branchDefault = isolationValue === 'worktree' ? gitInfo.defaultBranch : gitInfo.currentBranch; +- branchProperty = schemaProperty({ +- type: 'string', +- title: localize('agentHost.sessionConfig.branch', "Branch"), +- description: localize('agentHost.sessionConfig.branchDescription', "Base branch to work from"), +- enum: [branchDefault], +- enumLabels: [branchDefault], +- default: branchDefault, +- enumDynamic: !branchReadOnly, +- readOnly: branchReadOnly, +- }); +- } +- +- const sessionSchema = createSchema({ +- [SessionConfigKey.Isolation]: isolationProperty, +- ...platformSessionSchema.definition, +- ...(branchProperty ? { [SessionConfigKey.Branch]: branchProperty } : {}), +- }); +- +- const values = sessionSchema.validateOrDefault(params.config, { +- [SessionConfigKey.Isolation]: isolationValue, +- [SessionConfigKey.AutoApprove]: 'default' satisfies AutoApproveLevel, +- // Permissions intentionally omitted — leave unset so auto-approval +- // falls through to the host-level `permissions` default, and only +- // materializes on the session once the user hits "Allow in this +- // Session". +- ...(branchDefault !== undefined ? { [SessionConfigKey.Branch]: branchDefault } : {}), +- }); +- +- return { +- schema: sessionSchema.toProtocol(), +- values, +- }; +- } +- +- async sessionConfigCompletions(params: IAgentSessionConfigCompletionsParams): Promise { +- if (params.property !== 'branch' || !params.workingDirectory) { +- return { items: [] }; +- } +- +- const branches = await this._getBranches(params.workingDirectory, params.query); +- return { items: branches.map(branch => ({ value: branch, label: branch })) }; - } - -- async setClientCustomizations(clientId: string, customizations: ICustomizationRef[], progress?: (results: ISyncedCustomization[]) => void): Promise { +- async setClientCustomizations(clientId: string, customizations: CustomizationRef[], progress?: (results: ISyncedCustomization[]) => void): Promise { - return this._plugins.sync(clientId, customizations, progress); - } - +- setClientTools(session: URI, clientId: string, tools: ToolDefinition[]): void { +- const sessionId = AgentSession.id(session); +- const activeClient = this._getOrCreateActiveClient(session); +- const hasCachedEntry = this._sessions.has(sessionId); +- this._logService.info(`[Copilot:${sessionId}] setClientTools: clientId=${clientId}, tools=[${tools.map(t => t.name).join(', ') || '(none)'}], hasCachedSdkSession=${hasCachedEntry}`); +- activeClient.updateTools(clientId, tools); +- } +- +- onClientToolCallComplete(session: URI, toolCallId: string, result: ToolCallResult): void { +- const entry = this._sessions.get(AgentSession.id(session)); +- entry?.handleClientToolCallComplete(toolCallId, result); +- } +- - setCustomizationEnabled(uri: string, enabled: boolean): void { - this._plugins.setEnabled(uri, enabled); - } @@ -733,21 +893,49 @@ index 565e4160..00000000 - 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. +- // If the active client's config changed (tools or plugins), +- // dispose this session so it gets resumed with the updated config. - let entry = this._sessions.get(sessionId); -- if (entry && await this._plugins.needsSessionRefresh(entry)) { -- this._logService.info(`[Copilot:${sessionId}] Plugin config changed, refreshing session`); +- const activeClient = this._activeClients.get(session); +- const hadCachedEntry = !!entry; +- this._logService.info(`[Copilot:${sessionId}] sendMessage: cachedEntry=${hadCachedEntry}, hasActiveClient=${!!activeClient}, activeClientId=${activeClient ? '(set)' : '(none)'}`); +- if (entry && activeClient && await activeClient.isOutdated(entry.appliedSnapshot)) { +- this._logService.info(`[Copilot:${sessionId}] Session config changed (isOutdated=true), refreshing session. snapshotClientId=${entry.appliedSnapshot.clientId}`); - this._sessions.deleteAndDispose(sessionId); - entry = undefined; - } - +- if (!entry) { +- this._logService.info(`[Copilot:${sessionId}] No cached entry${hadCachedEntry ? ' (was evicted by isOutdated)' : ''}, calling _resumeSession`); +- } - entry ??= await this._resumeSession(sessionId); -- await entry.send(prompt, attachments, turnId); +- +- // Emit any pending first-turn announcements (e.g. worktree +- // created) as a synthetic streaming delta before delegating to +- // the SDK. The mapper treats it like any other assistant text — +- // the SDK's subsequent deltas append to the same markdown part. +- // The active turn has already been started by the state manager +- // at this point, so the event mapper can attach the delta to it. +- const pending = this._pendingFirstTurnAnnouncements.get(sessionId); +- if (pending) { +- this._pendingFirstTurnAnnouncements.delete(sessionId); +- const messageId = `copilot-announcement-${generateUuid()}`; +- const event: IAgentDeltaEvent = { type: 'delta', session, messageId, content: pending }; +- this._onDidSessionProgress.fire(event); +- } +- +- try { +- await entry.send(prompt, attachments, turnId); +- } catch (err) { +- const errCode = (err as { code?: number })?.code; +- const errMsg = err instanceof Error ? err.message : String(err); +- this._logService.error(`[Copilot:${sessionId}] entry.send() failed: code=${errCode}, message=${errMsg}, hadCachedEntry=${hadCachedEntry}, errorType=${err?.constructor?.name}`); +- throw err; +- } - }); - } - -- setPendingMessages(session: URI, steeringMessage: IPendingMessage | undefined, _queuedMessages: readonly IPendingMessage[]): void { +- setPendingMessages(session: URI, steeringMessage: PendingMessage | undefined, _queuedMessages: readonly PendingMessage[]): void { - const sessionId = AgentSession.id(session); - const entry = this._sessions.get(sessionId); - if (!entry) { @@ -765,19 +953,37 @@ index 565e4160..00000000 - // No SDK-level enqueue is needed. - } - -- async getSessionMessages(session: URI): Promise<(IAgentMessageEvent | IAgentToolStartEvent | IAgentToolCompleteEvent | IAgentSubagentStartedEvent)[]> { +- async getSessionMessages(session: URI): Promise { - const sessionId = AgentSession.id(session); -- const entry = this._sessions.get(sessionId) ?? await this._resumeSession(sessionId).catch(() => undefined); +- const entry = this._sessions.get(sessionId) ?? await this._resumeSession(sessionId).catch(err => { +- this._logService.warn(`[Copilot:${sessionId}] Failed to resume session for message lookup`, err); +- return undefined; +- }); - if (!entry) { - return []; - } -- return entry.getMessages(); +- const rawMessages = await entry.getMessages(); +- +- // If a worktree was created for this session at create-time, prepend +- // the announcement to the first assistant message's content so it +- // appears at the top of the first response when the session is +- // reopened. The live path (sendMessage) handles the very first turn +- // when the session is fresh; this path takes over on subsequent +- // loads, where _pendingFirstTurnAnnouncements is empty. +- const branchName = await this._readWorktreeBranchMetadata(session).catch(err => { +- this._logService.warn(`[Copilot:${sessionId}] Failed to read worktree branch metadata`, err); +- return undefined; +- }); +- if (!branchName) { +- return rawMessages; +- } +- return [...prependAnnouncementToFirstAssistantMessage(rawMessages, buildWorktreeAnnouncementText(branchName))]; - } - - async disposeSession(session: URI): Promise { - const sessionId = AgentSession.id(session); - await this._sessionSequencer.queue(sessionId, async () => { -- this._sessions.deleteAndDispose(sessionId); +- await this._destroyAndDisposeSession(sessionId); - }); - } - @@ -791,56 +997,56 @@ index 565e4160..00000000 - }); - } - -- async truncateSession(session: URI, turnIndex?: number): Promise { +- async truncateSession(session: URI, turnId?: string): 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._logService.info(`[Copilot:${sessionId}] Truncating session${turnId !== undefined ? ` at turnId=${turnId}` : ' (all turns)'}`); +- +- // Ensure the session is loaded so we can use the SDK RPC +- const entry = this._sessions.get(sessionId) ?? await this._resumeSession(sessionId); +- +- // Look up the SDK event ID for the truncation boundary. +- // The protocol semantics: turnId is the last turn to KEEP. +- // The SDK semantics: eventId and all events after it are removed. +- // So we need the event ID of the *next* turn after turnId. +- // For "remove all", we need the first turn's event ID. +- let eventId: string | undefined; +- if (turnId) { +- eventId = await entry.getNextTurnEventId(turnId); +- } else { +- eventId = await entry.getFirstTurnEventId(); - } -- 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}`); +- if (eventId) { +- await entry.truncateAtEventId(eventId, turnId); +- } else { +- this._logService.info(`[Copilot:${sessionId}] No event ID found for truncation, nothing to truncate`); +- } - -- const copilotDataDir = getCopilotDataDir(); -- await forkCopilotSessionOnDisk(copilotDataDir, sourceSessionId, newSessionId, turnIndex); -- this._logService.info(`[Copilot] Forked session ${newSessionId} created on disk`); +- this._logService.info(`[Copilot:${sessionId}] Session truncated`); - }); - } - -- async changeModel(session: URI, model: string): Promise { +- async changeModel(session: URI, model: ModelSelection): Promise { - const sessionId = AgentSession.id(session); - const entry = this._sessions.get(sessionId); - if (entry) { -- await entry.setModel(model); +- await entry.setModel(model.id, this._getReasoningEffort(model)); - } -- this._storeSessionMetadata(session, model, undefined, undefined); +- await 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; +- this._shutdownPromise ??= (async () => { +- this._logService.info('[Copilot] Shutting down...'); +- const sessionIds = new Set([...this._sessions.keys(), ...this._createdWorktrees.keys()]); +- for (const sessionId of sessionIds) { +- await this._sessionSequencer.queue(sessionId, () => this._destroyAndDisposeSession(sessionId)); +- } +- await this._client?.stop(); +- this._client = undefined; +- })(); +- return this._shutdownPromise; - } - - respondToPermissionRequest(requestId: string, approved: boolean): void { @@ -851,7 +1057,7 @@ index 565e4160..00000000 - } - } - -- respondToUserInputRequest(requestId: string, response: SessionInputResponseKind, answers?: Record): void { +- respondToUserInputRequest(requestId: string, response: SessionInputResponseKind, answers?: Record): void { - for (const [, session] of this._sessions) { - if (session.respondToUserInputRequest(requestId, response, answers)) { - return; @@ -868,28 +1074,53 @@ index 565e4160..00000000 - - // ---- helpers ------------------------------------------------------------ - +- private _getOrCreateActiveClient(session: URI): ActiveClient { +- let client = this._activeClients.get(session); +- if (!client) { +- client = new ActiveClient(() => this._plugins.getAppliedPlugins()); +- this._activeClients.set(session, client); +- } +- return client; +- } +- - /** - * 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 { +- private _createAgentSession(wrapperFactory: SessionWrapperFactory, sessionId: string, shellManager: ShellManager, workingDirectory: URI | undefined, snapshot?: IActiveClientSnapshot): CopilotAgentSession { - const sessionUri = AgentSession.uri(this.id, sessionId); - - const agentSession = this._instantiationService.createInstance( - CopilotAgentSession, -- sessionUri, -- sessionId, -- workingDirectory, -- this._onDidSessionProgress, -- wrapperFactory, -- shellManager, +- { +- sessionUri, +- rawSessionId: sessionId, +- onDidSessionProgress: this._onDidSessionProgress, +- wrapperFactory, +- shellManager, +- workingDirectory, +- clientSnapshot: snapshot, +- }, - ); - - this._sessions.set(sessionId, agentSession); - return agentSession; - } - +- private async _destroyAndDisposeSession(sessionId: string): Promise { +- const entry = this._sessions.get(sessionId); +- if (entry) { +- try { +- await entry.destroySession(); +- } catch (error) { +- this._logService.warn(`[Copilot:${sessionId}] Failed to destroy session before cleanup: ${error instanceof Error ? error.message : String(error)}`); +- } +- } +- this._sessions.deleteAndDispose(sessionId); +- await this._removeCreatedWorktree(sessionId); +- } +- - /** - * Builds the common session configuration (plugins + shell tools) shared - * by both {@link createSession} and {@link _resumeSession}. @@ -898,74 +1129,148 @@ index 565e4160..00000000 - * session's permission/hook callbacks, so it can be called lazily - * inside the {@link SessionWrapperFactory}. - */ -- private _buildSessionConfig(parsedPlugins: readonly IParsedPlugin[], shellManager: ShellManager) { +- private _buildSessionConfig(snapshot: IActiveClientSnapshot | undefined, shellManager: ShellManager): (args: Parameters[0]) => Promise { - const shellTools = createShellTools(shellManager, this._terminalManager, this._logService); +- const plugins = snapshot?.plugins ?? []; - - return async (callbacks: Parameters[0]) => { -- const customAgents = await toSdkCustomAgents(parsedPlugins.flatMap(p => p.agents), this._fileService); +- const customAgents = await toSdkCustomAgents(plugins.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)), +- hooks: toSdkHooks(plugins.flatMap(p => p.hooks), callbacks.hooks), +- mcpServers: toSdkMcpServers(plugins.flatMap(p => p.mcpServers)), - customAgents, -- skillDirectories: toSdkSkillDirectories(parsedPlugins.flatMap(p => p.skills)), -- tools: shellTools, +- skillDirectories: toSdkSkillDirectories(plugins.flatMap(p => p.skills)), +- tools: [...shellTools, ...callbacks.clientTools], - }; - }; - } - -- private async _resumeSession(sessionId: string): Promise { -- this._logService.info(`[Copilot:${sessionId}] Session not in memory, resuming...`); +- protected async _resumeSession(sessionId: string): Promise { +- this._logService.info(`[Copilot:${sessionId}] _resumeSession called — session not in memory, resuming...`); - const client = await this._ensureClient(); -- const parsedPlugins = await this._plugins.getAppliedPlugins(); - - const sessionUri = AgentSession.uri(this.id, sessionId); +- const activeClient = this._activeClients.get(sessionUri); +- const snapshot = activeClient ? await activeClient.snapshot() : undefined; +- const storedMetadata = await this._readSessionMetadata(sessionUri); - 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 workingDirectory = storedMetadata.workingDirectory ?? (typeof sessionMetadata?.context?.cwd === 'string' ? URI.file(sessionMetadata.context.cwd) : undefined); +- if (!workingDirectory) { +- throw new Error(`workingDirectory is required to resume Copilot session '${sessionId}'`); +- } +- +- const shellManager = this._instantiationService.createInstance(ShellManager, sessionUri, workingDirectory); +- const sessionConfig = this._buildSessionConfig(snapshot, shellManager); - - const factory: SessionWrapperFactory = async callbacks => { - const config = await sessionConfig(callbacks); - try { +- this._logService.info(`[Copilot:${sessionId}] Calling SDK resumeSession...`); - const raw = await client.resumeSession(sessionId, { - ...config, - workingDirectory: workingDirectory?.fsPath, - }); +- this._logService.info(`[Copilot:${sessionId}] SDK resumeSession succeeded`); - return new CopilotSessionWrapper(raw); - } catch (err) { +- const errCode = (err as { code?: number })?.code; +- const errMsg = err instanceof Error ? err.message : String(err); +- this._logService.warn(`[Copilot:${sessionId}] SDK resumeSession failed: code=${errCode}, message=${errMsg}`); - // 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) { +- if (!err || errCode !== -32603) { - throw err; - } - -- this._logService.warn(`[Copilot:${sessionId}] Resume failed (session not found in SDK), recreating`); -- const metadata = await this._readSessionMetadata(sessionUri); +- this._logService.warn(`[Copilot:${sessionId}] Resume failed (code=-32603), falling back to createSession with same ID`); - const raw = await client.createSession({ - ...config, - sessionId, - streaming: true, -- model: metadata.model, -- workingDirectory: metadata.workingDirectory?.fsPath, +- model: storedMetadata.model?.id, +- reasoningEffort: this._getReasoningEffort(storedMetadata.model), +- workingDirectory: workingDirectory?.fsPath, - }); +- this._logService.info(`[Copilot:${sessionId}] Fallback createSession succeeded`); - - return new CopilotSessionWrapper(raw); - } - }; - -- const agentSession = this._createAgentSession(factory, workingDirectory, sessionId, shellManager); -- this._plugins.setAppliedPlugins(agentSession, parsedPlugins); +- const agentSession = this._createAgentSession(factory, sessionId, shellManager, workingDirectory, snapshot); - await agentSession.initializeSession(); - - return agentSession; - } - +- private async _getGitInfo(workingDirectory: URI): Promise<{ currentBranch: string; defaultBranch: string } | undefined> { +- if (!await this._gitService.isInsideWorkTree(workingDirectory)) { +- return undefined; +- } +- +- const currentBranch = await this._gitService.getCurrentBranch(workingDirectory) ?? 'HEAD'; +- const defaultBranch = await this._gitService.getDefaultBranch(workingDirectory) ?? currentBranch; +- return { currentBranch, defaultBranch }; +- } +- +- private async _getBranches(workingDirectory: URI, query?: string): Promise { +- return this._gitService.getBranches(workingDirectory, { query, limit: CopilotAgent._BRANCH_COMPLETION_LIMIT }); +- } +- +- protected async _resolveSessionWorkingDirectory(config: IAgentCreateSessionConfig | undefined, sessionId: string): Promise { +- if (config?.config?.isolation !== 'worktree' || !config.workingDirectory || typeof config.config.branch !== 'string') { +- return config?.workingDirectory; +- } +- +- const repositoryRoot = await this._gitService.getRepositoryRoot(config.workingDirectory); +- if (!repositoryRoot) { +- return config.workingDirectory; +- } +- +- const worktreesRoot = getCopilotWorktreesRoot(repositoryRoot); +- const branchNameHintRaw = config.config[SessionConfigKey.BranchNameHint]; +- const branchNameHint = typeof branchNameHintRaw === 'string' ? branchNameHintRaw : undefined; +- const branchName = getCopilotWorktreeBranchName(sessionId, branchNameHint); +- const worktree = URI.joinPath(worktreesRoot, getCopilotWorktreeName(branchName)); +- await fs.mkdir(worktreesRoot.fsPath, { recursive: true }); +- const baseBranch = typeof config.config[SessionConfigKey.Branch] === 'string' ? config.config[SessionConfigKey.Branch] as string : undefined; +- // `addWorktree`'s signature requires a startPoint, but historically the +- // runtime accepted undefined when `branch` was not set in config. Preserve +- // that behavior by passing through whatever value (or undefined) was set. +- await this._gitService.addWorktree(repositoryRoot, worktree, branchName, baseBranch as string); +- this._createdWorktrees.set(sessionId, { repositoryRoot, worktree }); +- // Queue the worktree announcement so the first turn (live) and any +- // subsequent restore (history) both surface the message in the chat. +- this._pendingFirstTurnAnnouncements.set(sessionId, buildWorktreeAnnouncementText(branchName)); +- const sessionUri = AgentSession.uri(this.id, sessionId); +- try { +- await this._writeWorktreeBranchMetadata(sessionUri, branchName, baseBranch); +- } catch (error) { +- this._logService.warn(`[Copilot:${sessionId}] Failed to persist worktree branch metadata: ${error instanceof Error ? error.message : String(error)}`); +- } +- return worktree; +- } +- +- private async _removeCreatedWorktree(sessionId: string): Promise { +- const worktree = this._createdWorktrees.get(sessionId); +- if (!worktree) { +- return; +- } +- try { +- await this._gitService.removeWorktree(worktree.repositoryRoot, worktree.worktree); +- } catch (error) { +- this._logService.warn(`[Copilot:${sessionId}] Failed to remove worktree '${worktree.worktree.fsPath}': ${error instanceof Error ? error.message : String(error)}`); +- } finally { +- this._createdWorktrees.delete(sessionId); +- } +- } +- - // ---- session metadata persistence -------------------------------------- - - private static readonly _META_MODEL = 'copilot.model'; @@ -973,33 +1278,59 @@ index 565e4160..00000000 - 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 static readonly _META_WORKTREE_BRANCH = 'copilot.worktree.branchName'; - -- 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())); +- private async _writeWorktreeBranchMetadata(session: URI, branchName: string, baseBranch: string | undefined): Promise { +- const dbRef = this._sessionDataService.openDatabase(session); +- try { +- const work: Promise[] = [dbRef.object.setMetadata(CopilotAgent._META_WORKTREE_BRANCH, branchName)]; +- if (baseBranch) { +- work.push(dbRef.object.setMetadata(META_DIFF_BASE_BRANCH, baseBranch)); - } -- if (projectResolved) { -- work.push(db.setMetadata(CopilotAgent._META_PROJECT_RESOLVED, 'true')); +- await Promise.all(work); +- } finally { +- dbRef.dispose(); +- } +- } +- +- private async _readWorktreeBranchMetadata(session: URI): Promise { +- const ref = await this._sessionDataService.tryOpenDatabase(session); +- if (!ref) { +- return undefined; +- } +- try { +- const value = await ref.object.getMetadata(CopilotAgent._META_WORKTREE_BRANCH); +- return value ?? undefined; +- } finally { +- ref.dispose(); +- } +- } +- +- private async _storeSessionMetadata(session: URI, model: ModelSelection | undefined, workingDirectory: URI | undefined, project: IAgentSessionProjectInfo | undefined, projectResolved = project !== undefined): Promise { +- const dbRef = this._sessionDataService.openDatabase(session); +- const db = dbRef.object; +- try { +- const work: Promise[] = []; +- if (model) { +- work.push(db.setMetadata(CopilotAgent._META_MODEL, this._serializeModelSelection(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()); -- }); +- await Promise.all(work); +- } finally { +- dbRef.dispose(); +- } - } - -- private async _readSessionMetadata(session: URI): Promise<{ model?: string; workingDirectory?: URI }> { +- private async _readSessionMetadata(session: URI): Promise<{ model?: ModelSelection; workingDirectory?: URI }> { - const ref = await this._sessionDataService.tryOpenDatabase(session); - if (!ref) { - return {}; @@ -1010,7 +1341,7 @@ index 565e4160..00000000 - ref.object.getMetadata(CopilotAgent._META_CWD), - ]); - return { -- model, +- model: this._parseModelSelection(model), - workingDirectory: cwd ? URI.parse(cwd) : undefined, - }; - } finally { @@ -1018,26 +1349,29 @@ index 565e4160..00000000 - } - } - -- private async _readSessionProject(session: URI): Promise<{ project?: IAgentSessionProjectInfo; resolved: boolean }> { +- private async _readStoredSessionMetadata(session: URI): Promise<{ model?: ModelSelection; workingDirectory?: URI; project?: IAgentSessionProjectInfo; resolved: boolean } | undefined> { - const ref = await this._sessionDataService.tryOpenDatabase(session); - if (!ref) { -- return { resolved: false }; +- return undefined; - } - try { -- const [resolved, uri, displayName] = await Promise.all([ +- const [model, cwd, resolved, uri, displayName] = await Promise.all([ +- ref.object.getMetadata(CopilotAgent._META_MODEL), +- ref.object.getMetadata(CopilotAgent._META_CWD), - ref.object.getMetadata(CopilotAgent._META_PROJECT_RESOLVED), - ref.object.getMetadata(CopilotAgent._META_PROJECT_URI), - ref.object.getMetadata(CopilotAgent._META_PROJECT_DISPLAY_NAME), - ]); +- const workingDirectory = cwd ? URI.parse(cwd) : undefined; - const project = uri && displayName ? { uri: URI.parse(uri), displayName } : undefined; -- return { project, resolved: resolved === 'true' || project !== undefined }; +- return { model: this._parseModelSelection(model), workingDirectory, 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 async _storeSessionProjectResolution(session: URI, project: IAgentSessionProjectInfo | undefined): Promise { +- await this._storeSessionMetadata(session, undefined, undefined, project, true); - } - - private _resolveSessionProject(context: ICopilotSessionContext | undefined, limiter: Limiter, projectByContext: Map>): Promise { @@ -1048,7 +1382,7 @@ index 565e4160..00000000 - - let project = projectByContext.get(key); - if (!project) { -- project = limiter.queue(() => projectFromCopilotContext(context)); +- project = limiter.queue(() => projectFromCopilotContext(context, this._gitService)); - projectByContext.set(key, project); - } - return project; @@ -1068,8 +1402,9 @@ index 565e4160..00000000 - } - - override dispose(): void { -- this._client?.stop().catch(() => { /* best-effort */ }); -- super.dispose(); +- this.shutdown().catch(err => { +- this._logService.warn('[Copilot] Shutdown failed during dispose', err); +- }).finally(() => super.dispose()); - } -} - @@ -1077,9 +1412,6 @@ index 565e4160..00000000 - 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, @@ -1087,36 +1419,22 @@ index 565e4160..00000000 - ) { } - - /** -- * 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. +- * Returns the current parsed plugins, awaiting any pending sync. - */ - 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) { +- public sync(clientId: string, customizations: CustomizationRef[], progress?: (results: ISyncedCustomization[]) => void) { - const prev = this._lastSynced; -- const promise = this._lastSynced = prev.catch(() => []).then(async () => { +- const promise = this._lastSynced = prev.catch(err => { +- this._logService.warn('[Copilot:PluginController] Previous customization sync failed', err); +- }).then(async () => { - const result = await this._pluginManager.syncCustomizations(clientId, customizations, status => { - progress?.(status.map(c => ({ customization: c }))); - }); @@ -1148,867 +1466,368 @@ index 565e4160..00000000 - 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`. +- * Tracks per-session active client contributions (tools and plugins). +- * The {@link snapshot} captures the state at session creation time, and +- * {@link isOutdated} detects when the session needs to be refreshed. - */ --export interface ICopilotEventLogEntry { -- readonly type: string; -- readonly data: Record; -- readonly id: string; -- readonly timestamp: string; -- readonly parentId: string | null; --} +-class ActiveClient { +- private _tools: readonly ToolDefinition[] = []; +- private _clientId = ''; - --// ---- Promise wrappers around callback-based @vscode/sqlite3 API ----------- +- constructor( +- /** Resolves the current set of applied plugins. May block while a sync is in progress. */ +- private readonly _resolvePlugins: () => Promise, +- ) { } - --function dbExec(db: Database, sql: string): Promise { -- return new Promise((resolve, reject) => { -- db.exec(sql, err => err ? reject(err) : resolve()); -- }); --} +- updateTools(clientId: string, tools: readonly ToolDefinition[]): void { +- this._clientId = clientId; +- this._tools = tools; +- } - --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(); -- }); -- }); --} +- async snapshot(): Promise { +- return { clientId: this._clientId, tools: this._tools, plugins: await this._resolvePlugins() }; +- } - --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); +- async isOutdated(snap: IActiveClientSnapshot): Promise { +- const plugins = await this._resolvePlugins(); +- if (!parsedPluginsEqual(snap.plugins, plugins)) { +- return true; +- } +- if (snap.tools.length !== this._tools.length) { +- return true; +- } +- // Compare tool definitions by name, description, and schema — +- // not just names — so schema/description changes trigger a refresh. +- const snapByName = new Map(snap.tools.map(t => [t.name, t])); +- for (const tool of this._tools) { +- const prev = snapByName.get(tool.name); +- if (!prev +- || prev.description !== tool.description +- || !equals(prev.inputSchema, tool.inputSchema)) { +- return true; - } -- resolve(rows); -- }); -- }); +- } +- return false; +- } -} +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 4555cb5b..00000000 +--- a/src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts ++++ /dev/null +@@ -1,1076 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ - --function dbClose(db: Database): Promise { -- return new Promise((resolve, reject) => { -- db.close(err => err ? reject(err) : resolve()); -- }); --} +-import type { PermissionRequestResult, SessionConfig, Tool, ToolResultObject } from '@github/copilot-sdk'; +-import { DeferredPromise } from '../../../../base/common/async.js'; +-import { VSBuffer } from '../../../../base/common/buffer.js'; +-import { Emitter } from '../../../../base/common/event.js'; +-import { Disposable, IReference, toDisposable } from '../../../../base/common/lifecycle.js'; +-import { join } from '../../../../base/common/path.js'; +-import { extUriBiasedIgnorePathCase, normalizePath } from '../../../../base/common/resources.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { generateUuid } from '../../../../base/common/uuid.js'; +-import type { IParsedPlugin } from '../../../agentPlugins/common/pluginParsers.js'; +-import { INativeEnvironmentService } from '../../../environment/common/environment.js'; +-import { IFileService } from '../../../files/common/files.js'; +-import { IInstantiationService } from '../../../instantiation/common/instantiation.js'; +-import { ILogService } from '../../../log/common/log.js'; +-import { IAgentAttachment, IAgentProgressEvent, SessionHistoryEvent } from '../../common/agentService.js'; +-import { stripRedundantCdPrefix } from '../../common/commandLineHelpers.js'; +-import { ISessionDatabase, ISessionDataService } from '../../common/sessionDataService.js'; +-import type { FileEdit, ToolDefinition } from '../../common/state/protocol/state.js'; +-import { SessionInputAnswerState, SessionInputAnswerValueKind, SessionInputQuestionKind, SessionInputResponseKind, ToolResultContentType, type PendingMessage, type SessionInputAnswer, type SessionInputRequest, type ToolCallResult, type ToolResultContent } from '../../common/state/sessionState.js'; +-import { CopilotSessionWrapper } from './copilotSessionWrapper.js'; +-import type { ShellManager } from './copilotShellTools.js'; +-import { getEditFilePath, getInvocationMessage, getPastTenseMessage, getPermissionDisplay, getShellLanguage, getSubagentMetadata, getToolDisplayName, getToolInputString, getToolKind, isEditTool, isHiddenTool, isShellTool, synthesizeSkillToolEvents, tryStringify, type ITypedPermissionRequest } from './copilotToolDisplay.js'; +-import { FileEditTracker } from './fileEditTracker.js'; +-import { mapSessionEvents } from './mapSessionEvents.js'; +-import { buildPendingEditContentUri } from './pendingEditContentStore.js'; - --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); -- }); --} +-const COPILOT_HOME_DIRECTORY = '.copilot'; +-const SESSION_STATE_DIRECTORY = join(COPILOT_HOME_DIRECTORY, 'session-state'); - --// ---- Pure functions (testable, no I/O) ------------------------------------ +-type UserInputHandler = NonNullable; +-type UserInputRequest = Parameters[0]; +-type UserInputResponse = Awaited>; +-type SessionHooks = NonNullable; +-type PreToolUseHookInput = Parameters>[0]; +-type PostToolUseHookInput = Parameters>[0]; - --/** -- * 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; +-function getCopilotCLISessionStateDir(userHome: string): string { +- const xdgHome = process.env['XDG_STATE_HOME']; +- return xdgHome ? join(xdgHome, SESSION_STATE_DIRECTORY) : join(userHome, SESSION_STATE_DIRECTORY); -} - -/** -- * Serializes an array of event log entries back into a JSONL string. +- * Immutable snapshot of the active client's contributions at session creation +- * time. Used to detect when the session needs to be refreshed. - */ --export function serializeEventLog(entries: readonly ICopilotEventLogEntry[]): string { -- return entries.map(e => JSON.stringify(e)).join('\n') + '\n'; +-export interface IActiveClientSnapshot { +- readonly clientId: string; +- readonly tools: readonly ToolDefinition[]; +- readonly plugins: readonly IParsedPlugin[]; -} - -/** -- * 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. +- * 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. - * -- * @returns The inclusive index of the last event in the specified turn, -- * or `-1` if the turn is not found. +- * In production, the factory calls `CopilotClient.createSession()` or +- * `resumeSession()`. In tests, it returns a mock wrapper directly. - */ --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; --} +-export type SessionWrapperFactory = (callbacks: { +- readonly onPermissionRequest: (request: ITypedPermissionRequest) => Promise; +- readonly onUserInputRequest: (request: UserInputRequest, invocation: { sessionId: string }) => Promise; +- readonly hooks: { +- readonly onPreToolUse: (input: PreToolUseHookInput) => Promise; +- readonly onPostToolUse: (input: PostToolUseHookInput) => Promise; +- }; +- // eslint-disable-next-line @typescript-eslint/no-explicit-any +- readonly clientTools: Tool[]; +-}) => Promise; - -/** -- * 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. +- * Options for constructing a {@link CopilotAgentSession}. - */ --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; +-export interface ICopilotAgentSessionOptions { +- readonly sessionUri: URI; +- readonly rawSessionId: string; +- readonly onDidSessionProgress: Emitter; +- readonly wrapperFactory: SessionWrapperFactory; +- readonly shellManager: ShellManager | undefined; +- /** Working directory associated with the session, used to strip redundant `cd` prefixes from shell commands. */ +- readonly workingDirectory?: URI; +- /** Snapshot of the active client's tools and plugins at session creation time. */ +- readonly clientSnapshot?: IActiveClientSnapshot; -} - -/** -- * Builds a truncated event log from the source session's events. +- * Encapsulates a single Copilot SDK session and all its associated bookkeeping. - * -- * - 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. +- * 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 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, -- }; +-export class CopilotAgentSession extends Disposable { +- readonly sessionId: string; +- readonly sessionUri: URI; - -- // Re-chain: first remaining event points to the new start -- const idMap = new Map(); -- idMap.set(originalStart.id, newStartId); +- /** Tracks active tool invocations so we can produce past-tense messages on completion. */ +- private readonly _activeToolCalls = new Map | undefined; content: ToolResultContent[] }>(); +- /** 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; - -- const result: ICopilotEventLogEntry[] = [newStart]; -- let lastId = newStartId; +- /** Snapshot captured at session creation for refresh detection. */ +- private readonly _appliedSnapshot: IActiveClientSnapshot; +- /** Tool names that are client-provided, derived from snapshot. */ +- private readonly _clientToolNames: ReadonlySet; +- /** Deferred promises for pending client tool calls, keyed by toolCallId. */ +- private readonly _pendingClientToolCalls = new Map>(); +- /** `pending-edit-content:` URIs written during permission requests, keyed +- * by toolCallId. Cleaned up when the permission resolves or the session +- * is disposed. */ +- private readonly _pendingEditContentUris = new Map(); +- +- private readonly _onDidSessionProgress: Emitter; +- private readonly _wrapperFactory: SessionWrapperFactory; +- private readonly _shellManager: ShellManager | undefined; +- private readonly _workingDirectory: URI | undefined; - -- for (const entry of kept) { -- const newId = generateUuid(); -- idMap.set(entry.id, newId); +- constructor( +- options: ICopilotAgentSessionOptions, +- @IInstantiationService private readonly _instantiationService: IInstantiationService, +- @ILogService private readonly _logService: ILogService, +- @ISessionDataService sessionDataService: ISessionDataService, +- @IFileService private readonly _fileService: IFileService, +- @INativeEnvironmentService private readonly _environmentService: INativeEnvironmentService, +- ) { +- super(); +- this.sessionId = options.rawSessionId; +- this.sessionUri = options.sessionUri; +- this._onDidSessionProgress = options.onDidSessionProgress; +- this._wrapperFactory = options.wrapperFactory; +- this._shellManager = options.shellManager; +- this._workingDirectory = options.workingDirectory; - -- const newParentId = entry.parentId !== null -- ? (idMap.get(entry.parentId) ?? lastId) -- : lastId; +- this._appliedSnapshot = options.clientSnapshot ?? { clientId: '', tools: [], plugins: [] }; +- this._clientToolNames = new Set(this._appliedSnapshot.tools.map(t => t.name)); - -- result.push({ -- type: entry.type, -- data: entry.data, -- id: newId, -- timestamp: entry.timestamp, -- parentId: newParentId, -- }); -- lastId = newId; -- } +- this._databaseRef = sessionDataService.openDatabase(options.sessionUri); +- this._register(toDisposable(() => this._databaseRef.dispose())); - -- return result; --} +- this._editTracker = this._instantiationService.createInstance(FileEditTracker, options.sessionUri.toString(), this._databaseRef.object); - --/** -- * 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'); --} +- this._register(toDisposable(() => this._denyPendingPermissions())); +- this._register(toDisposable(() => this._shellManager?.dispose())); +- this._register(toDisposable(() => this._cancelPendingUserInputs())); - --// ---- SQLite operations (Copilot CLI session-store.db) --------------------- +- // When a shell tool associates a terminal with a tool call, fire a +- // tool_content_changed event so the UI can connect to the terminal +- // while the command is still running. +- if (this._shellManager) { +- this._register(this._shellManager.onDidAssociateTerminal(({ toolCallId, terminalUri, displayName }) => { +- const tracked = this._activeToolCalls.get(toolCallId); +- if (!tracked) { +- return; +- } - --/** -- * 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], -- ); +- tracked.content.push({ +- type: ToolResultContentType.Terminal, +- resource: terminalUri, +- title: displayName, +- }); - -- // 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], -- ); +- this._onDidSessionProgress.fire({ +- session: this.sessionUri, +- type: 'tool_content_changed', +- toolCallId, +- content: tracked.content, +- }); +- })); +- } +- this._register(toDisposable(() => this._cancelPendingClientToolCalls())); +- } - -- // 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], -- ); +- /** +- * The snapshot of client contributions captured when this session was +- * created. Used by the agent to detect when the session is 1stale. +- */ +- get appliedSnapshot(): IActiveClientSnapshot { +- return this._appliedSnapshot; +- } - -- // 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], -- ); +- /** +- * Creates SDK {@link Tool} objects for the client-provided tools in the +- * applied snapshot. The handler creates a {@link DeferredPromise} and waits +- * for the client to dispatch `session/toolCallComplete`. +- */ +- // eslint-disable-next-line @typescript-eslint/no-explicit-any +- createClientSdkTools(): Tool[] { +- const tools = this._appliedSnapshot.tools; +- if (tools.length === 0) { +- return []; +- } +- return tools.map(def => ({ +- name: def.name, +- description: def.description ?? '', +- parameters: def.inputSchema ?? { type: 'object' as const, properties: {} }, +- handler: async (_args: Record, { toolCallId }) => { +- try { +- let deferred = this._pendingClientToolCalls.get(toolCallId); +- if (!deferred) { +- deferred = new DeferredPromise(); +- this._pendingClientToolCalls.set(toolCallId, deferred); - } +- const result = await deferred.p; +- this._pendingClientToolCalls.delete(toolCallId); +- return result; +- } catch (error) { +- this._logService.error(error, `[Copilot:${this.sessionId}] Failed in client tool handler: tool=${def.name}, toolCallId=${toolCallId}`); +- throw error; - } -- } -- }); +- }, +- })); +- } - -- // 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], -- ); +- /** +- * Resolves a pending client tool call. Returns `true` if the +- * toolCallId was found and handled. +- */ +- handleClientToolCallComplete(toolCallId: string, result: ToolCallResult) { +- let deferred = this._pendingClientToolCalls.get(toolCallId); +- if (!deferred) { +- deferred = new DeferredPromise(); +- this._pendingClientToolCalls.set(toolCallId, deferred); +- } +- +- const textContent = result.content +- ?.filter(c => c.type === ToolResultContentType.Text) +- .map(c => c.text) +- .join('\n') ?? ''; +- +- const binaryResults = result.content +- ?.filter(c => c.type === ToolResultContentType.EmbeddedResource) +- .map(c => { +- return { data: c.data, mimeType: c.contentType, type: c.contentType }; +- }); - -- await dbExec(db, 'COMMIT'); -- } catch (err) { -- await dbExec(db, 'ROLLBACK'); -- throw err; +- if (result.success) { +- deferred.complete({ +- textResultForLlm: textContent, +- resultType: 'success', +- binaryResultsForLlm: binaryResults?.length ? binaryResults : undefined, +- }); +- } else { +- deferred.complete({ +- textResultForLlm: textContent || result.error?.message || 'Tool call failed', +- resultType: 'failure', +- error: result.error?.message, +- binaryResultsForLlm: binaryResults?.length ? binaryResults : undefined, +- }); +- } - } --} - --/** -- * 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(); +- /** +- * 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), +- clientTools: this.createClientSdkTools(), +- hooks: { +- onPreToolUse: input => this._handlePreToolUse(input), +- onPostToolUse: input => this._handlePostToolUse(input), +- }, +- })); +- this._subscribeToEvents(); +- this._subscribeForLogging(); +- } - -- // Delete turns after the truncation point -- await dbRun(db, -- `DELETE FROM turns WHERE session_id = ? AND turn_index > ?`, -- [sessionId, keepUpToTurnIndex], -- ); +- // ---- session operations ------------------------------------------------- - -- // Update session timestamp -- await dbRun(db, -- `UPDATE sessions SET updated_at = ? WHERE id = ?`, -- [now, sessionId], -- ); +- 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)`); - -- // 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], -- ); -- } -- } +- const sdkAttachments = attachments?.map(a => { +- const path = a.uri.scheme === 'file' ? a.uri.fsPath : a.uri.toString(); +- if (a.type === 'selection') { +- return { type: 'selection' as const, filePath: path, displayName: a.displayName ?? path, text: a.text, selection: a.selection }; - } +- return { type: a.type, 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 dbExec(db, 'COMMIT'); -- } catch (err) { -- await dbExec(db, 'ROLLBACK'); -- throw err; +- await this._wrapper.session.send({ prompt, attachments: sdkAttachments }); +- this._logService.info(`[Copilot:${this.sessionId}] session.send() returned`); - } --} - --// ---- 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 { +- async sendSteering(steeringMessage: PendingMessage): Promise { - this._logService.info(`[Copilot:${this.sessionId}] Sending steering message: "${steeringMessage.userMessage.text.substring(0, 100)}"`); - try { - await this._wrapper.session.send({ @@ -2025,7 +1844,7 @@ index e4e4203d..00000000 - } - } - -- async getMessages(): Promise<(IAgentMessageEvent | IAgentToolStartEvent | IAgentToolCompleteEvent | IAgentSubagentStartedEvent)[]> { +- async getMessages(): Promise { - const events = await this._wrapper.session.getMessages(); - let db: ISessionDatabase | undefined; - try { @@ -2033,7 +1852,7 @@ index e4e4203d..00000000 - } catch { - // Database may not exist yet — that's fine - } -- return mapSessionEvents(this.sessionUri, db, events); +- return mapSessionEvents(this.sessionUri, db, events, this._workingDirectory); - } - - async abort(): Promise { @@ -2052,9 +1871,9 @@ index e4e4203d..00000000 - await this._wrapper.session.destroy(); - } - -- async setModel(model: string): Promise { +- async setModel(model: string, reasoningEffort?: SessionConfig['reasoningEffort']): Promise { - this._logService.info(`[Copilot:${this.sessionId}] Changing model to: ${model}`); -- await this._wrapper.session.setModel(model); +- await this._wrapper.session.setModel(model, { reasoningEffort }); - } - - // ---- permission handling ------------------------------------------------ @@ -2065,132 +1884,241 @@ index e4e4203d..00000000 - * side-effects layer to respond via {@link respondToPermissionRequest}. - */ - async handlePermissionRequest( -- request: PermissionRequest, +- request: ITypedPermissionRequest, - ): 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}`); +- try { +- 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' }; +- } +- +- const sessionResourcePath = this._getInternalSessionResourcePath(request); +- if (sessionResourcePath) { +- this._logService.info(`[Copilot:${this.sessionId}] Auto-approving internal session resource ${sessionResourcePath}`); - 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}`); - -- this._logService.info(`[Copilot:${this.sessionId}] Requesting confirmation for tool call: ${toolCallId}`); +- const deferred = new DeferredPromise(); +- this._pendingPermissions.set(toolCallId, deferred); - -- const deferred = new DeferredPromise(); -- this._pendingPermissions.set(toolCallId, deferred); +- // Derive display information from the permission request kind +- const { confirmationTitle, invocationMessage, toolInput, permissionKind, permissionPath } = getPermissionDisplay(request, this._workingDirectory); - -- // Derive display information from the permission request kind -- const { confirmationTitle, invocationMessage, toolInput, permissionKind } = getPermissionDisplay(request); +- // For write permission requests, build an FileEdit preview so the +- // client can show a diff before the user approves or denies. This +- // awaits async filesystem operations; the SDK already calls +- // `handlePermissionRequest` from an arbitrary async context, so the +- // extra await here is fine. +- const edits = await this._buildEditsForPermission(request, toolCallId); - -- // 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), -- }); +- // If the session was aborted/disposed while we were building the +- // preview, the deferred has already been resolved and the +- // `pending-edit-content:` entry has been cleaned up. Bail without +- // firing tool_ready. +- if (!this._pendingPermissions.has(toolCallId)) { +- return { kind: 'denied-interactively-by-user' }; +- } - -- 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' }; -- } +- // 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, +- edits, +- }); - -- respondToPermissionRequest(requestId: string, approved: boolean): boolean { -- const deferred = this._pendingPermissions.get(requestId); -- if (deferred) { -- this._pendingPermissions.delete(requestId); -- deferred.complete(approved); -- return true; +- 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' }; +- } catch (error) { +- this._logService.error(error, `[Copilot:${this.sessionId}] Failed to handle permission request: kind=${request.kind}, toolCallId=${request.toolCallId ?? 'missing'}`); +- throw error; - } -- return false; - } - -- // ---- user input handling ------------------------------------------------ -- +- private _getInternalSessionResourcePath(request: ITypedPermissionRequest): string | undefined { +- let permissionPath: string | undefined; +- if (request.kind === 'read') { +- permissionPath = typeof request.path === 'string' ? request.path : undefined; +- } else if (request.kind === 'write') { +- permissionPath = typeof request.fileName === 'string' ? request.fileName : undefined; +- } +- +- if (!permissionPath) { +- return undefined; +- } +- +- const sessionStateDir = normalizePath(URI.file(getCopilotCLISessionStateDir(this._environmentService.userHome.fsPath))); +- const sessionDir = normalizePath(URI.joinPath(sessionStateDir, this.sessionId)); +- if (!extUriBiasedIgnorePathCase.isEqualOrParent(sessionDir, sessionStateDir)) { +- return undefined; +- } +- +- const permissionUri = normalizePath(URI.file(permissionPath)); +- return extUriBiasedIgnorePathCase.isEqualOrParent(permissionUri, sessionDir) ? permissionPath : undefined; +- } +- +- /** +- * Builds an {@link FileEdit} preview for a write permission request. +- * +- * The `before` side references the existing file on disk directly (if it +- * exists); the `after` side is written to the `pending-edit-content:` +- * in-memory filesystem so the client can fetch it via `resourceRead`. +- * +- * Returns `undefined` for permission kinds that don't describe file +- * edits or when the request is missing the fields needed to build a +- * preview. If the permission request is no longer pending by the time +- * the in-memory write completes (e.g. the session was aborted), the +- * just-written entry is deleted so it cannot leak. +- */ +- private async _buildEditsForPermission(request: ITypedPermissionRequest, toolCallId: string): Promise<{ items: FileEdit[] } | undefined> { +- if (request.kind !== 'write') { +- return undefined; +- } +- const filePath = typeof request.fileName === 'string' ? request.fileName : undefined; +- const newFileContents = typeof request.newFileContents === 'string' ? request.newFileContents : undefined; +- if (!filePath || newFileContents === undefined) { +- return undefined; +- } +- +- const fileUri = URI.file(filePath); +- const fileUriStr = fileUri.toString(); +- +- let beforeExists = false; +- try { +- beforeExists = await this._fileService.exists(fileUri); +- } catch (err) { +- this._logService.warn(`[Copilot:${this.sessionId}] Failed to check file for edit preview: ${filePath}`, err); +- } +- +- const afterUri = buildPendingEditContentUri(this.sessionUri.toString(), toolCallId, filePath); +- try { +- await this._fileService.writeFile(afterUri, VSBuffer.fromString(newFileContents)); +- } catch (err) { +- this._logService.warn(`[Copilot:${this.sessionId}] Failed to write pending edit content for ${filePath}`, err); +- return undefined; +- } +- +- // If the request was already resolved (aborted/disposed) while we +- // were awaiting the write, drop the in-memory entry immediately; +- // `_deletePendingEditContent` has already run and won't run again. +- if (!this._pendingPermissions.has(toolCallId)) { +- this._fileService.del(afterUri).catch(err => { +- this._logService.warn(`[Copilot:${this.sessionId}] Failed to delete orphaned pending edit content: ${afterUri.toString()}`, err); +- }); +- return undefined; +- } +- this._pendingEditContentUris.set(toolCallId, afterUri); +- +- const diffCounts = typeof request.diff === 'string' ? countUnifiedDiffLines(request.diff) : undefined; +- +- const edit: FileEdit = { +- ...(beforeExists ? { before: { uri: fileUriStr, content: { uri: fileUriStr } } } : {}), +- after: { uri: fileUriStr, content: { uri: afterUri.toString() } }, +- ...(diffCounts ? { diff: diffCounts } : {}), +- }; +- return { items: [edit] }; +- } +- +- respondToPermissionRequest(requestId: string, approved: boolean): boolean { +- const deferred = this._pendingPermissions.get(requestId); +- if (deferred) { +- this._pendingPermissions.delete(requestId); +- this._deletePendingEditContent(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, +- request: UserInputRequest, - _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, -- }, -- ], -- }; +- ): Promise { +- const questionPreview = request.question.substring(0, 100); +- try { +- const requestId = generateUuid(); +- const questionId = generateUuid(); +- this._logService.info(`[Copilot:${this.sessionId}] User input request: requestId=${requestId}, question="${questionPreview}"`); +- +- const deferred = new DeferredPromise<{ response: SessionInputResponseKind; answers?: Record }>(); +- this._pendingUserInputs.set(requestId, { deferred, questionId }); +- +- // Build the protocol SessionInputRequest from the SDK's simple format +- const inputRequest: SessionInputRequest = { +- 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, -- }); +- 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}`); +- 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 }; -- } +- 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 }; -- } +- // 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 }; -- } +- 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 }; +- return { answer: '', wasFreeform: true }; +- } catch (error) { +- this._logService.error(error, `[Copilot:${this.sessionId}] Failed to handle user input request: question="${questionPreview}"`); +- throw error; +- } - } - -- respondToUserInputRequest(requestId: string, response: SessionInputResponseKind, answers?: Record): boolean { +- respondToUserInputRequest(requestId: string, response: SessionInputResponseKind, answers?: Record): boolean { - const pending = this._pendingUserInputs.get(requestId); - if (pending) { - this._pendingUserInputs.delete(requestId); @@ -2200,6 +2128,34 @@ index e4e4203d..00000000 - return false; - } - +- private async _handlePreToolUse(input: PreToolUseHookInput): Promise { +- try { +- if (isEditTool(input.toolName)) { +- const filePath = getEditFilePath(input.toolArgs); +- if (filePath) { +- await this._editTracker.trackEditStart(filePath); +- } +- } +- } catch (error) { +- this._logService.error(error, `[Copilot:${this.sessionId}] Failed in onPreToolUse: tool=${input.toolName}`); +- throw error; +- } +- } +- +- private async _handlePostToolUse(input: PostToolUseHookInput): Promise { +- try { +- if (isEditTool(input.toolName)) { +- const filePath = getEditFilePath(input.toolArgs); +- if (filePath) { +- await this._editTracker.completeEdit(filePath); +- } +- } +- } catch (error) { +- this._logService.error(error, `[Copilot:${this.sessionId}] Failed in onPostToolUse: tool=${input.toolName}`); +- throw error; +- } +- } +- - // ---- event wiring ------------------------------------------------------- - - private _subscribeToEvents(): void { @@ -2207,6 +2163,15 @@ index e4e4203d..00000000 - const sessionId = this.sessionId; - const session = this.sessionUri; - +- // Capture SDK event IDs for each user.message event so we can map +- // protocol turn indices to the event IDs needed by the SDK's +- // history.truncate and sessions.fork RPCs. +- this._register(wrapper.onUserMessage(e => { +- if (this._turnId) { +- this._databaseRef.object.setTurnEventId(this._turnId, e.id); +- } +- })); +- - this._register(wrapper.onMessageDelta(e => { - this._logService.trace(`[Copilot:${sessionId}] delta: ${e.data.deltaContent}`); - this._onDidSessionProgress.fire({ @@ -2245,14 +2210,21 @@ index e4e4203d..00000000 - return; - } - this._logService.info(`[Copilot:${sessionId}] Tool started: ${e.data.toolName}`); -- const toolArgs = e.data.arguments !== undefined ? tryStringify(e.data.arguments) : undefined; +- let 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 */ } - } +- // Strip redundant `cd && …` prefixes from shell tool +- // commands so clients see the simplified form. Mirrors the logic in +- // mapSessionEvents (which handles the history-replay path). +- if (stripRedundantCdPrefix(e.data.toolName, parameters, this._workingDirectory)) { +- toolArgs = tryStringify(parameters); +- } - const displayName = getToolDisplayName(e.data.toolName); -- this._activeToolCalls.set(e.data.toolCallId, { toolName: e.data.toolName, displayName, parameters }); +- this._activeToolCalls.set(e.data.toolCallId, { toolName: e.data.toolName, displayName, parameters, content: [] }); - const toolKind = getToolKind(e.data.toolName); +- const subagentMeta = toolKind === 'subagent' ? getSubagentMetadata(parameters) : undefined; - - this._onDidSessionProgress.fire({ - session, @@ -2265,9 +2237,12 @@ index e4e4203d..00000000 - toolKind, - language: toolKind === 'terminal' ? getShellLanguage(e.data.toolName) : undefined, - toolArguments: toolArgs, +- subagentAgentName: subagentMeta?.agentName, +- subagentDescription: subagentMeta?.description, - mcpServerName: e.data.mcpServerName, - mcpToolName: e.data.mcpToolName, - parentToolCallId: e.data.parentToolCallId, +- toolClientId: this._clientToolNames.has(e.data.toolName) ? this._appliedSnapshot.clientId : undefined, - }); - })); - @@ -2281,7 +2256,7 @@ index e4e4203d..00000000 - const displayName = tracked.displayName; - const toolOutput = e.data.error?.message ?? e.data.result?.content; - -- const content: IToolResultContent[] = []; +- const content: ToolResultContent[] = [...tracked.content]; - if (toolOutput !== undefined) { - content.push({ type: ToolResultContentType.Text, text: toolOutput }); - } @@ -2298,10 +2273,11 @@ index e4e4203d..00000000 - } - } - -- // Add terminal content reference for shell tools +- // Add terminal content reference for shell tools (skip if already +- // added during onDidAssociateTerminal while the tool was running) - if (isShellTool(tracked.toolName) && this._shellManager) { - const terminalUri = this._shellManager.getTerminalUriForToolCall(e.data.toolCallId); -- if (terminalUri) { +- if (terminalUri && !content.some(c => c.type === ToolResultContentType.Terminal && c.resource === terminalUri)) { - content.push({ - type: ToolResultContentType.Terminal, - resource: terminalUri, @@ -2331,6 +2307,17 @@ index e4e4203d..00000000 - this._onDidSessionProgress.fire({ session, type: 'idle' }); - })); - +- // The SDK emits a `skill` tool call (which we hide) and a richer +- // `skill.invoked` event with the resolved SKILL.md path. Synthesize a +- // tool-start/complete pair from the latter so the UI can render a +- // clickable file link, matching the `view`-tool display style. +- this._register(wrapper.onSkillInvoked(e => { +- this._logService.info(`[Copilot:${sessionId}] Skill invoked: ${e.data.name} (${e.data.path})`); +- const { start, complete } = synthesizeSkillToolEvents(session, e.data, e.id); +- this._onDidSessionProgress.fire(start); +- this._onDidSessionProgress.fire(complete); +- })); +- - this._register(wrapper.onSubagentStarted(e => { - this._logService.info(`[Copilot:${sessionId}] Subagent started: toolCallId=${e.data.toolCallId}, agent=${e.data.agentName}`); - this._onDidSessionProgress.fire({ @@ -2497,38 +2484,132 @@ index e4e4203d..00000000 - })); - } - +- // ---- SDK event ID tracking & truncation --------------------------------- +- +- /** +- * Returns the SDK event ID for the turn inserted after the given turn, +- * or `undefined` if it's the last turn. +- */ +- getNextTurnEventId(turnId: string): Promise { +- return this._databaseRef.object.getNextTurnEventId(turnId); +- } +- +- /** +- * Returns the SDK event ID of the earliest turn. +- */ +- getFirstTurnEventId(): Promise { +- return this._databaseRef.object.getFirstTurnEventId(); +- } +- +- /** +- * Truncates the session history via the SDK's RPC and cleans up +- * stale turns from the session database. +- * +- * @param eventId The SDK event ID at which to truncate. This event +- * and all events after it are removed. +- * @param keepTurnId If provided, turns inserted after this turn are +- * deleted from the DB. If omitted, all turns are deleted. +- */ +- async truncateAtEventId(eventId: string, keepTurnId?: string): Promise { +- this._logService.info(`[Copilot:${this.sessionId}] Truncating via SDK RPC at eventId=${eventId}`); +- const result = await this._wrapper.session.rpc.history.truncate({ eventId }); +- this._logService.info(`[Copilot:${this.sessionId}] SDK truncation removed ${result.eventsRemoved} events`); +- +- // Clean up stale turns from our DB so getNextTurnEventId doesn't +- // return event IDs for turns that no longer exist in the SDK. +- if (keepTurnId) { +- await this._databaseRef.object.deleteTurnsAfter(keepTurnId); +- } else { +- await this._databaseRef.object.deleteAllTurns(); +- } +- } +- +- /** +- * Bulk-remaps turn IDs in this session's database. +- * Used after file-copying a source session's database for a fork. +- */ +- async remapTurnIds(mapping: ReadonlyMap): Promise { +- await this._databaseRef.object.remapTurnIds(mapping); +- } +- - // ---- cleanup ------------------------------------------------------------ - - private _denyPendingPermissions(): void { -- for (const [, deferred] of this._pendingPermissions) { +- for (const [toolCallId, deferred] of this._pendingPermissions) { +- this._deletePendingEditContent(toolCallId); - deferred.complete(false); - } - this._pendingPermissions.clear(); - } - +- /** +- * Removes any `pending-edit-content:` entries associated with a resolved +- * (approved, denied, or cancelled) permission request. +- */ +- private _deletePendingEditContent(toolCallId: string): void { +- const uri = this._pendingEditContentUris.get(toolCallId); +- if (!uri) { +- return; +- } +- this._pendingEditContentUris.delete(toolCallId); +- this._fileService.del(uri).catch(err => { +- this._logService.warn(`[Copilot:${this.sessionId}] Failed to delete pending edit content: ${uri.toString()}`, err); +- }); +- } +- - private _cancelPendingUserInputs(): void { - for (const [, pending] of this._pendingUserInputs) { - pending.deferred.complete({ response: SessionInputResponseKind.Cancel }); - } - this._pendingUserInputs.clear(); - } +- +- private _cancelPendingClientToolCalls(): void { +- for (const [, deferred] of this._pendingClientToolCalls) { +- deferred.complete({ textResultForLlm: 'Tool call cancelled: session ended', resultType: 'failure', error: 'Session ended' }); +- } +- this._pendingClientToolCalls.clear(); +- } +-} +- +-/** +- * Counts added/removed lines in a unified diff string. Ignores the `+++` and +- * `---` header rows and any non-hunk context. +- */ +-function countUnifiedDiffLines(diff: string): { added: number; removed: number } | undefined { +- let added = 0; +- let removed = 0; +- for (const line of diff.split('\n')) { +- if (line.startsWith('+++') || line.startsWith('---')) { +- continue; +- } +- if (line.startsWith('+')) { +- added++; +- } else if (line.startsWith('-')) { +- removed++; +- } +- } +- if (added === 0 && removed === 0) { +- return undefined; +- } +- return { added, removed }; -} 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 +index e45ca942..00000000 --- a/src/vs/platform/agentHost/node/copilot/copilotGitProject.ts +++ /dev/null -@@ -1,87 +0,0 @@ +@@ -1,58 +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'; +-import type { IAgentHostGitService } from '../agentHostGitService.js'; - -export interface ICopilotSessionContext { - readonly cwd?: string; @@ -2536,49 +2617,20 @@ index c1e2d22f..00000000 - 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 { +-export async function resolveGitProject(workingDirectory: URI | undefined, gitService: IAgentHostGitService): 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 { +- if (!await gitService.isInsideWorkTree(workingDirectory)) { - 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 = (await gitService.getWorktreeRoots(workingDirectory))[0] +- ?? await gitService.getRepositoryRoot(workingDirectory); +- if (!uri) { +- return undefined; - } -- -- const uri = URI.file(projectPath); - return { uri, displayName: basename(uri.fsPath) || uri.toString() }; -} - @@ -2589,13 +2641,13 @@ index c1e2d22f..00000000 - return { uri, displayName }; -} - --export async function projectFromCopilotContext(context: ICopilotSessionContext | undefined): Promise { +-export async function projectFromCopilotContext(context: ICopilotSessionContext | undefined, gitService: IAgentHostGitService): 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); +- const gitProject = await resolveGitProject(workingDirectory, gitService); - if (gitProject) { - return gitProject; - } @@ -2608,10 +2660,10 @@ index c1e2d22f..00000000 -} 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 +index 61e93e5a..00000000 --- a/src/vs/platform/agentHost/node/copilot/copilotPluginConverters.ts +++ /dev/null -@@ -1,341 +0,0 @@ +@@ -1,350 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. @@ -2626,6 +2678,12 @@ index c08d7e5f..00000000 -import { dirname } from '../../../../base/common/path.js'; - -type SessionHooks = NonNullable; +-type PreToolUseHookInput = Parameters>[0]; +-type PostToolUseHookInput = Parameters>[0]; +-type UserPromptSubmittedHookInput = Parameters>[0]; +-type SessionStartHookInput = Parameters>[0]; +-type SessionEndHookInput = Parameters>[0]; +-type ErrorOccurredHookInput = Parameters>[0]; - -// --------------------------------------------------------------------------- -// MCP servers @@ -2786,6 +2844,37 @@ index c08d7e5f..00000000 -} - -/** +- * Runs a list of hook commands sequentially, passing `input` as JSON stdin. +- * Returns the parsed output of the first command that emits a valid JSON object, +- * or `undefined` if no command produces parseable JSON output. +- * Command failures are swallowed — hooks are non-fatal. +- */ +-async function runHookCommands(commands: readonly IParsedHookCommand[] | undefined, input: unknown): Promise { +- if (!commands) { +- return undefined; +- } +- const stdin = JSON.stringify(input); +- for (const cmd of commands) { +- 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 +- } +- } +- return undefined; +-} +- +-/** - * Mapping from canonical hook type identifiers to SDK SessionHooks handler keys. - */ -const HOOK_TYPE_TO_SDK_KEY: Record = { @@ -2809,8 +2898,8 @@ index c08d7e5f..00000000 -export function toSdkHooks( - hookGroups: readonly IParsedHookGroup[], - editTrackingHooks?: { -- readonly onPreToolUse: (input: { toolName: string; toolArgs: unknown }) => Promise; -- readonly onPostToolUse: (input: { toolName: string; toolArgs: unknown }) => Promise; +- readonly onPreToolUse: (input: PreToolUseHookInput) => Promise; +- readonly onPostToolUse: (input: PostToolUseHookInput) => Promise; - }, -): SessionHooks { - // Group all commands by SDK handler key @@ -2830,53 +2919,25 @@ index c08d7e5f..00000000 - // Pre-tool-use handler - const preToolCommands = commandsByKey.get('onPreToolUse'); - if (preToolCommands?.length || editTrackingHooks) { -- hooks.onPreToolUse = async (input: { toolName: string; toolArgs: unknown }) => { +- hooks.onPreToolUse = async (input: PreToolUseHookInput) => { - 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 -- } -- } -- } +- return runHookCommands(preToolCommands, input); - }; - } - - // Post-tool-use handler - const postToolCommands = commandsByKey.get('onPostToolUse'); - if (postToolCommands?.length || editTrackingHooks) { -- hooks.onPostToolUse = async (input: { toolName: string; toolArgs: unknown }) => { +- hooks.onPostToolUse = async (input: PostToolUseHookInput) => { - 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 -- } -- } -- } +- return runHookCommands(postToolCommands, input); - }; - } - - // User-prompt-submitted handler - const promptCommands = commandsByKey.get('onUserPromptSubmitted'); - if (promptCommands?.length) { -- hooks.onUserPromptSubmitted = async (input: { prompt: string }) => { +- hooks.onUserPromptSubmitted = async (input: UserPromptSubmittedHookInput) => { - const stdin = JSON.stringify(input); - for (const cmd of promptCommands) { - try { @@ -2891,7 +2952,7 @@ index c08d7e5f..00000000 - // Session-start handler - const startCommands = commandsByKey.get('onSessionStart'); - if (startCommands?.length) { -- hooks.onSessionStart = async (input: { source: string }) => { +- hooks.onSessionStart = async (input: SessionStartHookInput) => { - const stdin = JSON.stringify(input); - for (const cmd of startCommands) { - try { @@ -2906,7 +2967,7 @@ index c08d7e5f..00000000 - // Session-end handler - const endCommands = commandsByKey.get('onSessionEnd'); - if (endCommands?.length) { -- hooks.onSessionEnd = async (input: { reason: string }) => { +- hooks.onSessionEnd = async (input: SessionEndHookInput) => { - const stdin = JSON.stringify(input); - for (const cmd of endCommands) { - try { @@ -2921,7 +2982,7 @@ index c08d7e5f..00000000 - // Error-occurred handler - const errorCommands = commandsByKey.get('onErrorOccurred'); - if (errorCommands?.length) { -- hooks.onErrorOccurred = async (input: { error: string }) => { +- hooks.onErrorOccurred = async (input: ErrorOccurredHookInput) => { - const stdin = JSON.stringify(input); - for (const cmd of errorCommands) { - try { @@ -3178,10 +3239,10 @@ index 36ad526d..00000000 -} 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 +index c46d2c75..00000000 --- a/src/vs/platform/agentHost/node/copilot/copilotShellTools.ts +++ /dev/null -@@ -1,455 +0,0 @@ +@@ -1,728 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. @@ -3193,8 +3254,9 @@ index b40abea2..00000000 -import { removeAnsiEscapeCodes } from '../../../../base/common/strings.js'; -import * as platform from '../../../../base/common/platform.js'; -import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; +-import { Emitter, Event } from '../../../../base/common/event.js'; -import { ILogService } from '../../../log/common/log.js'; --import { TerminalClaimKind, type ITerminalSessionClaim } from '../../common/state/protocol/state.js'; +-import { TerminalClaimKind, type TerminalSessionClaim } from '../../common/state/protocol/state.js'; -import { IAgentHostTerminalManager } from '../agentHostTerminalManager.js'; - -/** @@ -3222,7 +3284,7 @@ index b40abea2..00000000 - readonly shellType: ShellType; -} - --type ShellType = 'bash' | 'powershell'; +-export type ShellType = 'bash' | 'powershell'; - -function getShellExecutable(shellType: ShellType): string { - if (shellType === 'powershell') { @@ -3248,8 +3310,12 @@ index b40abea2..00000000 - private readonly _shells = new Map(); - private readonly _toolCallShells = new Map(); - +- private readonly _onDidAssociateTerminal = new Emitter<{ toolCallId: string; terminalUri: string; displayName: string }>(); +- readonly onDidAssociateTerminal: Event<{ toolCallId: string; terminalUri: string; displayName: string }> = this._onDidAssociateTerminal.event; +- - constructor( - private readonly _sessionUri: URI, +- private readonly _workingDirectory: URI | undefined, - @IAgentHostTerminalManager private readonly _terminalManager: IAgentHostTerminalManager, - @ILogService private readonly _logService: ILogService, - ) { } @@ -3274,7 +3340,7 @@ index b40abea2..00000000 - const id = generateUuid(); - const terminalUri = `agenthost-terminal://shell/${id}`; - -- const claim: ITerminalSessionClaim = { +- const claim: TerminalSessionClaim = { - kind: TerminalClaimKind.Session, - session: this._sessionUri.toString(), - turnId, @@ -3287,8 +3353,8 @@ index b40abea2..00000000 - terminal: terminalUri, - claim, - name: shellDisplayName, -- cwd, -- }, { shell: getShellExecutable(shellType) }); +- cwd: cwd ?? this._workingDirectory?.fsPath, +- }, { shell: getShellExecutable(shellType), preventShellHistory: true, nonInteractive: true }); - - const shell: IManagedShell = { id, terminalUri, shellType }; - this._shells.set(id, shell); @@ -3299,6 +3365,11 @@ index b40abea2..00000000 - - private _trackToolCall(toolCallId: string, shellId: string): void { - this._toolCallShells.set(toolCallId, shellId); +- const shell = this._shells.get(shellId); +- if (shell) { +- const displayName = shell.shellType === 'bash' ? 'Bash' : 'PowerShell'; +- this._onDidAssociateTerminal.fire({ toolCallId, terminalUri: shell.terminalUri, displayName }); +- } - } - - getTerminalUriForToolCall(toolCallId: string): string | undefined { @@ -3360,6 +3431,20 @@ index b40abea2..00000000 - return `echo "${SENTINEL_PREFIX}${sentinelId}_EXIT_$?>>>"`; -} - +-/** +- * For POSIX shells (bash/zsh) that honor `HISTCONTROL=ignorespace` / +- * `HIST_IGNORE_SPACE`, prepending a single space prevents the command from +- * being recorded in shell history. The shell integration scripts opt these +- * settings in via the `VSCODE_PREVENT_SHELL_HISTORY` env var (set when the +- * terminal is created with `preventShellHistory: true`). PowerShell +- * suppresses history through PSReadLine instead, so no prefix is needed. +- * +- * Exported for tests. +- */ +-export function prefixForHistorySuppression(shellType: ShellType): string { +- return shellType === 'powershell' ? '' : ' '; +-} +- -function parseSentinel(content: string, sentinelId: string): { found: boolean; exitCode: number; outputBeforeSentinel: string } { - const marker = `${SENTINEL_PREFIX}${sentinelId}_EXIT_`; - const idx = content.indexOf(marker); @@ -3406,6 +3491,91 @@ index b40abea2..00000000 - terminalManager: IAgentHostTerminalManager, - logService: ILogService, -): Promise { +- const result = terminalManager.supportsCommandDetection(shell.terminalUri) +- ? await executeCommandWithShellIntegration(shell, command, timeoutMs, terminalManager, logService) +- : await executeCommandWithSentinel(shell, command, timeoutMs, terminalManager, logService); +- return { +- ...result, +- textResultForLlm: `Shell ID: ${shell.id}\n${result.textResultForLlm}`, +- }; +-} +- +-/** +- * Execute a command using shell integration (OSC 633) for completion detection. +- * No sentinel echo is injected — the shell's own command-finished signal +- * provides the exit code and cleanly delineated output. +- */ +-async function executeCommandWithShellIntegration( +- shell: IManagedShell, +- command: string, +- timeoutMs: number, +- terminalManager: IAgentHostTerminalManager, +- logService: ILogService, +-): Promise { +- const disposables = new DisposableStore(); +- +- terminalManager.writeInput(shell.terminalUri, `${prefixForHistorySuppression(shell.shellType)}${command}\r`); +- +- return new Promise(resolve => { +- let resolved = false; +- const finish = (result: ToolResultObject) => { +- if (resolved) { +- return; +- } +- resolved = true; +- disposables.dispose(); +- resolve(result); +- }; +- +- disposables.add(terminalManager.onCommandFinished(shell.terminalUri, event => { +- const output = prepareOutputForModel(event.output); +- const exitCode = event.exitCode ?? 0; +- logService.info(`[ShellTool] Command completed (shell integration) with exit code ${exitCode}`); +- if (exitCode === 0) { +- finish(makeSuccessResult(`Exit code: ${exitCode}\n${output}`)); +- } else { +- finish(makeFailureResult(`Exit code: ${exitCode}\n${output}`)); +- } +- })); +- +- disposables.add(terminalManager.onExit(shell.terminalUri, (exitCode: number) => { +- logService.info(`[ShellTool] Shell exited unexpectedly with code ${exitCode}`); +- const fullContent = terminalManager.getContent(shell.terminalUri) ?? ''; +- const output = prepareOutputForModel(fullContent); +- 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 output = prepareOutputForModel(fullContent); +- finish(makeFailureResult( +- `Command timed out after ${Math.round(timeoutMs / 1000)}s. Partial output:\n${output}`, +- 'timeout', +- )); +- }, timeoutMs); +- disposables.add(toDisposable(() => clearTimeout(timer))); +- }); +-} +- +-/** +- * Fallback: execute a command using a sentinel echo to detect completion. +- * Used when shell integration is not available. +- */ +-async function executeCommandWithSentinel( +- shell: IManagedShell, +- command: string, +- timeoutMs: number, +- terminalManager: IAgentHostTerminalManager, +- logService: ILogService, +-): Promise { - const sentinelId = makeSentinelId(); - const sentinelCmd = buildSentinelCommand(sentinelId, shell.shellType); - const disposables = new DisposableStore(); @@ -3414,7 +3584,7 @@ index b40abea2..00000000 - const offsetBefore = contentBefore.length; - - // PTY input uses \r for line endings — the PTY translates to \r\n -- const input = `${command}\r${sentinelCmd}\r`; +- const input = `${prefixForHistorySuppression(shell.shellType)}${command}\r${sentinelCmd}\r`; - terminalManager.writeInput(shell.terminalUri, input); - - return new Promise(resolve => { @@ -3520,7 +3690,7 @@ index b40abea2..00000000 - - const primaryTool: Tool = { - name: shellType, -- description: `Execute a command in a persistent ${shellType} shell. The shell is reused across calls.`, +- description: shellType === 'bash' ? createBashModelDescription(false) : createPowerShellModelDescription(shellType, 'pwsh.exe', false), - parameters: { - type: 'object', - properties: { @@ -3551,6 +3721,7 @@ index b40abea2..00000000 - }, - }, - overridesBuiltInTool: true, +- skipPermission: true, - handler: (args) => { - const shells = shellManager.listShells(); - const shell = args.shell_id @@ -3578,6 +3749,7 @@ index b40abea2..00000000 - required: ['command'], - }, - overridesBuiltInTool: true, +- skipPermission: true, - handler: (args) => { - const shells = shellManager.listShells(); - const shell = shells[shells.length - 1]; @@ -3599,6 +3771,7 @@ index b40abea2..00000000 - }, - }, - overridesBuiltInTool: true, +- skipPermission: true, - handler: (args) => { - if (args.shell_id) { - const success = shellManager.shutdownShell(args.shell_id); @@ -3621,6 +3794,7 @@ index b40abea2..00000000 - description: `List active ${shellType} shell instances.`, - parameters: { type: 'object', properties: {} }, - overridesBuiltInTool: true, +- skipPermission: true, - handler: () => { - const shells = shellManager.listShells(); - if (shells.length === 0) { @@ -3637,1626 +3811,5224 @@ index b40abea2..00000000 - - 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. -- *--------------------------------------------------------------------------------------------*/ +-interface ITerminalSandboxResolvedNetworkDomains { +- allowedDomains: string[]; +- deniedDomains: string[]; +-} - --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, -- }; +-function isWindowsPowerShell(envShell: string): boolean { +- return envShell.endsWith('System32\\WindowsPowerShell\\v1.0\\powershell.exe'); -} - --/** -- * 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; +-function createPowerShellModelDescription(shellType: string, shellPath: string, isSandboxEnabled: boolean, networkDomains?: ITerminalSandboxResolvedNetworkDomains): string { +- const isWinPwsh = isWindowsPowerShell(shellPath); +- const parts = [ +- `This tool allows you to execute ${isWinPwsh ? 'Windows PowerShell 5.1' : 'PowerShell'} commands in a persistent terminal session, preserving environment variables, working directory, and other context across multiple commands.`, +- '', +- 'Command Execution:', +- // IMPORTANT: PowerShell 5 does not support `&&` so always re-write them to `;`. Note that +- // the behavior of `&&` differs a little from `;` but in general it's fine +- isWinPwsh ? '- Use semicolons ; to chain commands on one line, NEVER use && even when asked explicitly' : '- Prefer ; when chaining commands on one line', +- '- Prefer pipelines | for object-based data flow', +- '- Never create a sub-shell (eg. powershell -c "command") unless explicitly asked', +- '', +- 'Directory Management:', +- '- Prefer relative paths when navigating directories, only use absolute when the path is far away or the current cwd is not expected', +- '- By default (mode=sync), shell and cwd are reused by subsequent sync commands', +- '- Use $PWD or Get-Location for current directory', +- '- Use Push-Location/Pop-Location for directory stack', +- '', +- 'Program Execution:', +- '- Supports .NET, Python, Node.js, and other executables', +- '- Install modules via Install-Module, Install-Package', +- '- Use Get-Command to verify cmdlet/function availability', +- '', +- 'Async Mode:', +- '- For long-running tasks (e.g., servers), use mode=async', +- '- Returns a terminal ID for checking status and runtime later', +- '- Use Start-Job for background PowerShell jobs', +- '', +- `Use write_${shellType} to send commands or input to a terminal session.`, +- ]; - -- 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; +- if (isSandboxEnabled) { +- parts.push(...createSandboxLines(networkDomains)); +- } - -- const turnStart = makeEntry('assistant.turn_start', { -- id: `turn-start-${turn}`, -- data: { turnId: String(turn) }, -- parentId: lastId, -- }); -- entries.push(turnStart); -- lastId = turnStart.id; +- parts.push( +- '', +- 'Output Management:', +- '- Output is automatically truncated if longer than 60KB to prevent context overflow', +- '- Use Select-Object, Where-Object, Format-Table to filter output', +- '- Use -First/-Last parameters to limit results', +- '- For pager commands, add | Out-String or | Format-List', +- '', +- 'Best Practices:', +- '- Use proper cmdlet names instead of aliases in scripts', +- '- Quote paths with spaces: "C:\\Path With Spaces"', +- '- Prefer PowerShell cmdlets over external commands when available', +- '- Prefer idiomatic PowerShell like Get-ChildItem instead of dir or ls for file listings', +- '- Use Test-Path to check file/directory existence', +- '- Be specific with Select-Object properties to avoid excessive output', +- '- Avoid printing credentials unless absolutely required', +- '', +- 'Interactive Input Handling:', +- '- When a terminal command is waiting for interactive input, do NOT suggest alternatives or ask the user whether to proceed. Instead, use the ask_user tool to collect the needed values from the user, then send them.', +- `- Send exactly one answer per prompt using write_${shellType}. Never send multiple answers in a single send.`, +- `- After each send, call read_${shellType} to read the next prompt before sending the next answer.`, +- '- Continue one prompt at a time until the command finishes.', +- ); - -- const assistantMsg = makeEntry('assistant.message', { -- id: `assistant-msg-${turn}`, -- data: { content: `Response ${turn}` }, -- parentId: lastId, -- }); -- entries.push(assistantMsg); -- lastId = assistantMsg.id; +- return parts.join('\n'); +-} - -- const turnEnd = makeEntry('assistant.turn_end', { -- id: `turn-end-${turn}`, -- parentId: lastId, -- }); -- entries.push(turnEnd); -- lastId = turnEnd.id; +-function createSandboxLines(networkDomains?: ITerminalSandboxResolvedNetworkDomains): string[] { +- const lines = [ +- '', +- 'Sandboxing:', +- '- ATTENTION: Terminal sandboxing is enabled, commands run in a sandbox by default', +- '- When executing commands within the sandboxed environment, all operations requiring a temporary directory must utilize the $TMPDIR environment variable. The /tmp directory is not guaranteed to be accessible or writable and must be avoided', +- '- Tools and scripts should respect the TMPDIR environment variable, which is automatically set to an appropriate path within the sandbox', +- '- When a command fails due to sandbox restrictions, immediately re-run it with requestUnsandboxedExecution=true. Do NOT ask the user for permission — setting this flag automatically shows a confirmation prompt to the user', +- '- Only set requestUnsandboxedExecution=true when there is evidence of failures caused by the sandbox, e.g. \'Operation not permitted\' errors, network failures, or file access errors, etc', +- '- Do NOT set requestUnsandboxedExecution=true without first executing the command in sandbox mode. Always try the command in the sandbox first, and only set requestUnsandboxedExecution=true when retrying after that sandboxed execution failed due to sandbox restrictions.', +- '- When setting requestUnsandboxedExecution=true, also provide requestUnsandboxedExecutionReason explaining why the command needs unsandboxed access', +- ]; +- if (networkDomains) { +- const deniedSet = new Set(networkDomains.deniedDomains); +- const effectiveAllowed = networkDomains.allowedDomains.filter(d => !deniedSet.has(d)); +- if (effectiveAllowed.length === 0) { +- lines.push('- All network access is blocked in the sandbox'); +- } else { +- lines.push(`- Only the following domains are accessible in the sandbox (all other network access is blocked): ${effectiveAllowed.join(', ')}`); +- } +- if (networkDomains.deniedDomains.length > 0) { +- lines.push(`- The following domains are explicitly blocked in the sandbox: ${networkDomains.deniedDomains.join(', ')}`); +- } - } -- -- return entries; +- return lines; -} - --suite('CopilotAgentForking', () => { -- -- ensureNoDisposablesAreLeakedInTestSuite(); +-function createGenericDescription(shellType: string, isSandboxEnabled: boolean, networkDomains?: ITerminalSandboxResolvedNetworkDomains): string { +- const parts = [` +-Command Execution: +-- Use && to chain simple commands on one line +-- Prefer pipelines | over temporary files for data flow +-- Never create a sub-shell (eg. bash -c "command") unless explicitly asked +- +-Directory Management: +-- Prefer relative paths when navigating directories, only use absolute when the path is far away or the current cwd is not expected +-- By default (mode=sync), shell and cwd are reused by subsequent sync commands +-- Use $PWD for current directory references +-- Consider using pushd/popd for directory stack management +-- Supports directory shortcuts like ~ and - +- +-Program Execution: +-- Supports Python, Node.js, and other executables +-- Install packages via package managers (brew, apt, etc.) +-- Use which or command -v to verify command availability +- +-Async Mode: +-- For long-running tasks (e.g., servers), use mode=async +-- Returns a terminal ID for checking status and runtime later +- +-Use write_${shellType} to send commands or input to a terminal session.`]; +- +- if (isSandboxEnabled) { +- parts.push(createSandboxLines(networkDomains).join('\n')); +- } +- +- parts.push(` +- +-Output Management: +-- Output is automatically truncated if longer than 60KB to prevent context overflow +-- Use head, tail, grep, awk to filter and limit output size +-- For pager commands, disable paging: git --no-pager or add | cat +-- Use wc -l to count lines before displaying large outputs +- +-Best Practices: +-- Quote variables: "$var" instead of $var to handle spaces +-- Use find with -exec or xargs for file operations +-- Be specific with commands to avoid excessive output +-- Avoid printing credentials unless absolutely required +-- NEVER run sleep or similar wait commands in a terminal. You will be automatically notified on your next turn when async terminal commands or timed-out sync commands complete or need input. Use read_${shellType} to check output before then +- +-Interactive Input Handling: +-- When a terminal command is waiting for interactive input, do NOT suggest alternatives or ask the user whether to proceed. Instead, use the ask_user tool to collect the needed values from the user, then send them. +-- Send exactly one answer per prompt using write_${shellType}. Never send multiple answers in a single send. +-- After each send, call read_${shellType} to read the next prompt before sending the next answer. +-- Continue one prompt at a time until the command finishes.`); +- +- return parts.join(''); +-} - -- // ---- parseEventLog / serializeEventLog ------------------------------ +-function createBashModelDescription(isSandboxEnabled: boolean, networkDomains?: ITerminalSandboxResolvedNetworkDomains): string { +- return [ +- 'This tool allows you to execute shell commands in a persistent bash terminal session, preserving environment variables, working directory, and other context across multiple commands.', +- createGenericDescription('bash', isSandboxEnabled, networkDomains), +- '- Use [[ ]] for conditional tests instead of [ ]', +- '- Prefer $() over backticks for command substitution', +- '- Use set -e at start of complex commands to exit on errors' +- ].join('\n'); +-} +diff --git a/src/vs/platform/agentHost/node/copilot/copilotToolDisplay.ts b/src/vs/platform/agentHost/node/copilot/copilotToolDisplay.ts +deleted file mode 100644 +index 2d061529..00000000 +--- a/src/vs/platform/agentHost/node/copilot/copilotToolDisplay.ts ++++ /dev/null +@@ -1,735 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ - -- suite('parseEventLog', () => { +-import type { PermissionRequest } from '@github/copilot-sdk'; +-import { hasKey } from '../../../../base/common/types.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { appendEscapedMarkdownInlineCode, escapeMarkdownLinkLabel } from '../../../../base/common/htmlContent.js'; +-import { hash } from '../../../../base/common/hash.js'; +-import { localize } from '../../../../nls.js'; +-import type { IAgentToolCompleteEvent, IAgentToolReadyEvent, IAgentToolStartEvent } from '../../common/agentService.js'; +-import { stripRedundantCdPrefix } from '../../common/commandLineHelpers.js'; +-import { StringOrMarkdown } from '../../common/state/protocol/state.js'; +-import { basename } from '../../../../base/common/resources.js'; +- +-// ============================================================================= +-// Copilot CLI built-in tool interfaces +-// +-// The Copilot CLI (via @github/copilot) exposes these built-in tools. Tool names +-// and parameter shapes are not typed in the SDK -- they come from the CLI server +-// as plain strings. These interfaces are derived from observing the CLI's actual +-// tool events and the ShellConfig class in @github/copilot. +-// +-// Shell tool names follow a pattern per ShellConfig: +-// shellToolName, readShellToolName, writeShellToolName, +-// stopShellToolName, listShellsToolName +-// For bash: bash, read_bash, write_bash, bash_shutdown, list_bash +-// For powershell: powershell, read_powershell, write_powershell, list_powershell +-// ============================================================================= - -- 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'); -- }); +-/** +- * Known Copilot CLI tool names. These are the `toolName` values that appear +- * in `tool.execution_start` events from the SDK. +- */ +-const enum CopilotToolName { +- Bash = 'bash', +- ReadBash = 'read_bash', +- WriteBash = 'write_bash', +- BashShutdown = 'bash_shutdown', +- ListBash = 'list_bash', +- +- PowerShell = 'powershell', +- ReadPowerShell = 'read_powershell', +- WritePowerShell = 'write_powershell', +- ListPowerShell = 'list_powershell', +- +- View = 'view', +- Edit = 'edit', +- Create = 'create', +- Grep = 'grep', +- Glob = 'glob', +- ApplyPatch = 'apply_patch', +- GitApplyPatch = 'git_apply_patch', +- WebSearch = 'web_search', +- WebFetch = 'web_fetch', +- AskUser = 'ask_user', +- ReportIntent = 'report_intent', +- Skill = 'skill', +-} - -- 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); -- }); +-/** Parameters for the `bash` / `powershell` shell tools. */ +-interface ICopilotShellToolArgs { +- command: string; +- timeout?: number; +-} - -- 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); -- }); +-/** Parameters for file tools (`view`, `edit`, `create`). */ +-interface ICopilotFileToolArgs { +- path: string; +-} - -- test('empty input returns empty array', () => { -- assert.deepStrictEqual(parseEventLog(''), []); -- assert.deepStrictEqual(parseEventLog('\n\n'), []); -- }); -- }); +-/** +- * Parameters for the `view` tool. The Copilot CLI accepts an optional +- * `view_range: [startLine, endLine]` (1-based, inclusive). `endLine` may be +- * `-1` to mean "to end of file". +- */ +-interface ICopilotViewToolArgs extends ICopilotFileToolArgs { +- view_range?: number[]; +-} - -- suite('serializeEventLog', () => { +-/** +- * Normalizes a `view_range` array. Returns `undefined` unless the array has +- * exactly two integer elements with `startLine >= 0`. `endLine === -1` is +- * preserved as the "to end of file" sentinel; otherwise `endLine` must be +- * `>= startLine`. +- */ +-function formatViewRange(view_range: number[] | undefined): { startLine: number; endLine: number } | undefined { +- if (!Array.isArray(view_range) || view_range.length !== 2) { +- return undefined; +- } +- const [startLine, endLine] = view_range; +- if (!Number.isInteger(startLine) || !Number.isInteger(endLine)) { +- return undefined; +- } +- if (startLine < 0) { +- return undefined; +- } +- if (endLine !== -1 && endLine < startLine) { +- return undefined; +- } +- return { startLine, endLine }; +-} - -- 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); -- } -- }); +-/** Parameters for the `grep` tool. */ +-interface ICopilotGrepToolArgs { +- pattern: string; +- path?: string; +- include?: string; +-} - -- test('ends with a newline', () => { -- const entries = [makeEntry('session.start')]; -- const serialized = serializeEventLog(entries); -- assert.ok(serialized.endsWith('\n')); -- }); -- }); +-/** Parameters for the `glob` tool. */ +-interface ICopilotGlobToolArgs { +- pattern: string; +- path?: string; +-} - -- // ---- findTurnBoundaryInEventLog ------------------------------------- +-/** Set of tool names that perform file edits. */ +-const EDIT_TOOL_NAMES: ReadonlySet = new Set([ +- CopilotToolName.Edit, +- CopilotToolName.Create, +- CopilotToolName.ApplyPatch, +- CopilotToolName.GitApplyPatch, +-]); - -- suite('findTurnBoundaryInEventLog', () => { +-/** +- * Returns true if the tool modifies files on disk. +- */ +-export function isEditTool(toolName: string): boolean { +- return EDIT_TOOL_NAMES.has(toolName); +-} - -- 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'); -- }); +-/** +- * Extracts the target file path from an edit tool's parameters, if available. +- */ +-export function getEditFilePath(parameters: unknown): string | undefined { +- if (typeof parameters === 'string') { +- try { +- parameters = JSON.parse(parameters); +- } catch { +- return undefined; +- } +- } - -- 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'); -- }); +- const args = parameters as ICopilotFileToolArgs | undefined; +- return args?.path; +-} - -- 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'); -- }); +-/** Set of tool names that execute shell commands (bash or powershell). */ +-const SHELL_TOOL_NAMES: ReadonlySet = new Set([ +- CopilotToolName.Bash, +- CopilotToolName.PowerShell, +-]); +- +-/** Set of tool names that write input to an interactive shell session. */ +-const WRITE_SHELL_TOOL_NAMES: ReadonlySet = new Set([ +- CopilotToolName.WriteBash, +- CopilotToolName.WritePowerShell, +-]); +- +-/** Set of tool names that read output from an interactive shell session. */ +-const READ_SHELL_TOOL_NAMES: ReadonlySet = new Set([ +- CopilotToolName.ReadBash, +- CopilotToolName.ReadPowerShell, +-]); +- +-/** Set of tool names that spawn subagent sessions. */ +-const SUBAGENT_TOOL_NAMES: ReadonlySet = new Set([ +- 'task', +-]); - -- test('returns -1 for non-existent turn', () => { -- const entries = buildTestEventLog(2); -- assert.strictEqual(findTurnBoundaryInEventLog(entries, 5), -1); -- }); +-/** +- * Tools that should not be shown to the user. These are internal tools +- * used by the CLI for its own purposes (e.g., reporting intent to the model). +- * +- * `skill` is hidden because the SDK already emits a richer `skill.invoked` +- * lifecycle event with the resolved skill file path; the agent session +- * synthesizes a tool-start/complete pair from that event so the UI can +- * render a clickable file link instead of just the skill name. See +- * {@link synthesizeSkillToolEvents}. +- */ +-const HIDDEN_TOOL_NAMES: ReadonlySet = new Set([ +- CopilotToolName.ReportIntent, +- CopilotToolName.Skill, +-]); - -- test('returns -1 for empty log', () => { -- assert.strictEqual(findTurnBoundaryInEventLog([], 0), -1); -- }); -- }); +-/** +- * Returns true if the tool should be hidden from the UI. +- */ +-export function isHiddenTool(toolName: string): boolean { +- return HIDDEN_TOOL_NAMES.has(toolName); +-} - -- // ---- buildForkedEventLog -------------------------------------------- +-/** +- * Returns true if the tool executes shell commands. +- */ +-export function isShellTool(toolName: string): boolean { +- return SHELL_TOOL_NAMES.has(toolName); +-} - -- suite('buildForkedEventLog', () => { +-// ============================================================================= +-// Display helpers +-// +-// These functions translate Copilot CLI tool names and arguments into +-// human-readable display strings. This logic lives here -- in the agent-host +-// process -- so the IPC protocol stays agent-agnostic; the renderer never needs +-// to know about specific tool names. +-// ============================================================================= +- +-function truncate(text: string, maxLength: number): string { +- return text.length > maxLength ? text.substring(0, maxLength - 3) + '...' : text; +-} - -- test('forks at turn 0', () => { -- const entries = buildTestEventLog(3); -- const forked = buildForkedEventLog(entries, 0, 'new-session-id'); +-/** +- * Formats a file path as a markdown link `[](file-uri)` so it renders +- * as a clickable file widget in the chat UI. +- */ +-function formatPathAsMarkdownLink(path: string): string { +- const uri = URI.file(path); +- return `[${basename(uri)}](${uri})`; +-} - -- // 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'); -- }); +-/** +- * Wraps a localized message containing a markdown file link into a +- * `StringOrMarkdown` object so the renderer treats it as markdown. +- */ +-function md(value: string): StringOrMarkdown { +- return { markdown: value }; +-} - -- test('forks at turn 1', () => { -- const entries = buildTestEventLog(3); -- const forked = buildForkedEventLog(entries, 1, 'new-session-id'); +-export function getToolDisplayName(toolName: string): string { +- switch (toolName) { +- case CopilotToolName.Bash: return localize('toolName.bash', "Bash"); +- case CopilotToolName.PowerShell: return localize('toolName.powershell', "PowerShell"); +- case CopilotToolName.ReadBash: +- case CopilotToolName.ReadPowerShell: return localize('toolName.readShell', "Read Shell Output"); +- case CopilotToolName.WriteBash: +- case CopilotToolName.WritePowerShell: return localize('toolName.writeShell', "Write Shell Input"); +- case CopilotToolName.BashShutdown: return localize('toolName.bashShutdown', "Stop Shell"); +- case CopilotToolName.ListBash: +- case CopilotToolName.ListPowerShell: return localize('toolName.listShells', "List Shells"); +- case CopilotToolName.View: return localize('toolName.view', "View File"); +- case CopilotToolName.Edit: return localize('toolName.edit', "Edit File"); +- case CopilotToolName.Create: return localize('toolName.create', "Create File"); +- case CopilotToolName.Grep: return localize('toolName.grep', "Search"); +- case CopilotToolName.Glob: return localize('toolName.glob', "Find Files"); +- case CopilotToolName.ApplyPatch: +- case CopilotToolName.GitApplyPatch: return localize('toolName.patch', "Patch"); +- case CopilotToolName.WebSearch: return localize('toolName.webSearch', "Web Search"); +- case CopilotToolName.WebFetch: return localize('toolName.webFetch', "Web Fetch"); +- case CopilotToolName.AskUser: return localize('toolName.askUser', "Ask User"); +- default: return toolName; +- } +-} - -- // session.start + 2 turns × 4 events = 9 events -- assert.strictEqual(forked.length, 9); -- }); +-export function getInvocationMessage(toolName: string, displayName: string, parameters: Record | undefined): StringOrMarkdown { +- if (SHELL_TOOL_NAMES.has(toolName)) { +- const args = parameters as ICopilotShellToolArgs | undefined; +- if (args?.command) { +- const firstLine = args.command.split('\n')[0]; +- return md(localize('toolInvoke.shellCmd', "Running {0}", appendEscapedMarkdownInlineCode(truncate(firstLine, 80)))); +- } +- return localize('toolInvoke.shell', "Running {0} command", displayName); +- } - -- test('generates unique UUIDs', () => { -- const entries = buildTestEventLog(2); -- const forked = buildForkedEventLog(entries, 0, 'new-session-id'); +- if (WRITE_SHELL_TOOL_NAMES.has(toolName)) { +- const args = parameters as ICopilotShellToolArgs | undefined; +- if (args?.command) { +- const firstLine = args.command.split('\n')[0]; +- return md(localize('toolInvoke.writeShellCmd', "Sending {0} to shell", appendEscapedMarkdownInlineCode(truncate(firstLine, 80)))); +- } +- return localize('toolInvoke.writeShell', "Sending input to shell"); +- } - -- const ids = new Set(forked.map(e => e.id)); -- assert.strictEqual(ids.size, forked.length, 'All IDs should be unique'); +- if (READ_SHELL_TOOL_NAMES.has(toolName)) { +- return localize('toolInvoke.readShell', "Reading shell output"); +- } - -- // None should match the original -- for (const entry of forked) { -- assert.ok(!entries.some(e => e.id === entry.id), 'Should not reuse original IDs'); +- switch (toolName) { +- case CopilotToolName.View: { +- const args = parameters as ICopilotViewToolArgs | undefined; +- if (args?.path) { +- const link = formatPathAsMarkdownLink(args.path); +- const range = formatViewRange(args.view_range); +- if (range) { +- if (range.endLine === -1) { +- return md(localize('toolInvoke.viewFileFromLine', "Reading {0}, line {1} to the end", link, range.startLine)); +- } +- if (range.endLine !== range.startLine) { +- return md(localize('toolInvoke.viewFileRange', "Reading {0}, lines {1} to {2}", link, range.startLine, range.endLine)); +- } +- return md(localize('toolInvoke.viewFileLine', "Reading {0}, line {1}", link, range.startLine)); +- } +- return md(localize('toolInvoke.viewFile', "Reading {0}", link)); - } -- }); -- -- 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`, -- ); +- return localize('toolInvoke.view', "Reading file"); +- } +- case CopilotToolName.Edit: { +- const args = parameters as ICopilotFileToolArgs | undefined; +- if (args?.path) { +- return md(localize('toolInvoke.editFile', "Editing {0}", formatPathAsMarkdownLink(args.path))); - } -- }); -- -- 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}`, -- ); +- return localize('toolInvoke.edit', "Editing file"); +- } +- case CopilotToolName.Create: { +- const args = parameters as ICopilotFileToolArgs | undefined; +- if (args?.path) { +- return md(localize('toolInvoke.createFile', "Creating {0}", formatPathAsMarkdownLink(args.path))); - } -- }); -- }); -- -- // ---- 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`, -- ); +- return localize('toolInvoke.create', "Creating file"); +- } +- case CopilotToolName.Grep: { +- const args = parameters as ICopilotGrepToolArgs | undefined; +- if (args?.pattern) { +- return md(localize('toolInvoke.grepPattern', "Searching for {0}", appendEscapedMarkdownInlineCode(truncate(args.pattern, 80)))); - } -- }); -- -- 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 --------------------------------------------- +- return localize('toolInvoke.grep', "Searching files"); +- } +- case CopilotToolName.Glob: { +- const args = parameters as ICopilotGlobToolArgs | undefined; +- if (args?.pattern) { +- return md(localize('toolInvoke.globPattern', "Finding files matching {0}", appendEscapedMarkdownInlineCode(truncate(args.pattern, 80)))); +- } +- return localize('toolInvoke.glob', "Finding files"); +- } +- default: +- return localize('toolInvoke.generic', "Using \"{0}\"", displayName); +- } +-} - -- suite('buildWorkspaceYaml', () => { +-export function getPastTenseMessage(toolName: string, displayName: string, parameters: Record | undefined, success: boolean): StringOrMarkdown { +- if (!success) { +- return localize('toolComplete.failed', "\"{0}\" failed", displayName); +- } - -- 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:')); -- }); -- }); +- if (SHELL_TOOL_NAMES.has(toolName)) { +- const args = parameters as ICopilotShellToolArgs | undefined; +- if (args?.command) { +- const firstLine = args.command.split('\n')[0]; +- return md(localize('toolComplete.shellCmd', "Ran {0}", appendEscapedMarkdownInlineCode(truncate(firstLine, 80)))); +- } +- return localize('toolComplete.shell', "Ran {0} command", displayName); +- } - -- // ---- SQLite operations (in-memory) ---------------------------------- +- if (WRITE_SHELL_TOOL_NAMES.has(toolName)) { +- const args = parameters as ICopilotShellToolArgs | undefined; +- if (args?.command) { +- const firstLine = args.command.split('\n')[0]; +- return md(localize('toolComplete.writeShellCmd', "Sent {0} to shell", appendEscapedMarkdownInlineCode(truncate(firstLine, 80)))); +- } +- return localize('toolComplete.writeShell', "Sent input to shell"); +- } - -- suite('forkSessionInDb', () => { +- if (READ_SHELL_TOOL_NAMES.has(toolName)) { +- return localize('toolComplete.readShell', "Read shell output"); +- } - -- 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); +- switch (toolName) { +- case CopilotToolName.View: { +- const args = parameters as ICopilotViewToolArgs | undefined; +- if (args?.path) { +- const link = formatPathAsMarkdownLink(args.path); +- const range = formatViewRange(args.view_range); +- if (range) { +- if (range.endLine === -1) { +- return md(localize('toolComplete.viewFileFromLine', "Read {0}, line {1} to the end", link, range.startLine)); - } -- resolve(db); -- }); -- }); +- if (range.endLine !== range.startLine) { +- return md(localize('toolComplete.viewFileRange', "Read {0}, lines {1} to {2}", link, range.startLine, range.endLine)); +- } +- return md(localize('toolComplete.viewFileLine', "Read {0}, line {1}", link, range.startLine)); +- } +- return md(localize('toolComplete.viewFile', "Read {0}", link)); +- } +- return localize('toolComplete.view', "Read file"); - } -- -- function exec(db: import('@vscode/sqlite3').Database, sql: string): Promise { -- return new Promise((resolve, reject) => { -- db.exec(sql, err => err ? reject(err) : resolve()); -- }); +- case CopilotToolName.Edit: { +- const args = parameters as ICopilotFileToolArgs | undefined; +- if (args?.path) { +- return md(localize('toolComplete.editFile', "Edited {0}", formatPathAsMarkdownLink(args.path))); +- } +- return localize('toolComplete.edit', "Edited file"); - } -- -- 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); -- }); -- }); +- case CopilotToolName.Create: { +- const args = parameters as ICopilotFileToolArgs | undefined; +- if (args?.path) { +- return md(localize('toolComplete.createFile', "Created {0}", formatPathAsMarkdownLink(args.path))); +- } +- return localize('toolComplete.create', "Created file"); - } -- -- function close(db: import('@vscode/sqlite3').Database): Promise { -- return new Promise((resolve, reject) => { -- db.close(err => err ? reject(err) : resolve()); -- }); +- case CopilotToolName.Grep: { +- const args = parameters as ICopilotGrepToolArgs | undefined; +- if (args?.pattern) { +- return md(localize('toolComplete.grepPattern', "Searched for {0}", appendEscapedMarkdownInlineCode(truncate(args.pattern, 80)))); +- } +- return localize('toolComplete.grep', "Searched files"); - } -- -- 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'); -- `); +- case CopilotToolName.Glob: { +- const args = parameters as ICopilotGlobToolArgs | undefined; +- if (args?.pattern) { +- return md(localize('toolComplete.globPattern', "Found files matching {0}", appendEscapedMarkdownInlineCode(truncate(args.pattern, 80)))); - } +- return localize('toolComplete.glob', "Found files"); - } +- default: +- return localize('toolComplete.generic', "Used \"{0}\"", displayName); +- } +-} - -- test('copies session metadata', async () => { -- const db = await openTestDb(); -- try { -- await setupSchema(db); -- await seedTestData(db, 'source', 3); +-// ============================================================================= +-// Skill event synthesis +-// +-// The Copilot SDK emits a `skill` tool call (which we hide) and, separately, a +-// `skill.invoked` lifecycle event with the resolved skill file path. We turn +-// the latter into a synthesized tool-start/complete pair so clients can render +-// a clickable file link to the SKILL.md the agent loaded -- matching the +-// existing `view`-tool display style. Live and replay paths share this helper +-// so they stay in lock-step (see also the mirrored-pair gotcha for tool-call +-// display in this file). +-// ============================================================================= +- +-/** Subset of the SDK's `skill.invoked` payload that the synth helper needs. */ +-export interface ICopilotSkillInvokedData { +- readonly name: string; +- readonly path?: string; +- readonly description?: string; +-} - -- await forkSessionInDb(db, 'source', 'forked', 1); +-/** +- * Builds a stable synthetic tool call id for a `skill.invoked` event so +- * reconnect/replay produces the same id as the original live emit. The id +- * is used unencoded as a path segment (e.g. by `ChatResponseResource.createUri`), +- * so it must not contain characters like `/` -- we hash any fallback values +- * that could carry filesystem paths or arbitrary text. +- */ +-export function getSkillSyntheticToolCallId(eventId: string | undefined, data: ICopilotSkillInvokedData): string { +- if (eventId) { +- return `synth-skill-${eventId}`; +- } +- const seed = data.path ?? data.name; +- return `synth-skill-${hash(seed).toString(16)}`; +-} - -- 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); -- } -- }); +-/** +- * Synthesizes the `tool_start` and `tool_complete` agent progress events that +- * represent a successful `skill.invoked` lifecycle event. Used by both the +- * live session handler and the history-replay mapper so the two paths render +- * identically. +- */ +-export function synthesizeSkillToolEvents( +- session: URI, +- data: ICopilotSkillInvokedData, +- eventId: string | undefined, +-): { start: IAgentToolStartEvent; complete: IAgentToolCompleteEvent } { +- const toolCallId = getSkillSyntheticToolCallId(eventId, data); +- const displayName = localize('toolName.skill', "Read Skill"); +- // Use the skill name as the link text rather than the basename: every skill +- // file is named SKILL.md, so `Reading skill [plan]` reads better than the +- // always-identical `Reading skill [SKILL.md]`. The client may further upgrade +- // this link to a rich pill based on the `SKILL.md` basename. Skill names and +- // paths come from the SDK / agent host and are escaped to prevent markdown +- // injection from a malicious skill author. +- // Escape only the characters that would break out of markdown link text +- // syntax (`\` and `]`); a full markdown escape would leave visible +- // backslashes in renderers (like the skill pill) that extract link text +- // without re-parsing markdown. +- const escapedName = escapeMarkdownLinkLabel(data.name); +- const skillLink = data.path ? `[${escapedName}](${URI.file(data.path)})` : undefined; +- const invocationMessage: StringOrMarkdown = skillLink +- ? md(localize('toolInvoke.skill', "Reading skill {0}", skillLink)) +- : localize('toolInvoke.skillName', "Reading skill {0}", data.name); +- const pastTenseMessage: StringOrMarkdown = skillLink +- ? md(localize('toolComplete.skill', "Read skill {0}", skillLink)) +- : localize('toolComplete.skillName', "Read skill {0}", data.name); +- const start: IAgentToolStartEvent = { +- session, +- type: 'tool_start', +- toolCallId, +- toolName: CopilotToolName.Skill, +- displayName, +- invocationMessage, +- }; +- const complete: IAgentToolCompleteEvent = { +- session, +- type: 'tool_complete', +- toolCallId, +- result: { +- success: true, +- pastTenseMessage, +- }, +- }; +- return { start, complete }; +-} - -- test('copies turns up to fork point', async () => { -- const db = await openTestDb(); -- try { -- await setupSchema(db); -- await seedTestData(db, 'source', 3); +-export function getToolInputString(toolName: string, parameters: Record | undefined, rawArguments: string | undefined): string | undefined { +- if (!parameters && !rawArguments) { +- return undefined; +- } - -- await forkSessionInDb(db, 'source', 'forked', 1); +- if (SHELL_TOOL_NAMES.has(toolName) || WRITE_SHELL_TOOL_NAMES.has(toolName)) { +- const args = parameters as ICopilotShellToolArgs | undefined; +- // Custom tool overrides may wrap the args: { kind: 'custom-tool', args: { command: '...' } } +- const command = args?.command ?? (args as Record | undefined)?.args; +- if (typeof command === 'string') { +- return command; +- } +- if (typeof command === 'object' && command !== null && hasKey(command, { command: true })) { +- return (command as ICopilotShellToolArgs).command; +- } +- return rawArguments; +- } - -- 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); +- switch (toolName) { +- case CopilotToolName.Grep: { +- const args = parameters as ICopilotGrepToolArgs | undefined; +- return args?.pattern ?? rawArguments; +- } +- default: +- // For other tools, show the formatted JSON arguments +- if (parameters) { +- try { +- return JSON.stringify(parameters, null, 2); +- } catch { +- return rawArguments; +- } - } -- }); +- return rawArguments; +- } +-} - -- test('copies session files up to fork point', async () => { -- const db = await openTestDb(); -- try { -- await setupSchema(db); -- await seedTestData(db, 'source', 3); +-/** +- * Returns a rendering hint for the given tool. Currently only 'terminal' is +- * supported, which tells the renderer to display the tool as a terminal command +- * block. +- */ +-export function getToolKind(toolName: string): 'terminal' | 'subagent' | undefined { +- if (SHELL_TOOL_NAMES.has(toolName)) { +- return 'terminal'; +- } +- if (SUBAGENT_TOOL_NAMES.has(toolName)) { +- return 'subagent'; +- } +- return undefined; +-} - -- await forkSessionInDb(db, 'source', 'forked', 1); +-/** +- * Extracts subagent metadata (agent name, description) from the parsed +- * arguments of a Copilot SDK subagent tool call. The Copilot `task` tool +- * uses `agent_type` (snake_case), which this normalizes into the generic +- * `subagentAgentName` / `subagentDescription` shape used by the rest of the +- * agent host code. +- * +- * Only call this for tools where {@link getToolKind} returned `'subagent'`. +- */ +-export function getSubagentMetadata(parameters: Record | undefined): { agentName?: string; description?: string } { +- if (!parameters) { +- return {}; +- } +- const agentName = typeof parameters.agent_type === 'string' && parameters.agent_type.length > 0 +- ? parameters.agent_type +- : undefined; +- const description = typeof parameters.description === 'string' && parameters.description.length > 0 +- ? parameters.description +- : undefined; +- return { agentName, description }; +-} - -- const files = await all(db, 'SELECT * FROM session_files WHERE session_id = ?', ['forked']); -- assert.strictEqual(files.length, 2); // files from turns 0 and 1 +-/** +- * Returns the shell language identifier for syntax highlighting. +- * Used when creating terminal tool-specific data for the renderer. +- */ +-export function getShellLanguage(toolName: string): string { +- switch (toolName) { +- case CopilotToolName.PowerShell: +- case CopilotToolName.WritePowerShell: +- case CopilotToolName.ReadPowerShell: return 'powershell'; +- default: return 'shellscript'; +- } +-} +- +-// ============================================================================= +-// Permission display +-// +-// Derives display fields from SDK permission requests for the tool +-// confirmation UI. Colocated with the tool-start display helpers above so +-// that formatting utilities (formatPathAsMarkdownLink, md, etc.) are shared. +-// ============================================================================= +- +-export function tryStringify(value: unknown): string | undefined { +- try { +- return JSON.stringify(value); +- } catch { +- return undefined; +- } +-} +- +-/** +- * Extends the SDK's {@link PermissionRequest} with the known extra properties +- * that arrive on the index-signature. The SDK defines these as `[key: string]: unknown` +- * so this interface adds proper types for the fields we actually use. +- */ +-export interface ITypedPermissionRequest extends PermissionRequest { +- /** File path — set for `read` permission requests. */ +- path?: string; +- /** File path — set for `write` permission requests. */ +- fileName?: string; +- /** Full shell command text — set for `shell` permission requests. */ +- fullCommandText?: string; +- /** Human-readable intention describing the operation. */ +- intention?: string; +- /** MCP server name — set for `mcp` permission requests. */ +- serverName?: string; +- /** Tool name — set for `mcp` and `custom-tool` permission requests. */ +- toolName?: string; +- /** Tool arguments — set for `custom-tool` permission requests. */ +- args?: Record; +- /** URL — set for `url` permission requests. */ +- url?: string; +- /** Unified diff of the proposed change — set for `write` permission requests. */ +- diff?: string; +- /** New file contents that will be written — set for `write` permission requests. */ +- newFileContents?: string; +-} +- +-/** Safely extract a string value from an SDK field that may be `unknown` at runtime. */ +-function str(value: unknown): string | undefined { +- return typeof value === 'string' ? value : undefined; +-} +- +-/** +- * Derives display fields from a permission request for the tool confirmation UI. +- */ +-export function getPermissionDisplay(request: ITypedPermissionRequest, workingDirectory?: URI): { +- confirmationTitle: string; +- invocationMessage: StringOrMarkdown; +- toolInput?: string; +- /** Normalized permission kind for auto-approval routing. */ +- permissionKind: IAgentToolReadyEvent['permissionKind']; +- /** File path extracted from the request. */ +- permissionPath?: string; +-} { +- const path = str(request.path) ?? str(request.fileName); +- const fullCommandText = str(request.fullCommandText); +- const intention = str(request.intention); +- const serverName = str(request.serverName); +- const toolName = str(request.toolName); +- +- switch (request.kind) { +- case 'shell': { +- // Strip a redundant `cd && …` prefix so the +- // confirmation dialog shows the simplified command. +- const shellParams: Record | undefined = fullCommandText ? { command: fullCommandText } : undefined; +- stripRedundantCdPrefix(CopilotToolName.Bash, shellParams, workingDirectory); +- const cleanedCommand = typeof shellParams?.command === 'string' ? shellParams.command : fullCommandText; +- return { +- confirmationTitle: localize('copilot.permission.shell.title', "Run in terminal?"), +- invocationMessage: intention ?? getInvocationMessage(CopilotToolName.Bash, getToolDisplayName(CopilotToolName.Bash), cleanedCommand ? { command: cleanedCommand } : undefined), +- toolInput: cleanedCommand, +- permissionKind: 'shell', +- permissionPath: path, +- }; +- } +- 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 sdkToolName = str(request.toolName); +- if (args && sdkToolName && isShellTool(sdkToolName) && typeof args.command === 'string') { +- stripRedundantCdPrefix(sdkToolName, args, workingDirectory); +- const command = args.command as string; +- return { +- confirmationTitle: localize('copilot.permission.shell.title', "Run in terminal?"), +- invocationMessage: getInvocationMessage(sdkToolName, getToolDisplayName(sdkToolName), { command }), +- toolInput: command, +- permissionKind: 'shell', +- permissionPath: path, +- }; +- } +- return { +- confirmationTitle: localize('copilot.permission.default.title', "Allow tool call?"), +- invocationMessage: md(localize('copilot.permission.default.message', "Allow the model to call {0}?", appendEscapedMarkdownInlineCode(toolName ?? request.kind))), +- toolInput: args ? tryStringify(args) : tryStringify(request), +- permissionKind: request.kind, +- permissionPath: path, +- }; +- } +- case 'write': +- return { +- confirmationTitle: localize('copilot.permission.write.title', "Write file?"), +- invocationMessage: getInvocationMessage(CopilotToolName.Edit, getToolDisplayName(CopilotToolName.Edit), path ? { path } : undefined), +- toolInput: tryStringify(path ? { path } : request) ?? undefined, +- permissionKind: 'write', +- permissionPath: path, +- }; +- case 'mcp': { +- const title = toolName ?? localize('copilot.permission.mcp.defaultTool', "MCP Tool"); +- return { +- confirmationTitle: serverName +- ? localize('copilot.permission.mcp.title', "Allow tool from {0}?", serverName) +- : localize('copilot.permission.default.title', "Allow tool call?"), +- invocationMessage: serverName ? `${serverName}: ${title}` : title, +- toolInput: tryStringify({ serverName, toolName }) ?? undefined, +- permissionKind: 'mcp', +- permissionPath: path, +- }; +- } +- case 'read': +- return { +- confirmationTitle: localize('copilot.permission.read.title', "Read file?"), +- invocationMessage: intention ?? getInvocationMessage(CopilotToolName.View, getToolDisplayName(CopilotToolName.View), path ? { path } : undefined), +- toolInput: tryStringify(path ? { path, intention } : request) ?? undefined, +- permissionKind: 'read', +- permissionPath: path, +- }; +- case 'url': { +- const url = str(request.url); +- // Parse through URL for punycode escaping, but preserve the raw value if parsing fails. +- const normalizedUrl = url ? (URL.canParse(url) ? new URL(url).href : url) : undefined; +- return { +- confirmationTitle: localize('copilot.permission.url.title', "Fetch URL?"), +- invocationMessage: md(localize('copilot.permission.url.message', "Allow fetching web content?")), +- toolInput: normalizedUrl ? JSON.stringify({ url: normalizedUrl }) : undefined, +- permissionKind: 'url', +- }; +- } +- default: +- return { +- confirmationTitle: localize('copilot.permission.default.title', "Allow tool call?"), +- invocationMessage: md(localize('copilot.permission.default.message', "Allow the model to call {0}?", appendEscapedMarkdownInlineCode(toolName ?? request.kind))), +- toolInput: tryStringify(request) ?? undefined, +- permissionKind: request.kind, +- permissionPath: path, +- }; +- } +-} +diff --git a/src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts b/src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts +deleted file mode 100644 +index 5a306612..00000000 +--- a/src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts ++++ /dev/null +@@ -1,300 +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 { URI } from '../../../../base/common/uri.js'; +-import { SessionHistoryEvent } from '../../common/agentService.js'; +-import { stripRedundantCdPrefix } from '../../common/commandLineHelpers.js'; +-import { IFileEditRecord, ISessionDatabase } from '../../common/sessionDataService.js'; +-import { ToolResultContentType, type ToolResultContent } from '../../common/state/sessionState.js'; +-import { getInvocationMessage, getPastTenseMessage, getShellLanguage, getSubagentMetadata, getToolDisplayName, getToolInputString, getToolKind, isEditTool, isHiddenTool, synthesizeSkillToolEvents } from './copilotToolDisplay.js'; +-import { buildSessionDbUri } from './fileEditTracker.js'; +- +-function tryStringify(value: unknown): string | undefined { +- try { +- return JSON.stringify(value); +- } catch { +- return undefined; +- } +-} +- +-// ---- Minimal event shapes matching the SDK's SessionEvent union --------- +-// Defined here so tests can construct events without importing the SDK. +- +-export interface ISessionEventToolStart { +- type: 'tool.execution_start'; +- data: { +- toolCallId: string; +- toolName: string; +- arguments?: unknown; +- mcpServerName?: string; +- mcpToolName?: string; +- parentToolCallId?: string; +- }; +-} +- +-export interface ISessionEventToolComplete { +- type: 'tool.execution_complete'; +- data: { +- toolCallId: string; +- success: boolean; +- result?: { content?: string }; +- error?: { message: string; code?: string }; +- isUserRequested?: boolean; +- toolTelemetry?: unknown; +- parentToolCallId?: string; +- }; +-} +- +-export interface ISessionEventMessage { +- type: 'assistant.message' | 'user.message'; +- data?: { +- messageId?: string; +- interactionId?: string; +- content?: string; +- toolRequests?: readonly { toolCallId: string; name: string; arguments?: unknown; type?: 'function' | 'custom' }[]; +- reasoningOpaque?: string; +- reasoningText?: string; +- encryptedContent?: string; +- parentToolCallId?: string; +- /** +- * Origin of this message. The SDK sets this to a non-`'user'` value +- * (e.g. `'skill-pdf'`) for messages it injects on behalf of a skill or +- * other internal mechanism. We filter those out so they don't render +- * as user turns. +- */ +- source?: string; +- }; +-} +- +-/** Minimal event shape for `skill.invoked`, used to synthesize a tool-style render. */ +-export interface ISessionEventSkillInvoked { +- type: 'skill.invoked'; +- id?: string; +- data: { +- name: string; +- path?: string; +- description?: string; +- }; +-} +- +-/** +- * Returns true if the event is a SDK-injected `user.message` that should not +- * be shown to the user (e.g. skill-content injection). +- * +- * The SDK marks these via a non-`'user'` `source` field. Older sessions +- * persisted before `source` existed will not be filtered; that is accepted +- * leakage rather than guessed-at content sniffing. +- */ +-export function isSyntheticUserMessage(event: ISessionEvent): boolean { +- if (event.type !== 'user.message') { +- return false; +- } +- const source = (event as ISessionEventMessage).data?.source; +- return !!source && source.toLowerCase() !== 'user'; +-} +- +-/** Minimal event shape for session history mapping. */ +-export type ISessionEvent = ISessionEventToolStart | ISessionEventToolComplete | ISessionEventMessage | ISessionEventSubagentStarted | ISessionEventSkillInvoked | { type: string; data?: unknown }; +- +-export interface ISessionEventSubagentStarted { +- type: 'subagent.started'; +- data: { +- toolCallId: string; +- agentName: string; +- agentDisplayName: string; +- agentDescription: string; +- }; +-} +- +-/** +- * Maps raw SDK session events into agent protocol events, restoring +- * stored file-edit metadata from the session database when available. +- * +- * If `workingDirectory` is provided, redundant `cd &&` +- * (or PowerShell equivalent) prefixes are stripped from shell tool +- * commands so clients see the simplified form. +- * +- * Extracted as a standalone function so it can be tested without the +- * full CopilotAgent or SDK dependencies. +- */ +-export async function mapSessionEvents( +- session: URI, +- db: ISessionDatabase | undefined, +- events: readonly ISessionEvent[], +- workingDirectory?: URI, +-): Promise { +- const result: SessionHistoryEvent[] = []; +- const toolInfoByCallId = new Map | undefined; rewrittenArgs?: string }>(); +- +- // Collect all tool call IDs for edit tools so we can batch-query the database +- const editToolCallIds: string[] = []; +- +- // First pass: collect tool info and identify edit tool calls +- for (const e of events) { +- if (e.type === 'tool.execution_start') { +- const d = (e as ISessionEventToolStart).data; +- if (isHiddenTool(d.toolName)) { +- continue; +- } +- const toolArgs = d.arguments !== undefined ? tryStringify(d.arguments) : undefined; +- let parameters: Record | undefined; +- if (toolArgs) { +- try { parameters = JSON.parse(toolArgs) as Record; } catch { /* ignore */ } +- } +- const rewrittenArgs = stripRedundantCdPrefix(d.toolName, parameters, workingDirectory) ? tryStringify(parameters) : undefined; +- toolInfoByCallId.set(d.toolCallId, { toolName: d.toolName, parameters, rewrittenArgs }); +- if (isEditTool(d.toolName)) { +- editToolCallIds.push(d.toolCallId); +- } +- } +- } +- +- // Query the database for stored file edits (metadata only) +- let storedEdits: Map | undefined; +- if (db && editToolCallIds.length > 0) { +- try { +- const records = await db.getFileEdits(editToolCallIds); +- if (records.length > 0) { +- storedEdits = new Map(); +- for (const r of records) { +- let list = storedEdits.get(r.toolCallId); +- if (!list) { +- list = []; +- storedEdits.set(r.toolCallId, list); +- } +- list.push(r); +- } +- } +- } catch (_e) { +- // Database may not exist yet for new sessions — that's fine +- } +- } +- +- const sessionUriStr = session.toString(); +- +- // Second pass: build result events +- for (const e of events) { +- if (e.type === 'assistant.message' || e.type === 'user.message') { +- if (isSyntheticUserMessage(e)) { +- continue; +- } +- const d = (e as ISessionEventMessage).data; +- result.push({ +- session, +- type: 'message', +- role: e.type === 'user.message' ? 'user' : 'assistant', +- messageId: d?.messageId ?? d?.interactionId ?? '', +- content: d?.content ?? '', +- toolRequests: d?.toolRequests?.map((tr) => ({ +- toolCallId: tr.toolCallId, +- name: tr.name, +- arguments: tr.arguments !== undefined ? tryStringify(tr.arguments) : undefined, +- type: tr.type, +- })), +- reasoningOpaque: d?.reasoningOpaque, +- reasoningText: d?.reasoningText, +- encryptedContent: d?.encryptedContent, +- parentToolCallId: d?.parentToolCallId, +- }); +- } else if (e.type === 'tool.execution_start') { +- const d = (e as ISessionEventToolStart).data; +- if (isHiddenTool(d.toolName)) { +- continue; +- } +- const info = toolInfoByCallId.get(d.toolCallId); +- const displayName = getToolDisplayName(d.toolName); +- const toolKind = getToolKind(d.toolName); +- const toolArgs = info?.rewrittenArgs ?? (d.arguments !== undefined ? tryStringify(d.arguments) : undefined); +- const subagentMeta = toolKind === 'subagent' ? getSubagentMetadata(info?.parameters) : undefined; +- result.push({ +- session, +- type: 'tool_start', +- toolCallId: d.toolCallId, +- toolName: d.toolName, +- displayName, +- invocationMessage: getInvocationMessage(d.toolName, displayName, info?.parameters), +- toolInput: getToolInputString(d.toolName, info?.parameters, toolArgs), +- toolKind, +- language: toolKind === 'terminal' ? getShellLanguage(d.toolName) : undefined, +- toolArguments: toolArgs, +- subagentAgentName: subagentMeta?.agentName, +- subagentDescription: subagentMeta?.description, +- mcpServerName: d.mcpServerName, +- mcpToolName: d.mcpToolName, +- parentToolCallId: d.parentToolCallId, +- }); +- } else if (e.type === 'tool.execution_complete') { +- const d = (e as ISessionEventToolComplete).data; +- const info = toolInfoByCallId.get(d.toolCallId); +- if (!info) { +- continue; +- } +- toolInfoByCallId.delete(d.toolCallId); +- const displayName = getToolDisplayName(info.toolName); +- const toolOutput = d.error?.message ?? d.result?.content; +- const content: ToolResultContent[] = []; +- if (toolOutput !== undefined) { +- content.push({ type: ToolResultContentType.Text, text: toolOutput }); +- } +- +- // Restore file edit content references from the database +- const edits = storedEdits?.get(d.toolCallId); +- if (edits) { +- for (const edit of edits) { +- const beforeUri = edit.kind === 'rename' && edit.originalPath +- ? URI.file(edit.originalPath).toString() +- : URI.file(edit.filePath).toString(); +- const afterUri = URI.file(edit.filePath).toString(); +- const hasBefore = edit.kind !== 'create'; +- const hasAfter = edit.kind !== 'delete'; +- content.push({ +- type: ToolResultContentType.FileEdit, +- before: hasBefore ? { +- uri: beforeUri, +- content: { uri: buildSessionDbUri(sessionUriStr, edit.toolCallId, edit.filePath, 'before') }, +- } : undefined, +- after: hasAfter ? { +- uri: afterUri, +- content: { uri: buildSessionDbUri(sessionUriStr, edit.toolCallId, edit.filePath, 'after') }, +- } : undefined, +- diff: (edit.addedLines !== undefined || edit.removedLines !== undefined) +- ? { added: edit.addedLines, removed: edit.removedLines } +- : undefined, +- }); +- } +- } +- +- result.push({ +- session, +- type: 'tool_complete', +- toolCallId: d.toolCallId, +- result: { +- success: d.success, +- pastTenseMessage: getPastTenseMessage(info.toolName, displayName, info.parameters, d.success), +- content: content.length > 0 ? content : undefined, +- error: d.error, +- }, +- isUserRequested: d.isUserRequested, +- toolTelemetry: d.toolTelemetry !== undefined ? tryStringify(d.toolTelemetry) : undefined, +- parentToolCallId: d.parentToolCallId, +- }); +- } else if (e.type === 'subagent.started') { +- const d = (e as ISessionEventSubagentStarted).data; +- result.push({ +- session, +- type: 'subagent_started', +- toolCallId: d.toolCallId, +- agentName: d.agentName, +- agentDisplayName: d.agentDisplayName, +- agentDescription: d.agentDescription, +- }); +- } else if (e.type === 'skill.invoked') { +- const skillEvent = e as ISessionEventSkillInvoked; +- const { start, complete } = synthesizeSkillToolEvents(session, skillEvent.data, skillEvent.id); +- result.push(start, complete); +- } +- } +- return result; +-} +diff --git a/src/vs/platform/agentHost/test/node/agentService.test.ts b/src/vs/platform/agentHost/test/node/agentService.test.ts +deleted file mode 100644 +index 8cb64838..00000000 +--- a/src/vs/platform/agentHost/test/node/agentService.test.ts ++++ /dev/null +@@ -1,905 +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 { readFileSync } from 'fs'; +-import { fileURLToPath } from 'url'; +-import { VSBuffer } from '../../../../base/common/buffer.js'; +-import { DisposableStore, IReference, toDisposable } from '../../../../base/common/lifecycle.js'; +-import { Schemas } from '../../../../base/common/network.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import { hasKey } from '../../../../base/common/types.js'; +-import { NullLogService } from '../../../log/common/log.js'; +-import { FileService } from '../../../files/common/fileService.js'; +-import { InMemoryFileSystemProvider } from '../../../files/common/inMemoryFilesystemProvider.js'; +-import { AgentSession } from '../../common/agentService.js'; +-import { ISessionDatabase, ISessionDataService } from '../../common/sessionDataService.js'; +-import { SessionDatabase } from '../../node/sessionDatabase.js'; +-import { ActionType, ActionEnvelope } from '../../common/state/sessionActions.js'; +-import { SessionActiveClient, ResponsePartKind, SessionLifecycle, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, TurnState, buildSubagentSessionUri, type MarkdownResponsePart, type ToolCallCompletedState, type ToolCallResponsePart } from '../../common/state/sessionState.js'; +-import { IProductService } from '../../../product/common/productService.js'; +-import { AgentService } from '../../node/agentService.js'; +-import { MockAgent, ScriptedMockAgent } from './mockAgent.js'; +-import { mapSessionEvents, type ISessionEvent } from '../../node/copilot/mapSessionEvents.js'; +-import { createNoopGitService, createSessionDataService } from '../common/sessionTestHelpers.js'; +- +-/** +- * Loads a JSONL fixture of raw Copilot SDK events, runs them through +- * {@link mapSessionEvents}, and returns the result suitable for setting +- * on {@link MockAgent.sessionMessages}. This tests the full pipeline: +- * SDK events → mapSessionEvents → _buildTurnsFromMessages → Turn[]. +- * +- * Fixture files live in `test-cases/` and are sanitized copies of real +- * `events.jsonl` files from `~/.copilot/session-state/`. +- */ +-async function loadFixtureMessages(fixtureName: string, session: URI) { +- // Resolve the fixture from the source tree (test-cases/ is not compiled to out/) +- const thisFile = fileURLToPath(import.meta.url); +- // Navigate from out/vs/... to src/vs/... by replacing the out/ prefix. +- // Use a regex that handles both / and \ separators for Windows compat. +- const srcFile = thisFile.replace(/[/\\]out[/\\]/, (m) => m.replace('out', 'src')); +- const lastSep = Math.max(srcFile.lastIndexOf('/'), srcFile.lastIndexOf('\\')); +- const fixtureDir = srcFile.substring(0, lastSep); +- const sep = srcFile.includes('\\') ? '\\' : '/'; +- const raw = readFileSync(`${fixtureDir}${sep}test-cases${sep}${fixtureName}`, 'utf-8'); +- const events: ISessionEvent[] = raw.trim().split('\n').map(line => JSON.parse(line)); +- return mapSessionEvents(session, undefined, events); +-} +- +-suite('AgentService (node dispatcher)', () => { +- +- const disposables = new DisposableStore(); +- let service: AgentService; +- let copilotAgent: MockAgent; +- let fileService: FileService; +- let nullSessionDataService: ISessionDataService; +- +- setup(async () => { +- nullSessionDataService = { +- _serviceBrand: undefined, +- getSessionDataDir: () => URI.parse('inmemory:/session-data'), +- getSessionDataDirById: () => URI.parse('inmemory:/session-data'), +- openDatabase: () => { throw new Error('not implemented'); }, +- tryOpenDatabase: async () => undefined, +- deleteSessionData: async () => { }, +- cleanupOrphanedData: async () => { }, +- whenIdle: async () => { }, +- }; +- fileService = disposables.add(new FileService(new NullLogService())); +- disposables.add(fileService.registerProvider(Schemas.inMemory, disposables.add(new InMemoryFileSystemProvider()))); +- +- // Seed a directory for browseDirectory tests +- await fileService.createFolder(URI.from({ scheme: Schemas.inMemory, path: '/testDir' })); +- await fileService.writeFile(URI.from({ scheme: Schemas.inMemory, path: '/testDir/file.txt' }), VSBuffer.fromString('hello')); +- +- service = disposables.add(new AgentService(new NullLogService(), fileService, nullSessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); +- copilotAgent = new MockAgent('copilot'); +- disposables.add(toDisposable(() => copilotAgent.dispose())); +- }); +- +- teardown(() => disposables.clear()); +- ensureNoDisposablesAreLeakedInTestSuite(); +- +- // ---- Provider registration ------------------------------------------ +- +- suite('registerProvider', () => { +- +- test('registers a provider successfully', () => { +- service.registerProvider(copilotAgent); +- // No throw - success +- }); +- +- test('throws on duplicate provider registration', () => { +- service.registerProvider(copilotAgent); +- const duplicate = new MockAgent('copilot'); +- disposables.add(toDisposable(() => duplicate.dispose())); +- assert.throws(() => service.registerProvider(duplicate), /already registered/); +- }); +- +- test('maps progress events to protocol actions via onDidAction', async () => { +- service.registerProvider(copilotAgent); +- const session = await service.createSession({ provider: 'copilot' }); +- +- // Start a turn so there's an active turn to map events to +- service.dispatchAction( +- { type: ActionType.SessionTurnStarted, session: session.toString(), turnId: 'turn-1', userMessage: { text: 'hello' } }, +- 'test-client', 1, +- ); +- +- const envelopes: ActionEnvelope[] = []; +- disposables.add(service.onDidAction(e => envelopes.push(e))); +- +- copilotAgent.fireProgress({ session, type: 'delta', messageId: 'msg-1', content: 'hello' }); +- assert.ok(envelopes.some(e => e.action.type === ActionType.SessionResponsePart)); +- }); +- }); +- +- // ---- createSession -------------------------------------------------- +- +- suite('createSession', () => { +- +- test('creates session via specified provider', async () => { +- service.registerProvider(copilotAgent); +- +- const session = await service.createSession({ provider: 'copilot' }); +- assert.strictEqual(AgentSession.provider(session), 'copilot'); +- }); +- +- test('honors requested session URI', async () => { +- service.registerProvider(copilotAgent); +- +- const requestedSession = AgentSession.uri('copilot', 'requested-session'); +- const session = await service.createSession({ provider: 'copilot', session: requestedSession }); +- assert.strictEqual(session.toString(), requestedSession.toString()); +- }); +- +- test('scripted mock agent honors requested session URI', async () => { +- const agent = new ScriptedMockAgent(); +- disposables.add(toDisposable(() => agent.dispose())); +- +- const requestedSession = AgentSession.uri('mock', 'requested-session'); +- const result = await agent.createSession({ session: requestedSession }); +- const sessions = await agent.listSessions(); +- +- assert.deepStrictEqual({ +- created: result.session.toString(), +- listed: sessions.some(s => s.session.toString() === requestedSession.toString()), +- }, { +- created: requestedSession.toString(), +- listed: true, +- }); +- }); +- +- test('uses default provider when none specified', async () => { +- service.registerProvider(copilotAgent); +- +- const session = await service.createSession(); +- assert.strictEqual(AgentSession.provider(session), 'copilot'); +- }); +- +- test('throws when no providers are registered at all', async () => { +- await assert.rejects(() => service.createSession(), /No agent provider/); +- }); +- }); +- +- // ---- disposeSession ------------------------------------------------- +- +- suite('disposeSession', () => { +- +- test('dispatches to the correct provider and cleans up tracking', async () => { +- service.registerProvider(copilotAgent); +- +- const session = await service.createSession({ provider: 'copilot' }); +- await service.disposeSession(session); +- +- assert.strictEqual(copilotAgent.disposeSessionCalls.length, 1); +- }); +- +- test('is a no-op for unknown sessions', async () => { +- service.registerProvider(copilotAgent); +- const unknownSession = URI.from({ scheme: 'unknown', path: '/nope' }); +- +- // Should not throw +- await service.disposeSession(unknownSession); +- }); +- }); +- +- // ---- listSessions / listModels -------------------------------------- +- +- suite('aggregation', () => { +- +- test('listSessions aggregates sessions from all providers', async () => { +- service.registerProvider(copilotAgent); +- +- await service.createSession({ provider: 'copilot' }); +- +- const sessions = await service.listSessions(); +- assert.strictEqual(sessions.length, 1); +- }); +- +- test('listSessions overlays custom title from session database', async () => { +- // Pre-seed a custom title in an in-memory database +- const db = disposables.add(await SessionDatabase.open(':memory:')); +- await db.setMetadata('customTitle', 'My Custom Title'); +- +- const sessionId = 'test-session-abc'; +- const sessionUri = AgentSession.uri('copilot', sessionId); +- +- const sessionDataService: ISessionDataService = { +- _serviceBrand: undefined, +- getSessionDataDir: () => URI.parse('inmemory:/session-data'), +- getSessionDataDirById: () => URI.parse('inmemory:/session-data'), +- openDatabase: (): IReference => ({ +- object: db, +- dispose: () => { }, +- }), +- tryOpenDatabase: async (): Promise | undefined> => ({ +- object: db, +- dispose: () => { }, +- }), +- deleteSessionData: async () => { }, +- cleanupOrphanedData: async () => { }, +- whenIdle: async () => { }, +- }; +- +- // Create a mock that returns a session with that ID +- const agent = new MockAgent('copilot'); +- disposables.add(toDisposable(() => agent.dispose())); +- agent.sessionMetadataOverrides = { summary: 'SDK Title' }; +- // Manually add the session to the mock +- (agent as unknown as { _sessions: Map })._sessions.set(sessionId, sessionUri); +- +- const svc = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); +- svc.registerProvider(agent); +- +- const sessions = await svc.listSessions(); +- assert.strictEqual(sessions.length, 1); +- assert.strictEqual(sessions[0].summary, 'My Custom Title'); +- }); +- +- test('listSessions uses SDK title when no custom title exists', async () => { +- service.registerProvider(copilotAgent); +- copilotAgent.sessionMetadataOverrides = { summary: 'Auto-generated Title' }; +- +- await service.createSession({ provider: 'copilot' }); +- +- const sessions = await service.listSessions(); +- assert.strictEqual(sessions.length, 1); +- assert.strictEqual(sessions[0].summary, 'Auto-generated Title'); +- }); +- +- test('listSessions overlays live state manager title over SDK title', async () => { +- service.registerProvider(copilotAgent); +- +- const session = await service.createSession({ provider: 'copilot' }); +- +- // Simulate immediate title change via state manager +- service.stateManager.dispatchServerAction({ +- type: ActionType.SessionTitleChanged, +- session: session.toString(), +- title: 'User first message', +- }); +- +- const sessions = await service.listSessions(); +- assert.strictEqual(sessions.length, 1); +- assert.strictEqual(sessions[0].summary, 'User first message'); +- }); +- +- test('createSession attaches git state into state _meta when working directory is present', async () => { +- const workingDirectory = URI.file('/workspace/repo'); +- const gitState = { +- hasGitHubRemote: true, +- branchName: 'feature/x', +- baseBranchName: 'main', +- upstreamBranchName: 'origin/feature/x', +- incomingChanges: 1, +- outgoingChanges: 2, +- uncommittedChanges: 3, +- }; +- const calls: string[] = []; +- const gitService = { +- _serviceBrand: undefined, +- isInsideWorkTree: async () => true, +- getCurrentBranch: async () => undefined, +- getDefaultBranch: async () => undefined, +- getBranches: async () => [], +- getRepositoryRoot: async () => undefined, +- getWorktreeRoots: async () => [], +- addWorktree: async () => { }, +- removeWorktree: async () => { }, +- getSessionGitState: async (uri: URI) => { calls.push(uri.fsPath); return gitState; }, +- computeSessionFileDiffs: async () => undefined, +- showBlob: async () => undefined, +- }; +- const localService = disposables.add(new AgentService(new NullLogService(), fileService, nullSessionDataService, { _serviceBrand: undefined } as IProductService, gitService)); +- const agent = new MockAgent('copilot'); +- disposables.add(toDisposable(() => agent.dispose())); +- agent.resolvedWorkingDirectory = workingDirectory; +- agent.sessionMetadataOverrides = { workingDirectory }; +- localService.registerProvider(agent); +- +- const session = await localService.createSession({ provider: 'copilot' }); +- +- // _attachGitState is fire-and-forget; drain microtasks until the +- // git service's promise has resolved and setSessionMeta has run. +- for (let i = 0; i < 5; i++) { +- await Promise.resolve(); +- } +- +- const sessions = await localService.listSessions(); +- assert.strictEqual(sessions.length, 1); +- assert.deepStrictEqual(calls, [workingDirectory.fsPath]); +- assert.deepStrictEqual( +- localService.stateManager.getSessionState(session.toString())?._meta, +- { git: gitState }, +- ); +- }); +- +- test('createSession skips git overlay when no working directory or no git state', async () => { +- const gitService = { +- _serviceBrand: undefined, +- isInsideWorkTree: async () => false, +- getCurrentBranch: async () => undefined, +- getDefaultBranch: async () => undefined, +- getBranches: async () => [], +- getRepositoryRoot: async () => undefined, +- getWorktreeRoots: async () => [], +- addWorktree: async () => { }, +- removeWorktree: async () => { }, +- getSessionGitState: async () => undefined, +- computeSessionFileDiffs: async () => undefined, +- showBlob: async () => undefined, +- }; +- const localService = disposables.add(new AgentService(new NullLogService(), fileService, nullSessionDataService, { _serviceBrand: undefined } as IProductService, gitService)); +- const agent = new MockAgent('copilot'); +- disposables.add(toDisposable(() => agent.dispose())); +- // No resolvedWorkingDirectory set on the mock. +- localService.registerProvider(agent); +- +- const session = await localService.createSession({ provider: 'copilot' }); +- for (let i = 0; i < 5; i++) { +- await Promise.resolve(); +- } +- const sessions = await localService.listSessions(); +- +- assert.strictEqual(sessions.length, 1); +- assert.strictEqual(localService.stateManager.getSessionState(session.toString())?._meta, undefined); +- }); +- +- test('subscribe lazily attaches git state when an existing session has no _meta.git', async () => { +- // Regression test: previously AgentService was constructed without +- // a git service, so _attachGitState always bailed and `_meta.git` +- // was never populated. This test ensures the lazy-fire path on +- // subscribe() actually invokes the git service and writes git +- // state into the session's `_meta`. +- const workingDirectory = URI.file('/workspace/repo'); +- const gitState = { +- hasGitHubRemote: false, +- branchName: 'feature/lazy', +- baseBranchName: 'main', +- upstreamBranchName: undefined, +- incomingChanges: 0, +- outgoingChanges: 0, +- uncommittedChanges: 0, +- }; +- const calls: string[] = []; +- const gitService = createNoopGitService(); +- gitService.getSessionGitState = async (uri: URI) => { calls.push(uri.fsPath); return gitState; }; +- const localService = disposables.add(new AgentService(new NullLogService(), fileService, nullSessionDataService, { _serviceBrand: undefined } as IProductService, gitService)); +- const agent = new MockAgent('copilot'); +- disposables.add(toDisposable(() => agent.dispose())); +- agent.resolvedWorkingDirectory = workingDirectory; +- agent.sessionMetadataOverrides = { workingDirectory }; +- localService.registerProvider(agent); +- +- // Seed a session and clear its _meta so subscribe must lazily +- // recompute git state. +- const session = await localService.createSession({ provider: 'copilot' }); +- for (let i = 0; i < 5; i++) { +- await Promise.resolve(); +- } +- localService.stateManager.setSessionMeta(session.toString(), undefined); +- calls.length = 0; +- +- await localService.subscribe(session); +- for (let i = 0; i < 5; i++) { +- await Promise.resolve(); +- } +- +- assert.deepStrictEqual(calls, [workingDirectory.fsPath]); +- assert.deepStrictEqual( +- localService.stateManager.getSessionState(session.toString())?._meta, +- { git: gitState }, +- ); +- }); +- +- test('createSession stores live session config', async () => { +- service.registerProvider(copilotAgent); +- +- const config = { isolation: 'worktree', branch: 'feature/config' }; +- const session = await service.createSession({ provider: 'copilot', config }); +- +- assert.deepStrictEqual(service.stateManager.getSessionState(session.toString())?.config?.values, config); +- }); +- +- test('seeds activeClient into the initial session state when provided', async () => { +- service.registerProvider(copilotAgent); +- +- const envelopes: ActionEnvelope[] = []; +- disposables.add(service.onDidAction(env => envelopes.push(env))); +- +- const activeClient: SessionActiveClient = { +- clientId: 'client-eager', +- tools: [{ name: 't1', description: 'd', inputSchema: { type: 'object' } }], +- customizations: [{ uri: 'file:///plugin-a', displayName: 'A' }], +- }; +- const session = await service.createSession({ provider: 'copilot', activeClient }); +- +- assert.deepStrictEqual({ +- activeClient: service.stateManager.getSessionState(session.toString())?.activeClient, +- dispatchedActiveClientChanged: envelopes.some(e => e.action.type === ActionType.SessionActiveClientChanged), +- }, { +- activeClient, +- dispatchedActiveClientChanged: false, +- }); +- }); +- +- test('omits activeClient from the initial session state when not provided', async () => { +- service.registerProvider(copilotAgent); +- +- const session = await service.createSession({ provider: 'copilot' }); +- +- assert.strictEqual(service.stateManager.getSessionState(session.toString())?.activeClient, undefined); +- }); +- }); +- +- // ---- authenticate --------------------------------------------------- +- +- suite('authenticate', () => { +- +- test('routes token to provider matching the resource', async () => { +- service.registerProvider(copilotAgent); +- +- const result = await service.authenticate({ resource: 'https://api.github.com', token: 'ghp_test123' }); +- +- assert.deepStrictEqual(result, { authenticated: true }); +- assert.deepStrictEqual(copilotAgent.authenticateCalls, [{ resource: 'https://api.github.com', token: 'ghp_test123' }]); +- }); +- +- test('returns not authenticated for unknown resource', async () => { +- service.registerProvider(copilotAgent); +- +- const result = await service.authenticate({ resource: 'https://unknown.example.com', token: 'tok' }); +- +- assert.deepStrictEqual(result, { authenticated: false }); +- assert.strictEqual(copilotAgent.authenticateCalls.length, 0); +- }); +- }); +- +- // ---- shutdown ------------------------------------------------------- +- +- suite('shutdown', () => { +- +- test('shuts down all providers', async () => { +- let copilotShutdown = false; +- copilotAgent.shutdown = async () => { copilotShutdown = true; }; +- +- service.registerProvider(copilotAgent); +- +- await service.shutdown(); +- assert.ok(copilotShutdown); +- }); +- }); +- +- // ---- restoreSession ------------------------------------------------- +- +- suite('restoreSession', () => { +- +- test('restores a session with message history', async () => { +- service.registerProvider(copilotAgent); +- const { session } = await copilotAgent.createSession(); +- const sessions = await copilotAgent.listSessions(); +- const sessionResource = sessions[0].session; +- +- copilotAgent.sessionMessages = [ +- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Hello', toolRequests: [] }, +- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: 'Hi there!', toolRequests: [] }, +- ]; +- +- await service.restoreSession(sessionResource); +- +- const state = service.stateManager.getSessionState(sessionResource.toString()); +- assert.ok(state, 'session should be in state manager'); +- assert.strictEqual(state!.lifecycle, SessionLifecycle.Ready); +- assert.strictEqual(state!.turns.length, 1); +- assert.strictEqual(state!.turns[0].userMessage.text, 'Hello'); +- const mdPart = state!.turns[0].responseParts.find((p): p is MarkdownResponsePart => p.kind === ResponsePartKind.Markdown); +- assert.ok(mdPart); +- assert.strictEqual(mdPart.content, 'Hi there!'); +- assert.strictEqual(state!.turns[0].state, TurnState.Complete); +- }); +- +- test('restores a session with tool calls', async () => { +- service.registerProvider(copilotAgent); +- const { session } = await copilotAgent.createSession(); +- const sessions = await copilotAgent.listSessions(); +- const sessionResource = sessions[0].session; +- +- copilotAgent.sessionMessages = [ +- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Run a command', toolRequests: [] }, +- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: 'I will run a command.', toolRequests: [{ toolCallId: 'tc-1', name: 'shell' }] }, +- { type: 'tool_start', session, toolCallId: 'tc-1', toolName: 'shell', displayName: 'Shell', invocationMessage: 'Running command...' }, +- { type: 'tool_complete', session, toolCallId: 'tc-1', result: { success: true, pastTenseMessage: 'Ran command', content: [{ type: ToolResultContentType.Text, text: 'output' }] } }, +- { type: 'message', session, role: 'assistant', messageId: 'msg-3', content: 'Done!', toolRequests: [] }, +- ]; +- +- await service.restoreSession(sessionResource); +- +- const state = service.stateManager.getSessionState(sessionResource.toString()); +- assert.ok(state); +- const turn = state!.turns[0]; +- const toolCallParts = turn.responseParts.filter((p): p is ToolCallResponsePart => p.kind === ResponsePartKind.ToolCall); +- assert.strictEqual(toolCallParts.length, 1); +- const tc = toolCallParts[0].toolCall as ToolCallCompletedState; +- assert.strictEqual(tc.status, ToolCallStatus.Completed); +- assert.strictEqual(tc.toolCallId, 'tc-1'); +- assert.strictEqual(tc.confirmed, ToolCallConfirmationReason.NotNeeded); +- }); +- +- test('interleaves reasoning, markdown, and tool calls in stream order on resume', async () => { +- service.registerProvider(copilotAgent); +- const { session } = await copilotAgent.createSession(); +- const sessions = await copilotAgent.listSessions(); +- const sessionResource = sessions[0].session; +- +- copilotAgent.sessionMessages = [ +- { type: 'message', session, role: 'user', messageId: 'u-1', content: 'Hello', toolRequests: [] }, +- { type: 'message', session, role: 'assistant', messageId: 'a-1', content: 'Reply A', reasoningText: 'Thinking A', toolRequests: [{ toolCallId: 'tc-1', name: 'shell' }] }, +- { type: 'tool_start', session, toolCallId: 'tc-1', toolName: 'shell', displayName: 'Shell', invocationMessage: 'Running...' }, +- { type: 'tool_complete', session, toolCallId: 'tc-1', result: { success: true, pastTenseMessage: 'Ran', content: [{ type: ToolResultContentType.Text, text: 'ok' }] } }, +- { type: 'message', session, role: 'assistant', messageId: 'a-2', content: 'Reply B', reasoningText: 'Thinking B', toolRequests: [] }, +- ]; +- +- await service.restoreSession(sessionResource); +- +- const state = service.stateManager.getSessionState(sessionResource.toString()); +- assert.ok(state); +- const turn = state!.turns[0]; +- const summary = turn.responseParts.map(p => { +- if (p.kind === ResponsePartKind.Reasoning) { return ['reasoning', p.content]; } +- if (p.kind === ResponsePartKind.Markdown) { return ['markdown', p.content]; } +- if (p.kind === ResponsePartKind.ToolCall) { return ['toolCall', p.toolCall.toolCallId]; } +- return ['other']; +- }); +- assert.deepStrictEqual(summary, [ +- ['reasoning', 'Thinking A'], +- ['markdown', 'Reply A'], +- ['toolCall', 'tc-1'], +- ['reasoning', 'Thinking B'], +- ['markdown', 'Reply B'], +- ]); +- }); +- +- test('flushes interrupted turns', async () => { +- service.registerProvider(copilotAgent); +- const { session } = await copilotAgent.createSession(); +- const sessions = await copilotAgent.listSessions(); +- const sessionResource = sessions[0].session; +- +- copilotAgent.sessionMessages = [ +- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Interrupted', toolRequests: [] }, +- { type: 'message', session, role: 'user', messageId: 'msg-2', content: 'Retried', toolRequests: [] }, +- { type: 'message', session, role: 'assistant', messageId: 'msg-3', content: 'Answer', toolRequests: [] }, +- ]; +- +- await service.restoreSession(sessionResource); +- +- const state = service.stateManager.getSessionState(sessionResource.toString()); +- assert.ok(state); +- assert.strictEqual(state!.turns.length, 2); +- assert.strictEqual(state!.turns[0].state, TurnState.Cancelled); +- assert.strictEqual(state!.turns[1].state, TurnState.Complete); +- }); +- +- test('throws when session is not found on backend', async () => { +- service.registerProvider(copilotAgent); +- await assert.rejects( +- () => service.restoreSession(AgentSession.uri('copilot', 'nonexistent')), +- /Session not found on backend/, +- ); +- }); +- +- test('restores a session with subagent tool calls', async () => { +- service.registerProvider(copilotAgent); +- const { session } = await copilotAgent.createSession(); +- const sessions = await copilotAgent.listSessions(); +- const sessionResource = sessions[0].session; +- +- copilotAgent.sessionMessages = [ +- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Review this code', toolRequests: [] }, +- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: '', toolRequests: [{ toolCallId: 'tc-sub', name: 'task' }] }, +- { type: 'tool_start', session, toolCallId: 'tc-sub', toolName: 'task', displayName: 'Task', invocationMessage: 'Delegating...', toolKind: 'subagent' as const, subagentDescription: 'Find related files', subagentAgentName: 'explore' }, +- { type: 'subagent_started', session, toolCallId: 'tc-sub', agentName: 'explore', agentDisplayName: 'Explore', agentDescription: 'Explores the codebase' }, +- // Inner tool calls from the subagent (have parentToolCallId) +- { type: 'tool_start', session, toolCallId: 'tc-inner-1', toolName: 'bash', displayName: 'Bash', invocationMessage: 'Running ls...', parentToolCallId: 'tc-sub' }, +- { type: 'tool_complete', session, toolCallId: 'tc-inner-1', result: { success: true, pastTenseMessage: 'Ran ls', content: [{ type: ToolResultContentType.Text, text: 'file1.ts' }] }, parentToolCallId: 'tc-sub' }, +- { type: 'tool_start', session, toolCallId: 'tc-inner-2', toolName: 'view', displayName: 'View File', invocationMessage: 'Reading file1.ts', parentToolCallId: 'tc-sub' }, +- { type: 'tool_complete', session, toolCallId: 'tc-inner-2', result: { success: true, pastTenseMessage: 'Read file1.ts' }, parentToolCallId: 'tc-sub' }, +- // Parent tool completes +- { type: 'tool_complete', session, toolCallId: 'tc-sub', result: { success: true, pastTenseMessage: 'Delegated task', content: [{ type: ToolResultContentType.Text, text: 'Found 3 issues' }] } }, +- { type: 'message', session, role: 'assistant', messageId: 'msg-3', content: 'The review found 3 issues.', toolRequests: [] }, +- ]; +- +- await service.restoreSession(sessionResource); +- +- const state = service.stateManager.getSessionState(sessionResource.toString()); +- assert.ok(state); +- +- // Should produce exactly one turn +- assert.strictEqual(state!.turns.length, 1, `Expected 1 turn but got ${state!.turns.length}`); +- +- const turn = state!.turns[0]; +- assert.strictEqual(turn.userMessage.text, 'Review this code'); +- +- // The parent turn should only have the parent tool call — inner +- // tool calls are excluded from the parent and belong to the +- // child subagent session instead. +- const toolCallParts = turn.responseParts.filter((p): p is ToolCallResponsePart => p.kind === ResponsePartKind.ToolCall); +- assert.strictEqual(toolCallParts.length, 1, `Expected 1 tool call (parent only) but got ${toolCallParts.length}`); +- +- // Parent subagent tool call +- const parentTc = toolCallParts[0].toolCall as ToolCallCompletedState; +- assert.strictEqual(parentTc.toolCallId, 'tc-sub'); +- assert.strictEqual(parentTc.status, ToolCallStatus.Completed); +- assert.strictEqual(parentTc._meta?.toolKind, 'subagent'); +- assert.strictEqual(parentTc._meta?.subagentDescription, 'Find related files'); +- assert.strictEqual(parentTc._meta?.subagentAgentName, 'explore'); +- +- // Parent tool should have subagent content entry +- const content = parentTc.content ?? []; +- const subagentEntry = content.find(c => hasKey(c, { type: true }) && c.type === ToolResultContentType.Subagent); +- assert.ok(subagentEntry, 'Completed tool call should have subagent content entry'); +- +- // Subscribing to the child session should restore it with inner tool calls +- const childSessionUri = buildSubagentSessionUri(sessionResource.toString(), 'tc-sub'); +- const snapshot = await service.subscribe(URI.parse(childSessionUri)); +- const childState = service.stateManager.getSessionState(childSessionUri); +- assert.ok(snapshot?.state, 'Child session snapshot should exist'); +- assert.ok(childState, 'Child session state should exist'); +- assert.strictEqual(childState!.turns.length, 1, 'Child session should have 1 turn'); +- const childToolParts = childState!.turns[0].responseParts.filter((p): p is ToolCallResponsePart => p.kind === ResponsePartKind.ToolCall); +- assert.strictEqual(childToolParts.length, 2, `Child session should have 2 inner tool calls but got ${childToolParts.length}`); +- assert.ok(childToolParts.some(p => p.toolCall.toolCallId === 'tc-inner-1'), 'Should have tc-inner-1'); +- assert.ok(childToolParts.some(p => p.toolCall.toolCallId === 'tc-inner-2'), 'Should have tc-inner-2'); +- +- // The turn should also have the final markdown +- const mdParts = turn.responseParts.filter((p): p is MarkdownResponsePart => p.kind === ResponsePartKind.Markdown); +- assert.ok(mdParts.some(p => p.content.includes('3 issues')), 'Should have the final markdown response'); +- }); +- +- test('inner assistant messages from subagent do not create extra turns (fixture)', async () => { +- service.registerProvider(copilotAgent); +- const { session } = await copilotAgent.createSession(); +- const sessions = await copilotAgent.listSessions(); +- const sessionResource = sessions[0].session; +- +- // Load real SDK events from fixture (sanitized from ~/.copilot/session-state/) +- copilotAgent.sessionMessages = await loadFixtureMessages('subagent-session.jsonl', session); +- +- await service.restoreSession(sessionResource); +- +- const state = service.stateManager.getSessionState(sessionResource.toString()); +- assert.ok(state); +- assert.strictEqual(state!.turns.length, 1, `Expected 1 turn but got ${state!.turns.length}: ${state!.turns.map(t => `"${t.userMessage.text.substring(0, 40)}"`).join(', ')}`); +- assert.strictEqual(state!.turns[0].userMessage.text, 'Run a sync subagent to do some searches, just testing subagent rendering'); +- assert.strictEqual(state!.turns[0].state, TurnState.Complete); +- +- // Should have the parent subagent tool call with subagent content +- const toolCallParts = state!.turns[0].responseParts.filter((p): p is ToolCallResponsePart => p.kind === ResponsePartKind.ToolCall); +- const parentTc = toolCallParts.find(p => p.toolCall.toolName === 'task'); +- assert.ok(parentTc, 'Should have a task tool call'); +- assert.strictEqual(parentTc!.toolCall._meta?.toolKind, 'subagent'); +- +- // Inner tool calls should NOT be in the parent turn — they belong +- // to the child subagent session. +- const parentToolCallId = parentTc!.toolCall.toolCallId; +- const nonParentTools = toolCallParts.filter(p => p.toolCall.toolCallId !== parentToolCallId); +- assert.strictEqual(nonParentTools.length, 0, `Parent turn should only contain the task tool call, but found ${nonParentTools.length} extra tool calls`); +- +- // Subscribe to the child subagent session and verify inner tools +- const childSessionUri = buildSubagentSessionUri(sessionResource.toString(), parentToolCallId); +- const snapshot = await service.subscribe(URI.parse(childSessionUri)); +- assert.ok(snapshot?.state, 'Child session snapshot should exist'); +- const childState = service.stateManager.getSessionState(childSessionUri); +- assert.ok(childState, 'Child session state should exist'); +- assert.strictEqual(childState!.turns.length, 1, 'Child session should have 1 turn'); +- const childToolParts = childState!.turns[0].responseParts.filter((p): p is ToolCallResponsePart => p.kind === ResponsePartKind.ToolCall); +- assert.ok(childToolParts.length > 0, `Child session should have inner tool calls but got ${childToolParts.length}`); +- +- // Should have the final markdown +- const mdParts = state!.turns[0].responseParts.filter((p): p is MarkdownResponsePart => p.kind === ResponsePartKind.Markdown); +- assert.ok(mdParts.length > 0, 'Should have markdown content'); +- }); +- }); +- +- // ---- session config persistence ------------------------------------- +- +- suite('session config persistence', () => { +- +- test('createSession persists initial config values to the session DB', async () => { +- const sessionDb = disposables.add(await SessionDatabase.open(':memory:')); +- const sessionDataService = createSessionDataService(sessionDb); +- const localAgent = new MockAgent('copilot'); +- disposables.add(toDisposable(() => localAgent.dispose())); +- const localService = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); +- localService.registerProvider(localAgent); +- +- await localService.createSession({ provider: 'copilot', config: { autoApprove: 'autoApprove' } }); +- +- // Persistence is fire-and-forget; wait for it to flush +- await new Promise(r => setTimeout(r, 50)); +- +- const persisted = await sessionDb.getMetadata('configValues'); +- assert.ok(persisted, 'configValues should be persisted'); +- assert.deepStrictEqual(JSON.parse(persisted!), { autoApprove: 'autoApprove' }); +- }); +- +- test('createSession does not write configValues when there are no values', async () => { +- const sessionDb = disposables.add(await SessionDatabase.open(':memory:')); +- const sessionDataService = createSessionDataService(sessionDb); +- const localAgent = new MockAgent('copilot'); +- disposables.add(toDisposable(() => localAgent.dispose())); +- const localService = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); +- localService.registerProvider(localAgent); +- +- await localService.createSession({ provider: 'copilot' }); +- +- await new Promise(r => setTimeout(r, 50)); +- +- const persisted = await sessionDb.getMetadata('configValues'); +- assert.strictEqual(persisted, undefined); +- }); +- +- test('restoreSession overlays persisted config values onto the resolved config', async () => { +- const sessionDb = disposables.add(await SessionDatabase.open(':memory:')); +- const sessionDataService = createSessionDataService(sessionDb); +- const localAgent = new MockAgent('copilot'); +- disposables.add(toDisposable(() => localAgent.dispose())); +- const localService = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); +- localService.registerProvider(localAgent); +- +- // Create a session on the agent backend (no config) so listSessions can find it +- const { session } = await localAgent.createSession(); +- const sessions = await localAgent.listSessions(); +- const sessionResource = sessions[0].session; +- +- // Pre-seed persisted config values +- await sessionDb.setMetadata('configValues', JSON.stringify({ autoApprove: 'autoApprove' })); +- +- localAgent.sessionMessages = [ +- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Hello', toolRequests: [] }, +- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: 'Hi', toolRequests: [] }, +- ]; +- +- await localService.restoreSession(sessionResource); +- +- const state = localService.stateManager.getSessionState(sessionResource.toString()); +- assert.ok(state); +- // MockAgent.resolveSessionConfig echoes params.config back as values, so the +- // persisted values are forwarded through and end up on state.config.values. +- assert.deepStrictEqual(state!.config?.values, { autoApprove: 'autoApprove' }); +- }); +- +- test('createSession + restoreSession round-trip restores initial config without any mid-session changes', async () => { +- // Regression test: when a session is created with initial config but no +- // mid-session SessionConfigChanged actions are dispatched, restoring it +- // must still rehydrate the initial values. +- const sessionDb = disposables.add(await SessionDatabase.open(':memory:')); +- const sessionDataService = createSessionDataService(sessionDb); +- const localAgent = new MockAgent('copilot'); +- disposables.add(toDisposable(() => localAgent.dispose())); +- const localService = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); +- localService.registerProvider(localAgent); +- +- const session = await localService.createSession({ provider: 'copilot', config: { autoApprove: 'autoApprove' } }); +- +- // Wait for the fire-and-forget persistence to flush +- await new Promise(r => setTimeout(r, 50)); +- +- // Simulate a server restart: drop the in-memory state +- localService.stateManager.removeSession(session.toString()); +- +- localAgent.sessionMessages = [ +- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Hello', toolRequests: [] }, +- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: 'Hi', toolRequests: [] }, +- ]; +- await localService.restoreSession(session); +- +- const state = localService.stateManager.getSessionState(session.toString()); +- assert.ok(state); +- assert.deepStrictEqual(state!.config?.values, { autoApprove: 'autoApprove' }); +- }); +- +- test('restoreSession ignores malformed persisted configValues', async () => { +- const sessionDb = disposables.add(await SessionDatabase.open(':memory:')); +- const sessionDataService = createSessionDataService(sessionDb); +- const localAgent = new MockAgent('copilot'); +- disposables.add(toDisposable(() => localAgent.dispose())); +- const localService = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); +- localService.registerProvider(localAgent); +- +- const { session } = await localAgent.createSession(); +- const sessions = await localAgent.listSessions(); +- const sessionResource = sessions[0].session; +- +- await sessionDb.setMetadata('configValues', '{not json'); +- +- localAgent.sessionMessages = [ +- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Hello', toolRequests: [] }, +- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: 'Hi', toolRequests: [] }, +- ]; +- +- // Should not throw despite the malformed JSON +- await localService.restoreSession(sessionResource); +- +- const state = localService.stateManager.getSessionState(sessionResource.toString()); +- assert.ok(state); +- // MockAgent has a workingDirectory? No — but the metadata supplies it as undefined. +- // _resolveCreatedSessionConfig bails when both .config and .workingDirectory are +- // missing, so state.config is undefined here. The key point is: no throw. +- assert.strictEqual(state!.config, undefined); +- }); +- }); +- +- // ---- resourceList ------------------------------------------------ +- +- suite('resourceList', () => { +- +- test('throws when the directory does not exist', async () => { +- await assert.rejects( +- () => service.resourceList(URI.from({ scheme: Schemas.inMemory, path: '/nonexistent' })), +- /Directory not found/, +- ); +- }); +- +- test('throws when the target is not a directory', async () => { +- await assert.rejects( +- () => service.resourceList(URI.from({ scheme: Schemas.inMemory, path: '/testDir/file.txt' })), +- /Not a directory/, +- ); +- }); +- }); +- +- // ---- worktree working directory ------------------------------------- +- +- suite('worktree working directory', () => { +- +- test('createSession uses agent-resolved working directory in state', async () => { +- // Simulate an agent that resolves a worktree path different from the input +- const worktreeDir = URI.file('/source/repo.worktrees/agents-xyz'); +- copilotAgent.resolvedWorkingDirectory = worktreeDir; +- service.registerProvider(copilotAgent); +- +- const sourceDir = URI.file('/source/repo'); +- const session = await service.createSession({ provider: 'copilot', workingDirectory: sourceDir }); +- +- // The state manager should have the worktree path, not the source path +- const state = service.stateManager.getSessionState(session.toString()); +- assert.strictEqual(state?.summary.workingDirectory, worktreeDir.toString()); +- }); +- +- test('createSession falls back to config working directory when agent does not resolve', async () => { +- // Agent does not override the working directory (e.g. folder isolation) +- copilotAgent.resolvedWorkingDirectory = undefined; +- service.registerProvider(copilotAgent); +- +- const sourceDir = URI.file('/source/repo'); +- const session = await service.createSession({ provider: 'copilot', workingDirectory: sourceDir }); +- +- const state = service.stateManager.getSessionState(session.toString()); +- assert.strictEqual(state?.summary.workingDirectory, sourceDir.toString()); +- }); +- +- test('restoreSession uses agent working directory in state', async () => { +- // Agent returns the worktree path through listSessions +- const worktreeDir = URI.file('/source/repo.worktrees/agents-xyz'); +- copilotAgent.sessionMetadataOverrides = { workingDirectory: worktreeDir }; +- service.registerProvider(copilotAgent); +- +- const session = await service.createSession({ provider: 'copilot' }); +- +- // Delete from state to simulate a server restart +- service.stateManager.deleteSession(session.toString()); +- assert.strictEqual(service.stateManager.getSessionState(session.toString()), undefined); +- +- // Restore the session (simulates a client subscribing after restart) +- await service.restoreSession(session); +- +- const state = service.stateManager.getSessionState(session.toString()); +- assert.strictEqual(state?.summary.workingDirectory, worktreeDir.toString()); +- }); +- }); +-}); +diff --git a/src/vs/platform/agentHost/test/node/copilotAgent.test.ts b/src/vs/platform/agentHost/test/node/copilotAgent.test.ts +deleted file mode 100644 +index 3185e0b4..00000000 +--- a/src/vs/platform/agentHost/test/node/copilotAgent.test.ts ++++ /dev/null +@@ -1,603 +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 { CopilotSession, SessionEventPayload, SessionEventType, TypedSessionEventHandler } from '@github/copilot-sdk'; +-import assert from 'assert'; +-import * as fs from 'fs/promises'; +-import * as os from 'os'; +-import { Disposable, type DisposableStore, type IDisposable, type IReference } from '../../../../base/common/lifecycle.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import { INativeEnvironmentService } from '../../../environment/common/environment.js'; +-import { FileService } from '../../../files/common/fileService.js'; +-import { IFileService } from '../../../files/common/files.js'; +-import { IInstantiationService } from '../../../instantiation/common/instantiation.js'; +-import { InstantiationService } from '../../../instantiation/common/instantiationService.js'; +-import { ServiceCollection } from '../../../instantiation/common/serviceCollection.js'; +-import { ILogService, NullLogService } from '../../../log/common/log.js'; +-import { IAgentPluginManager, ISyncedCustomization } from '../../common/agentPluginManager.js'; +-import { AgentSession, type IAgentDeltaEvent, type IAgentMessageEvent, type IAgentProgressEvent, type IAgentSessionMetadata, type IAgentToolStartEvent } from '../../common/agentService.js'; +-import { ISessionDataService } from '../../common/sessionDataService.js'; +-import { AHP_AUTH_REQUIRED, ProtocolError } from '../../common/state/sessionProtocol.js'; +-import { SessionCustomization, CustomizationRef } from '../../common/state/sessionState.js'; +-import { IAgentHostGitService } from '../../node/agentHostGitService.js'; +-import { IAgentHostTerminalManager } from '../../node/agentHostTerminalManager.js'; +-import { CopilotAgent, getCopilotWorktreeBranchName, getCopilotWorktreeName, getCopilotWorktreesRoot, type ICopilotClient } from '../../node/copilot/copilotAgent.js'; +-import { CopilotAgentSession, type SessionWrapperFactory } from '../../node/copilot/copilotAgentSession.js'; +-import { CopilotSessionWrapper } from '../../node/copilot/copilotSessionWrapper.js'; +-import { ShellManager } from '../../node/copilot/copilotShellTools.js'; +-import { SessionDatabase } from '../../node/sessionDatabase.js'; +-import { createNullSessionDataService } from '../common/sessionTestHelpers.js'; +- +-class TestAgentPluginManager implements IAgentPluginManager { +- declare readonly _serviceBrand: undefined; +- +- async syncCustomizations(_clientId: string, _customizations: CustomizationRef[], _progress?: (status: SessionCustomization[]) => void): Promise { +- return []; +- } +-} +- +-class TestAgentHostGitService implements IAgentHostGitService { +- declare readonly _serviceBrand: undefined; +- +- repositoryRoot: URI | undefined = undefined; +- addedWorktrees: { repositoryRoot: URI; worktree: URI; branchName: string; startPoint: string }[] = []; +- +- async isInsideWorkTree(): Promise { return false; } +- async getCurrentBranch(): Promise { return undefined; } +- async getDefaultBranch(): Promise { return undefined; } +- async getBranches(): Promise { return []; } +- async getRepositoryRoot(): Promise { return this.repositoryRoot; } +- async getWorktreeRoots(): Promise { return []; } +- async addWorktree(repositoryRoot: URI, worktree: URI, branchName: string, startPoint: string): Promise { +- this.addedWorktrees.push({ repositoryRoot, worktree, branchName, startPoint }); +- } +- async removeWorktree(): Promise { } +- async getSessionGitState(): Promise { return undefined; } +- async computeSessionFileDiffs(): Promise { return undefined; } +- async showBlob(): Promise { return undefined; } +-} +- +-class TestAgentHostTerminalManager implements IAgentHostTerminalManager { +- declare readonly _serviceBrand: undefined; +- +- async createTerminal(): Promise { } +- writeInput(): void { } +- onData(): IDisposable { return Disposable.None; } +- onExit(): IDisposable { return Disposable.None; } +- onClaimChanged(): IDisposable { return Disposable.None; } +- onCommandFinished(): IDisposable { return Disposable.None; } +- getContent(): string | undefined { return undefined; } +- getClaim(): undefined { return undefined; } +- hasTerminal(): boolean { return false; } +- getExitCode(): number | undefined { return undefined; } +- supportsCommandDetection(): boolean { return false; } +- disposeTerminal(): void { } +- getTerminalInfos(): [] { return []; } +- getTerminalState(): undefined { return undefined; } +-} +- +-class TestSessionDataService extends Disposable implements ISessionDataService { +- declare readonly _serviceBrand: undefined; +- +- private readonly _databases = new Map(); +- readonly openedSessions: string[] = []; +- +- getSessionDataDir(session: URI): URI { return URI.from({ scheme: 'test', path: `/session-data/${AgentSession.id(session)}` }); } +- getSessionDataDirById(sessionId: string): URI { return URI.from({ scheme: 'test', path: `/session-data/${sessionId}` }); } +- +- openDatabase(session: URI): IReference { +- const sessionId = AgentSession.id(session); +- this.openedSessions.push(sessionId); +- let db = this._databases.get(sessionId); +- if (!db) { +- db = this._register(new SessionDatabase(':memory:')); +- this._databases.set(sessionId, db); +- } +- return { object: db, dispose: () => { } }; +- } +- +- async tryOpenDatabase(session: URI): Promise | undefined> { +- const db = this._databases.get(AgentSession.id(session)); +- return db ? { object: db, dispose: () => { } } : undefined; +- } +- +- deleteSessionData(): Promise { return Promise.resolve(); } +- cleanupOrphanedData(): Promise { return Promise.resolve(); } +- whenIdle(): Promise { return Promise.resolve(); } +-} +- +-class TestCopilotClient implements ICopilotClient { +- readonly rpc: ICopilotClient['rpc'] = { sessions: { fork: async () => ({ sessionId: 'forked-session' }) } }; +- +- constructor( +- private readonly _sessions: Awaited>, +- ) { } +- +- async start(): Promise { } +- async stop(): ReturnType { return []; } +- async listSessions(): ReturnType { return this._sessions; } +- async listModels(): ReturnType { return []; } +- async getSessionMetadata(): ReturnType { return undefined; } +- createSession: ICopilotClient['createSession'] = async () => { throw new Error('not implemented'); }; +- resumeSession: ICopilotClient['resumeSession'] = async () => { throw new Error('not implemented'); }; +-} +- +-interface IFakeAgentSession { +- send: (prompt: string, attachments?: unknown, turnId?: string) => Promise; +- getMessages: () => Promise; +- dispose: () => void; +-} +- +-class MockCopilotSession { +- readonly sessionId = 'test-session-1'; +- +- on(_eventType: K, _handler: TypedSessionEventHandler): () => void { +- return () => { }; +- } +- +- async send(): Promise { return ''; } +- async abort(): Promise { } +- async setModel(): Promise { } +- async getMessages(): Promise[]> { return []; } +- async destroy(): Promise { } +-} +- +-class TestableCopilotAgent extends CopilotAgent { +- private readonly _fakeSessions = new Map(); +- readonly resumeCalls: string[] = []; +- +- constructor( +- private readonly _copilotClient: ICopilotClient, +- @ILogService logService: ILogService, +- @IInstantiationService instantiationService: IInstantiationService, +- @IFileService fileService: IFileService, +- @ISessionDataService sessionDataService: ISessionDataService, +- @IAgentHostGitService gitService: IAgentHostGitService, +- @IAgentHostTerminalManager terminalManager: IAgentHostTerminalManager, +- ) { +- super(logService, instantiationService, fileService, sessionDataService, gitService, terminalManager); +- } +- +- protected override _createCopilotClient(): ICopilotClient { +- return this._copilotClient; +- } +- +- registerFakeSession(sessionId: string, fake: IFakeAgentSession): void { +- this._fakeSessions.set(sessionId, fake); +- } +- +- protected override async _resumeSession(sessionId: string): Promise { +- this.resumeCalls.push(sessionId); +- const fake = this._fakeSessions.get(sessionId); +- if (!fake) { +- throw new Error(`No fake session registered for '${sessionId}'`); +- } +- // `_sessions` is a DisposableMap, so it will dispose() the entry on +- // teardown. The fields below are the only ones touched by sendMessage +- // and getSessionMessages in the code under test. +- const stub = { +- send: fake.send, +- getMessages: fake.getMessages, +- appliedSnapshot: undefined, +- dispose: fake.dispose, +- } as unknown as CopilotAgentSession; +- return stub; +- } +- +- resolveWorktreeForTest(config: Parameters[0], sessionId: string): Promise { +- return this._resolveSessionWorkingDirectory(config, sessionId); +- } +-} +- +-function createTestAgentContext(disposables: Pick, options?: { sessionDataService?: ISessionDataService; copilotClient?: ICopilotClient; gitService?: TestAgentHostGitService; environmentServiceRegistration?: 'native' | 'none'; pluginManager?: IAgentPluginManager }): { agent: CopilotAgent; instantiationService: IInstantiationService } { +- const services = new ServiceCollection(); +- const logService = new NullLogService(); +- const fileService = disposables.add(new FileService(logService)); +- services.set(ILogService, logService); +- services.set(IFileService, fileService); +- services.set(ISessionDataService, options?.sessionDataService ?? createNullSessionDataService()); +- services.set(IAgentPluginManager, options?.pluginManager ?? new TestAgentPluginManager()); +- services.set(IAgentHostGitService, options?.gitService ?? new TestAgentHostGitService()); +- services.set(IAgentHostTerminalManager, new TestAgentHostTerminalManager()); +- if (options?.environmentServiceRegistration !== 'none') { +- const environmentService = { +- _serviceBrand: undefined, +- userHome: URI.file('/mock-home'), +- } as INativeEnvironmentService; +- services.set(INativeEnvironmentService, environmentService); +- } +- const instantiationService: IInstantiationService = disposables.add(new InstantiationService(services)); +- services.set(IInstantiationService, instantiationService); +- const agent = options?.copilotClient +- ? instantiationService.createInstance(TestableCopilotAgent, options.copilotClient) +- : instantiationService.createInstance(CopilotAgent); +- return { agent, instantiationService }; +-} +- +-function createTestAgent(disposables: Pick, options?: { sessionDataService?: ISessionDataService; copilotClient?: ICopilotClient; gitService?: TestAgentHostGitService; environmentServiceRegistration?: 'native' | 'none'; pluginManager?: IAgentPluginManager }): CopilotAgent { +- return createTestAgentContext(disposables, options).agent; +-} +- +-function createAgentSessionThroughAgent(agent: CopilotAgent, instantiationService: IInstantiationService): CopilotAgentSession { +- const sessionUri = AgentSession.uri('copilotcli', 'test-session-1'); +- const shellManager = instantiationService.createInstance(ShellManager, sessionUri, undefined); +- const wrapperFactory: SessionWrapperFactory = async () => new CopilotSessionWrapper(new MockCopilotSession() as unknown as CopilotSession); +- return (agent as unknown as { +- _createAgentSession: (wrapperFactory: SessionWrapperFactory, sessionId: string, shellManager: ShellManager) => CopilotAgentSession; +- })._createAgentSession(wrapperFactory, 'test-session-1', shellManager); +-} +- +-function withoutUndefinedProperties(metadata: IAgentSessionMetadata): Record { +- const result: Record = {}; +- for (const [key, value] of Object.entries(metadata)) { +- if (value !== undefined) { +- result[key] = value; +- } +- } +- return result; +-} +- +-function sdkSession(sessionId: string, cwd?: string): Awaited>[number] { +- return { +- sessionId, +- startTime: new Date(1000), +- modifiedTime: new Date(2000), +- summary: `SDK ${sessionId}`, +- isRemote: false, +- ...(cwd ? { context: { cwd } } : {}), +- }; +-} +- +-async function disposeAgent(agent: CopilotAgent): Promise { +- await agent.shutdown(); +- agent.dispose(); +- // CopilotAgent.dispose calls super.dispose() from a promise continuation so +- // async shutdown can stop SDK sessions before child disposables are released. +- // Let that continuation run before the disposable leak tracker checks. +- await Promise.resolve(); +-} +- +-suite('CopilotAgent', () => { +- const disposables = ensureNoDisposablesAreLeakedInTestSuite(); +- +- test('uses the Copilot CLI sibling worktrees root convention', () => { +- assert.strictEqual( +- getCopilotWorktreesRoot(URI.file('/Users/me/src/vscode')).fsPath, +- URI.file('/Users/me/src/vscode.worktrees').fsPath, +- ); +- }); +- +- test('uses Agents-window Copilot CLI branch prefix', () => { +- assert.strictEqual(getCopilotWorktreeBranchName('12345678-aaaa-bbbb-cccc-123456789abc', 'add-agent-host-config'), 'agents/add-agent-host-config-12345678'); +- assert.strictEqual(getCopilotWorktreeBranchName('12345678-aaaa-bbbb-cccc-123456789abc', undefined), 'agents/12345678-aaaa-bbbb-cccc-123456789abc'); +- }); +- +- test('uses Git extension branch-derived worktree folder names', () => { +- assert.strictEqual(getCopilotWorktreeName('agents/add-agent-host-config-12345678'), 'agents-add-agent-host-config-12345678'); +- }); +- +- test('keeps hinted branch names short', () => { +- assert.strictEqual(getCopilotWorktreeBranchName('12345678-aaaa-bbbb-cccc-123456789abc', 'a'.repeat(48)).length, 'agents/'.length + 48 + '-12345678'.length); +- }); +- +- test('returns empty models and throws AuthRequired for sessions before authentication', async () => { +- const agent = createTestAgent(disposables); +- try { +- assert.deepStrictEqual(agent.models.get(), []); +- await assert.rejects( +- () => agent.listSessions(), +- (error: Error) => error instanceof ProtocolError && error.code === AHP_AUTH_REQUIRED, +- ); +- } finally { +- await disposeAgent(agent); +- } +- }); +- +- test('requires authentication before creating a session', async () => { +- const agent = createTestAgent(disposables); +- try { +- await assert.rejects( +- () => agent.createSession({ workingDirectory: URI.file('/workspace') }), +- (error: Error) => error instanceof ProtocolError && error.code === AHP_AUTH_REQUIRED, +- ); +- } finally { +- await disposeAgent(agent); +- } +- }); +- +- test('agent-created sessions can resolve session-state paths via INativeEnvironmentService', async () => { +- const sessionDataService = disposables.add(new TestSessionDataService()); +- const { agent, instantiationService } = createTestAgentContext(disposables, { +- environmentServiceRegistration: 'native', +- sessionDataService, +- }); +- const previousXdgStateHome = process.env['XDG_STATE_HOME']; +- delete process.env['XDG_STATE_HOME']; +- try { +- const agentSession = disposables.add(createAgentSessionThroughAgent(agent, instantiationService)); +- await agentSession.initializeSession(); +- +- const result = await agentSession.handlePermissionRequest({ +- kind: 'read', +- path: URI.file('/mock-home/.copilot/session-state/test-session-1/plan.md').fsPath, +- toolCallId: 'tc-read-plan-agent-composition', +- }); +- +- assert.strictEqual(result.kind, 'approved'); +- } finally { +- if (previousXdgStateHome === undefined) { +- delete process.env['XDG_STATE_HOME']; +- } else { +- process.env['XDG_STATE_HOME'] = previousXdgStateHome; +- } +- await disposeAgent(agent); +- } +- }); +- +- test('listSessions only returns sessions with a database', async () => { +- const sessionDataService = disposables.add(new TestSessionDataService()); +- const ownedSession = AgentSession.uri('copilotcli', 'owned'); +- const ownedDb = sessionDataService.openDatabase(ownedSession); +- ownedDb.dispose(); +- +- const client = new TestCopilotClient([sdkSession('owned'), sdkSession('external')]); +- const agent = createTestAgent(disposables, { sessionDataService, copilotClient: client }); +- try { +- await agent.authenticate('https://api.github.com', 'token'); +- +- assert.deepStrictEqual((await agent.listSessions()).map(s => AgentSession.id(s.session)), ['owned']); +- } finally { +- await disposeAgent(agent); +- } +- }); +- +- test('listSessions reads stored metadata from sessions with a database', async () => { +- const sessionDataService = disposables.add(new TestSessionDataService()); +- const legacySession = AgentSession.uri('copilotcli', 'legacy'); +- const legacyDb = sessionDataService.openDatabase(legacySession); +- await legacyDb.object.setMetadata('copilot.workingDirectory', URI.file('/workspace').toString()); +- legacyDb.dispose(); +- +- const agent = createTestAgent(disposables, { sessionDataService, copilotClient: new TestCopilotClient([sdkSession('legacy')]) }); +- try { +- await agent.authenticate('https://api.github.com', 'token'); +- +- assert.deepStrictEqual((await agent.listSessions()).map(withoutUndefinedProperties), [{ +- session: legacySession, +- startTime: 1000, +- modifiedTime: 2000, +- summary: 'SDK legacy', +- workingDirectory: URI.file('/workspace'), +- }]); +- } finally { +- await disposeAgent(agent); +- } +- }); +- +- test('listSessions does not create databases for unowned SDK sessions', async () => { +- const sessionDataService = disposables.add(new TestSessionDataService()); +- const agent = createTestAgent(disposables, { sessionDataService, copilotClient: new TestCopilotClient([sdkSession('external', '/workspace')]) }); +- try { +- await agent.authenticate('https://api.github.com', 'token'); +- +- assert.deepStrictEqual(await agent.listSessions(), []); +- assert.deepStrictEqual(sessionDataService.openedSessions, []); +- } finally { +- await disposeAgent(agent); +- } +- }); +- +- suite('createSession activeClient eager-claim', () => { +- +- class SpyingPluginManager extends TestAgentPluginManager { +- public readonly calls: { clientId: string; customizations: CustomizationRef[] }[] = []; +- +- override async syncCustomizations(clientId: string, customizations: CustomizationRef[], _progress?: (status: SessionCustomization[]) => void): Promise { +- this.calls.push({ clientId, customizations: [...customizations] }); +- return []; +- } +- } +- +- test('createSession seeds activeClient tools and syncs customizations', async () => { +- const sessionDataService = disposables.add(new TestSessionDataService()); +- const client = new TestCopilotClient([]); +- const pluginManager = new SpyingPluginManager(); +- // Fail fast inside the SDK factory so we don't need to wire up a +- // real raw session. The seeding of activeClient and the plugin +- // sync both happen before `client.createSession` is invoked. +- client.createSession = async () => { throw new Error('sentinel'); }; +- +- const agent = createTestAgent(disposables, { sessionDataService, copilotClient: client, pluginManager }); +- try { +- await agent.authenticate('https://api.github.com', 'token'); +- +- const customizations: CustomizationRef[] = [{ uri: 'file:///plugin-a', displayName: 'Plugin A' }]; +- await assert.rejects( +- agent.createSession({ +- session: AgentSession.uri('copilotcli', 'test-session'), +- workingDirectory: URI.file('/workspace'), +- activeClient: { +- clientId: 'client-1', +- tools: [{ name: 't1', description: 'd', inputSchema: { type: 'object' } }], +- customizations, +- }, +- }), +- (err: Error) => /sentinel/.test(err.message), +- ); +- +- assert.deepStrictEqual(pluginManager.calls, [{ clientId: 'client-1', customizations }]); +- } finally { +- await disposeAgent(agent); +- } +- }); +- +- test('createSession without activeClient does not sync customizations', async () => { +- const sessionDataService = disposables.add(new TestSessionDataService()); +- const client = new TestCopilotClient([]); +- const pluginManager = new SpyingPluginManager(); +- client.createSession = async () => { throw new Error('sentinel'); }; +- +- const agent = createTestAgent(disposables, { sessionDataService, copilotClient: client, pluginManager }); +- try { +- await agent.authenticate('https://api.github.com', 'token'); +- +- await assert.rejects( +- agent.createSession({ +- session: AgentSession.uri('copilotcli', 'test-session-2'), +- workingDirectory: URI.file('/workspace'), +- }), +- (err: Error) => /sentinel/.test(err.message), +- ); +- +- assert.deepStrictEqual(pluginManager.calls, []); +- } finally { +- await disposeAgent(agent); +- } +- }); +- }); +- +- suite('worktree announcement', () => { +- // Drives a real session through worktree creation (calling the +- // agent's _resolveSessionWorkingDirectory via a test seam so we don't +- // need a full Copilot SDK), then exercises both the live path +- // (sendMessage emits a synthetic delta) and the restore path +- // (getSessionMessages prepends to the first assistant message). A +- // stubbed CopilotAgentSession is injected via overriding _resumeSession +- // because the real one requires a full SDK CopilotSession with ~30 +- // event subscriptions. +- +- let tmpDir: string; +- +- setup(async () => { +- tmpDir = await fs.mkdtemp(`${os.tmpdir()}/copilot-agent-worktree-test-`); +- }); +- +- teardown(async () => { +- await fs.rm(tmpDir, { recursive: true, force: true }); +- }); +- +- test('emits announcement live as a delta on first sendMessage and persists it for restore via getSessionMessages', async () => { +- const sessionId = 'wt-session'; +- const session = AgentSession.uri('copilotcli', sessionId); +- const repositoryRoot = URI.joinPath(URI.file(tmpDir), 'repo'); +- await fs.mkdir(repositoryRoot.fsPath, { recursive: true }); +- +- const gitService = new TestAgentHostGitService(); +- gitService.repositoryRoot = repositoryRoot; +- +- const sessionDataService = disposables.add(new TestSessionDataService()); +- const agent = createTestAgent(disposables, { +- sessionDataService, +- copilotClient: new TestCopilotClient([]), +- gitService, +- }) as TestableCopilotAgent; +- +- const fakeMessages: (IAgentMessageEvent | IAgentToolStartEvent)[] = [ +- { session, type: 'message', role: 'user', messageId: 'u1', content: 'hi' }, +- { session, type: 'message', role: 'assistant', messageId: 'a1', content: 'hello back' }, +- ]; +- let sendCalls = 0; +- agent.registerFakeSession(sessionId, { +- send: async () => { sendCalls++; }, +- getMessages: async () => fakeMessages, +- dispose: () => { }, +- }); +- +- try { +- await agent.authenticate('https://api.github.com', 'token'); +- +- // 1. Drive worktree resolution: this is what createSession does +- // before constructing the SDK session. Verifies that the +- // real production code path persists branch metadata and +- // queues the live announcement. +- const branchHint = 'add-feature'; +- const expectedBranchName = getCopilotWorktreeBranchName(sessionId, branchHint); +- const workingDir = await agent.resolveWorktreeForTest({ +- workingDirectory: repositoryRoot, +- config: { isolation: 'worktree', branch: 'main', branchNameHint: branchHint }, +- }, sessionId); +- assert.ok(workingDir, 'resolveWorktreeForTest must return a worktree URI'); +- assert.deepStrictEqual(gitService.addedWorktrees.length, 1, 'addWorktree must be called once'); +- assert.strictEqual(gitService.addedWorktrees[0].branchName, expectedBranchName); +- +- // 2. Live path: sendMessage must fire a synthetic delta event +- // carrying the announcement text before delegating to the SDK. +- const events: IAgentProgressEvent[] = []; +- disposables.add(agent.onDidSessionProgress(e => events.push(e))); +- +- await agent.sendMessage(session, 'hello'); +- assert.strictEqual(sendCalls, 1, 'underlying SDK send must still be called'); +- +- const deltas = events.filter((e): e is IAgentDeltaEvent => e.type === 'delta'); +- assert.strictEqual(deltas.length, 1, 'exactly one delta should be emitted for the worktree announcement'); +- const announcement = deltas[0]; +- assert.ok(announcement.content.includes(expectedBranchName), `announcement should contain branch name '${expectedBranchName}', got '${announcement.content}'`); +- assert.ok(announcement.messageId.startsWith('copilot-announcement-'), `announcement messageId should be synthetic, got '${announcement.messageId}'`); +- +- // 3. Live path is one-shot: a second sendMessage must not re-emit. +- events.length = 0; +- await agent.sendMessage(session, 'follow-up'); +- assert.strictEqual(events.filter(e => e.type === 'delta').length, 0, 'announcement must not be re-emitted on subsequent sends'); +- +- // 4. Restore path: getSessionMessages must prepend the +- // announcement to the first assistant message's content, +- // using the persisted branch metadata. +- const restored = await agent.getSessionMessages(session); +- const assistant = restored.find((m): m is IAgentMessageEvent => m.type === 'message' && m.role === 'assistant'); +- assert.ok(assistant, 'restored messages should include the assistant reply'); +- assert.ok(assistant.content.includes(expectedBranchName), `restored assistant content should include the branch name, got '${assistant.content}'`); +- assert.ok(assistant.content.endsWith('hello back'), `restored assistant content should still end with the original reply, got '${assistant.content}'`); +- } finally { +- await disposeAgent(agent); +- } +- }); +- +- test('does not announce or persist branch metadata when isolation is not worktree', async () => { +- const sessionId = 'no-wt-session'; +- const session = AgentSession.uri('copilotcli', sessionId); +- const repositoryRoot = URI.joinPath(URI.file(tmpDir), 'repo'); +- await fs.mkdir(repositoryRoot.fsPath, { recursive: true }); +- +- const gitService = new TestAgentHostGitService(); +- gitService.repositoryRoot = repositoryRoot; +- +- const sessionDataService = disposables.add(new TestSessionDataService()); +- const agent = createTestAgent(disposables, { +- sessionDataService, +- copilotClient: new TestCopilotClient([]), +- gitService, +- }) as TestableCopilotAgent; +- +- const fakeMessages: IAgentMessageEvent[] = [ +- { session, type: 'message', role: 'user', messageId: 'u1', content: 'hi' }, +- { session, type: 'message', role: 'assistant', messageId: 'a1', content: 'untouched reply' }, +- ]; +- agent.registerFakeSession(sessionId, { +- send: async () => { }, +- getMessages: async () => fakeMessages, +- dispose: () => { }, +- }); +- +- try { +- await agent.authenticate('https://api.github.com', 'token'); +- +- await agent.resolveWorktreeForTest({ workingDirectory: repositoryRoot }, sessionId); +- assert.deepStrictEqual(gitService.addedWorktrees, [], 'addWorktree must not be called without worktree isolation'); +- +- const events: IAgentProgressEvent[] = []; +- disposables.add(agent.onDidSessionProgress(e => events.push(e))); +- await agent.sendMessage(session, 'hello'); +- assert.deepStrictEqual(events.filter(e => e.type === 'delta'), [], 'no announcement should be emitted live'); +- +- const restored = await agent.getSessionMessages(session); +- const assistant = restored.find((m): m is IAgentMessageEvent => m.type === 'message' && m.role === 'assistant'); +- assert.strictEqual(assistant?.content, 'untouched reply', 'restored assistant content must not be modified'); +- } finally { +- await disposeAgent(agent); +- } +- }); +- }); +-}); +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 93b83312..00000000 +--- a/src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts ++++ /dev/null +@@ -1,1178 +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 { CopilotSession, SessionEvent, SessionEventPayload, SessionEventType, Tool, ToolResultObject, TypedSessionEventHandler } from '@github/copilot-sdk'; +-import assert from 'assert'; +-import { DeferredPromise } from '../../../../base/common/async.js'; +-import { Emitter } from '../../../../base/common/event.js'; +-import { DisposableStore } from '../../../../base/common/lifecycle.js'; +-import { join, sep } from '../../../../base/common/path.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import { INativeEnvironmentService } from '../../../environment/common/environment.js'; +-import { IFileService } from '../../../files/common/files.js'; +-import { InstantiationService } from '../../../instantiation/common/instantiationService.js'; +-import { ServiceCollection } from '../../../instantiation/common/serviceCollection.js'; +-import { ILogService, NullLogService } from '../../../log/common/log.js'; +-import { AgentSession, IAgentProgressEvent, IAgentUserInputRequestEvent } from '../../common/agentService.js'; +-import { IDiffComputeService } from '../../common/diffComputeService.js'; +-import { ISessionDataService } from '../../common/sessionDataService.js'; +-import { AttachmentType, SessionInputAnswerState, SessionInputAnswerValueKind, SessionInputQuestionKind, SessionInputResponseKind, ToolResultContentType } from '../../common/state/sessionState.js'; +-import { CopilotAgentSession, IActiveClientSnapshot, SessionWrapperFactory } from '../../node/copilot/copilotAgentSession.js'; +-import { CopilotSessionWrapper } from '../../node/copilot/copilotSessionWrapper.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'; +- readonly sendRequests: unknown[] = []; +- +- 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(request: unknown) { this.sendRequests.push(request); return ''; } +- async abort() { } +- async setModel() { } +- async getMessages() { return []; } +- async destroy() { } +-} +- +-class CapturingLogService extends NullLogService { +- readonly errors: Array<{ first: string | Error; args: unknown[] }> = []; +- +- override error(message: string | Error, ...args: unknown[]): void { +- this.errors.push({ first: message, args }); +- super.error(message, ...args); +- } +-} +- +-// ---- Helpers ---------------------------------------------------------------- +- +-/** +- * Invokes a client-SDK tool's handler with the minimal fields the SDK +- * contract requires, and narrows the `unknown` return type to +- * {@link ToolResultObject} — which is what {@link CopilotAgentSession}'s +- * handler implementation actually returns. +- */ +-function invokeClientToolHandler(tool: Pick, toolCallId: string): Promise { +- return Promise.resolve(tool.handler({}, { +- sessionId: 'test-session-1', +- toolCallId, +- toolName: tool.name, +- arguments: {}, +- })) as Promise; +-} +- +-type ISessionInternalsForTest = { +- _onDidSessionProgress: { fire(event: IAgentProgressEvent): void }; +- _editTracker: { +- trackEditStart(path: string): Promise; +- completeEdit(path: string): Promise; +- }; +- _pendingClientToolCalls: { +- get(toolCallId: string): DeferredPromise | undefined; +- set(toolCallId: string, value: DeferredPromise): Map>; +- delete(toolCallId: string): boolean; +- }; +-}; +- +-async function createAgentSession(disposables: DisposableStore, options?: { +- clientSnapshot?: IActiveClientSnapshot; +- environmentServiceRegistration?: 'native' | 'none'; +- logService?: ILogService; +- captureWrapperCallbacks?: { current?: Parameters[0] }; +- workingDirectory?: URI; +-}): Promise<{ +- session: CopilotAgentSession; +- mockSession: MockCopilotSession; +- progressEvents: IAgentProgressEvent[]; +- waitForProgress: (predicate: (event: IAgentProgressEvent) => boolean) => Promise; +-}> { +- const progressEmitter = disposables.add(new Emitter()); +- const progressEvents: IAgentProgressEvent[] = []; +- const waiters: { predicate: (event: IAgentProgressEvent) => boolean; deferred: DeferredPromise }[] = []; +- disposables.add(progressEmitter.event(e => { +- progressEvents.push(e); +- for (let i = waiters.length - 1; i >= 0; i--) { +- if (waiters[i].predicate(e)) { +- const { deferred } = waiters[i]; +- waiters.splice(i, 1); +- deferred.complete(e); +- } +- } +- })); +- +- const waitForProgress = (predicate: (event: IAgentProgressEvent) => boolean): Promise => { +- const existing = progressEvents.find(predicate); +- if (existing) { +- return Promise.resolve(existing); +- } +- const deferred = new DeferredPromise(); +- waiters.push({ predicate, deferred }); +- return deferred.p; +- }; +- +- const sessionUri = AgentSession.uri('copilot', 'test-session-1'); +- const mockSession = new MockCopilotSession(); +- +- const factory: SessionWrapperFactory = async callbacks => { +- if (options?.captureWrapperCallbacks) { +- options.captureWrapperCallbacks.current = callbacks; +- } +- return new CopilotSessionWrapper(mockSession as unknown as CopilotSession); +- }; +- +- const services = new ServiceCollection(); +- services.set(ILogService, options?.logService ?? new NullLogService()); +- services.set(IFileService, { _serviceBrand: undefined } as IFileService); +- services.set(ISessionDataService, createSessionDataService()); +- services.set(IDiffComputeService, createZeroDiffComputeService()); +- const environmentService = { +- _serviceBrand: undefined, +- userHome: URI.file('/mock-home'), +- } as INativeEnvironmentService; +- if (options?.environmentServiceRegistration !== 'none') { +- services.set(INativeEnvironmentService, environmentService); +- } +- const instantiationService = disposables.add(new InstantiationService(services)); +- +- const session = disposables.add(instantiationService.createInstance( +- CopilotAgentSession, +- { +- sessionUri, +- rawSessionId: 'test-session-1', +- onDidSessionProgress: progressEmitter, +- wrapperFactory: factory, +- shellManager: undefined, +- clientSnapshot: options?.clientSnapshot, +- workingDirectory: options?.workingDirectory, +- }, +- )); +- +- await session.initializeSession(); +- +- return { session, mockSession, progressEvents, waitForProgress }; +-} +- +-// ---- Tests ------------------------------------------------------------------ +- +-suite('CopilotAgentSession', () => { +- +- const disposables = new DisposableStore(); +- +- teardown(() => disposables.clear()); +- ensureNoDisposablesAreLeakedInTestSuite(); +- +- test('maps internal attachment URIs to Copilot SDK path fields', async () => { +- const { session, mockSession } = await createAgentSession(disposables); +- const fileUri = URI.file('/workspace/file.ts'); +- const selectionUri = URI.file('/workspace/selection.ts'); +- +- await session.send('hello', [ +- { type: AttachmentType.File, uri: fileUri, displayName: 'file.ts' }, +- { type: AttachmentType.Selection, uri: selectionUri, displayName: 'selection.ts' }, +- ]); +- +- assert.deepStrictEqual(mockSession.sendRequests, [{ +- prompt: 'hello', +- attachments: [ +- { type: 'file', path: fileUri.fsPath, displayName: 'file.ts' }, +- { type: 'selection', filePath: selectionUri.fsPath, displayName: 'selection.ts', text: undefined, selection: undefined }, +- ], +- }]); +- }); +- +- // ---- permission handling ---- +- +- suite('permission handling', () => { +- +- test('read permission fires tool_ready (deferred to side effects)', async () => { +- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); +- const resultPromise = session.handlePermissionRequest({ +- kind: 'read', +- path: '/workspace/src/file.ts', +- toolCallId: 'tc-1', +- }); +- +- await waitForProgress(e => e.type === 'tool_ready'); +- assert.strictEqual(progressEvents.length, 1); +- +- assert.ok(session.respondToPermissionRequest('tc-1', true)); +- const result = await resultPromise; +- assert.strictEqual(result.kind, 'approved'); +- }); +- +- test('auto-approves read permission for session-state plan files', async () => { +- const previousXdgStateHome = process.env['XDG_STATE_HOME']; +- process.env['XDG_STATE_HOME'] = '/mock-state-home'; +- try { +- const { session, progressEvents } = await createAgentSession(disposables); +- const result = await session.handlePermissionRequest({ +- kind: 'read', +- path: join('/mock-state-home', '.copilot', 'session-state', 'test-session-1', 'plan.md'), +- toolCallId: 'tc-read-plan', +- }); +- +- assert.strictEqual(result.kind, 'approved'); +- assert.strictEqual(progressEvents.length, 0); +- } finally { +- if (previousXdgStateHome === undefined) { +- delete process.env['XDG_STATE_HOME']; +- } else { +- process.env['XDG_STATE_HOME'] = previousXdgStateHome; +- } +- } +- }); +- +- test('resolves native environment through INativeEnvironmentService registration', async () => { +- const previousXdgStateHome = process.env['XDG_STATE_HOME']; +- delete process.env['XDG_STATE_HOME']; +- try { +- const { session, progressEvents } = await createAgentSession(disposables, { environmentServiceRegistration: 'native' }); +- const result = await session.handlePermissionRequest({ +- kind: 'read', +- path: join('/mock-home', '.copilot', 'session-state', 'test-session-1', 'plan.md'), +- toolCallId: 'tc-read-plan-native-env', +- }); +- +- assert.strictEqual(result.kind, 'approved'); +- assert.strictEqual(progressEvents.length, 0); +- } finally { +- if (previousXdgStateHome === undefined) { +- delete process.env['XDG_STATE_HOME']; +- } else { +- process.env['XDG_STATE_HOME'] = previousXdgStateHome; +- } +- } +- }); +- +- test('logs and rethrows permission failures', async () => { +- const previousXdgStateHome = process.env['XDG_STATE_HOME']; +- delete process.env['XDG_STATE_HOME']; +- const logService = new CapturingLogService(); +- try { +- const { session } = await createAgentSession(disposables, { +- environmentServiceRegistration: 'none', +- logService, +- }); +- +- await assert.rejects( +- session.handlePermissionRequest({ +- kind: 'read', +- path: join('/mock-home', '.copilot', 'session-state', 'test-session-1', 'plan.md'), +- toolCallId: 'tc-read-plan-missing-env', +- }), +- ); +- +- assert.strictEqual(logService.errors.length, 1); +- const [entry] = logService.errors; +- assert.ok(entry.first instanceof TypeError); +- assert.strictEqual(entry.args[0], '[Copilot:test-session-1] Failed to handle permission request: kind=read, toolCallId=tc-read-plan-missing-env'); +- } finally { +- if (previousXdgStateHome === undefined) { +- delete process.env['XDG_STATE_HOME']; +- } else { +- process.env['XDG_STATE_HOME'] = previousXdgStateHome; +- } +- } +- }); +- +- test('write permission fires tool_ready (deferred to side effects)', async () => { +- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); +- const resultPromise = session.handlePermissionRequest({ +- kind: 'write', +- fileName: '/workspace/src/file.ts', +- toolCallId: 'tc-1', +- }); +- +- await waitForProgress(e => e.type === 'tool_ready'); +- assert.strictEqual(progressEvents.length, 1); +- +- assert.ok(session.respondToPermissionRequest('tc-1', true)); +- const result = await resultPromise; +- assert.strictEqual(result.kind, 'approved'); +- }); +- +- test('auto-approves write permission for session-state plan files', async () => { +- const previousXdgStateHome = process.env['XDG_STATE_HOME']; +- process.env['XDG_STATE_HOME'] = '/mock-state-home'; +- try { +- const { session, progressEvents } = await createAgentSession(disposables); +- const result = await session.handlePermissionRequest({ +- kind: 'write', +- fileName: join('/mock-state-home', '.copilot', 'session-state', 'test-session-1', 'plan.md'), +- toolCallId: 'tc-write-plan', +- }); +- +- assert.strictEqual(result.kind, 'approved'); +- assert.strictEqual(progressEvents.length, 0); +- } finally { +- if (previousXdgStateHome === undefined) { +- delete process.env['XDG_STATE_HOME']; +- } else { +- process.env['XDG_STATE_HOME'] = previousXdgStateHome; +- } +- } +- }); +- +- test('does not auto-approve session-state files from another session', async () => { +- const previousXdgStateHome = process.env['XDG_STATE_HOME']; +- process.env['XDG_STATE_HOME'] = '/mock-state-home'; +- try { +- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); +- const resultPromise = session.handlePermissionRequest({ +- kind: 'write', +- fileName: join('/mock-state-home', '.copilot', 'session-state', 'different-session', 'plan.md'), +- toolCallId: 'tc-write-other-plan', +- }); +- +- await waitForProgress(e => e.type === 'tool_ready'); +- assert.strictEqual(progressEvents.length, 1); +- +- assert.ok(session.respondToPermissionRequest('tc-write-other-plan', true)); +- const result = await resultPromise; +- assert.strictEqual(result.kind, 'approved'); +- } finally { +- if (previousXdgStateHome === undefined) { +- delete process.env['XDG_STATE_HOME']; +- } else { +- process.env['XDG_STATE_HOME'] = previousXdgStateHome; +- } +- } +- }); +- +- test('does not auto-approve traversal paths that escape the session-state directory', async () => { +- const previousXdgStateHome = process.env['XDG_STATE_HOME']; +- process.env['XDG_STATE_HOME'] = '/mock-state-home'; +- try { +- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); +- const sessionDir = join('/mock-state-home', '.copilot', 'session-state', 'test-session-1'); +- const resultPromise = session.handlePermissionRequest({ +- kind: 'write', +- fileName: `${sessionDir}${sep}..${sep}outside.md`, +- toolCallId: 'tc-write-traversal', +- }); +- +- await waitForProgress(e => e.type === 'tool_ready'); +- assert.strictEqual(progressEvents.length, 1); +- +- assert.ok(session.respondToPermissionRequest('tc-write-traversal', true)); +- const result = await resultPromise; +- assert.strictEqual(result.kind, 'approved'); - } finally { -- await close(db); +- if (previousXdgStateHome === undefined) { +- delete process.env['XDG_STATE_HOME']; +- } else { +- process.env['XDG_STATE_HOME'] = previousXdgStateHome; +- } +- } +- }); +- +- test('write permission outside working directory fires tool_ready', async () => { +- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); +- +- const resultPromise = session.handlePermissionRequest({ +- kind: 'write', +- fileName: '/other/file.ts', +- toolCallId: 'tc-write-outside', +- }); +- +- await waitForProgress(e => e.type === 'tool_ready'); +- assert.strictEqual(progressEvents.length, 1); +- +- assert.ok(session.respondToPermissionRequest('tc-write-outside', true)); +- const result = await resultPromise; +- assert.strictEqual(result.kind, 'approved'); +- }); +- +- test('read permission outside working directory fires tool_ready', async () => { +- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); +- +- // 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 +- await waitForProgress(e => e.type === 'tool_ready'); +- assert.strictEqual(progressEvents.length, 1); +- +- // 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, waitForProgress } = await createAgentSession(disposables); +- const resultPromise = session.handlePermissionRequest({ +- kind: 'shell', +- toolCallId: 'tc-3', +- }); +- +- await waitForProgress(e => e.type === 'tool_ready'); +- 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('live tool_start strips redundant cd prefix matching workingDirectory', async () => { +- const wd = URI.file('/repo/project'); +- const { mockSession, progressEvents } = await createAgentSession(disposables, { workingDirectory: wd }); +- mockSession.fire('tool.execution_start', { +- toolCallId: 'tc-cd', +- toolName: 'bash', +- arguments: { command: 'cd /repo/project && npm test' }, +- } as SessionEventPayload<'tool.execution_start'>['data']); +- +- assert.strictEqual(progressEvents.length, 1); +- const ev = progressEvents[0]; +- assert.strictEqual(ev.type, 'tool_start'); +- if (ev.type === 'tool_start') { +- assert.strictEqual(ev.toolInput, 'npm test'); +- assert.ok(ev.toolArguments && ev.toolArguments.includes('"npm test"'), `toolArguments should contain rewritten command, was: ${ev.toolArguments}`); +- assert.ok(!ev.toolArguments?.includes('cd /repo/project'), 'toolArguments should not contain stripped prefix'); +- } +- }); +- +- test('live tool_complete past-tense message reflects the rewritten command', async () => { +- const wd = URI.file('/repo/project'); +- const { mockSession, progressEvents } = await createAgentSession(disposables, { workingDirectory: wd }); +- +- mockSession.fire('tool.execution_start', { +- toolCallId: 'tc-cd-complete', +- toolName: 'bash', +- arguments: { command: 'cd /repo/project && npm test' }, +- } as SessionEventPayload<'tool.execution_start'>['data']); +- +- mockSession.fire('tool.execution_complete', { +- toolCallId: 'tc-cd-complete', +- success: true, +- result: { content: 'all tests passed' }, +- } as SessionEventPayload<'tool.execution_complete'>['data']); +- +- assert.strictEqual(progressEvents.length, 2); +- const completeEv = progressEvents[1]; +- assert.strictEqual(completeEv.type, 'tool_complete'); +- if (completeEv.type === 'tool_complete') { +- const past = completeEv.result.pastTenseMessage; +- const pastStr = typeof past === 'string' ? past : (past?.markdown ?? ''); +- assert.ok(!pastStr.includes('cd /repo/project'), `past-tense message should not contain stripped prefix, got: ${pastStr}`); +- assert.ok(pastStr.includes('npm test'), `past-tense message should contain the rewritten command, got: ${pastStr}`); +- } +- }); +- +- test('live tool_start does not rewrite when cd target differs from workingDirectory', async () => { +- const wd = URI.file('/repo/project'); +- const { mockSession, progressEvents } = await createAgentSession(disposables, { workingDirectory: wd }); +- mockSession.fire('tool.execution_start', { +- toolCallId: 'tc-cd-other', +- toolName: 'bash', +- arguments: { command: 'cd /tmp && ls' }, +- } as SessionEventPayload<'tool.execution_start'>['data']); +- +- assert.strictEqual(progressEvents.length, 1); +- const ev = progressEvents[0]; +- assert.strictEqual(ev.type, 'tool_start'); +- if (ev.type === 'tool_start') { +- assert.strictEqual(ev.toolInput, 'cd /tmp && ls'); +- } +- }); +- +- test('live tool_start without workingDirectory passes command through', async () => { +- const { mockSession, progressEvents } = await createAgentSession(disposables); +- mockSession.fire('tool.execution_start', { +- toolCallId: 'tc-cd-nowd', +- toolName: 'bash', +- arguments: { command: 'cd /repo/project && npm test' }, +- } as SessionEventPayload<'tool.execution_start'>['data']); +- +- assert.strictEqual(progressEvents.length, 1); +- const ev = progressEvents[0]; +- assert.strictEqual(ev.type, 'tool_start'); +- if (ev.type === 'tool_start') { +- assert.strictEqual(ev.toolInput, 'cd /repo/project && npm test'); +- } +- }); +- +- 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('handleUserInputRequest returns empty answer on skipped question', async () => { +- const { session, progressEvents } = await createAgentSession(disposables); +- +- const resultPromise = session.handleUserInputRequest( +- { question: 'Skip me' }, +- { sessionId: 'test-session-1' } +- ); +- +- const event = progressEvents[0]; +- assertUserInputEvent(event); +- const questionId = event.request.questions![0].id; +- session.respondToUserInputRequest(event.request.id, SessionInputResponseKind.Accept, { +- [questionId]: { +- state: SessionInputAnswerState.Skipped, +- } +- }); +- +- const result = await resultPromise; +- assert.strictEqual(result.answer, ''); +- assert.strictEqual(result.wasFreeform, true); +- }); +- +- 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); +- }); +- }); +- +- suite('SDK callback logging', () => { +- +- test('logs and rethrows user input callback failures', async () => { +- const logService = new CapturingLogService(); +- const { session } = await createAgentSession(disposables, { logService }); +- const sessionInternals = session as unknown as ISessionInternalsForTest; +- sessionInternals._onDidSessionProgress.fire = () => { +- throw new Error('user input boom'); +- }; +- +- await assert.rejects( +- session.handleUserInputRequest( +- { question: 'Need input' }, +- { sessionId: 'test-session-1' }, +- ), +- /user input boom/, +- ); +- +- assert.strictEqual(logService.errors.length, 1); +- const [entry] = logService.errors; +- assert.ok(entry.first instanceof Error); +- assert.strictEqual((entry.first as Error).message, 'user input boom'); +- assert.strictEqual(entry.args[0], '[Copilot:test-session-1] Failed to handle user input request: question="Need input"'); +- }); +- +- test('logs and rethrows onPreToolUse failures', async () => { +- const logService = new CapturingLogService(); +- const capturedCallbacks: { current?: Parameters[0] } = {}; +- const { session } = await createAgentSession(disposables, { logService, captureWrapperCallbacks: capturedCallbacks }); +- const sessionInternals = session as unknown as ISessionInternalsForTest; +- sessionInternals._editTracker.trackEditStart = async () => { +- throw new Error('pre tool boom'); +- }; +- +- await assert.rejects( +- capturedCallbacks.current!.hooks.onPreToolUse({ +- timestamp: 0, +- cwd: '/tmp', +- toolName: 'edit', +- toolArgs: { path: '/tmp/file.ts' }, +- }), +- /pre tool boom/, +- ); +- +- assert.strictEqual(logService.errors.length, 1); +- const [entry] = logService.errors; +- assert.ok(entry.first instanceof Error); +- assert.strictEqual((entry.first as Error).message, 'pre tool boom'); +- assert.strictEqual(entry.args[0], '[Copilot:test-session-1] Failed in onPreToolUse: tool=edit'); +- }); +- +- test('logs and rethrows onPostToolUse failures', async () => { +- const logService = new CapturingLogService(); +- const capturedCallbacks: { current?: Parameters[0] } = {}; +- const { session } = await createAgentSession(disposables, { logService, captureWrapperCallbacks: capturedCallbacks }); +- const sessionInternals = session as unknown as ISessionInternalsForTest; +- sessionInternals._editTracker.completeEdit = async () => { +- throw new Error('post tool boom'); +- }; +- +- await assert.rejects( +- capturedCallbacks.current!.hooks.onPostToolUse({ +- timestamp: 0, +- cwd: '/tmp', +- toolName: 'edit', +- toolArgs: { path: '/tmp/file.ts' }, +- toolResult: { textResultForLlm: '', resultType: 'success' }, +- }), +- /post tool boom/, +- ); +- +- assert.strictEqual(logService.errors.length, 1); +- const [entry] = logService.errors; +- assert.ok(entry.first instanceof Error); +- assert.strictEqual((entry.first as Error).message, 'post tool boom'); +- assert.strictEqual(entry.args[0], '[Copilot:test-session-1] Failed in onPostToolUse: tool=edit'); +- }); +- }); +- +- // ---- client tool calls ---- +- +- suite('client tool calls', () => { +- +- const snapshot: IActiveClientSnapshot = { +- clientId: 'test-client', +- tools: [{ +- name: 'my_tool', +- description: 'A test tool', +- inputSchema: { type: 'object', properties: {} }, +- }], +- plugins: [], +- }; +- +- test('tool_start fires immediately for client tools', async () => { +- const { session, mockSession, progressEvents } = await createAgentSession(disposables, { clientSnapshot: snapshot }); +- +- // SDK emits tool.execution_start — tool_start fires immediately +- mockSession.fire('tool.execution_start', { +- toolCallId: 'tc-client-1', +- toolName: 'my_tool', +- arguments: {}, +- } as SessionEventPayload<'tool.execution_start'>['data']); +- +- // tool_start fires immediately +- assert.strictEqual(progressEvents.filter(e => e.type === 'tool_start').length, 1); +- if (progressEvents[0].type === 'tool_start') { +- assert.strictEqual(progressEvents[0].toolClientId, 'test-client'); +- } +- +- // SDK invokes the handler +- const tools = session.createClientSdkTools(); +- const handlerPromise = invokeClientToolHandler(tools[0], 'tc-client-1'); +- +- // Complete the tool call +- session.handleClientToolCallComplete('tc-client-1', { +- success: true, +- pastTenseMessage: 'did it', +- content: [{ type: ToolResultContentType.Text, text: 'result text' }], +- }); +- +- const result = await handlerPromise; +- assert.strictEqual(result.resultType, 'success'); +- assert.strictEqual(result.textResultForLlm, 'result text'); +- }); +- +- test('permission request consumes pending auto-ready for client tools', async () => { +- const { session, mockSession, progressEvents, waitForProgress } = await createAgentSession(disposables, { clientSnapshot: snapshot }); +- +- // SDK emits tool.execution_start — tool_start fires immediately +- mockSession.fire('tool.execution_start', { +- toolCallId: 'tc-client-perm', +- toolName: 'my_tool', +- arguments: {}, +- } as SessionEventPayload<'tool.execution_start'>['data']); +- +- // tool_start fired, no tool_ready yet +- assert.strictEqual(progressEvents.filter(e => e.type === 'tool_start').length, 1); +- assert.strictEqual(progressEvents.filter(e => e.type === 'tool_ready').length, 0); +- +- // Permission request fires — tool_ready from permission flow +- // (with confirmationTitle) replaces the auto-ready +- const resultPromise = session.handlePermissionRequest({ +- kind: 'custom-tool', +- toolCallId: 'tc-client-perm', +- toolName: 'my_tool', +- }); +- +- // tool_ready from permission flow should have fired (with confirmationTitle) +- await waitForProgress(e => e.type === 'tool_ready'); +- const toolReadys = progressEvents.filter(e => e.type === 'tool_ready'); +- assert.strictEqual(toolReadys.length, 1); +- if (toolReadys[0].type === 'tool_ready') { +- assert.strictEqual(toolReadys[0].toolCallId, 'tc-client-perm'); +- assert.ok(toolReadys[0].confirmationTitle); +- } +- +- // Approve and clean up +- session.respondToPermissionRequest('tc-client-perm', true); +- const permResult = await resultPromise; +- assert.strictEqual(permResult.kind, 'approved'); +- }); +- +- test('handleClientToolCallComplete pre-completes when no handler is waiting yet', async () => { +- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); +- +- // Completion arrives before handler — pre-creates deferred +- session.handleClientToolCallComplete('tc-unknown', { +- success: true, +- pastTenseMessage: 'done', +- }); +- +- // Handler picks up the pre-completed result +- const tools = session.createClientSdkTools(); +- const result = await invokeClientToolHandler(tools[0], 'tc-unknown'); +- assert.strictEqual(result.resultType, 'success'); +- }); +- +- test('handleClientToolCallComplete with failure result', async () => { +- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); +- +- const tools = session.createClientSdkTools(); +- const handlerPromise = invokeClientToolHandler(tools[0], 'tc-client-3'); +- +- session.handleClientToolCallComplete('tc-client-3', { +- success: false, +- pastTenseMessage: 'failed', +- error: { message: 'something broke' }, +- }); +- +- const result = await handlerPromise; +- assert.strictEqual(result.resultType, 'failure'); +- assert.strictEqual(result.error, 'something broke'); +- }); +- +- test('pending client tool calls are cancelled on dispose', async () => { +- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); +- +- const tools = session.createClientSdkTools(); +- const handlerPromise = invokeClientToolHandler(tools[0], 'tc-client-4'); +- +- session.dispose(); +- const result = await handlerPromise; +- assert.strictEqual(result.resultType, 'failure'); +- assert.ok(result.error); +- }); +- +- test('multiple concurrent client tool calls resolve independently', async () => { +- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); +- +- const tools = session.createClientSdkTools(); +- const promise1 = invokeClientToolHandler(tools[0], 'tc-multi-1'); +- const promise2 = invokeClientToolHandler(tools[0], 'tc-multi-2'); +- +- // Complete in reverse order +- session.handleClientToolCallComplete('tc-multi-2', { +- success: true, +- pastTenseMessage: 'second done', +- content: [{ type: ToolResultContentType.Text, text: 'result-2' }], +- }); +- session.handleClientToolCallComplete('tc-multi-1', { +- success: true, +- pastTenseMessage: 'first done', +- content: [{ type: ToolResultContentType.Text, text: 'result-1' }], +- }); +- +- const [result1, result2] = await Promise.all([promise1, promise2]); +- assert.strictEqual(result1.textResultForLlm, 'result-1'); +- assert.strictEqual(result2.textResultForLlm, 'result-2'); +- }); +- +- test('handler cleans up deferred after consuming result', async () => { +- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); +- +- const tools = session.createClientSdkTools(); +- const handlerPromise = invokeClientToolHandler(tools[0], 'tc-cleanup'); +- +- session.handleClientToolCallComplete('tc-cleanup', { +- success: true, +- pastTenseMessage: 'done', +- content: [{ type: ToolResultContentType.Text, text: 'ok' }], +- }); +- +- await handlerPromise; +- +- // A second complete for the same toolCallId should create a new +- // deferred (not fail). This tests the cleanup path. +- session.handleClientToolCallComplete('tc-cleanup', { +- success: true, +- pastTenseMessage: 'done again', +- }); +- }); +- +- test('client tool handler logs and rethrows failures', async () => { +- const logService = new CapturingLogService(); +- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot, logService }); +- const tools = session.createClientSdkTools(); +- const sessionInternals = session as unknown as ISessionInternalsForTest; +- sessionInternals._pendingClientToolCalls.get = () => { +- throw new Error('client tool boom'); +- }; +- +- await assert.rejects( +- invokeClientToolHandler(tools[0], 'tc-client-error'), +- /client tool boom/, +- ); +- +- assert.strictEqual(logService.errors.length, 1); +- const [entry] = logService.errors; +- assert.ok(entry.first instanceof Error); +- assert.strictEqual((entry.first as Error).message, 'client tool boom'); +- assert.strictEqual(entry.args[0], '[Copilot:test-session-1] Failed in client tool handler: tool=my_tool, toolCallId=tc-client-error'); +- }); +- +- test('tool_start stores pending auto-ready data for client tools', async () => { +- const { session, mockSession, progressEvents, waitForProgress } = await createAgentSession(disposables, { clientSnapshot: snapshot }); +- +- mockSession.fire('tool.execution_start', { +- toolCallId: 'tc-ready-data', +- toolName: 'my_tool', +- arguments: { file: 'test.ts' }, +- } as SessionEventPayload<'tool.execution_start'>['data']); +- +- // tool_start should have fired +- assert.strictEqual(progressEvents.filter(e => e.type === 'tool_start').length, 1); +- +- // The session should have stored pending auto-ready data. +- // We verify this indirectly: if we now fire a permission request +- // for the same toolCallId, the pending auto-ready is consumed +- // (tested by the permission request test above), and we get +- // tool_ready with confirmationTitle instead. +- const resultPromise = session.handlePermissionRequest({ +- kind: 'custom-tool', +- toolCallId: 'tc-ready-data', +- toolName: 'my_tool', +- }); +- +- await waitForProgress(e => e.type === 'tool_ready'); +- const toolReadys = progressEvents.filter(e => e.type === 'tool_ready'); +- assert.strictEqual(toolReadys.length, 1); +- if (toolReadys[0].type === 'tool_ready') { +- assert.ok(toolReadys[0].confirmationTitle); - } +- +- session.respondToPermissionRequest('tc-ready-data', true); +- await resultPromise; - }); - -- test('does not affect source session', async () => { -- const db = await openTestDb(); -- try { -- await setupSchema(db); -- await seedTestData(db, 'source', 3); +- test('handleClientToolCallComplete with content containing embedded resources', async () => { +- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); +- +- const tools = session.createClientSdkTools(); +- const handlerPromise = invokeClientToolHandler(tools[0], 'tc-embedded'); - -- await forkSessionInDb(db, 'source', 'forked', 1); +- session.handleClientToolCallComplete('tc-embedded', { +- success: true, +- pastTenseMessage: 'done', +- content: [ +- { type: ToolResultContentType.Text, text: 'text part' }, +- { type: ToolResultContentType.EmbeddedResource, data: 'base64data', contentType: 'image/png' }, +- ], +- }); - -- const sourceTurns = await all(db, 'SELECT * FROM turns WHERE session_id = ?', ['source']); -- assert.strictEqual(sourceTurns.length, 3); -- } finally { -- await close(db); -- } +- const result = await handlerPromise; +- assert.strictEqual(result.resultType, 'success'); +- // Text content should be extracted +- assert.strictEqual(result.textResultForLlm, 'text part'); - }); - }); +-}); +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 4f581253..00000000 +--- a/src/vs/platform/agentHost/test/node/copilotGitProject.test.ts ++++ /dev/null +@@ -1,100 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ - -- 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); -- }); -- }); -- } +-import assert from 'assert'; +-import { URI } from '../../../../base/common/uri.js'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import type { IAgentHostGitService } from '../../node/agentHostGitService.js'; +-import { projectFromCopilotContext, projectFromRepository, resolveGitProject } from '../../node/copilot/copilotGitProject.js'; - -- function exec(db: import('@vscode/sqlite3').Database, sql: string): Promise { -- return new Promise((resolve, reject) => { -- db.exec(sql, err => err ? reject(err) : resolve()); -- }); -- } +-class TestAgentHostGitService implements IAgentHostGitService { +- declare readonly _serviceBrand: undefined; +- +- insideWorkTree = true; +- repositoryRoot: URI | undefined; +- worktreeRoots: URI[] = []; +- +- async isInsideWorkTree(): Promise { return this.insideWorkTree; } +- async getCurrentBranch(): Promise { return undefined; } +- async getDefaultBranch(): Promise { return undefined; } +- async getBranches(): Promise { return []; } +- async getRepositoryRoot(): Promise { return this.repositoryRoot; } +- async getWorktreeRoots(): Promise { return this.worktreeRoots; } +- async addWorktree(): Promise { } +- async removeWorktree(): Promise { } +- async getSessionGitState(): Promise { return undefined; } +- async computeSessionFileDiffs(): Promise { return undefined; } +- async showBlob(): Promise { return undefined; } +-} - -- 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); -- }); -- }); -- } +-suite('Copilot Git Project', () => { +- ensureNoDisposablesAreLeakedInTestSuite(); - -- function close(db: import('@vscode/sqlite3').Database): Promise { -- return new Promise((resolve, reject) => { -- db.close(err => err ? reject(err) : resolve()); -- }); -- } +- let gitService: TestAgentHostGitService; - -- 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 -- ); -- `); -- } +- setup(() => { +- gitService = new TestAgentHostGitService(); +- }); - -- 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'); -- `); -- } +- test('resolves a repository project from a worktree working directory', async () => { +- gitService.worktreeRoots = [URI.file('/workspace/source-repo')]; - -- await truncateSessionInDb(db, 'sess', 2); +- const project = await resolveGitProject(URI.file('/workspace/worktree-checkout'), gitService); - -- 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); -- } +- assert.deepStrictEqual({ +- uri: project?.uri.toString(), +- displayName: project?.displayName, +- }, { +- uri: URI.file('/workspace/source-repo').toString(), +- displayName: 'source-repo', - }); +- }); - -- 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('resolves the repository itself for a normal git working directory', async () => { +- gitService.repositoryRoot = URI.file('/workspace/normal-repo'); +- +- const project = await resolveGitProject(URI.file('/workspace/normal-repo'), gitService); +- +- assert.deepStrictEqual({ +- uri: project?.uri.toString(), +- displayName: project?.displayName, +- }, { +- uri: URI.file('/workspace/normal-repo').toString(), +- displayName: 'normal-repo', - }); +- }); - -- 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}'); -- `); -- } +- test('returns undefined outside a git working tree', async () => { +- gitService.insideWorkTree = false; - -- await truncateSessionInDb(db, 'sess', 0); +- assert.strictEqual(await resolveGitProject(URI.file('/workspace/plain-folder'), gitService), undefined); +- }); - -- 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('falls back to repository context when no git project is available', async () => { +- gitService.insideWorkTree = false; - -- 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}'); -- `); -- } +- const project = await projectFromCopilotContext({ repository: 'microsoft/vscode' }, gitService); - -- await truncateSessionInDb(db, 'sess', -1); +- assert.deepStrictEqual({ +- uri: project?.uri.toString(), +- displayName: project?.displayName, +- }, { +- uri: 'https://github.com/microsoft/vscode', +- displayName: 'vscode', +- }); +- }); - -- const turns = await all(db, 'SELECT * FROM turns WHERE session_id = ?', ['sess']); -- assert.strictEqual(turns.length, 0, 'all turns should be removed'); +- test('parses repository URLs', () => { +- const project = projectFromRepository('https://github.com/microsoft/vscode.git'); - -- 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); -- } +- 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/copilotAgentSession.test.ts b/src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts +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 3b02e51c..00000000 ---- a/src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts +index 990a15bb..00000000 +--- a/src/vs/platform/agentHost/test/node/copilotPluginConverters.test.ts +++ /dev/null -@@ -1,456 +0,0 @@ +@@ -1,349 +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 { writeFileSync, unlinkSync } from 'fs'; +-import { fileURLToPath } from 'url'; +-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 { 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'; +-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, toSdkHooks } from '../../node/copilot/copilotPluginConverters.js'; +-import type { IMcpServerDefinition, INamedPluginResource, IParsedHookGroup, IParsedPlugin } from '../../../agentPlugins/common/pluginParsers.js'; - --// ---- Mock CopilotSession (SDK level) ---------------------------------------- +-suite('copilotPluginConverters', () => { - --/** -- * 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'; +- const disposables = new DisposableStore(); +- let fileService: FileService; - -- private readonly _handlers = new Map void>>(); +- setup(() => { +- fileService = disposables.add(new FileService(new NullLogService())); +- disposables.add(fileService.registerProvider(Schemas.inMemory, disposables.add(new InMemoryFileSystemProvider()))); +- }); - -- 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); }; -- } +- teardown(() => disposables.clear()); +- ensureNoDisposablesAreLeakedInTestSuite(); - -- /** 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); -- } -- } -- } +- // ---- toSdkMcpServers ------------------------------------------------ - -- // Stubs for methods the wrapper / session class calls -- async send() { return ''; } -- async abort() { } -- async setModel() { } -- async getMessages() { return []; } -- async destroy() { } --} +- suite('toSdkMcpServers', () => { - --// ---- Helpers ---------------------------------------------------------------- +- 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', +- }, +- }]; - --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 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', +- }, +- }); +- }); - -- const sessionUri = AgentSession.uri('copilot', 'test-session-1'); -- const mockSession = new MockCopilotSession(); +- 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 factory: SessionWrapperFactory = async () => new CopilotSessionWrapper(mockSession as unknown as CopilotSession); +- const agents: INamedPluginResource[] = [{ uri: agentUri, name: 'helper' }]; +- const result = await toSdkCustomAgents(agents, fileService); - -- 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)); +- assert.deepStrictEqual(result, [{ +- name: 'helper', +- prompt: 'You are a helpful assistant', +- }]); +- }); - -- const session = disposables.add(instantiationService.createInstance( -- CopilotAgentSession, -- sessionUri, -- 'test-session-1', -- options?.workingDirectory, -- progressEmitter, -- factory, -- undefined, // shellManager -- )); +- 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, []); +- }); - -- await session.initializeSession(); +- 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')); - -- return { session, mockSession, progressEvents }; --} +- 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'); +- }); +- }); - --// ---- Tests ------------------------------------------------------------------ +- // ---- toSdkSkillDirectories ------------------------------------------ - --suite('CopilotAgentSession', () => { +- suite('toSdkSkillDirectories', () => { - -- const disposables = new DisposableStore(); +- 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); +- }); - -- teardown(() => disposables.clear()); -- ensureNoDisposablesAreLeakedInTestSuite(); +- 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); +- }); - -- // ---- permission handling ---- +- test('handles empty input', () => { +- const result = toSdkSkillDirectories([]); +- assert.deepStrictEqual(result, []); +- }); +- }); - -- suite('permission handling', () => { +- // ---- toSdkHooks ------------------------------------------------------- - -- 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'); -- }); +- suite('toSdkHooks', () => { - -- test('does not auto-approve read outside working directory', async () => { -- const { session, progressEvents } = await createAgentSession(disposables, { workingDirectory: URI.file('/workspace') }); +- function makeHookGroup(type: string, command: string): IParsedHookGroup { +- return { +- type, +- commands: [{ command }], +- uri: URI.file('/plugin/hooks.json'), +- originalId: type, +- }; +- } - -- // Kick off permission request but don't await — it will block -- const resultPromise = session.handlePermissionRequest({ -- kind: 'read', -- path: '/other/file.ts', -- toolCallId: 'tc-2', -- }); +- /** +- * Writes a temp JS script that outputs JSON to stdout and returns +- * a `node ` command. Works on both bash (/bin/sh -c) and +- * cmd.exe without any shell-quoting issues. +- * The script is written alongside the compiled test file which is +- * guaranteed to exist, be writable, and have no spaces in CI. +- */ +- function echoJsonCmd(value: object): { command: string; cleanup: () => void } { +- const json = JSON.stringify(value); +- // fileURLToPath(new URL('.', import.meta.url)) is the Node ESM equivalent +- // of __dirname and works on Node 12+, unlike import.meta.dirname (Node 21.2+). +- const dir = fileURLToPath(new URL('.', import.meta.url)).replace(/[\\/]$/, ''); +- const filePath = `${dir}/vscode-test-hook-${Date.now()}.js`; +- writeFileSync(filePath, `process.stdout.write(${JSON.stringify(json)});\n`); +- // Do NOT quote the path: cmd.exe /c "node path" strips the outer quotes, +- // leaving "node path" without inner quoting which cmd.exe handles cleanly. +- const command = `node ${filePath}`; +- return { command, cleanup: () => { try { unlinkSync(filePath); } catch { /* ignore */ } } }; +- } - -- // Should have fired a tool_ready event -- assert.strictEqual(progressEvents.length, 1); -- assert.strictEqual(progressEvents[0].type, 'tool_ready'); +- test('onPostToolUse returns parsed JSON output as hook result', async () => { +- const expectedOutput = { additionalContext: 'Before presenting the plan, run review-plan skill' }; +- const { command, cleanup } = echoJsonCmd(expectedOutput); +- try { +- const hookGroup = makeHookGroup('PostToolUse', command); +- const hooks = toSdkHooks([hookGroup]); +- const toolResult = { textResultForLlm: 'ok', resultType: 'success' as const }; +- const result = await hooks.onPostToolUse!({ toolName: 'memory', toolArgs: {}, toolResult, timestamp: 0, cwd: '/' }, { sessionId: 'test' }); +- assert.deepStrictEqual(result, expectedOutput); +- } finally { +- cleanup(); +- } +- }); - -- // Respond to it -- assert.ok(session.respondToPermissionRequest('tc-2', true)); -- const result = await resultPromise; -- assert.strictEqual(result.kind, 'approved'); +- test('onPostToolUse returns undefined when output is non-JSON', async () => { +- // Use a script file so there are no cmd.exe quoting issues on Windows. +- const dir = fileURLToPath(new URL('.', import.meta.url)).replace(/[\\/]$/, ''); +- const filePath = `${dir}/vscode-test-hook-nonjson-${Date.now()}.js`; +- writeFileSync(filePath, `process.stdout.write('not-json');\n`); +- try { +- const hookGroup = makeHookGroup('PostToolUse', `node ${filePath}`); +- const hooks = toSdkHooks([hookGroup]); +- const toolResult = { textResultForLlm: 'ok', resultType: 'success' as const }; +- const result = await hooks.onPostToolUse!({ toolName: 'memory', toolArgs: {}, toolResult, timestamp: 0, cwd: '/' }, { sessionId: 'test' }); +- assert.strictEqual(result, undefined); +- } finally { +- try { unlinkSync(filePath); } catch { /* ignore */ } +- } - }); - -- 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('onPostToolUse returns undefined when command fails', async () => { +- const dir = fileURLToPath(new URL('.', import.meta.url)).replace(/[\\/]$/, ''); +- const filePath = `${dir}/vscode-test-hook-fail-${Date.now()}.js`; +- writeFileSync(filePath, `process.exit(1);\n`); +- try { +- const hookGroup = makeHookGroup('PostToolUse', `node ${filePath}`); +- const hooks = toSdkHooks([hookGroup]); +- const toolResult = { textResultForLlm: 'ok', resultType: 'success' as const }; +- const result = await hooks.onPostToolUse!({ toolName: 'memory', toolArgs: {}, toolResult, timestamp: 0, cwd: '/' }, { sessionId: 'test' }); +- assert.strictEqual(result, undefined); +- } finally { +- try { unlinkSync(filePath); } catch { /* ignore */ } +- } - }); - -- test('denied-interactively when user denies', async () => { -- const { session, progressEvents } = await createAgentSession(disposables); -- const resultPromise = session.handlePermissionRequest({ -- kind: 'shell', -- toolCallId: 'tc-3', -- }); +- test('onPostToolUse returns undefined when no commands', async () => { +- const hooks = toSdkHooks([]); +- assert.strictEqual(hooks.onPostToolUse, undefined); +- }); - -- assert.strictEqual(progressEvents.length, 1); -- session.respondToPermissionRequest('tc-3', false); -- const result = await resultPromise; -- assert.strictEqual(result.kind, 'denied-interactively-by-user'); +- test('onPostToolUse calls editTrackingHooks and returns command output', async () => { +- const expectedOutput = { additionalContext: 'context from hook' }; +- const { command, cleanup } = echoJsonCmd(expectedOutput); +- try { +- const hookGroup = makeHookGroup('PostToolUse', command); +- let trackingInput: unknown; +- const editTrackingHooks = { +- onPreToolUse: async () => { }, +- onPostToolUse: async (input: unknown) => { trackingInput = input; }, +- }; +- const hooks = toSdkHooks([hookGroup], editTrackingHooks); +- const toolResult = { textResultForLlm: 'ok', resultType: 'success' as const }; +- const callInput = { toolName: 'memory', toolArgs: {}, toolResult, timestamp: 0, cwd: '/' }; +- const result = await hooks.onPostToolUse!(callInput, { sessionId: 'test' }); +- assert.deepStrictEqual(result, expectedOutput); +- assert.deepStrictEqual(trackingInput, callInput); +- } finally { +- cleanup(); +- } - }); +- }); - -- test('pending permissions are denied on dispose', async () => { -- const { session } = await createAgentSession(disposables); -- const resultPromise = session.handlePermissionRequest({ -- kind: 'write', -- toolCallId: 'tc-4', -- }); +- // ---- parsedPluginsEqual --------------------------------------------- - -- session.dispose(); -- const result = await resultPromise; -- assert.strictEqual(result.kind, 'denied-interactively-by-user'); +- 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('pending permissions are denied on abort', async () => { -- const { session } = await createAgentSession(disposables); -- const resultPromise = session.handlePermissionRequest({ -- kind: 'write', -- toolCallId: 'tc-5', +- 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); +- }); - -- await session.abort(); -- const result = await resultPromise; -- assert.strictEqual(result.kind, 'denied-interactively-by-user'); +- 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('respondToPermissionRequest returns false for unknown id', async () => { -- const { session } = await createAgentSession(disposables); -- assert.strictEqual(session.respondToPermissionRequest('unknown-id', true), false); +- test('returns false for different lengths', () => { +- assert.strictEqual(parsedPluginsEqual([makePlugin()], [makePlugin(), makePlugin()]), false); - }); -- }); - -- // ---- sendSteering ---- +- test('returns true for empty arrays', () => { +- assert.strictEqual(parsedPluginsEqual([], []), true); +- }); +- }); +-}); +diff --git a/src/vs/platform/agentHost/test/node/copilotShellTools.test.ts b/src/vs/platform/agentHost/test/node/copilotShellTools.test.ts +deleted file mode 100644 +index 698ab70d..00000000 +--- a/src/vs/platform/agentHost/test/node/copilotShellTools.test.ts ++++ /dev/null +@@ -1,89 +0,0 @@ +-/*--------------------------------------------------------------------------------------------- +- * Copyright (c) Microsoft Corporation. All rights reserved. +- * Licensed under the MIT License. See License.txt in the project root for license information. +- *--------------------------------------------------------------------------------------------*/ - -- suite('sendSteering', () => { +-import assert from 'assert'; +-import { URI } from '../../../../base/common/uri.js'; +-import { Disposable, DisposableStore, type IDisposable } from '../../../../base/common/lifecycle.js'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import { IInstantiationService } from '../../../instantiation/common/instantiation.js'; +-import { InstantiationService } from '../../../instantiation/common/instantiationService.js'; +-import { ServiceCollection } from '../../../instantiation/common/serviceCollection.js'; +-import { ILogService, NullLogService } from '../../../log/common/log.js'; +-import type { CreateTerminalParams } from '../../common/state/protocol/commands.js'; +-import type { TerminalClaim, TerminalInfo } from '../../common/state/protocol/state.js'; +-import { IAgentHostTerminalManager } from '../../node/agentHostTerminalManager.js'; +-import { ShellManager, prefixForHistorySuppression } from '../../node/copilot/copilotShellTools.js'; +- +-class TestAgentHostTerminalManager implements IAgentHostTerminalManager { +- declare readonly _serviceBrand: undefined; +- +- readonly created: { params: CreateTerminalParams; options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean } }[] = []; +- +- async createTerminal(params: CreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise { +- this.created.push({ params, options }); +- } +- writeInput(): void { } +- onData(): IDisposable { return Disposable.None; } +- onExit(): IDisposable { return Disposable.None; } +- onClaimChanged(): IDisposable { return Disposable.None; } +- onCommandFinished(): IDisposable { return Disposable.None; } +- getContent(): string | undefined { return undefined; } +- getClaim(): TerminalClaim | undefined { return undefined; } +- hasTerminal(): boolean { return false; } +- getExitCode(): number | undefined { return undefined; } +- supportsCommandDetection(): boolean { return false; } +- disposeTerminal(): void { } +- getTerminalInfos(): TerminalInfo[] { return []; } +- getTerminalState(): undefined { return undefined; } +-} - -- test('fires steering_consumed after send resolves', async () => { -- const { session, progressEvents } = await createAgentSession(disposables); +-suite('CopilotShellTools', () => { - -- await session.sendSteering({ id: 'steer-1', userMessage: { text: 'focus on tests' } }); +- const disposables = new DisposableStore(); - -- 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'); -- }); +- teardown(() => disposables.clear()); +- ensureNoDisposablesAreLeakedInTestSuite(); - -- test('does not fire steering_consumed when send fails', async () => { -- const { session, mockSession, progressEvents } = await createAgentSession(disposables); +- test('uses session working directory for created shells', async () => { +- const terminalManager = new TestAgentHostTerminalManager(); +- const services = new ServiceCollection(); +- services.set(ILogService, new NullLogService()); +- services.set(IAgentHostTerminalManager, terminalManager); +- const instantiationService: IInstantiationService = disposables.add(new InstantiationService(services)); +- services.set(IInstantiationService, instantiationService); +- const worktreePath = URI.file('/workspace/worktree').fsPath; +- const explicitCwd = URI.file('/explicit/cwd').fsPath; +- const shellManager = disposables.add(instantiationService.createInstance(ShellManager, URI.parse('copilot:/session-1'), URI.file(worktreePath))); +- +- await shellManager.getOrCreateShell('bash', 'turn-1', 'tool-1'); +- await shellManager.getOrCreateShell('bash', 'turn-2', 'tool-2', explicitCwd); +- +- assert.deepStrictEqual(terminalManager.created.map(c => c.params.cwd), [ +- worktreePath, +- explicitCwd, +- ]); +- }); - -- mockSession.send = async () => { throw new Error('send failed'); }; +- test('opts every managed shell into shell-history suppression and non-interactive mode', async () => { +- const terminalManager = new TestAgentHostTerminalManager(); +- const services = new ServiceCollection(); +- services.set(ILogService, new NullLogService()); +- services.set(IAgentHostTerminalManager, terminalManager); +- const instantiationService: IInstantiationService = disposables.add(new InstantiationService(services)); +- services.set(IInstantiationService, instantiationService); +- const shellManager = disposables.add(instantiationService.createInstance(ShellManager, URI.parse('copilot:/session-1'), undefined)); - -- await session.sendSteering({ id: 'steer-fail', userMessage: { text: 'will fail' } }); +- await shellManager.getOrCreateShell('bash', 'turn-1', 'tool-1'); - -- const consumed = progressEvents.find(e => e.type === 'steering_consumed'); -- assert.strictEqual(consumed, undefined, 'should not fire steering_consumed on failure'); -- }); +- assert.strictEqual(terminalManager.created.length, 1); +- assert.strictEqual(terminalManager.created[0].options?.preventShellHistory, true); +- assert.strictEqual(terminalManager.created[0].options?.nonInteractive, true); - }); - -- // ---- 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']); +- test('prefixForHistorySuppression prepends a space for POSIX shells, no-op for PowerShell', () => { +- assert.strictEqual(prefixForHistorySuppression('bash'), ' '); +- assert.strictEqual(prefixForHistorySuppression('powershell'), ''); +- }); +-}); +diff --git a/src/vs/platform/agentHost/test/node/copilotToolDisplay.test.ts b/src/vs/platform/agentHost/test/node/copilotToolDisplay.test.ts +deleted file mode 100644 +index 07d9471f..00000000 +--- a/src/vs/platform/agentHost/test/node/copilotToolDisplay.test.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. +- *--------------------------------------------------------------------------------------------*/ - -- 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'); -- } -- }); +-import assert from 'assert'; +-import { URI } from '../../../../base/common/uri.js'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import { getInvocationMessage, getPastTenseMessage, getPermissionDisplay, getShellLanguage, getToolInputString, getToolKind, isHiddenTool, synthesizeSkillToolEvents, type ITypedPermissionRequest } from '../../node/copilot/copilotToolDisplay.js'; - -- 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']); +-suite('getPermissionDisplay — cd-prefix stripping', () => { - -- assert.strictEqual(progressEvents.length, 0); -- }); +- ensureNoDisposablesAreLeakedInTestSuite(); - -- test('tool_complete event produces past-tense message', async () => { -- const { mockSession, progressEvents } = await createAgentSession(disposables); +- const wd = URI.file('/repo/project'); - -- // 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']); +- test('strips redundant cd from shell permission request fullCommandText', () => { +- const request: ITypedPermissionRequest = { +- kind: 'shell', +- fullCommandText: 'cd /repo/project && npm test', +- } as ITypedPermissionRequest; +- const display = getPermissionDisplay(request, wd); +- assert.strictEqual(display.toolInput, 'npm test'); +- assert.strictEqual(display.permissionKind, 'shell'); +- }); - -- // 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']); +- test('leaves shell command alone when cd target differs from working directory', () => { +- const request: ITypedPermissionRequest = { +- kind: 'shell', +- fullCommandText: 'cd /tmp && ls', +- } as ITypedPermissionRequest; +- const display = getPermissionDisplay(request, wd); +- assert.strictEqual(display.toolInput, 'cd /tmp && ls'); +- }); - -- 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('leaves shell command alone when no working directory provided', () => { +- const request: ITypedPermissionRequest = { +- kind: 'shell', +- fullCommandText: 'cd /repo/project && npm test', +- } as ITypedPermissionRequest; +- const display = getPermissionDisplay(request, undefined); +- assert.strictEqual(display.toolInput, 'cd /repo/project && npm test'); +- }); - -- 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']); +- test('strips redundant cd from custom-tool shell permission request', () => { +- const request: ITypedPermissionRequest = { +- kind: 'custom-tool', +- toolName: 'bash', +- args: { command: 'cd /repo/project && echo hi' }, +- } as ITypedPermissionRequest; +- const display = getPermissionDisplay(request, wd); +- assert.strictEqual(display.toolInput, 'echo hi'); +- assert.strictEqual(display.permissionKind, 'shell'); +- }); - -- assert.strictEqual(progressEvents.length, 0); -- }); +- test('does not affect non-shell custom-tool requests', () => { +- const request: ITypedPermissionRequest = { +- kind: 'custom-tool', +- toolName: 'some_other_tool', +- args: { command: 'cd /repo/project && echo hi' }, +- } as ITypedPermissionRequest; +- const display = getPermissionDisplay(request, wd); +- // Falls through to the generic branch — toolInput is the JSON-stringified args. +- assert.ok(display.toolInput?.includes('cd /repo/project'), `expected unrewritten args, got: ${display.toolInput}`); +- assert.strictEqual(display.permissionKind, 'custom-tool'); +- }); - -- test('idle event is forwarded', async () => { -- const { mockSession, progressEvents } = await createAgentSession(disposables); -- mockSession.fire('session.idle', {} as SessionEventPayload<'session.idle'>['data']); +- test('handles powershell custom-tool with semicolon separator', () => { +- const request: ITypedPermissionRequest = { +- kind: 'custom-tool', +- toolName: 'powershell', +- args: { command: 'cd /repo/project; dir' }, +- } as ITypedPermissionRequest; +- const display = getPermissionDisplay(request, wd); +- assert.strictEqual(display.toolInput, 'dir'); +- }); +-}); - -- assert.strictEqual(progressEvents.length, 1); -- assert.strictEqual(progressEvents[0].type, 'idle'); -- }); +-suite('view tool — view_range display', () => { - -- 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']); +- ensureNoDisposablesAreLeakedInTestSuite(); - -- 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'); -- } -- }); +- function invocation(parameters: Record | undefined): string { +- const result = getInvocationMessage('view', 'View File', parameters); +- return typeof result === 'string' ? result : result.markdown; +- } - -- 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']); +- function pastTense(parameters: Record | undefined): string { +- const result = getPastTenseMessage('view', 'View File', parameters, true); +- return typeof result === 'string' ? result : result.markdown; +- } - -- assert.strictEqual(progressEvents.length, 1); -- assert.strictEqual(progressEvents[0].type, 'delta'); -- if (progressEvents[0].type === 'delta') { -- assert.strictEqual(progressEvents[0].content, 'Hello '); -- } -- }); +- test('renders path-only when view_range is absent', () => { +- assert.ok(invocation({ path: '/repo/file.ts' }).startsWith('Reading [')); +- assert.ok(pastTense({ path: '/repo/file.ts' }).startsWith('Read [')); +- }); - -- 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']); +- test('renders "lines X to Y" for a valid two-element range', () => { +- assert.ok(invocation({ path: '/repo/file.ts', view_range: [10, 20] }).endsWith(', lines 10 to 20')); +- assert.ok(pastTense({ path: '/repo/file.ts', view_range: [10, 20] }).endsWith(', lines 10 to 20')); +- }); - -- 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'); -- } -- }); +- test('renders "line X" when start === end', () => { +- assert.ok(invocation({ path: '/repo/file.ts', view_range: [10, 10] }).endsWith(', line 10')); +- assert.ok(pastTense({ path: '/repo/file.ts', view_range: [10, 10] }).endsWith(', line 10')); - }); - -- // ---- user input handling ---- +- test('renders "line X to the end" for the -1 EOF sentinel', () => { +- assert.ok(invocation({ path: '/repo/file.ts', view_range: [10, -1] }).endsWith(', line 10 to the end')); +- assert.ok(pastTense({ path: '/repo/file.ts', view_range: [10, -1] }).endsWith(', line 10 to the end')); +- }); - -- suite('user input handling', () => { +- test('falls back to path-only for invalid ranges', () => { +- // end < start (and not -1) +- assert.ok(!invocation({ path: '/repo/file.ts', view_range: [20, 10] }).includes(',')); +- // negative start +- assert.ok(!invocation({ path: '/repo/file.ts', view_range: [-5, 10] }).includes(',')); +- // non-integer +- assert.ok(!invocation({ path: '/repo/file.ts', view_range: [1.5, 10] }).includes(',')); +- // wrong arity +- assert.ok(!invocation({ path: '/repo/file.ts', view_range: [10] }).includes(',')); +- assert.ok(!invocation({ path: '/repo/file.ts', view_range: [10, 20, 30] }).includes(',')); +- // non-array +- assert.ok(!invocation({ path: '/repo/file.ts', view_range: 'whatever' }).includes(',')); +- }); +-}); - -- function assertUserInputEvent(event: IAgentProgressEvent): asserts event is IAgentUserInputRequestEvent { -- assert.strictEqual(event.type, 'user_input_request'); -- } +-// ---- write_/read_ shell tool display --------------------------------------- +-// +-// Coverage for the secondary shell helpers (write_bash, read_bash, and their +-// powershell siblings). These never appear in a permission dialog (they're +-// registered with `skipPermission: true` — see copilotShellTools.ts), but they +-// still flow through the tool-execution display pipeline. - -- test('handleUserInputRequest fires user_input_request progress event', async () => { -- const { session, progressEvents } = await createAgentSession(disposables); +-suite('copilotToolDisplay — write_/read_ shell tools', () => { - -- // Start the request (don't await — it blocks waiting for response) -- const resultPromise = session.handleUserInputRequest( -- { question: 'What is your name?' }, -- { sessionId: 'test-session-1' } -- ); +- ensureNoDisposablesAreLeakedInTestSuite(); - -- // 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; +- suite('getToolKind', () => { - -- // Respond to unblock the promise -- session.respondToUserInputRequest(requestId, SessionInputResponseKind.Accept, { -- [questionId]: { -- state: SessionInputAnswerState.Submitted, -- value: { kind: SessionInputAnswerValueKind.Text, value: 'Alice' } -- } -- }); +- test('returns terminal for bash', () => { +- assert.strictEqual(getToolKind('bash'), 'terminal'); +- }); - -- const result = await resultPromise; -- assert.strictEqual(result.answer, 'Alice'); -- assert.strictEqual(result.wasFreeform, true); +- test('returns terminal for powershell', () => { +- assert.strictEqual(getToolKind('powershell'), 'terminal'); - }); - -- test('handleUserInputRequest with choices generates SingleSelect question', async () => { -- const { session, progressEvents } = await createAgentSession(disposables); +- test('returns undefined for write_bash (sending input to a running program, not launching a terminal)', () => { +- assert.strictEqual(getToolKind('write_bash'), undefined); +- }); - -- const resultPromise = session.handleUserInputRequest( -- { question: 'Pick a color', choices: ['red', 'blue', 'green'] }, -- { sessionId: 'test-session-1' } -- ); +- test('returns undefined for write_powershell', () => { +- assert.strictEqual(getToolKind('write_powershell'), undefined); +- }); - -- 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'); -- } +- test('returns undefined for read_bash (reading output, not launching a terminal)', () => { +- assert.strictEqual(getToolKind('read_bash'), undefined); +- }); - -- // 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' } -- } -- }); +- test('returns undefined for read_powershell', () => { +- assert.strictEqual(getToolKind('read_powershell'), undefined); +- }); - -- const result = await resultPromise; -- assert.strictEqual(result.answer, 'blue'); -- assert.strictEqual(result.wasFreeform, false); +- test('returns subagent for task', () => { +- assert.strictEqual(getToolKind('task'), 'subagent'); - }); - -- test('handleUserInputRequest returns empty answer on cancel', async () => { -- const { session, progressEvents } = await createAgentSession(disposables); +- test('returns undefined for view', () => { +- assert.strictEqual(getToolKind('view'), undefined); +- }); +- }); - -- const resultPromise = session.handleUserInputRequest( -- { question: 'Cancel me' }, -- { sessionId: 'test-session-1' } -- ); +- suite('getShellLanguage', () => { - -- const event = progressEvents[0]; -- assertUserInputEvent(event); -- session.respondToUserInputRequest(event.request.id, SessionInputResponseKind.Cancel); +- test('bash returns shellscript', () => { +- assert.strictEqual(getShellLanguage('bash'), 'shellscript'); +- }); - -- const result = await resultPromise; -- assert.strictEqual(result.answer, ''); -- assert.strictEqual(result.wasFreeform, true); +- test('powershell returns powershell', () => { +- assert.strictEqual(getShellLanguage('powershell'), 'powershell'); - }); - -- test('respondToUserInputRequest returns false for unknown id', async () => { -- const { session } = await createAgentSession(disposables); -- assert.strictEqual(session.respondToUserInputRequest('unknown-id', SessionInputResponseKind.Accept), false); +- test('write_bash returns shellscript', () => { +- assert.strictEqual(getShellLanguage('write_bash'), 'shellscript'); - }); - -- test('pending user inputs are cancelled on dispose', async () => { -- const { session } = await createAgentSession(disposables); +- test('write_powershell returns powershell', () => { +- assert.strictEqual(getShellLanguage('write_powershell'), 'powershell'); +- }); - -- const resultPromise = session.handleUserInputRequest( -- { question: 'Will be cancelled' }, -- { sessionId: 'test-session-1' } -- ); +- test('read_bash returns shellscript', () => { +- assert.strictEqual(getShellLanguage('read_bash'), 'shellscript'); +- }); - -- session.dispose(); -- const result = await resultPromise; -- assert.strictEqual(result.answer, ''); -- assert.strictEqual(result.wasFreeform, true); +- test('read_powershell returns powershell', () => { +- assert.strictEqual(getShellLanguage('read_powershell'), 'powershell'); - }); - }); --}); -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'; +- suite('getInvocationMessage', () => { - --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()); +- function getText(msg: ReturnType): string { +- return typeof msg === 'string' ? msg : msg.markdown; +- } +- +- test('write_bash with command includes the command text', () => { +- const msg = getInvocationMessage('write_bash', 'Write Shell Input', { command: 'echo hello' }); +- assert.ok(getText(msg).includes('echo hello'), `expected 'echo hello' in: ${getText(msg)}`); - }); -- }); --} - --suite('Copilot Git Project', () => { -- ensureNoDisposablesAreLeakedInTestSuite(); +- test('write_bash without command returns a non-empty fallback message', () => { +- const msg = getInvocationMessage('write_bash', 'Write Shell Input', undefined); +- assert.ok(getText(msg).length > 0); +- assert.ok(!getText(msg).includes('undefined')); +- }); - -- let testDir: string; +- test('write_powershell with command includes the command text', () => { +- const msg = getInvocationMessage('write_powershell', 'Write Shell Input', { command: 'Get-Date' }); +- assert.ok(getText(msg).includes('Get-Date'), `expected 'Get-Date' in: ${getText(msg)}`); +- }); - -- setup(async () => { -- testDir = getRandomTestPath(tmpdir(), 'vsctests', 'copilot-git-project'); -- await fs.promises.mkdir(testDir, { recursive: true }); -- }); +- test('read_bash returns a non-empty message', () => { +- const msg = getInvocationMessage('read_bash', 'Read Shell Output', undefined); +- assert.ok(getText(msg).length > 0); +- }); - -- teardown(async () => { -- await Promises.rm(testDir); +- test('read_powershell returns a non-empty message', () => { +- const msg = getInvocationMessage('read_powershell', 'Read Shell Output', undefined); +- assert.ok(getText(msg).length > 0); +- }); +- +- test('write_bash message differs from bash message (distinct wording)', () => { +- const writeBashMsg = getText(getInvocationMessage('write_bash', 'Write Shell Input', { command: 'echo hi' })); +- const bashMsg = getText(getInvocationMessage('bash', 'Bash', { command: 'echo hi' })); +- // Both include the command, but the surrounding text should differ +- assert.notStrictEqual(writeBashMsg, bashMsg); +- }); - }); - -- 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; -- } +- suite('getPastTenseMessage', () => { - -- 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]); +- function getText(msg: ReturnType): string { +- return typeof msg === 'string' ? msg : msg.markdown; +- } - -- const project = await resolveGitProject(URI.file(worktreePath)); +- test('write_bash with command includes the command text', () => { +- const msg = getPastTenseMessage('write_bash', 'Write Shell Input', { command: 'echo hello' }, true); +- assert.ok(getText(msg).includes('echo hello'), `expected 'echo hello' in: ${getText(msg)}`); +- }); - -- assert.deepStrictEqual({ -- uri: project?.uri.toString(), -- displayName: project?.displayName, -- }, { -- uri: URI.file(canonicalRepositoryPath).toString(), -- displayName: 'source-repo', +- test('write_bash without command returns a non-empty fallback message', () => { +- const msg = getPastTenseMessage('write_bash', 'Write Shell Input', undefined, true); +- assert.ok(getText(msg).length > 0); - }); -- }); - -- 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); +- test('write_powershell with command includes the command text', () => { +- const msg = getPastTenseMessage('write_powershell', 'Write Shell Input', { command: 'Get-Date' }, true); +- assert.ok(getText(msg).includes('Get-Date'), `expected 'Get-Date' in: ${getText(msg)}`); +- }); - -- const project = await resolveGitProject(URI.file(repositoryPath)); +- test('read_bash success returns a non-empty message', () => { +- const msg = getPastTenseMessage('read_bash', 'Read Shell Output', undefined, true); +- assert.ok(getText(msg).length > 0); +- }); - -- assert.deepStrictEqual({ -- uri: project?.uri.toString(), -- displayName: project?.displayName, -- }, { -- uri: URI.file(canonicalRepositoryPath).toString(), -- displayName: 'normal-repo', +- test('write_bash failure returns a non-empty error message', () => { +- const msg = getPastTenseMessage('write_bash', 'Write Shell Input', { command: 'echo hello' }, false); +- assert.ok(getText(msg).length > 0); - }); - }); - -- test('returns undefined outside a git working tree', async () => { -- const folder = join(testDir, 'plain-folder'); -- await fs.promises.mkdir(folder); +- suite('getToolInputString', () => { - -- assert.strictEqual(await resolveGitProject(URI.file(folder)), undefined); -- }); +- test('write_bash extracts command field', () => { +- assert.strictEqual(getToolInputString('write_bash', { command: 'echo hello' }, undefined), 'echo hello'); +- }); - -- test('falls back to repository context when no git project is available', async () => { -- const project = await projectFromCopilotContext({ repository: 'microsoft/vscode' }); +- test('write_powershell extracts command field', () => { +- assert.strictEqual(getToolInputString('write_powershell', { command: 'Get-Date' }, undefined), 'Get-Date'); +- }); - -- assert.deepStrictEqual({ -- uri: project?.uri.toString(), -- displayName: project?.displayName, -- }, { -- uri: 'https://github.com/microsoft/vscode', -- displayName: 'vscode', +- test('write_bash falls back to rawArguments when no command field', () => { +- assert.strictEqual(getToolInputString('write_bash', {}, '{"command":"echo hello"}'), '{"command":"echo hello"}'); +- }); +- +- test('write_bash returns undefined when both parameters and rawArguments are absent', () => { +- assert.strictEqual(getToolInputString('write_bash', undefined, undefined), undefined); +- }); +- +- test('read_bash with no parameters returns undefined', () => { +- assert.strictEqual(getToolInputString('read_bash', undefined, undefined), undefined); - }); - }); +-}); - -- test('parses repository URLs', () => { -- const project = projectFromRepository('https://github.com/microsoft/vscode.git'); +-suite('skill events', () => { +- +- ensureNoDisposablesAreLeakedInTestSuite(); +- +- const session = URI.parse('agent://copilot/test'); +- +- test('hides the raw `skill` tool call and synthesizes a tool-start/complete pair from `skill.invoked`', () => { +- const withPath = synthesizeSkillToolEvents( +- session, +- { name: 'plan', path: '/abs/repo/skills/plan/SKILL.md' }, +- 'evt-123', +- ); +- const noPath = synthesizeSkillToolEvents( +- session, +- { name: 'plan' }, +- undefined, +- ); - - assert.deepStrictEqual({ -- uri: project?.uri.toString(), -- displayName: project?.displayName, +- skillIsHidden: isHiddenTool('skill'), +- withPathToolCallId: withPath.start.toolCallId, +- withPathSameIdOnComplete: withPath.start.toolCallId === withPath.complete.toolCallId, +- withPathToolName: withPath.start.toolName, +- withPathDisplayName: withPath.start.displayName, +- withPathInvocation: withPath.start.invocationMessage, +- withPathPastTense: withPath.complete.result.pastTenseMessage, +- withPathSuccess: withPath.complete.result.success, +- noPathToolCallId: noPath.start.toolCallId, +- noPathInvocation: noPath.start.invocationMessage, +- noPathPastTense: noPath.complete.result.pastTenseMessage, - }, { -- uri: 'https://github.com/microsoft/vscode.git', -- displayName: 'vscode', +- skillIsHidden: true, +- withPathToolCallId: 'synth-skill-evt-123', +- withPathSameIdOnComplete: true, +- withPathToolName: 'skill', +- withPathDisplayName: 'Read Skill', +- withPathInvocation: { markdown: 'Reading skill [plan](file:///abs/repo/skills/plan/SKILL.md)' }, +- withPathPastTense: { markdown: 'Read skill [plan](file:///abs/repo/skills/plan/SKILL.md)' }, +- withPathSuccess: true, +- noPathToolCallId: 'synth-skill-2108d652', +- noPathInvocation: 'Reading skill plan', +- noPathPastTense: 'Read skill plan', - }); - }); -}); -diff --git a/src/vs/platform/agentHost/test/node/copilotPluginConverters.test.ts b/src/vs/platform/agentHost/test/node/copilotPluginConverters.test.ts +diff --git a/src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts b/src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts deleted file mode 100644 -index 4a18ade2..00000000 ---- a/src/vs/platform/agentHost/test/node/copilotPluginConverters.test.ts +index d5105061..00000000 +--- a/src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts +++ /dev/null -@@ -1,242 +0,0 @@ +@@ -1,401 +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'; +-import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +-import { AgentSession } from '../../common/agentService.js'; +-import { FileEditKind, ToolResultContentType } from '../../common/state/sessionState.js'; +-import { SessionDatabase } from '../../node/sessionDatabase.js'; +-import { parseSessionDbUri } from '../../node/copilot/fileEditTracker.js'; +-import { mapSessionEvents, type ISessionEvent } from '../../node/copilot/mapSessionEvents.js'; - --suite('copilotPluginConverters', () => { +-suite('mapSessionEvents', () => { - - const disposables = new DisposableStore(); -- let fileService: FileService; +- let db: SessionDatabase | undefined; +- const session = AgentSession.uri('copilot', 'test-session'); - -- setup(() => { -- fileService = disposables.add(new FileService(new NullLogService())); -- disposables.add(fileService.registerProvider(Schemas.inMemory, disposables.add(new InMemoryFileSystemProvider()))); +- teardown(async () => { +- disposables.clear(); +- await db?.close(); - }); -- -- teardown(() => disposables.clear()); - ensureNoDisposablesAreLeakedInTestSuite(); - -- // ---- toSdkMcpServers ------------------------------------------------ +- // ---- Basic event mapping -------------------------------------------- +- +- test('maps user and assistant messages', async () => { +- const events: ISessionEvent[] = [ +- { type: 'user.message', data: { messageId: 'msg-1', content: 'hello' } }, +- { type: 'assistant.message', data: { messageId: 'msg-2', content: 'world' } }, +- ]; +- +- const result = await mapSessionEvents(session, undefined, events); +- assert.strictEqual(result.length, 2); +- assert.deepStrictEqual(result[0], { +- session, +- type: 'message', +- role: 'user', +- messageId: 'msg-1', +- content: 'hello', +- toolRequests: undefined, +- reasoningOpaque: undefined, +- reasoningText: undefined, +- encryptedContent: undefined, +- parentToolCallId: undefined, +- }); +- assert.strictEqual(result[1].type, 'message'); +- assert.strictEqual((result[1] as { role: string }).role, 'assistant'); +- }); - -- suite('toSdkMcpServers', () => { +- test('maps tool start and complete events', async () => { +- const events: ISessionEvent[] = [ +- { +- type: 'tool.execution_start', +- data: { toolCallId: 'tc-1', toolName: 'shell', arguments: { command: 'echo hi' } }, +- }, +- { +- type: 'tool.execution_complete', +- data: { toolCallId: 'tc-1', success: true, result: { content: 'hi\n' } }, +- }, +- ]; - -- 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 = await mapSessionEvents(session, undefined, events); +- assert.strictEqual(result.length, 2); +- assert.strictEqual(result[0].type, 'tool_start'); +- assert.strictEqual(result[1].type, 'tool_complete'); - -- 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', -- }, -- }); -- }); +- const complete = result[1] as { result: { content?: readonly { type: string; text?: string }[] } }; +- assert.ok(complete.result.content); +- assert.strictEqual(complete.result.content[0].type, ToolResultContentType.Text); +- }); - -- 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' }, -- }, -- }]; +- test('skips tool_complete without matching tool_start', async () => { +- const events: ISessionEvent[] = [ +- { type: 'tool.execution_complete', data: { toolCallId: 'orphan', success: true } }, +- ]; - -- const result = toSdkMcpServers(defs); -- assert.deepStrictEqual(result, { -- 'remote-server': { -- type: 'http', -- url: 'https://example.com/mcp', -- tools: ['*'], -- headers: { 'Authorization': 'Bearer token' }, -- }, -- }); -- }); +- const result = await mapSessionEvents(session, undefined, events); +- assert.strictEqual(result.length, 0); +- }); - -- test('handles empty definitions', () => { -- const result = toSdkMcpServers([]); -- assert.deepStrictEqual(result, {}); -- }); +- test('ignores unknown event types', async () => { +- const events: ISessionEvent[] = [ +- { type: 'some.unknown.event', data: {} }, +- { type: 'user.message', data: { messageId: 'msg-1', content: 'test' } }, +- ]; - -- test('omits optional fields when undefined', () => { -- const defs: IMcpServerDefinition[] = [{ -- name: 'minimal', -- uri: URI.file('/plugin'), -- configuration: { -- type: McpServerType.LOCAL, -- command: 'echo', -- }, -- }]; +- const result = await mapSessionEvents(session, undefined, events); +- assert.strictEqual(result.length, 1); +- }); - -- 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); -- }); +- // ---- File edit restoration ------------------------------------------ +- +- suite('file edit restoration', () => { +- +- test('restores file edits from database for edit tools', async () => { +- db = disposables.add(await SessionDatabase.open(':memory:')); +- await db.createTurn('turn-1'); +- await db.storeFileEdit({ +- turnId: 'turn-1', +- toolCallId: 'tc-edit', +- filePath: '/workspace/file.ts', +- kind: FileEditKind.Edit, +- beforeContent: new TextEncoder().encode('before'), +- afterContent: new TextEncoder().encode('after'), +- addedLines: 3, +- removedLines: 1, +- }); - -- 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 events: ISessionEvent[] = [ +- { +- type: 'tool.execution_start', +- data: { toolCallId: 'tc-edit', toolName: 'edit', arguments: { filePath: '/workspace/file.ts' } }, - }, -- }]; +- { +- type: 'tool.execution_complete', +- data: { toolCallId: 'tc-edit', success: true, result: { content: 'Edited file.ts' } }, +- }, +- ]; - -- const result = toSdkMcpServers(defs); -- const env = (result['with-null-env'] as { env?: Record }).env; -- assert.deepStrictEqual(env, { KEEP: 'value' }); -- }); -- }); +- const result = await mapSessionEvents(session, db, events); +- const complete = result[1]; +- assert.strictEqual(complete.type, 'tool_complete'); - -- // ---- toSdkCustomAgents ---------------------------------------------- +- const content = (complete as { result: { content?: readonly Record[] } }).result.content; +- assert.ok(content); +- // Should have text content + file edit +- assert.strictEqual(content.length, 2); +- assert.strictEqual(content[0].type, ToolResultContentType.Text); +- assert.strictEqual(content[1].type, ToolResultContentType.FileEdit); - -- suite('toSdkCustomAgents', () => { +- // 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 beforeFields = parseSessionDbUri(fileEdit.before.content.uri); +- assert.ok(beforeFields); +- assert.strictEqual(beforeFields.toolCallId, 'tc-edit'); +- assert.strictEqual(beforeFields.filePath, '/workspace/file.ts'); +- assert.strictEqual(beforeFields.part, 'before'); +- assert.deepStrictEqual(fileEdit.diff, { added: 3, removed: 1 }); +- }); - -- 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')); +- test('handles multiple file edits for one tool call', async () => { +- db = disposables.add(await SessionDatabase.open(':memory:')); +- await db.createTurn('turn-1'); +- await db.storeFileEdit({ +- turnId: 'turn-1', +- toolCallId: 'tc-multi', +- filePath: '/workspace/a.ts', +- kind: FileEditKind.Edit, +- beforeContent: new Uint8Array(0), +- afterContent: new TextEncoder().encode('a'), +- addedLines: undefined, +- removedLines: undefined, +- }); +- await db.storeFileEdit({ +- turnId: 'turn-1', +- toolCallId: 'tc-multi', +- filePath: '/workspace/b.ts', +- kind: FileEditKind.Edit, +- beforeContent: new Uint8Array(0), +- afterContent: new TextEncoder().encode('b'), +- addedLines: undefined, +- removedLines: undefined, +- }); - -- const agents: INamedPluginResource[] = [{ uri: agentUri, name: 'helper' }]; -- const result = await toSdkCustomAgents(agents, fileService); +- const events: ISessionEvent[] = [ +- { +- type: 'tool.execution_start', +- data: { toolCallId: 'tc-multi', toolName: 'edit' }, +- }, +- { +- type: 'tool.execution_complete', +- data: { toolCallId: 'tc-multi', success: true }, +- }, +- ]; - -- assert.deepStrictEqual(result, [{ -- name: 'helper', -- prompt: 'You are a helpful assistant', -- }]); +- const result = await mapSessionEvents(session, db, events); +- const content = (result[1] as { result: { content?: readonly Record[] } }).result.content; +- assert.ok(content); +- // Two file edits (no text since result had no content) +- const fileEdits = content.filter(c => c.type === ToolResultContentType.FileEdit); +- assert.strictEqual(fileEdits.length, 2); - }); - -- test('skips agents whose files cannot be read', async () => { -- const agents: INamedPluginResource[] = [ -- { uri: URI.from({ scheme: Schemas.inMemory, path: '/nonexistent/agent.md' }), name: 'missing' }, +- test('works without database (no file edits restored)', async () => { +- const events: ISessionEvent[] = [ +- { +- type: 'tool.execution_start', +- data: { toolCallId: 'tc-1', toolName: 'edit', arguments: { filePath: '/workspace/file.ts' } }, +- }, +- { +- type: 'tool.execution_complete', +- data: { toolCallId: 'tc-1', success: true, result: { content: 'done' } }, +- }, - ]; -- const result = await toSdkCustomAgents(agents, fileService); -- assert.deepStrictEqual(result, []); +- +- const result = await mapSessionEvents(session, undefined, events); +- const content = (result[1] as { result: { content?: readonly Record[] } }).result.content; +- assert.ok(content); +- // Only text content, no file edits +- assert.strictEqual(content.length, 1); +- assert.strictEqual(content[0].type, ToolResultContentType.Text); - }); - -- 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')); +- test('non-edit tools do not get file edits even if db has data', async () => { +- db = disposables.add(await SessionDatabase.open(':memory:')); - -- const agents: INamedPluginResource[] = [ -- { uri: goodUri, name: 'good' }, -- { uri: URI.from({ scheme: Schemas.inMemory, path: '/agents/bad.md' }), name: 'bad' }, +- const events: ISessionEvent[] = [ +- { +- type: 'tool.execution_start', +- data: { toolCallId: 'tc-1', toolName: 'shell', arguments: { command: 'ls' } }, +- }, +- { +- type: 'tool.execution_complete', +- data: { toolCallId: 'tc-1', success: true, result: { content: 'files' } }, +- }, - ]; -- const result = await toSdkCustomAgents(agents, fileService); -- assert.strictEqual(result.length, 1); -- assert.strictEqual(result[0].name, 'good'); +- +- const result = await mapSessionEvents(session, db, events); +- const content = (result[1] as { result: { content?: readonly Record[] } }).result.content; +- assert.ok(content); +- assert.strictEqual(content.length, 1); +- assert.strictEqual(content[0].type, ToolResultContentType.Text); - }); - }); - -- // ---- toSdkSkillDirectories ------------------------------------------ +- // ---- Subagent events ------------------------------------------------ - -- suite('toSdkSkillDirectories', () => { +- suite('subagent events', () => { - -- 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' }, +- test('maps subagent.started event to subagent_started progress event', async () => { +- const events: ISessionEvent[] = [ +- { +- type: 'subagent.started', +- data: { +- toolCallId: 'tc-1', +- agentName: 'code-reviewer', +- agentDisplayName: 'Code Reviewer', +- agentDescription: 'Reviews code', +- }, +- }, - ]; -- 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); +- const result = await mapSessionEvents(session, undefined, events); - assert.strictEqual(result.length, 1); +- assert.strictEqual(result[0].type, 'subagent_started'); +- const event = result[0] as { type: string; toolCallId: string; agentName: string; agentDisplayName: string }; +- assert.strictEqual(event.toolCallId, 'tc-1'); +- assert.strictEqual(event.agentName, 'code-reviewer'); +- assert.strictEqual(event.agentDisplayName, 'Code Reviewer'); - }); +- }); - -- test('handles empty input', () => { -- const result = toSdkSkillDirectories([]); -- assert.deepStrictEqual(result, []); +- // ---- Skill events --------------------------------------------------- +- +- suite('skill events', () => { +- +- test('synthesizes tool start/complete from skill.invoked and filters synthetic skill-injected user messages', async () => { +- const events: ISessionEvent[] = [ +- { +- type: 'tool.execution_start', +- data: { toolCallId: 'tc-skill', toolName: 'skill', arguments: { skill: 'plan' } }, +- }, +- { +- type: 'tool.execution_complete', +- data: { toolCallId: 'tc-skill', success: true }, +- }, +- { +- type: 'skill.invoked', +- id: 'evt-42', +- data: { name: 'plan', path: '/abs/repo/skills/plan/SKILL.md' }, +- }, +- { +- type: 'user.message', +- data: { messageId: 'msg-skill', content: '', source: 'skill-plan' }, +- }, +- { +- type: 'assistant.message', +- data: { messageId: 'msg-1', content: 'ok' }, +- }, +- ]; +- +- const result = await mapSessionEvents(session, undefined, events); +- +- assert.deepStrictEqual({ +- count: result.length, +- types: result.map(r => r.type), +- skillStart: result[0], +- skillComplete: result[1], +- assistantRole: (result[2] as { role: string }).role, +- }, { +- count: 3, +- types: ['tool_start', 'tool_complete', 'message'], +- skillStart: { +- session, +- type: 'tool_start', +- toolCallId: 'synth-skill-evt-42', +- toolName: 'skill', +- displayName: 'Read Skill', +- invocationMessage: { markdown: 'Reading skill [plan](file:///abs/repo/skills/plan/SKILL.md)' }, +- }, +- skillComplete: { +- session, +- type: 'tool_complete', +- toolCallId: 'synth-skill-evt-42', +- result: { +- success: true, +- pastTenseMessage: { markdown: 'Read skill [plan](file:///abs/repo/skills/plan/SKILL.md)' }, +- }, +- }, +- assistantRole: 'assistant', +- }); - }); - }); - -- // ---- parsedPluginsEqual --------------------------------------------- +- // ---- cd-prefix rewriting -------------------------------------------- - -- suite('parsedPluginsEqual', () => { +- suite('cd-prefix rewriting', () => { - -- function makePlugin(overrides?: Partial): IParsedPlugin { +- const cwd = URI.file('/workspace/proj'); +- +- function makeBashEvent(command: string, toolCallId = 'tc-1'): ISessionEvent { - return { -- hooks: [], -- mcpServers: [], -- skills: [], -- agents: [], -- ...overrides, +- type: 'tool.execution_start', +- data: { toolCallId, toolName: 'bash', arguments: { command } }, - }; - } - -- test('returns true for identical empty plugins', () => { -- assert.strictEqual(parsedPluginsEqual([makePlugin()], [makePlugin()]), true); +- function getStart(events: ReturnType extends Promise ? R : never) { +- return events[0] as { toolInput: string; toolArguments?: string }; +- } +- +- test('strips redundant bash cd prefix matching workingDirectory', async () => { +- const result = await mapSessionEvents(session, undefined, [ +- makeBashEvent('cd /workspace/proj && ls -la'), +- ], cwd); +- const start = getStart(result); +- assert.strictEqual(start.toolInput, 'ls -la'); +- assert.deepStrictEqual(JSON.parse(start.toolArguments!), { command: 'ls -la' }); - }); - -- 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('leaves command unchanged when cd dir does not match', async () => { +- const result = await mapSessionEvents(session, undefined, [ +- makeBashEvent('cd /other && ls'), +- ], cwd); +- const start = getStart(result); +- assert.strictEqual(start.toolInput, 'cd /other && ls'); - }); - -- 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('leaves command unchanged when no workingDirectory provided', async () => { +- const result = await mapSessionEvents(session, undefined, [ +- makeBashEvent('cd /workspace/proj && ls'), +- ]); +- const start = getStart(result); +- assert.strictEqual(start.toolInput, 'cd /workspace/proj && ls'); - }); - -- test('returns false for different lengths', () => { -- assert.strictEqual(parsedPluginsEqual([makePlugin()], [makePlugin(), makePlugin()]), false); +- test('non-shell tools are not rewritten even with matching command field', async () => { +- const result = await mapSessionEvents(session, undefined, [ +- { +- type: 'tool.execution_start', +- data: { toolCallId: 'tc-1', toolName: 'edit', arguments: { command: 'cd /workspace/proj && ls' } }, +- }, +- ], cwd); +- const start = getStart(result); +- // edit tool's toolInput is derived from filePath, not command — but toolArguments preserves original +- assert.deepStrictEqual(JSON.parse(start.toolArguments!), { command: 'cd /workspace/proj && ls' }); - }); - -- test('returns true for empty arrays', () => { -- assert.strictEqual(parsedPluginsEqual([], []), true); +- test('handles trailing slash on workingDirectory', async () => { +- const result = await mapSessionEvents(session, undefined, [ +- makeBashEvent('cd /workspace/proj && ls'), +- ], URI.file('/workspace/proj/')); +- const start = getStart(result); +- assert.strictEqual(start.toolInput, 'ls'); +- }); +- +- test('handles quoted directory in cd prefix', async () => { +- const cwdWithSpaces = URI.file('/workspace/my proj'); +- const result = await mapSessionEvents(session, undefined, [ +- makeBashEvent('cd "/workspace/my proj" && ls'), +- ], cwdWithSpaces); +- const start = getStart(result); +- assert.strictEqual(start.toolInput, 'ls'); +- }); +- +- test('rewrites powershell commands too', async () => { +- const result = await mapSessionEvents(session, undefined, [ +- { +- type: 'tool.execution_start', +- data: { toolCallId: 'tc-1', toolName: 'powershell', arguments: { command: 'cd /workspace/proj; dir' } }, +- }, +- ], cwd); +- const start = getStart(result); +- assert.strictEqual(start.toolInput, 'dir'); - }); - }); -}); -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/linux/00-build-improve-qemu.patch b/patches/linux/00-build-improve-qemu.patch index b287302b0bd..5206db23d3e 100644 --- a/patches/linux/00-build-improve-qemu.patch +++ b/patches/linux/00-build-improve-qemu.patch @@ -1,11 +1,11 @@ diff --git a/build/npm/postinstall.ts b/build/npm/postinstall.ts -index ae2651c..3bc46fa 100644 +index dc154d4b..f5ef3c57 100644 --- a/build/npm/postinstall.ts +++ b/build/npm/postinstall.ts @@ -74,5 +74,7 @@ async function npmInstallAsync(dir: string, opts?: child_process.SpawnOptions): - if (process.env['npm_config_arch'] === 'arm64') { -- run('sudo', ['docker', 'run', '--rm', '--privileged', 'multiarch/qemu-user-static', '--reset', '-p', 'yes'], syncOpts); +- run('sudo', ['docker', 'run', '--rm', '--privileged', 'vscodehub.azurecr.io/multiarch/qemu-user-static@sha256:fe60359c92e86a43cc87b3d906006245f77bfc0565676b80004cc666e4feb9f0', '--reset', '-p', 'yes'], syncOpts); - } + const emulateArchList = ['arm64', 'arm', 'ppc64', 'riscv64', 's390x', 'loong64']; + if (process.env['DISABLE_QEMU'] !== 'true' && !!process.env['npm_config_arch'] && emulateArchList.includes(process.env['npm_config_arch'])) { diff --git a/product.json b/product.json index efeaa2c0a85..46a631a96b5 100644 --- a/product.json +++ b/product.json @@ -443,6 +443,10 @@ "ms-vscode.vscode-eng-codereview": [ "chatContextProvider" ], + "ms-vscode.vscode-chat-customizations-evaluations": [ + "chatSessionCustomizationProvider", + "contribEditorContentMenu" + ], "jeanp413.open-remote-ssh": [ "resolvers", "tunnels", diff --git a/upstream/insider.json b/upstream/insider.json index 0909ccb60e9..d394f1c84a2 100644 --- a/upstream/insider.json +++ b/upstream/insider.json @@ -1,4 +1,4 @@ { - "tag": "1.116.0", - "commit": "560a9dba96f961efea7b1612916f89e5d5d4d679" + "tag": "1.118.1", + "commit": "034f571df509819cc10b0c8129f66ef77a542f0e" } From 6d855935dc72e321c1804f2cb28a62876c266f7c Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 2 May 2026 21:06:23 +0200 Subject: [PATCH 35/79] feat: remove update version since we use gh releases (#2821) --- .github/workflows/publish-insider-linux.yml | 7 - .github/workflows/publish-insider-macos.yml | 7 - .github/workflows/publish-insider-windows.yml | 7 - .github/workflows/publish-stable-linux.yml | 7 - .github/workflows/publish-stable-macos.yml | 7 - .github/workflows/publish-stable-windows.yml | 7 - update_version.sh | 216 ------------------ 7 files changed, 258 deletions(-) delete mode 100755 update_version.sh diff --git a/.github/workflows/publish-insider-linux.yml b/.github/workflows/publish-insider-linux.yml index a8fdfa9a908..25d286a83fb 100644 --- a/.github/workflows/publish-insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -222,13 +222,6 @@ jobs: run: ./release.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - - name: Update versions repo - env: - GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} - GITHUB_USERNAME: ${{ github.repository_owner }} - run: ./update_version.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - reh_linux: needs: - check diff --git a/.github/workflows/publish-insider-macos.yml b/.github/workflows/publish-insider-macos.yml index 04eecfaa4f6..f92791d25f7 100644 --- a/.github/workflows/publish-insider-macos.yml +++ b/.github/workflows/publish-insider-macos.yml @@ -82,13 +82,6 @@ jobs: run: ./release.sh if: env.SHOULD_BUILD == 'yes' - - name: Update versions repo - env: - GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} - GITHUB_USERNAME: ${{ github.repository_owner }} - run: ./update_version.sh - if: env.SHOULD_BUILD == 'yes' - - name: Clean up keychain if: always() run: | diff --git a/.github/workflows/publish-insider-windows.yml b/.github/workflows/publish-insider-windows.yml index bd0e1aeaf49..bf7ac7a38f1 100644 --- a/.github/workflows/publish-insider-windows.yml +++ b/.github/workflows/publish-insider-windows.yml @@ -213,13 +213,6 @@ jobs: run: ./release.sh if: env.SHOULD_BUILD == 'yes' - - name: Update versions repo - env: - GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} - GITHUB_USERNAME: ${{ github.repository_owner }} - run: ./update_version.sh - if: env.SHOULD_BUILD == 'yes' - winget: needs: build runs-on: windows-2022 diff --git a/.github/workflows/publish-stable-linux.yml b/.github/workflows/publish-stable-linux.yml index da325131bc0..9fb69c5b18c 100644 --- a/.github/workflows/publish-stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -223,13 +223,6 @@ jobs: run: ./release.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - - name: Update versions repo - env: - GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} - GITHUB_USERNAME: ${{ github.repository_owner }} - run: ./update_version.sh - if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - reh_linux: needs: - check diff --git a/.github/workflows/publish-stable-macos.yml b/.github/workflows/publish-stable-macos.yml index 3ebf93fab8a..b212981dd2b 100644 --- a/.github/workflows/publish-stable-macos.yml +++ b/.github/workflows/publish-stable-macos.yml @@ -81,13 +81,6 @@ jobs: run: ./release.sh if: env.SHOULD_BUILD == 'yes' - - name: Update versions repo - env: - GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} - GITHUB_USERNAME: ${{ github.repository_owner }} - run: ./update_version.sh - if: env.SHOULD_BUILD == 'yes' - - name: Clean up keychain if: always() run: | diff --git a/.github/workflows/publish-stable-windows.yml b/.github/workflows/publish-stable-windows.yml index 96ccab1645b..fd064d4623d 100644 --- a/.github/workflows/publish-stable-windows.yml +++ b/.github/workflows/publish-stable-windows.yml @@ -212,13 +212,6 @@ jobs: run: ./release.sh if: env.SHOULD_BUILD == 'yes' - - name: Update versions repo - env: - GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} - GITHUB_USERNAME: ${{ github.repository_owner }} - run: ./update_version.sh - if: env.SHOULD_BUILD == 'yes' - winget: needs: build runs-on: windows-2022 diff --git a/update_version.sh b/update_version.sh deleted file mode 100755 index 4c081eb6dcd..00000000000 --- a/update_version.sh +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC1091 - -set -e - -if [[ "${SHOULD_BUILD}" != "yes" && "${FORCE_UPDATE}" != "true" ]]; then - echo "Will not update version JSON because we did not build" - exit 0 -fi - -if [[ -z "${GH_TOKEN}" ]] && [[ -z "${GITHUB_TOKEN}" ]] && [[ -z "${GH_ENTERPRISE_TOKEN}" ]] && [[ -z "${GITHUB_ENTERPRISE_TOKEN}" ]]; then - echo "Will not update version JSON because no GITHUB_TOKEN defined" - exit 0 -else - GITHUB_TOKEN="${GH_TOKEN:-${GITHUB_TOKEN:-${GH_ENTERPRISE_TOKEN:-${GITHUB_ENTERPRISE_TOKEN}}}}" -fi - -# Support for GitHub Enterprise -GH_HOST="${GH_HOST:-github.com}" - -if [[ "${FORCE_UPDATE}" == "true" ]]; then - . version.sh -fi - -if [[ -z "${BUILD_SOURCEVERSION}" ]]; then - echo "Will not update version JSON because no BUILD_SOURCEVERSION defined" - exit 0 -fi - -# if [[ "${VSCODE_ARCH}" == "ppc64le" ]] || [[ "${VSCODE_ARCH}" == "riscv64" ]] ; then -# echo "Skip PPC64LE since only reh is published" -# exit 0 -# fi - -# { -# "url": "https://az764295.vo.msecnd.net/stable/51b0b28134d51361cf996d2f0a1c698247aeabd8/VSCode-darwin-stable.zip", -# "name": "1.33.1", -# "version": "51b0b28134d51361cf996d2f0a1c698247aeabd8", -# "productVersion": "1.33.1", -# "hash": "cb4109f196d23b9d1e8646ce43145c5bb62f55a8", -# "timestamp": 1554971059007, -# "sha256hash": "ac2a1c8772501732cd5ff539a04bb4dc566b58b8528609d2b34bbf970d08cf01" -# } - -# `url` is URL_BASE + filename of asset e.g. -# darwin: https://github.com/${ASSETS_REPOSITORY}/releases/download/${RELEASE_VERSION}/${APP_NAME}-darwin-${RELEASE_VERSION}.zip -# `name` is $RELEASE_VERSION -# `version` is $BUILD_SOURCEVERSION -# `productVersion` is $RELEASE_VERSION -# `hash` in .sha1 -# `timestamp` is $(node -e 'console.log(Date.now())') -# `sha256hash` in .sha256 - -REPOSITORY_NAME="${VERSIONS_REPOSITORY/*\//}" -URL_BASE="https://${GH_HOST}/${ASSETS_REPOSITORY}/releases/download/${RELEASE_VERSION}" - -generateJson() { - local url name version productVersion sha1hash sha256hash timestamp - JSON_DATA="{}" - - # generate parts - url="${URL_BASE}/${ASSET_NAME}" - name="${RELEASE_VERSION}" - version="${BUILD_SOURCEVERSION}" - productVersion="$( transformVersion "${RELEASE_VERSION}" )" - timestamp=$( node -e 'console.log(Date.now())' ) - - if [[ ! -f "assets/${ASSET_NAME}" ]]; then - echo "Downloading asset '${ASSET_NAME}'" - gh release download --repo "${ASSETS_REPOSITORY}" "${RELEASE_VERSION}" --dir "assets" --pattern "${ASSET_NAME}*" - fi - - sha1hash=$( awk '{ print $1 }' "assets/${ASSET_NAME}.sha1" ) - sha256hash=$( awk '{ print $1 }' "assets/${ASSET_NAME}.sha256" ) - - # check that nothing is blank (blank indicates something awry with build) - for key in url name version productVersion sha1hash timestamp sha256hash; do - if [[ -z "${key}" ]]; then - echo "Variable '${key}' is empty; exiting..." - exit 1 - fi - done - - # generate json - JSON_DATA=$( jq \ - --arg url "${url}" \ - --arg name "${name}" \ - --arg version "${version}" \ - --arg productVersion "${productVersion}" \ - --arg hash "${sha1hash}" \ - --arg timestamp "${timestamp}" \ - --arg sha256hash "${sha256hash}" \ - '. | .url=$url | .name=$name | .version=$version | .productVersion=$productVersion | .hash=$hash | .timestamp=$timestamp | .sha256hash=$sha256hash' \ - <<<'{}' ) -} - -transformVersion() { - local version parts - - version="${1%-insider}" - - IFS='.' read -r -a parts <<< "${version}" - - # Remove leading zeros from third part - parts[2]="$((10#${parts[2]}))" - - version="${parts[0]}.${parts[1]}.${parts[2]}.0" - - if [[ "${1}" == *-insider ]]; then - version="${version}-insider" - fi - - echo "${version}" -} - -updateLatestVersion() { - echo "Updating ${VERSION_PATH}/latest.json" - - # do not update the same version - if [[ -f "${REPOSITORY_NAME}/${VERSION_PATH}/latest.json" ]]; then - CURRENT_VERSION=$( jq -r '.name' "${REPOSITORY_NAME}/${VERSION_PATH}/latest.json" ) - echo "CURRENT_VERSION: ${CURRENT_VERSION}" - - if [[ "${CURRENT_VERSION}" == "${RELEASE_VERSION}" && "${FORCE_UPDATE}" != "true" ]]; then - return 0 - fi - fi - - echo "Generating ${VERSION_PATH}/latest.json" - - mkdir -p "${REPOSITORY_NAME}/${VERSION_PATH}" - - generateJson - - echo "${JSON_DATA}" > "${REPOSITORY_NAME}/${VERSION_PATH}/latest.json" - - echo "${JSON_DATA}" -} - -# init versions repo for later commiting + pushing the json file to it -# thank you https://www.vinaygopinath.me/blog/tech/commit-to-master-branch-on-github-using-travis-ci/ -git clone "https://${GH_HOST}/${VERSIONS_REPOSITORY}.git" -cd "${REPOSITORY_NAME}" || { echo "'${REPOSITORY_NAME}' dir not found"; exit 1; } -git config user.email "$( echo "${GITHUB_USERNAME}" | awk '{print tolower($0)}' )-ci@not-real.com" -git config user.name "${GITHUB_USERNAME} CI" -git remote rm origin -git remote add origin "https://${GITHUB_USERNAME}:${GITHUB_TOKEN}@${GH_HOST}/${VERSIONS_REPOSITORY}.git" &> /dev/null -cd .. - -if [[ "${OS_NAME}" == "osx" ]]; then - ASSET_NAME="${APP_NAME}-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" - VERSION_PATH="${VSCODE_QUALITY}/darwin/${VSCODE_ARCH}" - updateLatestVersion -elif [[ "${OS_NAME}" == "windows" ]]; then - # system installer - ASSET_NAME="${APP_NAME}Setup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" - VERSION_PATH="${VSCODE_QUALITY}/win32/${VSCODE_ARCH}/system" - updateLatestVersion - - # user installer - ASSET_NAME="${APP_NAME}UserSetup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" - VERSION_PATH="${VSCODE_QUALITY}/win32/${VSCODE_ARCH}/user" - updateLatestVersion - - # windows archive - ASSET_NAME="${APP_NAME}-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" - VERSION_PATH="${VSCODE_QUALITY}/win32/${VSCODE_ARCH}/archive" - updateLatestVersion - - if [[ "${VSCODE_ARCH}" == "ia32" || "${VSCODE_ARCH}" == "x64" ]]; then - # msi - if [[ "${SHOULD_BUILD_MSI}" != "no" ]]; then - ASSET_NAME="${APP_NAME}-${VSCODE_ARCH}-${RELEASE_VERSION}.msi" - VERSION_PATH="${VSCODE_QUALITY}/win32/${VSCODE_ARCH}/msi" - updateLatestVersion - fi - - # updates-disabled msi - if [[ "${SHOULD_BUILD_MSI_NOUP}" != "no" ]]; then - ASSET_NAME="${APP_NAME}-${VSCODE_ARCH}-updates-disabled-${RELEASE_VERSION}.msi" - VERSION_PATH="${VSCODE_QUALITY}/win32/${VSCODE_ARCH}/msi-updates-disabled" - updateLatestVersion - fi - fi -else # linux - # update service links to tar.gz file - # see https://update.code.visualstudio.com/api/update/linux-x64/stable/VERSION - # as examples - ASSET_NAME="${APP_NAME}-linux-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" - VERSION_PATH="${VSCODE_QUALITY}/linux/${VSCODE_ARCH}" - updateLatestVersion -fi - -cd "${REPOSITORY_NAME}" || { echo "'${REPOSITORY_NAME}' dir not found"; exit 1; } - -git pull origin master # in case another build just pushed -git add . - -CHANGES=$( git status --porcelain ) - -if [[ -n "${CHANGES}" ]]; then - echo "Some changes have been found, pushing them" - - dateAndMonth=$( date "+%D %T" ) - - git commit -m "CI update: ${dateAndMonth} (Build ${GITHUB_RUN_NUMBER})" - - if ! git push origin master --quiet; then - git pull origin master - git push origin master --quiet - fi -else - echo "No changes" -fi - -cd .. From 0c87817c6d20af5c23a8c120d4fa970842372f90 Mon Sep 17 00:00:00 2001 From: Cristian Date: Mon, 11 May 2026 17:13:05 +0300 Subject: [PATCH 36/79] fix(appimage): pass arguments (#2818) --- build/linux/appimage/build.sh | 8 ++++---- build/linux/appimage/recipe.yml | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/build/linux/appimage/build.sh b/build/linux/appimage/build.sh index 6f4e3fd5d9d..78289318301 100755 --- a/build/linux/appimage/build.sh +++ b/build/linux/appimage/build.sh @@ -33,12 +33,12 @@ if [[ "${VSCODE_ARCH}" == "x64" ]]; then APP_NAME_LC="$( echo "${APP_NAME}" | awk '{print tolower($0)}' )" if [[ "${VSCODE_QUALITY}" == "insider" ]]; then - sed -i "s|@@NAME@@|${APP_NAME}-Insiders|g" recipe.yml - sed -i "s|@@APPNAME@@|${BINARY_NAME}|g" recipe.yml + sed -i "s|@@APP_NAME@@|${APP_NAME}-Insiders|g" recipe.yml + sed -i "s|@@BINARY_NAME@@|${BINARY_NAME}|g" recipe.yml sed -i "s|@@ICON@@|${APP_NAME_LC}-insiders|g" recipe.yml else - sed -i "s|@@NAME@@|${APP_NAME}|g" recipe.yml - sed -i "s|@@APPNAME@@|${BINARY_NAME}|g" recipe.yml + sed -i "s|@@APP_NAME@@|${APP_NAME}|g" recipe.yml + sed -i "s|@@BINARY_NAME@@|${BINARY_NAME}|g" recipe.yml sed -i "s|@@ICON@@|${APP_NAME_LC}|g" recipe.yml fi diff --git a/build/linux/appimage/recipe.yml b/build/linux/appimage/recipe.yml index 6dad8bf221d..38dc72297e3 100644 --- a/build/linux/appimage/recipe.yml +++ b/build/linux/appimage/recipe.yml @@ -5,7 +5,7 @@ # wget -c "https://github.com/AppImage/pkg2appimage/raw/master/pkg2appimage" # bash -ex pkg2appimage VSCodium -app: @@NAME@@ +app: @@APP_NAME@@ ingredients: packages: @@ -17,11 +17,11 @@ ingredients: script: - pwd - cp ../../../../vscode/.build/linux/deb/amd64/deb/*.deb . - - ls @@APPNAME@@_*.deb | cut -d _ -f 2 > VERSION + - ls @@BINARY_NAME@@_*.deb | cut -d _ -f 2 > VERSION script: - - sed -i -e 's|/usr/share/pixmaps/||g' usr/share/applications/@@APPNAME@@.desktop - - cp usr/share/applications/@@APPNAME@@.desktop . + - sed -i -e 's|/usr/share/pixmaps/||g' usr/share/applications/@@BINARY_NAME@@.desktop + - cp usr/share/applications/@@BINARY_NAME@@.desktop . - cp usr/share/pixmaps/@@ICON@@.png . - /usr/bin/convert @@ICON@@.png -resize 512x512 usr/share/icons/hicolor/512x512/apps/@@ICON@@.png - /usr/bin/convert @@ICON@@.png -resize 256x256 usr/share/icons/hicolor/256x256/apps/@@ICON@@.png @@ -29,7 +29,7 @@ script: - /usr/bin/convert @@ICON@@.png -resize 64x64 usr/share/icons/hicolor/64x64/apps/@@ICON@@.png - /usr/bin/convert @@ICON@@.png -resize 48x48 usr/share/icons/hicolor/48x48/apps/@@ICON@@.png - /usr/bin/convert @@ICON@@.png -resize 32x32 usr/share/icons/hicolor/32x32/apps/@@ICON@@.png - - ( cd usr/bin/ ; ln -s ../share/@@APPNAME@@/@@APPNAME@@ . ) + - ( cd usr/bin/ ; ln -s ../share/@@BINARY_NAME@@/@@BINARY_NAME@@ . ) - rm -rf usr/lib/x86_64-linux-gnu - rm -f lib/x86_64-linux-gnu/libglib* - cat > AppRun <<\EOF @@ -40,6 +40,11 @@ script: - export PERLLIB="${HERE}"/usr/share/perl5/:"${HERE}"/usr/lib/perl5/:"${PERLLIB}" - export GSETTINGS_SCHEMA_DIR="${HERE}"/usr/share/glib-2.0/schemas/:"${GSETTINGS_SCHEMA_DIR}" - export QT_PLUGIN_PATH="${HERE}"/usr/lib/qt4/plugins/:"${HERE}"/usr/lib/i386-linux-gnu/qt4/plugins/:"${HERE}"/usr/lib/x86_64-linux-gnu/qt4/plugins/:"${HERE}"/usr/lib32/qt4/plugins/:"${HERE}"/usr/lib64/qt4/plugins/:"${HERE}"/usr/lib/qt5/plugins/:"${HERE}"/usr/lib/i386-linux-gnu/qt5/plugins/:"${HERE}"/usr/lib/x86_64-linux-gnu/qt5/plugins/:"${HERE}"/usr/lib32/qt5/plugins/:"${HERE}"/usr/lib64/qt5/plugins/:"${QT_PLUGIN_PATH}" - - EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2- | sed -e 's|%.||g') - - exec ${EXEC} "$@" + - EXEC="${HERE}/usr/share/@@BINARY_NAME@@/@@BINARY_NAME@@" + - EXEC_CLI="${HERE}/usr/share/@@BINARY_NAME@@/bin/@@BINARY_NAME@@" + - if [ "$1" = "--" ]; then + - shift + - exec "${EXEC_CLI}" "$@" + - fi + - exec "${EXEC}" "$@" - EOF From e61bd9832da943dae796268659124872324b7a76 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 15 May 2026 21:29:21 +0200 Subject: [PATCH 37/79] feat: move secrets to environments (#2835) --- .github/workflows/publish-insider-linux.yml | 2 +- .github/workflows/publish-insider-macos.yml | 12 ++++++------ .github/workflows/publish-insider-spearhead.yml | 2 +- .github/workflows/publish-insider-windows.yml | 2 +- .github/workflows/publish-stable-linux.yml | 6 +++--- .github/workflows/publish-stable-macos.yml | 12 ++++++------ .github/workflows/publish-stable-spearhead.yml | 2 +- .github/workflows/publish-stable-windows.yml | 2 +- build/osx/prepare_assets.sh | 2 +- dev/build.sh | 2 +- dev/osx/codesign.env.template | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/publish-insider-linux.yml b/.github/workflows/publish-insider-linux.yml index 25d286a83fb..cef6f26600e 100644 --- a/.github/workflows/publish-insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -410,7 +410,7 @@ jobs: - check - build runs-on: ubuntu-latest - environment: publish + environment: publish-linux-aur permissions: {} strategy: fail-fast: false diff --git a/.github/workflows/publish-insider-macos.yml b/.github/workflows/publish-insider-macos.yml index f92791d25f7..92e33a91acc 100644 --- a/.github/workflows/publish-insider-macos.yml +++ b/.github/workflows/publish-insider-macos.yml @@ -20,7 +20,7 @@ env: jobs: build: runs-on: ${{ matrix.runner }} - environment: publish + environment: publish-osx permissions: contents: write env: @@ -67,11 +67,11 @@ jobs: - name: Prepare assets env: - CERTIFICATE_OSX_APP_PASSWORD: ${{ secrets.CERTIFICATE_OSX_NEW_APP_PASSWORD }} - CERTIFICATE_OSX_ID: ${{ secrets.CERTIFICATE_OSX_NEW_ID }} - CERTIFICATE_OSX_P12_DATA: ${{ secrets.CERTIFICATE_OSX_NEW_P12_DATA }} - CERTIFICATE_OSX_P12_PASSWORD: ${{ secrets.CERTIFICATE_OSX_NEW_P12_PASSWORD }} - CERTIFICATE_OSX_TEAM_ID: ${{ secrets.CERTIFICATE_OSX_NEW_TEAM_ID }} + CERTIFICATE_OSX_APP_PASSWORD: ${{ secrets.CERTIFICATE_OSX_APP_PASSWORD }} + CERTIFICATE_OSX_APPLE_ID: ${{ secrets.CERTIFICATE_OSX_APPLE_ID }} + CERTIFICATE_OSX_P12_DATA: ${{ secrets.CERTIFICATE_OSX_P12_DATA }} + CERTIFICATE_OSX_P12_PASSWORD: ${{ secrets.CERTIFICATE_OSX_P12_PASSWORD }} + CERTIFICATE_OSX_TEAM_ID: ${{ secrets.CERTIFICATE_OSX_TEAM_ID }} run: ./prepare_assets.sh if: env.SHOULD_BUILD == 'yes' diff --git a/.github/workflows/publish-insider-spearhead.yml b/.github/workflows/publish-insider-spearhead.yml index f2ec331c421..d5d76d232ef 100644 --- a/.github/workflows/publish-insider-spearhead.yml +++ b/.github/workflows/publish-insider-spearhead.yml @@ -56,7 +56,7 @@ jobs: needs: - check runs-on: macos-15 - environment: publish + environment: publish-release permissions: contents: write # Release env: diff --git a/.github/workflows/publish-insider-windows.yml b/.github/workflows/publish-insider-windows.yml index bf7ac7a38f1..fac1c7ebb24 100644 --- a/.github/workflows/publish-insider-windows.yml +++ b/.github/workflows/publish-insider-windows.yml @@ -110,7 +110,7 @@ jobs: - check - compile runs-on: windows-2022 - environment: publish + environment: publish-windows permissions: contents: write strategy: diff --git a/.github/workflows/publish-stable-linux.yml b/.github/workflows/publish-stable-linux.yml index 9fb69c5b18c..e15fb0749d3 100644 --- a/.github/workflows/publish-stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -411,7 +411,7 @@ jobs: - check - build runs-on: ubuntu-latest - environment: publish + environment: publish-linux-aur permissions: {} strategy: fail-fast: false @@ -435,7 +435,7 @@ jobs: - check - build runs-on: ubuntu-latest - environment: publish + environment: publish-linux-snap permissions: {} env: RELEASE_VERSION: ${{ needs.check.outputs.RELEASE_VERSION }} @@ -498,7 +498,7 @@ jobs: - check - build runs-on: ubuntu-latest - environment: publish + environment: publish-linux-rudy permissions: {} steps: diff --git a/.github/workflows/publish-stable-macos.yml b/.github/workflows/publish-stable-macos.yml index b212981dd2b..493dd0f369e 100644 --- a/.github/workflows/publish-stable-macos.yml +++ b/.github/workflows/publish-stable-macos.yml @@ -20,7 +20,7 @@ env: jobs: build: runs-on: ${{ matrix.runner }} - environment: publish + environment: publish-osx permissions: contents: write env: @@ -67,11 +67,11 @@ jobs: - name: Prepare assets env: - CERTIFICATE_OSX_APP_PASSWORD: ${{ secrets.CERTIFICATE_OSX_NEW_APP_PASSWORD }} - CERTIFICATE_OSX_ID: ${{ secrets.CERTIFICATE_OSX_NEW_ID }} - CERTIFICATE_OSX_P12_DATA: ${{ secrets.CERTIFICATE_OSX_NEW_P12_DATA }} - CERTIFICATE_OSX_P12_PASSWORD: ${{ secrets.CERTIFICATE_OSX_NEW_P12_PASSWORD }} - CERTIFICATE_OSX_TEAM_ID: ${{ secrets.CERTIFICATE_OSX_NEW_TEAM_ID }} + CERTIFICATE_OSX_APP_PASSWORD: ${{ secrets.CERTIFICATE_OSX_APP_PASSWORD }} + CERTIFICATE_OSX_APPLE_ID: ${{ secrets.CERTIFICATE_OSX_APPLE_ID }} + CERTIFICATE_OSX_P12_DATA: ${{ secrets.CERTIFICATE_OSX_P12_DATA }} + CERTIFICATE_OSX_P12_PASSWORD: ${{ secrets.CERTIFICATE_OSX_P12_PASSWORD }} + CERTIFICATE_OSX_TEAM_ID: ${{ secrets.CERTIFICATE_OSX_TEAM_ID }} run: ./prepare_assets.sh if: env.SHOULD_BUILD == 'yes' diff --git a/.github/workflows/publish-stable-spearhead.yml b/.github/workflows/publish-stable-spearhead.yml index 532dd4322a0..34ec21e004b 100644 --- a/.github/workflows/publish-stable-spearhead.yml +++ b/.github/workflows/publish-stable-spearhead.yml @@ -56,7 +56,7 @@ jobs: needs: - check runs-on: macos-15 - environment: publish + environment: publish-release permissions: contents: write # Release env: diff --git a/.github/workflows/publish-stable-windows.yml b/.github/workflows/publish-stable-windows.yml index fd064d4623d..49f7d9c7ace 100644 --- a/.github/workflows/publish-stable-windows.yml +++ b/.github/workflows/publish-stable-windows.yml @@ -110,7 +110,7 @@ jobs: - check - compile runs-on: windows-2022 - environment: publish + environment: publish-windows permissions: contents: write strategy: diff --git a/build/osx/prepare_assets.sh b/build/osx/prepare_assets.sh index 8bc2d193153..c1b03e41507 100644 --- a/build/osx/prepare_assets.sh +++ b/build/osx/prepare_assets.sh @@ -43,7 +43,7 @@ if [[ -n "${CERTIFICATE_OSX_P12_DATA}" ]]; then zip -r -X -y "${ZIP_FILE}" ./*.app - xcrun notarytool store-credentials "${APP_NAME}" --apple-id "${CERTIFICATE_OSX_ID}" --team-id "${CERTIFICATE_OSX_TEAM_ID}" --password "${CERTIFICATE_OSX_APP_PASSWORD}" --keychain "${KEYCHAIN}" + xcrun notarytool store-credentials "${APP_NAME}" --apple-id "${CERTIFICATE_OSX_APPLE_ID}" --team-id "${CERTIFICATE_OSX_TEAM_ID}" --password "${CERTIFICATE_OSX_APP_PASSWORD}" --keychain "${KEYCHAIN}" # xcrun notarytool history --keychain-profile "${APP_NAME}" --keychain "${KEYCHAIN}" xcrun notarytool submit "${ZIP_FILE}" --keychain-profile "${APP_NAME}" --wait --keychain "${KEYCHAIN}" diff --git a/dev/build.sh b/dev/build.sh index 4efd2963235..2fa271b2fa9 100755 --- a/dev/build.sh +++ b/dev/build.sh @@ -155,7 +155,7 @@ if [[ "${SKIP_ASSETS}" == "no" ]]; then if [[ "${OS_NAME}" == "osx" && -f "dev/osx/codesign.env" ]]; then . dev/osx/macos-codesign.env - echo "CERTIFICATE_OSX_ID: ${CERTIFICATE_OSX_ID}" + echo "CERTIFICATE_OSX_APPLE_ID: ${CERTIFICATE_OSX_APPLE_ID}" fi . prepare_assets.sh diff --git a/dev/osx/codesign.env.template b/dev/osx/codesign.env.template index 43b6c907a0f..cb9a09a3a1c 100644 --- a/dev/osx/codesign.env.template +++ b/dev/osx/codesign.env.template @@ -1,5 +1,5 @@ CERTIFICATE_OSX_APP_PASSWORD= -CERTIFICATE_OSX_ID= +CERTIFICATE_OSX_APPLE_ID= CERTIFICATE_OSX_P12_DATA= CERTIFICATE_OSX_P12_PASSWORD= CERTIFICATE_OSX_TEAM_ID= From e6eb2376a617c3aa9b744b59f324a0e9378122c3 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 16 May 2026 04:28:05 +0200 Subject: [PATCH 38/79] revert: #2821 (#2838) --- .github/workflows/publish-insider-linux.yml | 7 + .github/workflows/publish-insider-macos.yml | 7 + .github/workflows/publish-insider-windows.yml | 7 + .github/workflows/publish-stable-linux.yml | 7 + .github/workflows/publish-stable-macos.yml | 7 + .github/workflows/publish-stable-windows.yml | 7 + update_version.sh | 216 ++++++++++++++++++ 7 files changed, 258 insertions(+) create mode 100644 update_version.sh diff --git a/.github/workflows/publish-insider-linux.yml b/.github/workflows/publish-insider-linux.yml index cef6f26600e..34137cc2de4 100644 --- a/.github/workflows/publish-insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -222,6 +222,13 @@ jobs: run: ./release.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' + - name: Update versions repo + env: + GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} + GITHUB_USERNAME: ${{ github.repository_owner }} + run: ./update_version.sh + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' + reh_linux: needs: - check diff --git a/.github/workflows/publish-insider-macos.yml b/.github/workflows/publish-insider-macos.yml index 92e33a91acc..f3bd0cc2552 100644 --- a/.github/workflows/publish-insider-macos.yml +++ b/.github/workflows/publish-insider-macos.yml @@ -82,6 +82,13 @@ jobs: run: ./release.sh if: env.SHOULD_BUILD == 'yes' + - name: Update versions repo + env: + GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} + GITHUB_USERNAME: ${{ github.repository_owner }} + run: ./update_version.sh + if: env.SHOULD_BUILD == 'yes' + - name: Clean up keychain if: always() run: | diff --git a/.github/workflows/publish-insider-windows.yml b/.github/workflows/publish-insider-windows.yml index fac1c7ebb24..3595dcd3406 100644 --- a/.github/workflows/publish-insider-windows.yml +++ b/.github/workflows/publish-insider-windows.yml @@ -213,6 +213,13 @@ jobs: run: ./release.sh if: env.SHOULD_BUILD == 'yes' + - name: Update versions repo + env: + GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} + GITHUB_USERNAME: ${{ github.repository_owner }} + run: ./update_version.sh + if: env.SHOULD_BUILD == 'yes' + winget: needs: build runs-on: windows-2022 diff --git a/.github/workflows/publish-stable-linux.yml b/.github/workflows/publish-stable-linux.yml index e15fb0749d3..423ceba1638 100644 --- a/.github/workflows/publish-stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -223,6 +223,13 @@ jobs: run: ./release.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' + - name: Update versions repo + env: + GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} + GITHUB_USERNAME: ${{ github.repository_owner }} + run: ./update_version.sh + if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' + reh_linux: needs: - check diff --git a/.github/workflows/publish-stable-macos.yml b/.github/workflows/publish-stable-macos.yml index 493dd0f369e..664660b3ad6 100644 --- a/.github/workflows/publish-stable-macos.yml +++ b/.github/workflows/publish-stable-macos.yml @@ -81,6 +81,13 @@ jobs: run: ./release.sh if: env.SHOULD_BUILD == 'yes' + - name: Update versions repo + env: + GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} + GITHUB_USERNAME: ${{ github.repository_owner }} + run: ./update_version.sh + if: env.SHOULD_BUILD == 'yes' + - name: Clean up keychain if: always() run: | diff --git a/.github/workflows/publish-stable-windows.yml b/.github/workflows/publish-stable-windows.yml index 49f7d9c7ace..5f371c8a393 100644 --- a/.github/workflows/publish-stable-windows.yml +++ b/.github/workflows/publish-stable-windows.yml @@ -212,6 +212,13 @@ jobs: run: ./release.sh if: env.SHOULD_BUILD == 'yes' + - name: Update versions repo + env: + GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} + GITHUB_USERNAME: ${{ github.repository_owner }} + run: ./update_version.sh + if: env.SHOULD_BUILD == 'yes' + winget: needs: build runs-on: windows-2022 diff --git a/update_version.sh b/update_version.sh new file mode 100644 index 00000000000..4c081eb6dcd --- /dev/null +++ b/update_version.sh @@ -0,0 +1,216 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 + +set -e + +if [[ "${SHOULD_BUILD}" != "yes" && "${FORCE_UPDATE}" != "true" ]]; then + echo "Will not update version JSON because we did not build" + exit 0 +fi + +if [[ -z "${GH_TOKEN}" ]] && [[ -z "${GITHUB_TOKEN}" ]] && [[ -z "${GH_ENTERPRISE_TOKEN}" ]] && [[ -z "${GITHUB_ENTERPRISE_TOKEN}" ]]; then + echo "Will not update version JSON because no GITHUB_TOKEN defined" + exit 0 +else + GITHUB_TOKEN="${GH_TOKEN:-${GITHUB_TOKEN:-${GH_ENTERPRISE_TOKEN:-${GITHUB_ENTERPRISE_TOKEN}}}}" +fi + +# Support for GitHub Enterprise +GH_HOST="${GH_HOST:-github.com}" + +if [[ "${FORCE_UPDATE}" == "true" ]]; then + . version.sh +fi + +if [[ -z "${BUILD_SOURCEVERSION}" ]]; then + echo "Will not update version JSON because no BUILD_SOURCEVERSION defined" + exit 0 +fi + +# if [[ "${VSCODE_ARCH}" == "ppc64le" ]] || [[ "${VSCODE_ARCH}" == "riscv64" ]] ; then +# echo "Skip PPC64LE since only reh is published" +# exit 0 +# fi + +# { +# "url": "https://az764295.vo.msecnd.net/stable/51b0b28134d51361cf996d2f0a1c698247aeabd8/VSCode-darwin-stable.zip", +# "name": "1.33.1", +# "version": "51b0b28134d51361cf996d2f0a1c698247aeabd8", +# "productVersion": "1.33.1", +# "hash": "cb4109f196d23b9d1e8646ce43145c5bb62f55a8", +# "timestamp": 1554971059007, +# "sha256hash": "ac2a1c8772501732cd5ff539a04bb4dc566b58b8528609d2b34bbf970d08cf01" +# } + +# `url` is URL_BASE + filename of asset e.g. +# darwin: https://github.com/${ASSETS_REPOSITORY}/releases/download/${RELEASE_VERSION}/${APP_NAME}-darwin-${RELEASE_VERSION}.zip +# `name` is $RELEASE_VERSION +# `version` is $BUILD_SOURCEVERSION +# `productVersion` is $RELEASE_VERSION +# `hash` in .sha1 +# `timestamp` is $(node -e 'console.log(Date.now())') +# `sha256hash` in .sha256 + +REPOSITORY_NAME="${VERSIONS_REPOSITORY/*\//}" +URL_BASE="https://${GH_HOST}/${ASSETS_REPOSITORY}/releases/download/${RELEASE_VERSION}" + +generateJson() { + local url name version productVersion sha1hash sha256hash timestamp + JSON_DATA="{}" + + # generate parts + url="${URL_BASE}/${ASSET_NAME}" + name="${RELEASE_VERSION}" + version="${BUILD_SOURCEVERSION}" + productVersion="$( transformVersion "${RELEASE_VERSION}" )" + timestamp=$( node -e 'console.log(Date.now())' ) + + if [[ ! -f "assets/${ASSET_NAME}" ]]; then + echo "Downloading asset '${ASSET_NAME}'" + gh release download --repo "${ASSETS_REPOSITORY}" "${RELEASE_VERSION}" --dir "assets" --pattern "${ASSET_NAME}*" + fi + + sha1hash=$( awk '{ print $1 }' "assets/${ASSET_NAME}.sha1" ) + sha256hash=$( awk '{ print $1 }' "assets/${ASSET_NAME}.sha256" ) + + # check that nothing is blank (blank indicates something awry with build) + for key in url name version productVersion sha1hash timestamp sha256hash; do + if [[ -z "${key}" ]]; then + echo "Variable '${key}' is empty; exiting..." + exit 1 + fi + done + + # generate json + JSON_DATA=$( jq \ + --arg url "${url}" \ + --arg name "${name}" \ + --arg version "${version}" \ + --arg productVersion "${productVersion}" \ + --arg hash "${sha1hash}" \ + --arg timestamp "${timestamp}" \ + --arg sha256hash "${sha256hash}" \ + '. | .url=$url | .name=$name | .version=$version | .productVersion=$productVersion | .hash=$hash | .timestamp=$timestamp | .sha256hash=$sha256hash' \ + <<<'{}' ) +} + +transformVersion() { + local version parts + + version="${1%-insider}" + + IFS='.' read -r -a parts <<< "${version}" + + # Remove leading zeros from third part + parts[2]="$((10#${parts[2]}))" + + version="${parts[0]}.${parts[1]}.${parts[2]}.0" + + if [[ "${1}" == *-insider ]]; then + version="${version}-insider" + fi + + echo "${version}" +} + +updateLatestVersion() { + echo "Updating ${VERSION_PATH}/latest.json" + + # do not update the same version + if [[ -f "${REPOSITORY_NAME}/${VERSION_PATH}/latest.json" ]]; then + CURRENT_VERSION=$( jq -r '.name' "${REPOSITORY_NAME}/${VERSION_PATH}/latest.json" ) + echo "CURRENT_VERSION: ${CURRENT_VERSION}" + + if [[ "${CURRENT_VERSION}" == "${RELEASE_VERSION}" && "${FORCE_UPDATE}" != "true" ]]; then + return 0 + fi + fi + + echo "Generating ${VERSION_PATH}/latest.json" + + mkdir -p "${REPOSITORY_NAME}/${VERSION_PATH}" + + generateJson + + echo "${JSON_DATA}" > "${REPOSITORY_NAME}/${VERSION_PATH}/latest.json" + + echo "${JSON_DATA}" +} + +# init versions repo for later commiting + pushing the json file to it +# thank you https://www.vinaygopinath.me/blog/tech/commit-to-master-branch-on-github-using-travis-ci/ +git clone "https://${GH_HOST}/${VERSIONS_REPOSITORY}.git" +cd "${REPOSITORY_NAME}" || { echo "'${REPOSITORY_NAME}' dir not found"; exit 1; } +git config user.email "$( echo "${GITHUB_USERNAME}" | awk '{print tolower($0)}' )-ci@not-real.com" +git config user.name "${GITHUB_USERNAME} CI" +git remote rm origin +git remote add origin "https://${GITHUB_USERNAME}:${GITHUB_TOKEN}@${GH_HOST}/${VERSIONS_REPOSITORY}.git" &> /dev/null +cd .. + +if [[ "${OS_NAME}" == "osx" ]]; then + ASSET_NAME="${APP_NAME}-darwin-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" + VERSION_PATH="${VSCODE_QUALITY}/darwin/${VSCODE_ARCH}" + updateLatestVersion +elif [[ "${OS_NAME}" == "windows" ]]; then + # system installer + ASSET_NAME="${APP_NAME}Setup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" + VERSION_PATH="${VSCODE_QUALITY}/win32/${VSCODE_ARCH}/system" + updateLatestVersion + + # user installer + ASSET_NAME="${APP_NAME}UserSetup-${VSCODE_ARCH}-${RELEASE_VERSION}.exe" + VERSION_PATH="${VSCODE_QUALITY}/win32/${VSCODE_ARCH}/user" + updateLatestVersion + + # windows archive + ASSET_NAME="${APP_NAME}-win32-${VSCODE_ARCH}-${RELEASE_VERSION}.zip" + VERSION_PATH="${VSCODE_QUALITY}/win32/${VSCODE_ARCH}/archive" + updateLatestVersion + + if [[ "${VSCODE_ARCH}" == "ia32" || "${VSCODE_ARCH}" == "x64" ]]; then + # msi + if [[ "${SHOULD_BUILD_MSI}" != "no" ]]; then + ASSET_NAME="${APP_NAME}-${VSCODE_ARCH}-${RELEASE_VERSION}.msi" + VERSION_PATH="${VSCODE_QUALITY}/win32/${VSCODE_ARCH}/msi" + updateLatestVersion + fi + + # updates-disabled msi + if [[ "${SHOULD_BUILD_MSI_NOUP}" != "no" ]]; then + ASSET_NAME="${APP_NAME}-${VSCODE_ARCH}-updates-disabled-${RELEASE_VERSION}.msi" + VERSION_PATH="${VSCODE_QUALITY}/win32/${VSCODE_ARCH}/msi-updates-disabled" + updateLatestVersion + fi + fi +else # linux + # update service links to tar.gz file + # see https://update.code.visualstudio.com/api/update/linux-x64/stable/VERSION + # as examples + ASSET_NAME="${APP_NAME}-linux-${VSCODE_ARCH}-${RELEASE_VERSION}.tar.gz" + VERSION_PATH="${VSCODE_QUALITY}/linux/${VSCODE_ARCH}" + updateLatestVersion +fi + +cd "${REPOSITORY_NAME}" || { echo "'${REPOSITORY_NAME}' dir not found"; exit 1; } + +git pull origin master # in case another build just pushed +git add . + +CHANGES=$( git status --porcelain ) + +if [[ -n "${CHANGES}" ]]; then + echo "Some changes have been found, pushing them" + + dateAndMonth=$( date "+%D %T" ) + + git commit -m "CI update: ${dateAndMonth} (Build ${GITHUB_RUN_NUMBER})" + + if ! git push origin master --quiet; then + git pull origin master + git push origin master --quiet + fi +else + echo "No changes" +fi + +cd .. From 02577422eac2aa0b5e646bef6ee3e96e952bc8cf Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 16 May 2026 04:29:01 +0200 Subject: [PATCH 39/79] feat(1.120): update patches (#2837) --- dev/patch.sh | 15 + dev/update_patches.sh | 15 +- patches/00-brand-remove-branding.patch | 134 +- patches/00-remote-add-url.patch | 6 +- patches/20-keymap-use-custom-lib.patch | 34 +- patches/21-policy-use-custom-lib.patch | 32 +- patches/40-cli-use-reh-archive.patch | 42 +- patches/50-build-improve-gulp-tasks.patch | 22 +- patches/51-ext-copilot-remove-it.json | 40 + patches/51-ext-copilot-remove-it.patch | 9034 ----------------- patches/52-ext-copilot-remove-it.patch | 491 + patches/build-replace-unicode.patch | 9 + patches/linux/00-log-use-state-home.patch | 63 +- patches/linux/31-build-fix-dependencies.patch | 267 +- patches/windows/71-build-disable-appx.patch | 16 +- prepare_vscode.sh | 8 +- product.json | 5 +- upstream/insider.json | 4 +- utils.sh | 26 + 19 files changed, 912 insertions(+), 9351 deletions(-) create mode 100644 patches/51-ext-copilot-remove-it.json delete mode 100644 patches/51-ext-copilot-remove-it.patch create mode 100644 patches/52-ext-copilot-remove-it.patch create mode 100644 patches/build-replace-unicode.patch diff --git a/dev/patch.sh b/dev/patch.sh index e6965c33ef0..da264c97abb 100755 --- a/dev/patch.sh +++ b/dev/patch.sh @@ -18,6 +18,9 @@ normalize_file() { cd vscode || { echo "'vscode' dir not found"; exit 1; } +# include common functions +. ../utils.sh + git add . git reset -q --hard HEAD @@ -69,6 +72,18 @@ if [[ "${FILE}" != "../patches/helper/settings.patch" ]]; then fi done + if (( $NOT_FOUND )); then + for CANDIDATE in "${DIRNAME}/${GROUP_ID}${I}-"*.json; do + if [[ -f "$CANDIDATE" ]]; then + echo "Candidate: ${CANDIDATE}" + + apply_actions "${CANDIDATE}" + + NOT_FOUND=0 + fi + done + fi + if (( $NOT_FOUND )); then for CANDIDATE in "${DIRNAME}/../${GROUP_ID}${I}-"*.patch; do if [[ -f "$CANDIDATE" ]]; then diff --git a/dev/update_patches.sh b/dev/update_patches.sh index 32f5e5bb176..c667cdac37c 100755 --- a/dev/update_patches.sh +++ b/dev/update_patches.sh @@ -137,6 +137,9 @@ check_file() { cd vscode || { echo "'vscode' dir not found"; exit 1; } +# include common functions +. ../utils.sh + git add . git reset -q --hard HEAD @@ -192,15 +195,23 @@ for ARCH in alpine linux osx windows; do for CANDIDATE in "${DIRNAME}/${GROUP_ID}${I}-"*.patch; do if [[ -f "$CANDIDATE" ]]; then - ADDITIONAL_FILES+=("$CANDIDATE") + ADDITIONAL_FILES+=( "${CANDIDATE}" ) NOT_FOUND=0 fi done + if (( $NOT_FOUND )); then + for CANDIDATE in "${DIRNAME}/../${GROUP_ID}${I}-"*.json; do + if [[ -f "$CANDIDATE" ]]; then + apply_actions "${CANDIDATE}" + fi + done + fi + if (( $NOT_FOUND )); then for CANDIDATE in "${DIRNAME}/../${GROUP_ID}${I}-"*.patch; do if [[ -f "$CANDIDATE" ]]; then - ADDITIONAL_FILES+=("$CANDIDATE") + ADDITIONAL_FILES+=( "${CANDIDATE}" ) fi done fi diff --git a/patches/00-brand-remove-branding.patch b/patches/00-brand-remove-branding.patch index e0b960b6fb9..8d6cc5fc823 100644 --- a/patches/00-brand-remove-branding.patch +++ b/patches/00-brand-remove-branding.patch @@ -1,5 +1,5 @@ diff --git a/build/win32/i18n/messages.en.isl b/build/win32/i18n/messages.en.isl -index a5cc5820..57b03db1 100644 +index 6255123d..dd33a446 100644 --- a/build/win32/i18n/messages.en.isl +++ b/build/win32/i18n/messages.en.isl @@ -16,2 +16,2 @@ SourceFile=%1 Source File @@ -7,7 +7,7 @@ index a5cc5820..57b03db1 100644 -UpdatingVisualStudioCode=Updating Visual Studio Code... +UpdatingVisualStudioCode=Updating !!APP_NAME!!... diff --git a/build/win32/i18n/messages.es.isl b/build/win32/i18n/messages.es.isl -index 66b7534a..4d4d360e 100644 +index 7f6f1bd0..97962525 100644 --- a/build/win32/i18n/messages.es.isl +++ b/build/win32/i18n/messages.es.isl @@ -9,2 +9,2 @@ SourceFile=Archivo de origen %1 @@ -17,7 +17,7 @@ index 66b7534a..4d4d360e 100644 +UpdatingVisualStudioCode=Actualizando !!APP_NAME!!... \ No newline at end of file diff --git a/build/win32/i18n/messages.it.isl b/build/win32/i18n/messages.it.isl -index bc238258..a94d91cb 100644 +index ac64aae7..dd65f740 100644 --- a/build/win32/i18n/messages.it.isl +++ b/build/win32/i18n/messages.it.isl @@ -9,2 +9,2 @@ SourceFile=File di origine %1 @@ -27,7 +27,7 @@ index bc238258..a94d91cb 100644 +UpdatingVisualStudioCode=Aggiornamento di !!APP_NAME!!... \ No newline at end of file diff --git a/build/win32/i18n/messages.pt-br.isl b/build/win32/i18n/messages.pt-br.isl -index e85aede3..76eda20e 100644 +index 08c77290..584ac529 100644 --- a/build/win32/i18n/messages.pt-br.isl +++ b/build/win32/i18n/messages.pt-br.isl @@ -9,2 +9,2 @@ SourceFile=Arquivo Fonte %1 @@ -320,15 +320,15 @@ index 30199b2b..008aed75 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 45df4704..0957c550 100644 +index dc8f6504..af461f85 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json -@@ -22,3 +22,3 @@ +@@ -29,3 +29,3 @@ "markdown.trace.extension.desc": "Enable debug logging for the Markdown extension.", - "markdown.trace.server.desc": "Traces the communication between VS Code and the Markdown language server.", + "markdown.trace.server.desc": "Traces the communication between !!APP_NAME!! and the Markdown language server.", "markdown.server.log.desc": "Controls the logging level of the Markdown language server.", -@@ -76,3 +76,3 @@ +@@ -83,3 +83,3 @@ "comment": [ - "This setting is use the user drops or pastes image data into the editor. In this case, VS Code automatically creates a new image file in the workspace containing the dropped/pasted image.", + "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.", @@ -343,16 +343,16 @@ index 920ced76..755a1669 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 282d579b..e3dfb4be 100644 +index 01aaf33a..47b7065d 100644 --- a/extensions/media-preview/src/audioPreview.ts +++ b/extensions/media-preview/src/audioPreview.ts -@@ -83,3 +83,3 @@ class AudioPreview extends MediaPreview { +@@ -85,3 +85,3 @@ class AudioPreview extends MediaPreview {

${vscode.l10n.t("An error occurred while loading the audio file.")}

- ${vscode.l10n.t("Open file using VS Code's standard text/binary editor?")} + ${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 6c2c8a73..064afc6d 100644 +index 79cb70c9..abe67371 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 { @@ -361,10 +361,10 @@ index 6c2c8a73..064afc6d 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 1cb74c58..3f5f8928 100644 +index 2ee1169f..0489215c 100644 --- a/extensions/media-preview/src/videoPreview.ts +++ b/extensions/media-preview/src/videoPreview.ts -@@ -87,3 +87,3 @@ class VideoPreview extends MediaPreview { +@@ -89,3 +89,3 @@ class VideoPreview extends MediaPreview {

${vscode.l10n.t("An error occurred while loading the video file.")}

- ${vscode.l10n.t("Open file using VS Code's standard text/binary editor?")} + ${vscode.l10n.t("Open file using !!APP_NAME!!'s standard text/binary editor?")} @@ -595,10 +595,10 @@ index 42f599c9..1f90b59e 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 e77eda91..e33f22ba 100644 +index d886313c..a76267f5 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts -@@ -552,3 +552,3 @@ export class CodeApplication extends Disposable { +@@ -550,3 +550,3 @@ export class CodeApplication extends Disposable { async startup(): Promise { - this.logService.debug('Starting VS Code'); + this.logService.debug('Starting !!APP_NAME!!'); @@ -662,16 +662,16 @@ index e7cf3f54..4424cc7f 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 27fd88b4..ad97d7b4 100644 +index cf059eab..16a69a47 100644 --- a/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts +++ b/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts -@@ -339,3 +339,3 @@ const terminalPlatformConfiguration: IConfigurationNode = { +@@ -396,3 +396,3 @@ const terminalPlatformConfiguration: IConfigurationNode = { scope: ConfigurationScope.APPLICATION, - description: localize('terminal.integrated.inheritEnv', "Whether new shells should inherit their environment from VS Code, which may source a login shell to ensure $PATH and other development variables are initialized. This has no effect on Windows."), + 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 6061e15b..69ca3707 100644 +index 2c63e9e6..db27f74e 100644 --- a/src/vs/platform/update/common/update.config.contribution.ts +++ b/src/vs/platform/update/common/update.config.contribution.ts @@ -70,3 +70,3 @@ configurationRegistry.registerConfiguration({ @@ -680,7 +680,7 @@ index 6061e15b..69ca3707 100644 + 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 e13d1ba5..7c411624 100644 +index 8cf5dceb..611001b3 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -74,3 +74,3 @@ export type UpdateErrorClassification = { @@ -762,10 +762,10 @@ index 0d713847..ae8d169b 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 91f52f5d..dbb0ad40 100644 +index c0e1300b..fc0a34f9 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 { +@@ -690,3 +690,3 @@ class PolicyDiagnosticsAction extends Action2 { - let content = '# VS Code Policy Diagnostics\n\n'; + let content = '# !!APP_NAME!! Policy Diagnostics\n\n'; @@ -789,39 +789,39 @@ index e342f838..7c314e6b 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 bdc603ff..fbb18cfe 100644 +index eeddba00..313f8b1f 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts -@@ -807,3 +807,3 @@ const registry = Registry.as(ConfigurationExtensions.Con +@@ -829,3 +829,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 73e207b8..7019013c 100644 +index c650e128..611a4282 100644 --- a/src/vs/workbench/common/contextkeys.ts +++ b/src/vs/workbench/common/contextkeys.ts -@@ -41,3 +41,3 @@ export const EmbedderIdentifierContext = new RawContextKey(' +@@ -40,3 +40,3 @@ export const EmbedderIdentifierContext = new RawContextKey(' -export const InAutomationContext = new RawContextKey('inAutomation', false, localize('inAutomation', "Whether VS Code is running under automation/smoke test")); +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 7e9c73f6..d576466a 100644 +index 08680900..f1881970 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts -@@ -636,3 +636,3 @@ configurationRegistry.registerConfiguration({ +@@ -720,3 +720,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.") -@@ -663,3 +663,3 @@ configurationRegistry.registerConfiguration({ +@@ -747,3 +747,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 ffc5b0ec..e441a041 100644 +index 019f314d..ff8318e9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts @@ -120,3 +120,3 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation { @@ -837,10 +837,10 @@ index ffc5b0ec..e441a041 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 f351c7f8..8cf4cab5 100644 +index 7f0e8c25..24148540 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptValidator.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptValidator.ts -@@ -263,5 +263,5 @@ export class PromptValidator { +@@ -291,5 +291,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.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])); @@ -848,17 +848,17 @@ index f351c7f8..8cf4cab5 100644 - 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 { +@@ -299,3 +299,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 { +@@ -305,3 +305,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 +@@ -1108,3 +1108,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. @@ -873,30 +873,30 @@ index 0b9ced32..731d9526 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 69bec710..f83a14cc 100644 +index 82c7072f..fe802bb5 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({ +@@ -383,3 +383,3 @@ CommandsRegistry.registerCommand({ description: '(optional) Options for installing the extension. Object with the following properties: ' + - '`installOnlyNewlyAddedFromExtensionPackVSIX`: When enabled, VS Code installs only newly added extensions from the extension pack VSIX. This option is considered only when installing VSIX. ', + '`installOnlyNewlyAddedFromExtensionPackVSIX`: When enabled, !!APP_NAME!! installs only newly added extensions from the extension pack VSIX. This option is considered only when installing VSIX. ', isOptional: true, -@@ -370,3 +370,3 @@ CommandsRegistry.registerCommand({ +@@ -390,3 +390,3 @@ CommandsRegistry.registerCommand({ 'type': 'boolean', - 'description': localize('workbench.extensions.installExtension.option.installOnlyNewlyAddedFromExtensionPackVSIX', "When enabled, VS Code installs only newly added extensions from the extension pack VSIX. This option is considered only while installing a VSIX."), + 'description': localize('workbench.extensions.installExtension.option.installOnlyNewlyAddedFromExtensionPackVSIX', "When enabled, !!APP_NAME!! installs only newly added extensions from the extension pack VSIX. This option is considered only while installing a VSIX."), default: false -@@ -375,3 +375,3 @@ CommandsRegistry.registerCommand({ +@@ -395,3 +395,3 @@ CommandsRegistry.registerCommand({ 'type': 'boolean', - 'description': localize('workbench.extensions.installExtension.option.installPreReleaseVersion', "When enabled, VS Code installs the pre-release version of the extension if available."), + 'description': localize('workbench.extensions.installExtension.option.installPreReleaseVersion', "When enabled, !!APP_NAME!! installs the pre-release version of the extension if available."), default: false -@@ -380,3 +380,3 @@ CommandsRegistry.registerCommand({ +@@ -400,3 +400,3 @@ CommandsRegistry.registerCommand({ 'type': 'boolean', - '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 -@@ -910,4 +910,4 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi +@@ -930,4 +930,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."), @@ -904,7 +904,7 @@ index 69bec710..f83a14cc 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 75896f1c..debf353a 100644 +index bdf319de..4fa098b5 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 { @@ -922,13 +922,13 @@ index 75896f1c..debf353a 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) { -@@ -2768,3 +2768,3 @@ export class ExtensionStatusAction extends ExtensionAction { +@@ -2810,3 +2810,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 58422886..fa26dc8a 100644 +index 49b3ec6a..5177602a 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 { @@ -1026,10 +1026,10 @@ index b533feb1..112d6952 100644 + `// ${nls.localize('doc', 'Open !!APP_NAME!! and run "Developer: Inspect Key Mappings (JSON)" from Command Palette.')}`, ``, diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts -index 808dcdd8..9539cf32 100644 +index 36209197..8c9d5b35 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts -@@ -3479,3 +3479,3 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer +@@ -3483,3 +3483,3 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (response.code && response.code === TerminateResponseCode.ProcessNotFound) { - this._notificationService.error(nls.localize('TerminateAction.noProcess', 'The launched process doesn\'t exist anymore. If the task spawned background tasks exiting VS Code might result in orphaned processes.')); + this._notificationService.error(nls.localize('TerminateAction.noProcess', 'The launched process doesn\'t exist anymore. If the task spawned background tasks exiting !!APP_NAME!! might result in orphaned processes.')); @@ -1108,50 +1108,50 @@ index 72a329b8..7992809f 100644 + Write-Host "`e[0m`e[7m * `e[0;103m !!APP_NAME!! Python powershell activation failed with exit code $($activationError.Exception.Message) `e[0m" } diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts -index 0f3708cc..bb6e950d 100644 +index fa6607bc..8dfffd3b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts -@@ -316,3 +316,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -322,3 +322,3 @@ const terminalConfiguration: IStringDictionary = { [TerminalSettingId.DetectLocale]: { - markdownDescription: localize('terminal.integrated.detectLocale', "Controls whether to detect and set the `$LANG` environment variable to a UTF-8 compliant option since VS Code's terminal only supports UTF-8 encoded data coming from the shell."), + markdownDescription: localize('terminal.integrated.detectLocale', "Controls whether to detect and set the `$LANG` environment variable to a UTF-8 compliant option since !!APP_NAME!!'s terminal only supports UTF-8 encoded data coming from the shell."), type: 'string', -@@ -330,3 +330,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -336,3 +336,3 @@ const terminalConfiguration: IStringDictionary = { markdownEnumDescriptions: [ - localize('terminal.integrated.gpuAcceleration.auto', "Let VS Code detect which renderer will give the best experience."), + localize('terminal.integrated.gpuAcceleration.auto', "Let !!APP_NAME!! detect which renderer will give the best experience."), localize('terminal.integrated.gpuAcceleration.on', "Enable GPU acceleration within the terminal."), -@@ -418,3 +418,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -424,3 +424,3 @@ const terminalConfiguration: IStringDictionary = { 'terminal.integrated.commandsToSkipShell', - "A set of command IDs whose keybindings will not be sent to the shell but instead always be handled by VS Code. This allows keybindings that would normally be consumed by the shell to act instead the same as when the terminal is not focused, for example `Ctrl+P` to launch Quick Open.\n\n \n\nMany commands are skipped by default. To override a default and pass that command's keybinding to the shell instead, add the command prefixed with the `-` character. For example add `-workbench.action.quickOpen` to allow `Ctrl+P` to reach the shell.\n\n \n\nThe following list of default skipped commands is truncated when viewed in Settings Editor. To see the full list, {1} and search for the first command from the list below.\n\n \n\nDefault Skipped Commands:\n\n{0}", + "A set of command IDs whose keybindings will not be sent to the shell but instead always be handled by !!APP_NAME!!. This allows keybindings that would normally be consumed by the shell to act instead the same as when the terminal is not focused, for example `Ctrl+P` to launch Quick Open.\n\n \n\nMany commands are skipped by default. To override a default and pass that command's keybinding to the shell instead, add the command prefixed with the `-` character. For example add `-workbench.action.quickOpen` to allow `Ctrl+P` to reach the shell.\n\n \n\nThe following list of default skipped commands is truncated when viewed in Settings Editor. To see the full list, {1} and search for the first command from the list below.\n\n \n\nDefault Skipped Commands:\n\n{0}", DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n'), -@@ -430,3 +430,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -436,3 +436,3 @@ const terminalConfiguration: IStringDictionary = { [TerminalSettingId.AllowChords]: { - markdownDescription: localize('terminal.integrated.allowChords', "Whether or not to allow chord keybindings in the terminal. Note that when this is true and the keystroke results in a chord it will bypass {0}, setting this to false is particularly useful when you want ctrl+k to go to your shell (not VS Code).", '`#terminal.integrated.commandsToSkipShell#`'), + markdownDescription: localize('terminal.integrated.allowChords', "Whether or not to allow chord keybindings in the terminal. Note that when this is true and the keystroke results in a chord it will bypass {0}, setting this to false is particularly useful when you want ctrl+k to go to your shell (not !!APP_NAME!!).", '`#terminal.integrated.commandsToSkipShell#`'), type: 'boolean', -@@ -441,3 +441,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -447,3 +447,3 @@ const terminalConfiguration: IStringDictionary = { restricted: true, - markdownDescription: localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the VS Code process to be used by the terminal on macOS. Set to `null` to delete the environment variable."), + markdownDescription: localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the !!APP_NAME!! process to be used by the terminal on macOS. Set to `null` to delete the environment variable."), type: 'object', -@@ -450,3 +450,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -456,3 +456,3 @@ const terminalConfiguration: IStringDictionary = { restricted: true, - markdownDescription: localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Linux. Set to `null` to delete the environment variable."), + markdownDescription: localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the !!APP_NAME!! process to be used by the terminal on Linux. Set to `null` to delete the environment variable."), type: 'object', -@@ -459,3 +459,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -465,3 +465,3 @@ const terminalConfiguration: IStringDictionary = { restricted: true, - markdownDescription: localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Windows. Set to `null` to delete the environment variable."), + markdownDescription: localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the !!APP_NAME!! process to be used by the terminal on Windows. Set to `null` to delete the environment variable."), type: 'object', -@@ -478,3 +478,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -484,3 +484,3 @@ const terminalConfiguration: IStringDictionary = { restricted: true, - 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', -@@ -608,3 +608,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -614,3 +614,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#`'), @@ -1193,17 +1193,17 @@ index 0f23e5a5..27f311e2 100644 + message = localize('terminal.voice.installSpeechExtension', "Would you like to install '!!APP_NAME!! Speech' extension from 'Microsoft'?"); run = () => 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 2d6b6764..db7b70f5 100644 +index c182b9c2..7090ac01 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts -@@ -396,4 +396,4 @@ export class SwitchProductQualityContribution extends Disposable implements IWor +@@ -385,4 +385,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") -@@ -432,3 +432,3 @@ export class SwitchProductQualityContribution extends Disposable implements IWor +@@ -421,3 +421,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."), @@ -1223,7 +1223,7 @@ index 393c8c36..9268a6c2 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 5e452010..9402651b 100644 +index 067580a4..591d9b0d 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 { @@ -1246,7 +1246,7 @@ index 297598ef..1fc5b45c 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 40e38cd8..7c0ff9dd 100644 +index 79eae7a1..7c0ff9dd 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -200,24 +200,2 @@ export const startEntries: GettingStartedStartEntryContent = [ @@ -1315,7 +1315,7 @@ index 40e38cd8..7c0ff9dd 100644 steps: [ - createCopilotSetupStep('CopilotSetupAnonymous', CopilotAnonymousButton, 'chatAnonymous && !chatSetupCompleted', true), - createCopilotSetupStep('CopilotSetupSignedOut', CopilotSignedOutButton, 'chatEntitlementSignedOut && !chatAnonymous', false), -- createCopilotSetupStep('CopilotSetupComplete', CopilotCompleteButton, 'chatSetupCompleted && !chatSetupDisabled && (chatAnonymous || chatPlanPro || chatPlanProPlus || chatPlanBusiness || chatPlanEnterprise || chatPlanFree)', false), +- createCopilotSetupStep('CopilotSetupComplete', CopilotCompleteButton, 'chatSetupCompleted && !chatSetupDisabled && (chatAnonymous || chatPlanPro || chatPlanProPlus || chatPlanMax || chatPlanBusiness || chatPlanEnterprise || chatPlanFree)', false), - createCopilotSetupStep('CopilotSetupSignedIn', CopilotSignedInButton, '!chatEntitlementSignedOut && (!chatSetupCompleted || chatSetupDisabled || chatPlanCanSignUp)', false), { @@ -280,4 +230,4 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ @@ -1402,7 +1402,7 @@ index 40e38cd8..7c0ff9dd 100644 + type: 'svg', altText: '!!APP_NAME!! extension marketplace with featured language extensions', path: 'extensions.svg' }, diff --git a/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts b/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts -index c9d06f40..eb2117e4 100644 +index a77b2cdb..9e2f08d3 100644 --- a/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts +++ b/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts @@ -173,3 +173,3 @@ export class OnboardingVariationA extends Disposable implements IOnboardingServi @@ -1435,29 +1435,29 @@ index bdd30bf2..317d11c8 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 d1345355..93cae5fd 100644 +index 64b8586a..dc331c17 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) +@@ -801,3 +801,3 @@ Registry.as(ConfigurationExtensions.Configuration) default: true, - description: localize('workspace.trust.description', "Controls whether or not Workspace Trust is enabled within VS Code."), + description: localize('workspace.trust.description', "Controls whether or not Workspace Trust is enabled within !!APP_NAME!!."), tags: [WORKSPACE_TRUST_SETTING_TAG], -@@ -811,3 +811,3 @@ Registry.as(ConfigurationExtensions.Configuration) +@@ -847,3 +847,3 @@ Registry.as(ConfigurationExtensions.Configuration) default: true, - markdownDescription: localize('workspace.trust.emptyWindow.description', "Controls whether or not the empty window is trusted by default within VS Code. When used with `#{0}#`, you can enable the full functionality of VS Code without prompting in an empty window.", WORKSPACE_TRUST_UNTRUSTED_FILES), + 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 928e7ea2..2b474fc4 100644 +index 885cbf9b..0cc63956 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts -@@ -449,3 +449,3 @@ import product from '../../platform/product/common/product.js'; +@@ -452,3 +452,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.") }, -@@ -453,3 +453,3 @@ import product from '../../platform/product/common/product.js'; +@@ -456,3 +456,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/patches/00-remote-add-url.patch b/patches/00-remote-add-url.patch index d0e287d72fc..bd7b4aab86f 100644 --- a/patches/00-remote-add-url.patch +++ b/patches/00-remote-add-url.patch @@ -8,10 +8,10 @@ index 3c0e3a16..cb47f50b 100644 + .pipe(jsonEditor({ commit, date: readISODate(sourceFolderName), version, serverDownloadUrlTemplate: 'https://github.com/!!ASSETS_REPOSITORY!!/releases/download/!!RELEASE_VERSION!!/!!APP_NAME_LC!!-reh-${os}-${arch}-!!RELEASE_VERSION!!.tar.gz' })) .pipe(es.through(function (file) { diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index 028938bd..765da3f4 100644 +index a636b48c..0d85c1e4 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -380,2 +380,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d +@@ -309,2 +309,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d json.version = version; + json.serverDownloadUrlTemplate = 'https://github.com/!!ASSETS_REPOSITORY!!/releases/download/!!RELEASE_VERSION!!/!!APP_NAME_LC!!-reh-${os}-${arch}-!!RELEASE_VERSION!!.tar.gz'; - if (embedded) { + return json; diff --git a/patches/20-keymap-use-custom-lib.patch b/patches/20-keymap-use-custom-lib.patch index a67374162c4..ef9134f9265 100644 --- a/patches/20-keymap-use-custom-lib.patch +++ b/patches/20-keymap-use-custom-lib.patch @@ -1,5 +1,5 @@ diff --git a/.npmrc b/.npmrc -index 8255ca3e..98dc0d47 100644 +index 025e4b04..5e409d87 100644 --- a/.npmrc +++ b/.npmrc @@ -6,2 +6,3 @@ ignore-scripts=false @@ -7,7 +7,7 @@ index 8255ca3e..98dc0d47 100644 +build_from_source_native_keymap="no" legacy-peer-deps="true" diff --git a/build/.moduleignore b/build/.moduleignore -index f83624f8..aaa384bf 100644 +index d1a8a227..b7209206 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -65,7 +65,7 @@ fsevents/test/** @@ -24,27 +24,27 @@ index f83624f8..aaa384bf 100644 +!@vscodium/native-keymap/build/Release/*.node diff --git a/eslint.config.js b/eslint.config.js -index 51bda2c3..494a2c2c 100644 +index dad155b9..91ed6530 100644 --- a/eslint.config.js +++ b/eslint.config.js -@@ -1526,3 +1526,3 @@ export default tseslint.config( +@@ -1518,3 +1518,3 @@ export default tseslint.config( 'node:module', - 'native-keymap', + '@vscodium/native-keymap', 'net', diff --git a/package-lock.json b/package-lock.json -index 75b98719..88c2aee4 100644 +index 96150298..caa581eb 100644 --- a/package-lock.json +++ b/package-lock.json -@@ -39,2 +39,3 @@ +@@ -41,2 +41,3 @@ "@vscode/windows-registry": "^1.2.0", + "@vscodium/native-keymap": "3.3.7-258424", - "@xterm/addon-clipboard": "^0.3.0-beta.197", -@@ -56,3 +57,2 @@ + "@xterm/addon-clipboard": "^0.3.0-beta.213", +@@ -59,3 +60,2 @@ "native-is-elevated": "0.9.0", - "native-keymap": "^3.3.5", "node-pty": "^1.2.0-beta.12", -@@ -4168,2 +4168,9 @@ +@@ -4368,2 +4368,9 @@ }, + "node_modules/@vscodium/native-keymap": { + "version": "3.3.7-258424", @@ -53,8 +53,8 @@ index 75b98719..88c2aee4 100644 + "hasInstallScript": true, + "license": "MIT" + }, - "node_modules/@wdio/config": { -@@ -14280,5 +14287,6 @@ + "node_modules/@webgpu/types": { +@@ -14103,5 +14110,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 75b98719..88c2aee4 100644 + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" }, -@@ -14291,9 +14299,2 @@ +@@ -14114,9 +14122,2 @@ }, - "node_modules/native-keymap": { - "version": "3.3.9", @@ -74,7 +74,7 @@ index 75b98719..88c2aee4 100644 - "license": "MIT" - }, "node_modules/natural-compare": { -@@ -15805,5 +15806,6 @@ +@@ -15517,5 +15518,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 75b98719..88c2aee4 100644 + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", "dependencies": { -@@ -15814,3 +15816,3 @@ +@@ -15526,3 +15528,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 b925a690..166f0cae 100644 +index 79f619ca..7f89f523 100644 --- a/package.json +++ b/package.json -@@ -133,3 +133,3 @@ +@@ -136,3 +136,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.12", diff --git a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts -index c30c6da5..ca6cea22 100644 +index 50360a79..93df77af 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)', () => { diff --git a/patches/21-policy-use-custom-lib.patch b/patches/21-policy-use-custom-lib.patch index 35bf3732c2a..01c2a5b8df7 100644 --- a/patches/21-policy-use-custom-lib.patch +++ b/patches/21-policy-use-custom-lib.patch @@ -1,5 +1,5 @@ diff --git a/build/.moduleignore b/build/.moduleignore -index aaa384bf..25adcdbf 100644 +index b7209206..f6f1f761 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -153,9 +153,11 @@ vsda/** @@ -65,27 +65,27 @@ index 47b485d1..8437fd47 100644 + !!ORG_NAME!! PayloadType diff --git a/eslint.config.js b/eslint.config.js -index 494a2c2c..c53b46aa 100644 +index 91ed6530..ef68a90d 100644 --- a/eslint.config.js +++ b/eslint.config.js -@@ -1509,3 +1509,3 @@ export default tseslint.config( +@@ -1500,3 +1500,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 88c2aee4..d8852aa8 100644 +index caa581eb..8244642a 100644 --- a/package-lock.json +++ b/package-lock.json -@@ -28,3 +28,2 @@ +@@ -29,3 +29,2 @@ "@vscode/native-watchdog": "^1.4.6", - "@vscode/policy-watcher": "^1.3.2", "@vscode/proxy-agent": "^0.41.0", -@@ -40,2 +39,3 @@ +@@ -42,2 +41,3 @@ "@vscodium/native-keymap": "3.3.7-258424", + "@vscodium/policy-watcher": "^1.3.2-252465", - "@xterm/addon-clipboard": "^0.3.0-beta.197", -@@ -3844,22 +3844,2 @@ + "@xterm/addon-clipboard": "^0.3.0-beta.213", +@@ -4017,22 +4017,2 @@ }, - "node_modules/@vscode/policy-watcher": { - "version": "1.3.7", @@ -108,7 +108,7 @@ index 88c2aee4..d8852aa8 100644 - } - }, "node_modules/@vscode/proxy-agent": { -@@ -4175,2 +4155,22 @@ +@@ -4375,2 +4355,22 @@ }, + "node_modules/@vscodium/policy-watcher": { + "version": "1.3.2-255408", @@ -122,20 +122,20 @@ index 88c2aee4..d8852aa8 100644 + } + }, + "node_modules/@vscodium/policy-watcher/node_modules/node-addon-api": { -+ "version": "8.6.0", -+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.6.0.tgz", -+ "integrity": "sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==", ++ "version": "8.7.0", ++ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", ++ "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, - "node_modules/@wdio/config": { + "node_modules/@webgpu/types": { diff --git a/package.json b/package.json -index 166f0cae..51cd5977 100644 +index 7f89f523..8c411b1e 100644 --- a/package.json +++ b/package.json -@@ -105,3 +105,3 @@ +@@ -106,3 +106,3 @@ "@vscode/native-watchdog": "^1.4.6", - "@vscode/policy-watcher": "^1.3.2", + "@vscodium/policy-watcher": "^1.3.2-252465", @@ -240,7 +240,7 @@ index ee0a24b5..881ce36a 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 ca6cea22..32b22fed 100644 +index 93df77af..69989991 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)', () => { diff --git a/patches/40-cli-use-reh-archive.patch b/patches/40-cli-use-reh-archive.patch index 5ff88785c8d..afcc6af2150 100644 --- a/patches/40-cli-use-reh-archive.patch +++ b/patches/40-cli-use-reh-archive.patch @@ -1,19 +1,19 @@ diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs -index d3a9a88a..7cf60f0b 100644 +index 74f2abdb..4d5d6bce 100644 --- a/cli/src/commands/serve_web.rs +++ b/cli/src/commands/serve_web.rs -@@ -756,3 +756,3 @@ impl ConnectionManager { +@@ -792,3 +792,3 @@ impl ConnectionManager { let dir_fut = cache.create(&args.release.commit, |target_dir| async move { - info!(log_for_fut, "Downloading server {}", release_for_fut.commit); + info!(log_for_fut, "Downloading server {}/{}", release_for_fut.commit, release_for_fut.name); let tmpdir = tempfile::tempdir().unwrap(); -@@ -784,3 +784,3 @@ impl ConnectionManager { +@@ -820,3 +820,3 @@ impl ConnectionManager { .join("bin") - .join(args.release.quality.server_entrypoint()); + .join(args.release.quality.server_entrypoint().unwrap()); diff --git a/cli/src/constants.rs b/cli/src/constants.rs -index 9e2b066d..974ee57c 100644 +index f95cc77a..9a817c0c 100644 --- a/cli/src/constants.rs +++ b/cli/src/constants.rs @@ -37,3 +37,6 @@ pub const DOCUMENTATION_URL: Option<&'static str> = option_env!("VSCODE_CLI_DOCU @@ -58,19 +58,19 @@ index 7d152c0e..c0f2fb2e 100644 + Ok(server_name) } diff --git a/cli/src/tunnels/agent_host.rs b/cli/src/tunnels/agent_host.rs -index 9d1f240c..2e67da43 100644 +index 2d50c926..11a349cb 100644 --- a/cli/src/tunnels/agent_host.rs +++ b/cli/src/tunnels/agent_host.rs -@@ -162,3 +162,3 @@ impl AgentHostManager { +@@ -166,3 +166,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 ffabbad1..62f7d3cc 100644 +index 3e4bb9eb..b4440f18 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs -@@ -462,3 +462,3 @@ impl<'a> ServerBuilder<'a> { +@@ -460,3 +460,3 @@ impl<'a> ServerBuilder<'a> { .join("bin") - .join(self.server_params.release.quality.server_entrypoint()), + .join(self.server_params.release.quality.server_entrypoint().unwrap()), @@ -85,7 +85,7 @@ index 3d7d718a..98529bc6 100644 + .join(self.quality.server_entrypoint().unwrap()) }, diff --git a/cli/src/update_service.rs b/cli/src/update_service.rs -index de977b73..97b8b5fa 100644 +index 1070dac0..db064277 100644 --- a/cli/src/update_service.rs +++ b/cli/src/update_service.rs @@ -10,3 +10,3 @@ use serde::{Deserialize, Serialize}; @@ -111,8 +111,8 @@ index de977b73..97b8b5fa 100644 fn get_update_endpoint() -> Result { @@ -66,3 +74,3 @@ fn get_update_endpoint() -> Result { .map(|s| s.to_string()) -- .ok_or_else(|| CodeError::UpdatesNotConfigured("no service url")) -+ .ok_or_else(|| CodeError::UpdatesNotConfigured("no update url")) +- .ok_or(CodeError::UpdatesNotConfigured("no service url")) ++ .ok_or(CodeError::UpdatesNotConfigured("no update url")) } @@ -74,3 +82,4 @@ impl UpdateService { @@ -138,12 +138,12 @@ index de977b73..97b8b5fa 100644 + platform.os(), + platform.arch(), ); -@@ -104,3 +109,3 @@ impl UpdateService { +@@ -100,3 +105,3 @@ impl UpdateService { let res = response.json::().await?; - debug!(self.log, "Resolved version {} to {}", version, res.version); + debug!(self.log, "Resolved quality {} to {}", quality, res.version); -@@ -115,40 +120,17 @@ impl UpdateService { +@@ -111,36 +116,17 @@ impl UpdateService { - /// Gets the latest commit for the target of the given quality. - pub async fn get_latest_commit( @@ -174,11 +174,7 @@ index de977b73..97b8b5fa 100644 + release.name, ); -- let mut response = spanf!( -- self.log, -- self.log.span("server.version.resolve"), -- self.client.make_request("GET", download_url) -- )?; +- let mut response = self.client.make_request("GET", download_url).await?; - - if !response.status_code.is_success() { - return Err(response.into_err().await.into()); @@ -196,7 +192,7 @@ index de977b73..97b8b5fa 100644 - }) + Ok(download_url) } -@@ -157,15 +139,3 @@ impl UpdateService { +@@ -149,15 +135,3 @@ impl UpdateService { pub async fn get_download_stream(&self, release: &Release) -> Result { - let update_endpoint = get_update_endpoint()?; - let download_segment = release @@ -213,7 +209,7 @@ index de977b73..97b8b5fa 100644 - ); + let download_url = self.get_download_url(release)?; -@@ -203,13 +173,2 @@ pub enum TargetKind { +@@ -195,13 +169,2 @@ pub enum TargetKind { -impl TargetKind { - fn download_segment(&self, platform: Platform) -> Option { @@ -227,7 +223,7 @@ index de977b73..97b8b5fa 100644 -} - #[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] -@@ -232,30 +191,17 @@ pub enum Platform { +@@ -224,30 +187,17 @@ pub enum Platform { impl Platform { - pub fn archive(&self) -> Option { - match self { @@ -272,7 +268,7 @@ index de977b73..97b8b5fa 100644 + Platform::WindowsX86 => "ia42", + Platform::WindowsARM64 => "arm64", } -@@ -264,17 +210,17 @@ impl Platform { +@@ -256,17 +206,17 @@ impl Platform { - pub fn cli(&self) -> String { + pub fn os(&self) -> String { @@ -304,7 +300,7 @@ index de977b73..97b8b5fa 100644 + Platform::WindowsX86 => "win32", + Platform::WindowsARM64 => "win32", } -@@ -283,6 +229,2 @@ impl Platform { +@@ -275,6 +225,2 @@ impl Platform { - pub fn web(&self) -> String { - format!("{}-web", self.headless()) diff --git a/patches/50-build-improve-gulp-tasks.patch b/patches/50-build-improve-gulp-tasks.patch index 22364eb5d25..b133513ea68 100644 --- a/patches/50-build-improve-gulp-tasks.patch +++ b/patches/50-build-improve-gulp-tasks.patch @@ -1,5 +1,5 @@ diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index 080d97f3..93b5f711 100644 +index a636b48c..25aa6a57 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts @@ -30,3 +30,3 @@ import { createAsar } from './lib/asar.ts'; @@ -7,24 +7,20 @@ index 080d97f3..93b5f711 100644 -import { compileBuildWithoutManglingTask, compileBuildWithManglingTask } from './gulpfile.compile.ts'; +import { compileBuildWithoutManglingTask } from './gulpfile.compile.ts'; import { compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileAllExtensionsBuildTask, compileExtensionMediaBuildTask, cleanExtensionsBuildTask, compileCopilotExtensionBuildTask } from './gulpfile.extensions.ts'; -@@ -197,3 +197,2 @@ function runEsbuildBundle(outDir: string, minify: boolean, nls: boolean, target: - args.push('--minify'); -- args.push('--mangle-privates'); - } -@@ -757,3 +756,4 @@ BUILD_TARGETS.forEach(buildTarget => { +@@ -623,3 +623,4 @@ BUILD_TARGETS.forEach(buildTarget => { ); - 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 => { +@@ -629,2 +630,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 => { +@@ -633,5 +638,11 @@ BUILD_TARGETS.forEach(buildTarget => { )); - } else { + gulp.task(packingTask); @@ -38,7 +34,7 @@ index 080d97f3..93b5f711 100644 + const prepackTask = task.define(`vscode${dashed(minified)}-prepack`, task.series( + compileBuildWithoutManglingTask, cleanExtensionsBuildTask, -@@ -775,4 +785,14 @@ BUILD_TARGETS.forEach(buildTarget => { +@@ -641,4 +652,14 @@ BUILD_TARGETS.forEach(buildTarget => { minified ? minifyVSCodeTask : bundleVSCodeTask, + )); + gulp.task(prepackTask); @@ -53,3 +49,11 @@ index 080d97f3..93b5f711 100644 + packingTask, + )); } +diff --git a/build/lib/esbuild.ts b/build/lib/esbuild.ts +index d066767b..0d09e32a 100644 +--- a/build/lib/esbuild.ts ++++ b/build/lib/esbuild.ts +@@ -42,3 +42,2 @@ export function runEsbuildBundle(outDir: string, minify: boolean, nls: boolean, + args.push('--minify'); +- args.push('--mangle-privates'); + } diff --git a/patches/51-ext-copilot-remove-it.json b/patches/51-ext-copilot-remove-it.json new file mode 100644 index 00000000000..9499fa26af0 --- /dev/null +++ b/patches/51-ext-copilot-remove-it.json @@ -0,0 +1,40 @@ +[ + { + "action": "remove", + "paths": [ + "extensions/copilot", + "src/vs/platform/agentHost/node/claude/anthropicErrors.ts", + "src/vs/platform/agentHost/node/claude/claudeAgent.ts", + "src/vs/platform/agentHost/node/claude/claudeAgentSdkService.ts", + "src/vs/platform/agentHost/node/claude/claudeAgentSession.ts", + "src/vs/platform/agentHost/node/claude/claudeMapSessionEvents.ts", + "src/vs/platform/agentHost/node/claude/claudePromptResolver.ts", + "src/vs/platform/agentHost/node/claude/claudeProxyService.ts", + "src/vs/platform/agentHost/node/copilot/copilotAgent.ts", + "src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts", + "src/vs/platform/agentHost/node/copilot/copilotGitProject.ts", + "src/vs/platform/agentHost/node/copilot/copilotPluginConverters.ts", + "src/vs/platform/agentHost/node/copilot/copilotSessionWrapper.ts", + "src/vs/platform/agentHost/node/copilot/copilotShellTools.ts", + "src/vs/platform/agentHost/node/copilot/copilotToolDisplay.ts", + "src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts", + "src/vs/platform/agentHost/node/shared/copilotApiService.ts", + "src/vs/platform/agentHost/test/node/agentService.test.ts", + "src/vs/platform/agentHost/test/node/agentSideEffects.test.ts", + "src/vs/platform/agentHost/test/node/claudeAgent.integrationTest.ts", + "src/vs/platform/agentHost/test/node/claudeAgent.test.ts", + "src/vs/platform/agentHost/test/node/claudeMapSessionEvents.test.ts", + "src/vs/platform/agentHost/test/node/claudeMapSessionEventsTestUtils.ts", + "src/vs/platform/agentHost/test/node/claudeProxyService.test.ts", + "src/vs/platform/agentHost/test/node/copilotAgent.test.ts", + "src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts", + "src/vs/platform/agentHost/test/node/copilotGitProject.test.ts", + "src/vs/platform/agentHost/test/node/copilotPluginConverters.test.ts", + "src/vs/platform/agentHost/test/node/copilotShellTools.test.ts", + "src/vs/platform/agentHost/test/node/copilotToolDisplay.test.ts", + "src/vs/platform/agentHost/test/node/historyRecordFixtures.test.ts", + "src/vs/platform/agentHost/test/node/historyRecordFixtures.ts", + "src/vs/platform/agentHost/test/node/shared/copilotApiService.test.ts" + ] + } +] diff --git a/patches/51-ext-copilot-remove-it.patch b/patches/51-ext-copilot-remove-it.patch deleted file mode 100644 index b61d395d22d..00000000000 --- a/patches/51-ext-copilot-remove-it.patch +++ /dev/null @@ -1,9034 +0,0 @@ -diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts -index 3c0e3a16..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, prepareBuiltInCopilotRipgrepShim } 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,12 +463,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { - --function prepareCopilotRipgrepShimTaskREH(platform: string, arch: string, destinationFolderName: string) { -- return async () => { -- const outputDir = path.join(BUILD_ROOT, destinationFolderName); -- const nodeModulesDir = path.join(outputDir, 'node_modules'); -- -- const builtInCopilotExtensionDir = path.join(outputDir, 'extensions', 'copilot'); -- prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, nodeModulesDir); -- }; --} -- - /** -@@ -524,3 +512,2 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { - packageTask(type, platform, arch, sourceFolderName, destinationFolderName), -- prepareCopilotRipgrepShimTaskREH(platform, arch, destinationFolderName) - ]; -@@ -538,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 6b366a16..1984fce5 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, prepareBuiltInCopilotRipgrepShim } from './lib/copilot.ts'; - import type { EmbeddedProductInfo } from './lib/embeddedType.ts'; -@@ -438,3 +437,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) -@@ -445,3 +443,2 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d - '**/@vscode/ripgrep/bin/*', -- '**/@github/copilot-*/**', - '**/node-pty/build/Release/*', -@@ -683,19 +680,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { - --function prepareCopilotRipgrepShimTask(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'); -- -- const builtInCopilotExtensionDir = path.join(appBase, 'extensions', 'copilot'); -- prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, appNodeModulesDir); -- }; --} -- - const buildRoot = path.dirname(root); -@@ -725,3 +705,2 @@ BUILD_TARGETS.forEach(buildTarget => { - packageTask(platform, arch, sourceFolderName, destinationFolderName, opts), -- prepareCopilotRipgrepShimTask(platform, arch, destinationFolderName) - ]; -@@ -752,3 +731,2 @@ BUILD_TARGETS.forEach(buildTarget => { - compileNonNativeExtensionsBuildTask, -- compileCopilotExtensionBuildTask, - compileExtensionMediaBuildTask, -@@ -773,3 +751,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 dc154d4b..834a4a78 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 69695f7e..df0fe9d4 100644 ---- a/package-lock.json -+++ b/package-lock.json -@@ -2431,8 +2431,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": { -@@ -17227,2 +17221,3 @@ - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", -+ "dev": true, - "license": "MIT", -@@ -19604,11 +19599,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": { -@@ -20322,2 +20308,3 @@ - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", -+ "dev": true, - "license": "MIT", -diff --git a/package.json b/package.json -index e5549c39..c4eee403 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", -@@ -83,4 +80,2 @@ - "perf:chat-leak": "node scripts/chat-simulation/test-chat-mem-leaks.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)", -@@ -91,5 +86,2 @@ - "dependencies": { -- "@anthropic-ai/sandbox-runtime": "0.0.49", -- "@github/copilot": "^1.0.34", -- "@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 e40bbb51..89931c4d 100644 ---- a/src/vs/platform/agentHost/node/agentHostMain.ts -+++ b/src/vs/platform/agentHost/node/agentHostMain.ts -@@ -19,3 +19,2 @@ import { AgentService } from './agentService.js'; - import { IAgentHostTerminalManager } from './agentHostTerminalManager.js'; --import { CopilotAgent } from './copilot/copilotAgent.js'; - import { ProtocolServerHandler } from './protocolServerHandler.js'; -@@ -108,3 +107,2 @@ function startAgentHost(): void { - diServices.set(IAgentHostTerminalManager, agentService.terminalManager); -- 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 6bf63cdb..e307f639 100644 ---- a/src/vs/platform/agentHost/node/agentHostServerMain.ts -+++ b/src/vs/platform/agentHost/node/agentHostServerMain.ts -@@ -32,3 +32,2 @@ import { InstantiationService } from '../../instantiation/common/instantiationSe - import { ServiceCollection } from '../../instantiation/common/serviceCollection.js'; --import { CopilotAgent } from './copilot/copilotAgent.js'; - import { AgentService } from './agentService.js'; -@@ -189,5 +188,2 @@ async function main(): Promise { - diServices.set(IAgentHostGitService, gitService); -- const copilotAgent = disposables.add(instantiationService.createInstance(CopilotAgent)); -- agentService.registerProvider(copilotAgent); -- log('CopilotAgent registered'); - } -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 14b27128..00000000 ---- a/src/vs/platform/agentHost/node/copilot/copilotAgent.ts -+++ /dev/null -@@ -1,1288 +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, ResumeSessionConfig, type CopilotClientOptions, type SessionConfig } from '@github/copilot-sdk'; --import { rgPath } from '@vscode/ripgrep'; --import * as fs from 'fs/promises'; --import { Limiter, SequencerByKey } from '../../../../base/common/async.js'; --import { Emitter } from '../../../../base/common/event.js'; --import { appendEscapedMarkdownInlineCode } from '../../../../base/common/htmlContent.js'; --import { Disposable, DisposableMap } from '../../../../base/common/lifecycle.js'; --import { ResourceMap } from '../../../../base/common/map.js'; --import { FileAccess } from '../../../../base/common/network.js'; --import { equals } from '../../../../base/common/objects.js'; --import { observableValue } from '../../../../base/common/observable.js'; --import { basename, delimiter, dirname } from '../../../../base/common/path.js'; --import { URI } from '../../../../base/common/uri.js'; --import { generateUuid } from '../../../../base/common/uuid.js'; --import { localize } from '../../../../nls.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, IAgentDeltaEvent, IAgentMessageEvent, IAgentModelInfo, IAgentProgressEvent, IAgentResolveSessionConfigParams, IAgentSessionConfigCompletionsParams, IAgentSessionMetadata, IAgentSessionProjectInfo, SessionHistoryEvent } from '../../common/agentService.js'; --import { AutoApproveLevel, ISchemaProperty, createSchema, platformSessionSchema, schemaProperty } from '../../common/agentHostSchema.js'; --import { SessionConfigKey } from '../../common/sessionConfigKeys.js'; --import { ISessionDataService, SESSION_DB_FILENAME } from '../../common/sessionDataService.js'; --import type { ResolveSessionConfigResult, SessionConfigCompletionsResult } from '../../common/state/protocol/commands.js'; --import { ProtectedResourceMetadata, type ConfigSchema, type ModelSelection, type ToolDefinition } from '../../common/state/protocol/state.js'; --import { AHP_AUTH_REQUIRED, ProtocolError } from '../../common/state/sessionProtocol.js'; --import { CustomizationStatus, CustomizationRef, SessionInputResponseKind, type PendingMessage, type SessionInputAnswer, type ToolCallResult, type PolicyState } from '../../common/state/sessionState.js'; --import { IAgentHostGitService, META_DIFF_BASE_BRANCH } from '../agentHostGitService.js'; --import { IAgentHostTerminalManager } from '../agentHostTerminalManager.js'; --import { CopilotAgentSession, SessionWrapperFactory, type IActiveClientSnapshot } from './copilotAgentSession.js'; --import { ICopilotSessionContext, projectFromCopilotContext } from './copilotGitProject.js'; --import { parsedPluginsEqual, toSdkCustomAgents, toSdkHooks, toSdkMcpServers, toSdkSkillDirectories } from './copilotPluginConverters.js'; --import { CopilotSessionWrapper } from './copilotSessionWrapper.js'; --import { createShellTools, ShellManager } from './copilotShellTools.js'; -- --interface ICreatedWorktree { -- readonly repositoryRoot: URI; -- readonly worktree: URI; --} -- --const ThinkingLevelConfigKey = 'thinkingLevel'; --const ReasoningEfforts = ['low', 'medium', 'high', 'xhigh'] as const; --type ReasoningEffort = NonNullable; -- --interface ISerializedModelSelection { -- id?: unknown; -- config?: unknown; --} -- --/** -- * Narrow surface of {@link CopilotClient} used by this provider. The SDK class -- * has private members, so tests use this structural type to inject a fake. -- */ --export interface ICopilotClient { -- start(): Promise; -- stop: CopilotClient['stop']; -- listSessions: CopilotClient['listSessions']; -- listModels: () => Promise; -- createSession: CopilotClient['createSession']; -- resumeSession: CopilotClient['resumeSession']; -- getSessionMetadata: CopilotClient['getSessionMetadata']; -- readonly rpc: { readonly sessions: { readonly fork: CopilotClient['rpc']['sessions']['fork'] } }; --} -- --/** -- * Corrected shape of {@link CopilotClient.listModels} entries. -- * -- * The SDK's `ModelInfo` type declares `capabilities`, `capabilities.limits`, -- * and `capabilities.limits.max_context_window_tokens` as required, but at -- * runtime synthetic entries (e.g. the `auto` router) ship with an empty -- * `capabilities: {}` object. We mirror the SDK fields we consume but mark the -- * unreliable parts as optional so callers must defensively handle them. -- */ --export interface ICopilotModelInfo { -- readonly id: string; -- readonly name: string; -- readonly capabilities?: { -- readonly supports?: { readonly vision?: boolean }; -- readonly limits?: { readonly max_context_window_tokens?: number }; -- }; -- readonly policy?: { readonly state?: string }; -- readonly supportedReasoningEfforts?: readonly string[]; -- readonly defaultReasoningEffort?: string; --} -- --function isReasoningEffort(value: string | undefined): value is ReasoningEffort { -- return ReasoningEfforts.some(reasoningEffort => reasoningEffort === value); --} -- --export function getCopilotWorktreesRoot(repositoryRoot: URI): URI { -- return URI.joinPath(repositoryRoot, '..', `${basename(repositoryRoot.fsPath)}.worktrees`); --} -- --export function getCopilotWorktreeName(branchName: string): string { -- return branchName.replace(/\//g, '-'); --} -- --export function getCopilotWorktreeBranchName(sessionId: string, branchNameHint: string | undefined): string { -- return `agents/${branchNameHint ? `${branchNameHint}-${sessionId.substring(0, 8)}` : sessionId}`; --} -- --/** -- * Builds the localized "Created isolated worktree for branch X" markdown -- * shown at the top of the first response in worktree-isolated sessions. -- * The branch name is wrapped as inline code so the localized template -- * doesn't have to embed markdown punctuation. The trailing blank line -- * keeps the announcement visually separated when it gets merged into the -- * same markdown part as the model's reply. -- */ --function buildWorktreeAnnouncementText(branchName: string): string { -- return localize( -- 'copilotAgent.worktreeCreated', -- "Created isolated worktree for branch {0}", -- appendEscapedMarkdownInlineCode(branchName) -- ) + '\n\n'; --} -- --type AgentMessageOrEvent = SessionHistoryEvent; -- --/** -- * Returns a copy of `messages` where `announcement` has been prepended to -- * the first top-level assistant message's content. Subagent inner messages -- * (those with a `parentToolCallId`) are skipped so the announcement lands -- * on the parent turn. If no assistant message exists yet, returns the -- * messages unchanged — the live announcement path is responsible for the -- * very first turn before any reply has been recorded. -- */ --function prependAnnouncementToFirstAssistantMessage( -- messages: readonly AgentMessageOrEvent[], -- announcement: string, --): readonly AgentMessageOrEvent[] { -- const firstAssistantIdx = messages.findIndex(m => m.type === 'message' && m.role === 'assistant' && !m.parentToolCallId); -- if (firstAssistantIdx === -1) { -- return messages; -- } -- const target = messages[firstAssistantIdx] as IAgentMessageEvent; -- const updated: IAgentMessageEvent = { ...target, content: announcement + target.content }; -- return [ -- ...messages.slice(0, firstAssistantIdx), -- updated, -- ...messages.slice(firstAssistantIdx + 1), -- ]; --} -- --/** -- * Agent provider backed by the Copilot SDK {@link CopilotClient}. -- */ --export class CopilotAgent extends Disposable implements IAgent { -- readonly id = 'copilotcli' as const; -- private static readonly _BRANCH_COMPLETION_LIMIT = 25; -- -- private readonly _onDidSessionProgress = this._register(new Emitter()); -- readonly onDidSessionProgress = this._onDidSessionProgress.event; -- private readonly _models = observableValue(this, []); -- readonly models = this._models; -- -- private _client: ICopilotClient | undefined; -- private _clientStarting: Promise | undefined; -- private _githubToken: string | undefined; -- private readonly _sessions = this._register(new DisposableMap()); -- private readonly _createdWorktrees = new Map(); -- /** -- * Per-session announcement (markdown string) that should be emitted as -- * a synthetic streaming `delta` event the first time {@link sendMessage} -- * is called for the session. Currently used to surface the "Created -- * isolated worktree for branch X" message live during the first turn. -- * The same announcement is also injected on restore via -- * {@link getSessionMessages} by prepending to the first assistant -- * message's content so it stays visible after the session is reopened. -- */ -- private readonly _pendingFirstTurnAnnouncements = new Map(); -- private readonly _sessionSequencer = new SequencerByKey(); -- private _shutdownPromise: Promise | undefined; -- private readonly _plugins: PluginController; -- /** Per-session active client state for tools + plugin snapshot tracking. */ -- private readonly _activeClients = new ResourceMap(); -- -- constructor( -- @ILogService private readonly _logService: ILogService, -- @IInstantiationService private readonly _instantiationService: IInstantiationService, -- @IFileService private readonly _fileService: IFileService, -- @ISessionDataService private readonly _sessionDataService: ISessionDataService, -- @IAgentHostGitService private readonly _gitService: IAgentHostGitService, -- @IAgentHostTerminalManager private readonly _terminalManager: IAgentHostTerminalManager, -- ) { -- super(); -- this._plugins = this._instantiationService.createInstance(PluginController); -- } -- -- protected _createCopilotClient(options: CopilotClientOptions): ICopilotClient { -- return new CopilotClient(options); -- } -- -- // ---- auth --------------------------------------------------------------- -- -- getDescriptor(): IAgentDescriptor { -- return { -- provider: 'copilotcli', -- displayName: 'Copilot CLI', -- description: 'Copilot SDK agent running in a dedicated process', -- }; -- } -- -- getProtectedResources(): ProtectedResourceMetadata[] { -- 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'); -- await this._stopClient(); -- } -- if (tokenChanged) { -- void this._refreshModels(); -- } -- return true; -- } -- -- private async _refreshModels(): Promise { -- const tokenAtRefreshStart = this._githubToken; -- if (!tokenAtRefreshStart) { -- this._models.set([], undefined); -- return; -- } -- try { -- const models = await this._listModels(); -- if (this._githubToken === tokenAtRefreshStart) { -- this._models.set(models, undefined); -- } -- } catch (err) { -- this._logService.error(err, '[Copilot] Failed to refresh models'); -- if (this._githubToken === tokenAtRefreshStart) { -- this._models.set([], undefined); -- } -- } -- } -- -- private async _stopClient(): Promise { -- const client = this._client; -- this._client = undefined; -- this._clientStarting = undefined; -- await client?.stop(); -- } -- -- // ---- client lifecycle --------------------------------------------------- -- -- private async _ensureClient(): Promise { -- const tokenAtStartup = this._githubToken; -- if (!tokenAtStartup) { -- throw new ProtocolError(AHP_AUTH_REQUIRED, 'Authentication is required to use Copilot'); -- } -- if (this._client) { -- return this._client; -- } -- if (this._clientStarting) { -- return this._clientStarting; -- } -- const clientStarting = (async () => { -- this._logService.info('[Copilot] Starting CopilotClient... (with 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 = this._createCopilotClient({ -- githubToken: tokenAtStartup, -- useLoggedInUser: false, -- useStdio: true, -- autoStart: true, -- env, -- cliPath, -- }); -- await client.start(); -- if (this._githubToken !== tokenAtStartup) { -- await client.stop(); -- throw new Error('Copilot authentication changed while the client was starting'); -- } -- this._logService.info('[Copilot] CopilotClient started successfully'); -- this._client = client; -- this._clientStarting = undefined; -- return client; -- })(); -- this._clientStarting = clientStarting; -- void clientStarting.catch(() => { -- this._clientStarting = undefined; -- }); -- return clientStarting; -- } -- -- // ---- session management ------------------------------------------------- -- -- private _createThinkingLevelConfigSchema(supportedReasoningEfforts: readonly string[] | undefined, defaultReasoningEffort: string | undefined): ConfigSchema | undefined { -- if (!supportedReasoningEfforts?.length) { -- return undefined; -- } -- -- const enumLabels = supportedReasoningEfforts.map(value => { -- switch (value) { -- case 'low': return localize('copilot.modelThinkingLevel.low', "Low"); -- case 'medium': return localize('copilot.modelThinkingLevel.medium', "Medium"); -- case 'high': return localize('copilot.modelThinkingLevel.high', "High"); -- case 'xhigh': return localize('copilot.modelThinkingLevel.xhigh', "Extra High"); -- default: return value; -- } -- }); -- -- return { -- type: 'object', -- properties: { -- [ThinkingLevelConfigKey]: { -- type: 'string', -- title: localize('copilot.modelThinkingLevel.title', "Thinking Level"), -- description: localize('copilot.modelThinkingLevel.description', "Controls how much reasoning effort the model uses."), -- default: defaultReasoningEffort, -- enum: [...supportedReasoningEfforts], -- enumLabels, -- }, -- }, -- }; -- } -- -- private _getReasoningEffort(model: ModelSelection | undefined): SessionConfig['reasoningEffort'] { -- const thinkingLevel = model?.config?.[ThinkingLevelConfigKey]; -- return isReasoningEffort(thinkingLevel) ? thinkingLevel : undefined; -- } -- -- private _serializeModelSelection(model: ModelSelection): string { -- return JSON.stringify(model); -- } -- -- private _parseModelSelection(raw: string | undefined): ModelSelection | undefined { -- if (!raw) { -- return undefined; -- } -- -- try { -- const value: ISerializedModelSelection | string | number | boolean | null = JSON.parse(raw); -- if (value && typeof value === 'object' && typeof value.id === 'string') { -- const modelSelection: ModelSelection = { id: value.id }; -- if (value.config && typeof value.config === 'object') { -- const config: Record = {}; -- for (const [key, configValue] of Object.entries(value.config)) { -- if (typeof configValue === 'string') { -- config[key] = configValue; -- } -- } -- if (Object.keys(config).length > 0) { -- modelSelection.config = config; -- } -- } -- return modelSelection; -- } -- } catch { -- // Older session metadata stored the raw model id as a plain string. -- } -- -- return { id: raw }; -- } -- -- 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 mapped = await Promise.all(sessions.map(async s => { -- const session = AgentSession.uri(this.id, s.sessionId); -- const metadata = await this._readStoredSessionMetadata(session); -- if (!metadata) { -- return undefined; -- } -- let { project, resolved } = metadata; -- if (!resolved) { -- project = await this._resolveSessionProject(s.context, projectLimiter, projectByContext); -- void this._storeSessionProjectResolution(session, project); -- } -- const workingDirectory = metadata.workingDirectory ?? (typeof s.context?.cwd === 'string' ? URI.file(s.context.cwd) : undefined); -- const result: IAgentSessionMetadata = { -- session, -- startTime: s.startTime.getTime(), -- modifiedTime: s.modifiedTime.getTime(), -- project, -- summary: s.summary, -- model: metadata.model, -- workingDirectory, -- }; -- return result; -- })); -- const result = mapped.filter((s): s is IAgentSessionMetadata => s !== undefined); -- this._logService.info(`[Copilot] Found ${result.length} sessions`); -- return result; -- } -- -- private async _listModels(): Promise { -- this._logService.info('[Copilot] Listing models...'); -- const client = await this._ensureClient(); -- const models = await client.listModels(); -- const result = models.map((m): IAgentModelInfo => ({ -- provider: this.id, -- id: m.id, -- name: m.name, -- // Synthetic SDK entries like `auto` ship with `capabilities: {}` and -- // no fixed context window — surface them with maxContextWindow undefined. -- maxContextWindow: m.capabilities?.limits?.max_context_window_tokens, -- supportsVision: !!m.capabilities?.supports?.vision, -- configSchema: this._createThinkingLevelConfigSchema(m.supportedReasoningEfforts, m.defaultReasoningEffort), -- policyState: m.policy?.state as PolicyState | undefined, -- })); -- this._logService.info(`[Copilot] Found ${result.length} models`); -- return result; -- } -- -- async createSession(config?: IAgentCreateSessionConfig): Promise { -- if (!config?.workingDirectory) { -- throw new Error('workingDirectory is required to create a Copilot session'); -- } -- -- this._logService.info(`[Copilot] Creating session... ${config?.model ? `model=${config.model.id}` : ''}`); -- const client = await this._ensureClient(); -- -- // When forking, use the SDK's sessions.fork RPC. -- if (config?.fork) { -- const sourceSessionId = AgentSession.id(config.fork.session); -- -- // Serialize against the source session to prevent concurrent -- // modifications while we read its state. -- return this._sessionSequencer.queue(sourceSessionId, async () => { -- this._logService.info(`[Copilot] Forking session ${sourceSessionId} at turnId=${config.fork!.turnId}`); -- -- // Ensure the source session is loaded so we can read its event IDs -- const sourceEntry = this._sessions.get(sourceSessionId) ?? await this._resumeSession(sourceSessionId); -- -- // Look up the SDK event ID for the turn *after* the fork point. -- // toEventId is exclusive — events before it are included. -- // If there's no next turn, omit toEventId to include all events. -- const toEventId = await sourceEntry.getNextTurnEventId(config.fork!.turnId); -- -- const forkResult = await client.rpc.sessions.fork({ -- sessionId: sourceSessionId, -- ...(toEventId ? { toEventId } : {}), -- }); -- const newSessionId = forkResult.sessionId; -- -- // Copy the source session's database using VACUUM INTO so the -- // forked session inherits turn event IDs and file-edit snapshots. -- // VACUUM INTO is safe even while the source DB is open. -- const targetDbDir = this._sessionDataService.getSessionDataDirById(newSessionId); -- const targetDbPath = URI.joinPath(targetDbDir, SESSION_DB_FILENAME); -- try { -- const sourceDbRef = await this._sessionDataService.tryOpenDatabase(config.fork!.session); -- if (sourceDbRef) { -- try { -- await fs.mkdir(targetDbDir.fsPath, { recursive: true }); -- await sourceDbRef.object.vacuumInto(targetDbPath.fsPath); -- } finally { -- sourceDbRef.dispose(); -- } -- } -- } catch (err) { -- this._logService.warn(`[Copilot] Failed to copy session database for fork: ${err instanceof Error ? err.message : String(err)}`); -- } -- -- // Resume the forked session so the SDK loads the forked history -- const agentSession = await this._resumeSession(newSessionId); -- -- // Remap turn IDs to match the new protocol turn IDs -- if (config.fork!.turnIdMapping) { -- await agentSession.remapTurnIds(config.fork!.turnIdMapping); -- } -- -- const session = agentSession.sessionUri; -- this._logService.info(`[Copilot] Forked session created: ${session.toString()}`); -- const project = await projectFromCopilotContext({ cwd: config.workingDirectory?.fsPath }, this._gitService); -- await this._storeSessionMetadata(session, config.model, config.workingDirectory, project, true); -- return { session, workingDirectory: config.workingDirectory, ...(project ? { project } : {}) }; -- }); -- } -- -- const sessionId = config?.session ? AgentSession.id(config.session) : generateUuid(); -- const sessionUri = AgentSession.uri(this.id, sessionId); -- let seededActiveClient = false; -- if (config?.activeClient) { -- const ac = this._getOrCreateActiveClient(sessionUri); -- seededActiveClient = true; -- ac.updateTools(config.activeClient.clientId, config.activeClient.tools); -- if (config.activeClient.customizations !== undefined) { -- await this._plugins.sync(config.activeClient.clientId, config.activeClient.customizations); -- } -- } -- const activeClient = this._activeClients.get(sessionUri); -- const snapshot = activeClient ? await activeClient.snapshot() : undefined; -- const workingDirectory = await this._resolveSessionWorkingDirectory(config, sessionId); -- const shellManager = this._instantiationService.createInstance(ShellManager, sessionUri, workingDirectory); -- const sessionConfig = this._buildSessionConfig(snapshot, shellManager); -- -- const factory: SessionWrapperFactory = async callbacks => { -- const raw = await client.createSession({ -- model: config?.model?.id, -- reasoningEffort: this._getReasoningEffort(config?.model), -- sessionId, -- streaming: true, -- workingDirectory: workingDirectory?.fsPath, -- ...await sessionConfig(callbacks), -- }); -- return new CopilotSessionWrapper(raw); -- }; -- -- let agentSession: CopilotAgentSession; -- try { -- agentSession = this._createAgentSession(factory, sessionId, shellManager, workingDirectory, snapshot); -- await agentSession.initializeSession(); -- } catch (error) { -- if (seededActiveClient) { -- this._activeClients.delete(sessionUri); -- } -- await this._removeCreatedWorktree(sessionId); -- throw error; -- } -- const session = agentSession.sessionUri; -- this._logService.info(`[Copilot] Session created: ${session.toString()}`); -- const project = await projectFromCopilotContext({ cwd: workingDirectory?.fsPath }, this._gitService); -- // Persist model, working directory, and project so we can recreate the -- // session if the SDK loses it and avoid rediscovering git metadata. -- await this._storeSessionMetadata(agentSession.sessionUri, config?.model, workingDirectory, project, true); -- return { session, workingDirectory, ...(project ? { project } : {}) }; -- } -- -- async resolveSessionConfig(params: IAgentResolveSessionConfigParams): Promise { -- const gitInfo = params.workingDirectory ? await this._getGitInfo(params.workingDirectory) : undefined; -- -- const isolationProperty = schemaProperty<'folder' | 'worktree'>({ -- type: 'string', -- title: localize('agentHost.sessionConfig.isolation', "Isolation"), -- description: localize('agentHost.sessionConfig.isolationDescription', "Where the agent should make changes"), -- enum: gitInfo ? ['folder', 'worktree'] : ['folder'], -- enumLabels: gitInfo ? [localize('agentHost.sessionConfig.isolation.folder', "Folder"), localize('agentHost.sessionConfig.isolation.worktree', "Worktree")] : [localize('agentHost.sessionConfig.isolation.folder', "Folder")], -- enumDescriptions: gitInfo ? [localize('agentHost.sessionConfig.isolation.folderDescription', "Work directly in the folder"), localize('agentHost.sessionConfig.isolation.worktreeDescription', "Create a Git worktree for isolation")] : [localize('agentHost.sessionConfig.isolation.folderDescription', "Work directly in the folder")], -- default: gitInfo ? 'worktree' : 'folder', -- readOnly: !gitInfo, -- }); -- -- // Resolve isolation first — downstream schema shapes (branch's -- // read-only mode + enum restriction) depend on the effective value. -- const isolationDefault: 'folder' | 'worktree' = gitInfo ? 'worktree' : 'folder'; -- const isolationValue = isolationProperty.validate(params.config?.[SessionConfigKey.Isolation]) -- ? params.config[SessionConfigKey.Isolation] as 'folder' | 'worktree' -- : isolationDefault; -- -- let branchProperty: ISchemaProperty | undefined; -- let branchDefault: string | undefined; -- if (gitInfo) { -- const branchReadOnly = isolationValue === 'folder'; -- branchDefault = isolationValue === 'worktree' ? gitInfo.defaultBranch : gitInfo.currentBranch; -- branchProperty = schemaProperty({ -- type: 'string', -- title: localize('agentHost.sessionConfig.branch', "Branch"), -- description: localize('agentHost.sessionConfig.branchDescription', "Base branch to work from"), -- enum: [branchDefault], -- enumLabels: [branchDefault], -- default: branchDefault, -- enumDynamic: !branchReadOnly, -- readOnly: branchReadOnly, -- }); -- } -- -- const sessionSchema = createSchema({ -- [SessionConfigKey.Isolation]: isolationProperty, -- ...platformSessionSchema.definition, -- ...(branchProperty ? { [SessionConfigKey.Branch]: branchProperty } : {}), -- }); -- -- const values = sessionSchema.validateOrDefault(params.config, { -- [SessionConfigKey.Isolation]: isolationValue, -- [SessionConfigKey.AutoApprove]: 'default' satisfies AutoApproveLevel, -- // Permissions intentionally omitted — leave unset so auto-approval -- // falls through to the host-level `permissions` default, and only -- // materializes on the session once the user hits "Allow in this -- // Session". -- ...(branchDefault !== undefined ? { [SessionConfigKey.Branch]: branchDefault } : {}), -- }); -- -- return { -- schema: sessionSchema.toProtocol(), -- values, -- }; -- } -- -- async sessionConfigCompletions(params: IAgentSessionConfigCompletionsParams): Promise { -- if (params.property !== 'branch' || !params.workingDirectory) { -- return { items: [] }; -- } -- -- const branches = await this._getBranches(params.workingDirectory, params.query); -- return { items: branches.map(branch => ({ value: branch, label: branch })) }; -- } -- -- async setClientCustomizations(clientId: string, customizations: CustomizationRef[], progress?: (results: ISyncedCustomization[]) => void): Promise { -- return this._plugins.sync(clientId, customizations, progress); -- } -- -- setClientTools(session: URI, clientId: string, tools: ToolDefinition[]): void { -- const sessionId = AgentSession.id(session); -- const activeClient = this._getOrCreateActiveClient(session); -- const hasCachedEntry = this._sessions.has(sessionId); -- this._logService.info(`[Copilot:${sessionId}] setClientTools: clientId=${clientId}, tools=[${tools.map(t => t.name).join(', ') || '(none)'}], hasCachedSdkSession=${hasCachedEntry}`); -- activeClient.updateTools(clientId, tools); -- } -- -- onClientToolCallComplete(session: URI, toolCallId: string, result: ToolCallResult): void { -- const entry = this._sessions.get(AgentSession.id(session)); -- entry?.handleClientToolCallComplete(toolCallId, result); -- } -- -- 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 the active client's config changed (tools or plugins), -- // dispose this session so it gets resumed with the updated config. -- let entry = this._sessions.get(sessionId); -- const activeClient = this._activeClients.get(session); -- const hadCachedEntry = !!entry; -- this._logService.info(`[Copilot:${sessionId}] sendMessage: cachedEntry=${hadCachedEntry}, hasActiveClient=${!!activeClient}, activeClientId=${activeClient ? '(set)' : '(none)'}`); -- if (entry && activeClient && await activeClient.isOutdated(entry.appliedSnapshot)) { -- this._logService.info(`[Copilot:${sessionId}] Session config changed (isOutdated=true), refreshing session. snapshotClientId=${entry.appliedSnapshot.clientId}`); -- this._sessions.deleteAndDispose(sessionId); -- entry = undefined; -- } -- -- if (!entry) { -- this._logService.info(`[Copilot:${sessionId}] No cached entry${hadCachedEntry ? ' (was evicted by isOutdated)' : ''}, calling _resumeSession`); -- } -- entry ??= await this._resumeSession(sessionId); -- -- // Emit any pending first-turn announcements (e.g. worktree -- // created) as a synthetic streaming delta before delegating to -- // the SDK. The mapper treats it like any other assistant text — -- // the SDK's subsequent deltas append to the same markdown part. -- // The active turn has already been started by the state manager -- // at this point, so the event mapper can attach the delta to it. -- const pending = this._pendingFirstTurnAnnouncements.get(sessionId); -- if (pending) { -- this._pendingFirstTurnAnnouncements.delete(sessionId); -- const messageId = `copilot-announcement-${generateUuid()}`; -- const event: IAgentDeltaEvent = { type: 'delta', session, messageId, content: pending }; -- this._onDidSessionProgress.fire(event); -- } -- -- try { -- await entry.send(prompt, attachments, turnId); -- } catch (err) { -- const errCode = (err as { code?: number })?.code; -- const errMsg = err instanceof Error ? err.message : String(err); -- this._logService.error(`[Copilot:${sessionId}] entry.send() failed: code=${errCode}, message=${errMsg}, hadCachedEntry=${hadCachedEntry}, errorType=${err?.constructor?.name}`); -- throw err; -- } -- }); -- } -- -- setPendingMessages(session: URI, steeringMessage: PendingMessage | undefined, _queuedMessages: readonly PendingMessage[]): 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 { -- const sessionId = AgentSession.id(session); -- const entry = this._sessions.get(sessionId) ?? await this._resumeSession(sessionId).catch(err => { -- this._logService.warn(`[Copilot:${sessionId}] Failed to resume session for message lookup`, err); -- return undefined; -- }); -- if (!entry) { -- return []; -- } -- const rawMessages = await entry.getMessages(); -- -- // If a worktree was created for this session at create-time, prepend -- // the announcement to the first assistant message's content so it -- // appears at the top of the first response when the session is -- // reopened. The live path (sendMessage) handles the very first turn -- // when the session is fresh; this path takes over on subsequent -- // loads, where _pendingFirstTurnAnnouncements is empty. -- const branchName = await this._readWorktreeBranchMetadata(session).catch(err => { -- this._logService.warn(`[Copilot:${sessionId}] Failed to read worktree branch metadata`, err); -- return undefined; -- }); -- if (!branchName) { -- return rawMessages; -- } -- return [...prependAnnouncementToFirstAssistantMessage(rawMessages, buildWorktreeAnnouncementText(branchName))]; -- } -- -- async disposeSession(session: URI): Promise { -- const sessionId = AgentSession.id(session); -- await this._sessionSequencer.queue(sessionId, async () => { -- await this._destroyAndDisposeSession(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, turnId?: string): Promise { -- const sessionId = AgentSession.id(session); -- await this._sessionSequencer.queue(sessionId, async () => { -- this._logService.info(`[Copilot:${sessionId}] Truncating session${turnId !== undefined ? ` at turnId=${turnId}` : ' (all turns)'}`); -- -- // Ensure the session is loaded so we can use the SDK RPC -- const entry = this._sessions.get(sessionId) ?? await this._resumeSession(sessionId); -- -- // Look up the SDK event ID for the truncation boundary. -- // The protocol semantics: turnId is the last turn to KEEP. -- // The SDK semantics: eventId and all events after it are removed. -- // So we need the event ID of the *next* turn after turnId. -- // For "remove all", we need the first turn's event ID. -- let eventId: string | undefined; -- if (turnId) { -- eventId = await entry.getNextTurnEventId(turnId); -- } else { -- eventId = await entry.getFirstTurnEventId(); -- } -- -- if (eventId) { -- await entry.truncateAtEventId(eventId, turnId); -- } else { -- this._logService.info(`[Copilot:${sessionId}] No event ID found for truncation, nothing to truncate`); -- } -- -- this._logService.info(`[Copilot:${sessionId}] Session truncated`); -- }); -- } -- -- async changeModel(session: URI, model: ModelSelection): Promise { -- const sessionId = AgentSession.id(session); -- const entry = this._sessions.get(sessionId); -- if (entry) { -- await entry.setModel(model.id, this._getReasoningEffort(model)); -- } -- await this._storeSessionMetadata(session, model, undefined, undefined); -- } -- -- async shutdown(): Promise { -- this._shutdownPromise ??= (async () => { -- this._logService.info('[Copilot] Shutting down...'); -- const sessionIds = new Set([...this._sessions.keys(), ...this._createdWorktrees.keys()]); -- for (const sessionId of sessionIds) { -- await this._sessionSequencer.queue(sessionId, () => this._destroyAndDisposeSession(sessionId)); -- } -- await this._client?.stop(); -- this._client = undefined; -- })(); -- return this._shutdownPromise; -- } -- -- 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 ------------------------------------------------------------ -- -- private _getOrCreateActiveClient(session: URI): ActiveClient { -- let client = this._activeClients.get(session); -- if (!client) { -- client = new ActiveClient(() => this._plugins.getAppliedPlugins()); -- this._activeClients.set(session, client); -- } -- return client; -- } -- -- /** -- * 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, sessionId: string, shellManager: ShellManager, workingDirectory: URI | undefined, snapshot?: IActiveClientSnapshot): CopilotAgentSession { -- const sessionUri = AgentSession.uri(this.id, sessionId); -- -- const agentSession = this._instantiationService.createInstance( -- CopilotAgentSession, -- { -- sessionUri, -- rawSessionId: sessionId, -- onDidSessionProgress: this._onDidSessionProgress, -- wrapperFactory, -- shellManager, -- workingDirectory, -- clientSnapshot: snapshot, -- }, -- ); -- -- this._sessions.set(sessionId, agentSession); -- return agentSession; -- } -- -- private async _destroyAndDisposeSession(sessionId: string): Promise { -- const entry = this._sessions.get(sessionId); -- if (entry) { -- try { -- await entry.destroySession(); -- } catch (error) { -- this._logService.warn(`[Copilot:${sessionId}] Failed to destroy session before cleanup: ${error instanceof Error ? error.message : String(error)}`); -- } -- } -- this._sessions.deleteAndDispose(sessionId); -- await this._removeCreatedWorktree(sessionId); -- } -- -- /** -- * 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(snapshot: IActiveClientSnapshot | undefined, shellManager: ShellManager): (args: Parameters[0]) => Promise { -- const shellTools = createShellTools(shellManager, this._terminalManager, this._logService); -- const plugins = snapshot?.plugins ?? []; -- -- return async (callbacks: Parameters[0]) => { -- const customAgents = await toSdkCustomAgents(plugins.flatMap(p => p.agents), this._fileService); -- return { -- onPermissionRequest: callbacks.onPermissionRequest, -- onUserInputRequest: callbacks.onUserInputRequest, -- hooks: toSdkHooks(plugins.flatMap(p => p.hooks), callbacks.hooks), -- mcpServers: toSdkMcpServers(plugins.flatMap(p => p.mcpServers)), -- customAgents, -- skillDirectories: toSdkSkillDirectories(plugins.flatMap(p => p.skills)), -- tools: [...shellTools, ...callbacks.clientTools], -- }; -- }; -- } -- -- protected async _resumeSession(sessionId: string): Promise { -- this._logService.info(`[Copilot:${sessionId}] _resumeSession called — session not in memory, resuming...`); -- const client = await this._ensureClient(); -- -- const sessionUri = AgentSession.uri(this.id, sessionId); -- const activeClient = this._activeClients.get(sessionUri); -- const snapshot = activeClient ? await activeClient.snapshot() : undefined; -- const storedMetadata = await this._readSessionMetadata(sessionUri); -- const sessionMetadata = await client.getSessionMetadata(sessionId).catch(err => { -- this._logService.warn(`[Copilot:${sessionId}] getSessionMetadata failed`, err); -- return undefined; -- }); -- const workingDirectory = storedMetadata.workingDirectory ?? (typeof sessionMetadata?.context?.cwd === 'string' ? URI.file(sessionMetadata.context.cwd) : undefined); -- if (!workingDirectory) { -- throw new Error(`workingDirectory is required to resume Copilot session '${sessionId}'`); -- } -- -- const shellManager = this._instantiationService.createInstance(ShellManager, sessionUri, workingDirectory); -- const sessionConfig = this._buildSessionConfig(snapshot, shellManager); -- -- const factory: SessionWrapperFactory = async callbacks => { -- const config = await sessionConfig(callbacks); -- try { -- this._logService.info(`[Copilot:${sessionId}] Calling SDK resumeSession...`); -- const raw = await client.resumeSession(sessionId, { -- ...config, -- workingDirectory: workingDirectory?.fsPath, -- }); -- this._logService.info(`[Copilot:${sessionId}] SDK resumeSession succeeded`); -- return new CopilotSessionWrapper(raw); -- } catch (err) { -- const errCode = (err as { code?: number })?.code; -- const errMsg = err instanceof Error ? err.message : String(err); -- this._logService.warn(`[Copilot:${sessionId}] SDK resumeSession failed: code=${errCode}, message=${errMsg}`); -- // 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 || errCode !== -32603) { -- throw err; -- } -- -- this._logService.warn(`[Copilot:${sessionId}] Resume failed (code=-32603), falling back to createSession with same ID`); -- const raw = await client.createSession({ -- ...config, -- sessionId, -- streaming: true, -- model: storedMetadata.model?.id, -- reasoningEffort: this._getReasoningEffort(storedMetadata.model), -- workingDirectory: workingDirectory?.fsPath, -- }); -- this._logService.info(`[Copilot:${sessionId}] Fallback createSession succeeded`); -- -- return new CopilotSessionWrapper(raw); -- } -- }; -- -- const agentSession = this._createAgentSession(factory, sessionId, shellManager, workingDirectory, snapshot); -- await agentSession.initializeSession(); -- -- return agentSession; -- } -- -- private async _getGitInfo(workingDirectory: URI): Promise<{ currentBranch: string; defaultBranch: string } | undefined> { -- if (!await this._gitService.isInsideWorkTree(workingDirectory)) { -- return undefined; -- } -- -- const currentBranch = await this._gitService.getCurrentBranch(workingDirectory) ?? 'HEAD'; -- const defaultBranch = await this._gitService.getDefaultBranch(workingDirectory) ?? currentBranch; -- return { currentBranch, defaultBranch }; -- } -- -- private async _getBranches(workingDirectory: URI, query?: string): Promise { -- return this._gitService.getBranches(workingDirectory, { query, limit: CopilotAgent._BRANCH_COMPLETION_LIMIT }); -- } -- -- protected async _resolveSessionWorkingDirectory(config: IAgentCreateSessionConfig | undefined, sessionId: string): Promise { -- if (config?.config?.isolation !== 'worktree' || !config.workingDirectory || typeof config.config.branch !== 'string') { -- return config?.workingDirectory; -- } -- -- const repositoryRoot = await this._gitService.getRepositoryRoot(config.workingDirectory); -- if (!repositoryRoot) { -- return config.workingDirectory; -- } -- -- const worktreesRoot = getCopilotWorktreesRoot(repositoryRoot); -- const branchNameHintRaw = config.config[SessionConfigKey.BranchNameHint]; -- const branchNameHint = typeof branchNameHintRaw === 'string' ? branchNameHintRaw : undefined; -- const branchName = getCopilotWorktreeBranchName(sessionId, branchNameHint); -- const worktree = URI.joinPath(worktreesRoot, getCopilotWorktreeName(branchName)); -- await fs.mkdir(worktreesRoot.fsPath, { recursive: true }); -- const baseBranch = typeof config.config[SessionConfigKey.Branch] === 'string' ? config.config[SessionConfigKey.Branch] as string : undefined; -- // `addWorktree`'s signature requires a startPoint, but historically the -- // runtime accepted undefined when `branch` was not set in config. Preserve -- // that behavior by passing through whatever value (or undefined) was set. -- await this._gitService.addWorktree(repositoryRoot, worktree, branchName, baseBranch as string); -- this._createdWorktrees.set(sessionId, { repositoryRoot, worktree }); -- // Queue the worktree announcement so the first turn (live) and any -- // subsequent restore (history) both surface the message in the chat. -- this._pendingFirstTurnAnnouncements.set(sessionId, buildWorktreeAnnouncementText(branchName)); -- const sessionUri = AgentSession.uri(this.id, sessionId); -- try { -- await this._writeWorktreeBranchMetadata(sessionUri, branchName, baseBranch); -- } catch (error) { -- this._logService.warn(`[Copilot:${sessionId}] Failed to persist worktree branch metadata: ${error instanceof Error ? error.message : String(error)}`); -- } -- return worktree; -- } -- -- private async _removeCreatedWorktree(sessionId: string): Promise { -- const worktree = this._createdWorktrees.get(sessionId); -- if (!worktree) { -- return; -- } -- try { -- await this._gitService.removeWorktree(worktree.repositoryRoot, worktree.worktree); -- } catch (error) { -- this._logService.warn(`[Copilot:${sessionId}] Failed to remove worktree '${worktree.worktree.fsPath}': ${error instanceof Error ? error.message : String(error)}`); -- } finally { -- this._createdWorktrees.delete(sessionId); -- } -- } -- -- // ---- 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 static readonly _META_WORKTREE_BRANCH = 'copilot.worktree.branchName'; -- -- private async _writeWorktreeBranchMetadata(session: URI, branchName: string, baseBranch: string | undefined): Promise { -- const dbRef = this._sessionDataService.openDatabase(session); -- try { -- const work: Promise[] = [dbRef.object.setMetadata(CopilotAgent._META_WORKTREE_BRANCH, branchName)]; -- if (baseBranch) { -- work.push(dbRef.object.setMetadata(META_DIFF_BASE_BRANCH, baseBranch)); -- } -- await Promise.all(work); -- } finally { -- dbRef.dispose(); -- } -- } -- -- private async _readWorktreeBranchMetadata(session: URI): Promise { -- const ref = await this._sessionDataService.tryOpenDatabase(session); -- if (!ref) { -- return undefined; -- } -- try { -- const value = await ref.object.getMetadata(CopilotAgent._META_WORKTREE_BRANCH); -- return value ?? undefined; -- } finally { -- ref.dispose(); -- } -- } -- -- private async _storeSessionMetadata(session: URI, model: ModelSelection | undefined, workingDirectory: URI | undefined, project: IAgentSessionProjectInfo | undefined, projectResolved = project !== undefined): Promise { -- const dbRef = this._sessionDataService.openDatabase(session); -- const db = dbRef.object; -- try { -- const work: Promise[] = []; -- if (model) { -- work.push(db.setMetadata(CopilotAgent._META_MODEL, this._serializeModelSelection(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)); -- } -- await Promise.all(work); -- } finally { -- dbRef.dispose(); -- } -- } -- -- private async _readSessionMetadata(session: URI): Promise<{ model?: ModelSelection; 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: this._parseModelSelection(model), -- workingDirectory: cwd ? URI.parse(cwd) : undefined, -- }; -- } finally { -- ref.dispose(); -- } -- } -- -- private async _readStoredSessionMetadata(session: URI): Promise<{ model?: ModelSelection; workingDirectory?: URI; project?: IAgentSessionProjectInfo; resolved: boolean } | undefined> { -- const ref = await this._sessionDataService.tryOpenDatabase(session); -- if (!ref) { -- return undefined; -- } -- try { -- const [model, cwd, resolved, uri, displayName] = await Promise.all([ -- ref.object.getMetadata(CopilotAgent._META_MODEL), -- ref.object.getMetadata(CopilotAgent._META_CWD), -- ref.object.getMetadata(CopilotAgent._META_PROJECT_RESOLVED), -- ref.object.getMetadata(CopilotAgent._META_PROJECT_URI), -- ref.object.getMetadata(CopilotAgent._META_PROJECT_DISPLAY_NAME), -- ]); -- const workingDirectory = cwd ? URI.parse(cwd) : undefined; -- const project = uri && displayName ? { uri: URI.parse(uri), displayName } : undefined; -- return { model: this._parseModelSelection(model), workingDirectory, project, resolved: resolved === 'true' || project !== undefined }; -- } finally { -- ref.dispose(); -- } -- } -- -- private async _storeSessionProjectResolution(session: URI, project: IAgentSessionProjectInfo | undefined): Promise { -- await 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, this._gitService)); -- 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.shutdown().catch(err => { -- this._logService.warn('[Copilot] Shutdown failed during dispose', err); -- }).finally(() => super.dispose()); -- } --} -- --class PluginController { -- private readonly _enablement = new Map(); -- private _lastSynced: Promise<{ synced: ISyncedCustomization[]; parsed: IParsedPlugin[] }> = Promise.resolve({ synced: [], parsed: [] }); -- -- constructor( -- @IAgentPluginManager private readonly _pluginManager: IAgentPluginManager, -- @ILogService private readonly _logService: ILogService, -- @IFileService private readonly _fileService: IFileService, -- ) { } -- -- /** -- * Returns the current parsed plugins, awaiting any pending sync. -- */ -- public async getAppliedPlugins(): Promise { -- const { parsed } = await this._lastSynced; -- return parsed; -- } -- -- public setEnabled(pluginProtocolUri: string, enabled: boolean) { -- this._enablement.set(pluginProtocolUri, enabled); -- } -- -- public sync(clientId: string, customizations: CustomizationRef[], progress?: (results: ISyncedCustomization[]) => void) { -- const prev = this._lastSynced; -- const promise = this._lastSynced = prev.catch(err => { -- this._logService.warn('[Copilot:PluginController] Previous customization sync failed', err); -- }).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'] ?? ''; -- } --} -- --/** -- * Tracks per-session active client contributions (tools and plugins). -- * The {@link snapshot} captures the state at session creation time, and -- * {@link isOutdated} detects when the session needs to be refreshed. -- */ --class ActiveClient { -- private _tools: readonly ToolDefinition[] = []; -- private _clientId = ''; -- -- constructor( -- /** Resolves the current set of applied plugins. May block while a sync is in progress. */ -- private readonly _resolvePlugins: () => Promise, -- ) { } -- -- updateTools(clientId: string, tools: readonly ToolDefinition[]): void { -- this._clientId = clientId; -- this._tools = tools; -- } -- -- async snapshot(): Promise { -- return { clientId: this._clientId, tools: this._tools, plugins: await this._resolvePlugins() }; -- } -- -- async isOutdated(snap: IActiveClientSnapshot): Promise { -- const plugins = await this._resolvePlugins(); -- if (!parsedPluginsEqual(snap.plugins, plugins)) { -- return true; -- } -- if (snap.tools.length !== this._tools.length) { -- return true; -- } -- // Compare tool definitions by name, description, and schema — -- // not just names — so schema/description changes trigger a refresh. -- const snapByName = new Map(snap.tools.map(t => [t.name, t])); -- for (const tool of this._tools) { -- const prev = snapByName.get(tool.name); -- if (!prev -- || prev.description !== tool.description -- || !equals(prev.inputSchema, tool.inputSchema)) { -- return true; -- } -- } -- return false; -- } --} -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 4555cb5b..00000000 ---- a/src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts -+++ /dev/null -@@ -1,1076 +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 { PermissionRequestResult, SessionConfig, Tool, ToolResultObject } from '@github/copilot-sdk'; --import { DeferredPromise } from '../../../../base/common/async.js'; --import { VSBuffer } from '../../../../base/common/buffer.js'; --import { Emitter } from '../../../../base/common/event.js'; --import { Disposable, IReference, toDisposable } from '../../../../base/common/lifecycle.js'; --import { join } from '../../../../base/common/path.js'; --import { extUriBiasedIgnorePathCase, normalizePath } from '../../../../base/common/resources.js'; --import { URI } from '../../../../base/common/uri.js'; --import { generateUuid } from '../../../../base/common/uuid.js'; --import type { IParsedPlugin } from '../../../agentPlugins/common/pluginParsers.js'; --import { INativeEnvironmentService } from '../../../environment/common/environment.js'; --import { IFileService } from '../../../files/common/files.js'; --import { IInstantiationService } from '../../../instantiation/common/instantiation.js'; --import { ILogService } from '../../../log/common/log.js'; --import { IAgentAttachment, IAgentProgressEvent, SessionHistoryEvent } from '../../common/agentService.js'; --import { stripRedundantCdPrefix } from '../../common/commandLineHelpers.js'; --import { ISessionDatabase, ISessionDataService } from '../../common/sessionDataService.js'; --import type { FileEdit, ToolDefinition } from '../../common/state/protocol/state.js'; --import { SessionInputAnswerState, SessionInputAnswerValueKind, SessionInputQuestionKind, SessionInputResponseKind, ToolResultContentType, type PendingMessage, type SessionInputAnswer, type SessionInputRequest, type ToolCallResult, type ToolResultContent } from '../../common/state/sessionState.js'; --import { CopilotSessionWrapper } from './copilotSessionWrapper.js'; --import type { ShellManager } from './copilotShellTools.js'; --import { getEditFilePath, getInvocationMessage, getPastTenseMessage, getPermissionDisplay, getShellLanguage, getSubagentMetadata, getToolDisplayName, getToolInputString, getToolKind, isEditTool, isHiddenTool, isShellTool, synthesizeSkillToolEvents, tryStringify, type ITypedPermissionRequest } from './copilotToolDisplay.js'; --import { FileEditTracker } from './fileEditTracker.js'; --import { mapSessionEvents } from './mapSessionEvents.js'; --import { buildPendingEditContentUri } from './pendingEditContentStore.js'; -- --const COPILOT_HOME_DIRECTORY = '.copilot'; --const SESSION_STATE_DIRECTORY = join(COPILOT_HOME_DIRECTORY, 'session-state'); -- --type UserInputHandler = NonNullable; --type UserInputRequest = Parameters[0]; --type UserInputResponse = Awaited>; --type SessionHooks = NonNullable; --type PreToolUseHookInput = Parameters>[0]; --type PostToolUseHookInput = Parameters>[0]; -- --function getCopilotCLISessionStateDir(userHome: string): string { -- const xdgHome = process.env['XDG_STATE_HOME']; -- return xdgHome ? join(xdgHome, SESSION_STATE_DIRECTORY) : join(userHome, SESSION_STATE_DIRECTORY); --} -- --/** -- * Immutable snapshot of the active client's contributions at session creation -- * time. Used to detect when the session needs to be refreshed. -- */ --export interface IActiveClientSnapshot { -- readonly clientId: string; -- readonly tools: readonly ToolDefinition[]; -- readonly plugins: readonly IParsedPlugin[]; --} -- --/** -- * 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: ITypedPermissionRequest) => Promise; -- readonly onUserInputRequest: (request: UserInputRequest, invocation: { sessionId: string }) => Promise; -- readonly hooks: { -- readonly onPreToolUse: (input: PreToolUseHookInput) => Promise; -- readonly onPostToolUse: (input: PostToolUseHookInput) => Promise; -- }; -- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- readonly clientTools: Tool[]; --}) => Promise; -- --/** -- * Options for constructing a {@link CopilotAgentSession}. -- */ --export interface ICopilotAgentSessionOptions { -- readonly sessionUri: URI; -- readonly rawSessionId: string; -- readonly onDidSessionProgress: Emitter; -- readonly wrapperFactory: SessionWrapperFactory; -- readonly shellManager: ShellManager | undefined; -- /** Working directory associated with the session, used to strip redundant `cd` prefixes from shell commands. */ -- readonly workingDirectory?: URI; -- /** Snapshot of the active client's tools and plugins at session creation time. */ -- readonly clientSnapshot?: IActiveClientSnapshot; --} -- --/** -- * 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; content: ToolResultContent[] }>(); -- /** 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; -- -- /** Snapshot captured at session creation for refresh detection. */ -- private readonly _appliedSnapshot: IActiveClientSnapshot; -- /** Tool names that are client-provided, derived from snapshot. */ -- private readonly _clientToolNames: ReadonlySet; -- /** Deferred promises for pending client tool calls, keyed by toolCallId. */ -- private readonly _pendingClientToolCalls = new Map>(); -- /** `pending-edit-content:` URIs written during permission requests, keyed -- * by toolCallId. Cleaned up when the permission resolves or the session -- * is disposed. */ -- private readonly _pendingEditContentUris = new Map(); -- -- private readonly _onDidSessionProgress: Emitter; -- private readonly _wrapperFactory: SessionWrapperFactory; -- private readonly _shellManager: ShellManager | undefined; -- private readonly _workingDirectory: URI | undefined; -- -- constructor( -- options: ICopilotAgentSessionOptions, -- @IInstantiationService private readonly _instantiationService: IInstantiationService, -- @ILogService private readonly _logService: ILogService, -- @ISessionDataService sessionDataService: ISessionDataService, -- @IFileService private readonly _fileService: IFileService, -- @INativeEnvironmentService private readonly _environmentService: INativeEnvironmentService, -- ) { -- super(); -- this.sessionId = options.rawSessionId; -- this.sessionUri = options.sessionUri; -- this._onDidSessionProgress = options.onDidSessionProgress; -- this._wrapperFactory = options.wrapperFactory; -- this._shellManager = options.shellManager; -- this._workingDirectory = options.workingDirectory; -- -- this._appliedSnapshot = options.clientSnapshot ?? { clientId: '', tools: [], plugins: [] }; -- this._clientToolNames = new Set(this._appliedSnapshot.tools.map(t => t.name)); -- -- this._databaseRef = sessionDataService.openDatabase(options.sessionUri); -- this._register(toDisposable(() => this._databaseRef.dispose())); -- -- this._editTracker = this._instantiationService.createInstance(FileEditTracker, options.sessionUri.toString(), this._databaseRef.object); -- -- this._register(toDisposable(() => this._denyPendingPermissions())); -- this._register(toDisposable(() => this._shellManager?.dispose())); -- this._register(toDisposable(() => this._cancelPendingUserInputs())); -- -- // When a shell tool associates a terminal with a tool call, fire a -- // tool_content_changed event so the UI can connect to the terminal -- // while the command is still running. -- if (this._shellManager) { -- this._register(this._shellManager.onDidAssociateTerminal(({ toolCallId, terminalUri, displayName }) => { -- const tracked = this._activeToolCalls.get(toolCallId); -- if (!tracked) { -- return; -- } -- -- tracked.content.push({ -- type: ToolResultContentType.Terminal, -- resource: terminalUri, -- title: displayName, -- }); -- -- this._onDidSessionProgress.fire({ -- session: this.sessionUri, -- type: 'tool_content_changed', -- toolCallId, -- content: tracked.content, -- }); -- })); -- } -- this._register(toDisposable(() => this._cancelPendingClientToolCalls())); -- } -- -- /** -- * The snapshot of client contributions captured when this session was -- * created. Used by the agent to detect when the session is 1stale. -- */ -- get appliedSnapshot(): IActiveClientSnapshot { -- return this._appliedSnapshot; -- } -- -- /** -- * Creates SDK {@link Tool} objects for the client-provided tools in the -- * applied snapshot. The handler creates a {@link DeferredPromise} and waits -- * for the client to dispatch `session/toolCallComplete`. -- */ -- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- createClientSdkTools(): Tool[] { -- const tools = this._appliedSnapshot.tools; -- if (tools.length === 0) { -- return []; -- } -- return tools.map(def => ({ -- name: def.name, -- description: def.description ?? '', -- parameters: def.inputSchema ?? { type: 'object' as const, properties: {} }, -- handler: async (_args: Record, { toolCallId }) => { -- try { -- let deferred = this._pendingClientToolCalls.get(toolCallId); -- if (!deferred) { -- deferred = new DeferredPromise(); -- this._pendingClientToolCalls.set(toolCallId, deferred); -- } -- const result = await deferred.p; -- this._pendingClientToolCalls.delete(toolCallId); -- return result; -- } catch (error) { -- this._logService.error(error, `[Copilot:${this.sessionId}] Failed in client tool handler: tool=${def.name}, toolCallId=${toolCallId}`); -- throw error; -- } -- }, -- })); -- } -- -- /** -- * Resolves a pending client tool call. Returns `true` if the -- * toolCallId was found and handled. -- */ -- handleClientToolCallComplete(toolCallId: string, result: ToolCallResult) { -- let deferred = this._pendingClientToolCalls.get(toolCallId); -- if (!deferred) { -- deferred = new DeferredPromise(); -- this._pendingClientToolCalls.set(toolCallId, deferred); -- } -- -- const textContent = result.content -- ?.filter(c => c.type === ToolResultContentType.Text) -- .map(c => c.text) -- .join('\n') ?? ''; -- -- const binaryResults = result.content -- ?.filter(c => c.type === ToolResultContentType.EmbeddedResource) -- .map(c => { -- return { data: c.data, mimeType: c.contentType, type: c.contentType }; -- }); -- -- if (result.success) { -- deferred.complete({ -- textResultForLlm: textContent, -- resultType: 'success', -- binaryResultsForLlm: binaryResults?.length ? binaryResults : undefined, -- }); -- } else { -- deferred.complete({ -- textResultForLlm: textContent || result.error?.message || 'Tool call failed', -- resultType: 'failure', -- error: result.error?.message, -- binaryResultsForLlm: binaryResults?.length ? binaryResults : undefined, -- }); -- } -- } -- -- /** -- * 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), -- clientTools: this.createClientSdkTools(), -- hooks: { -- onPreToolUse: input => this._handlePreToolUse(input), -- onPostToolUse: input => this._handlePostToolUse(input), -- }, -- })); -- 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 => { -- const path = a.uri.scheme === 'file' ? a.uri.fsPath : a.uri.toString(); -- if (a.type === 'selection') { -- return { type: 'selection' as const, filePath: path, displayName: a.displayName ?? path, text: a.text, selection: a.selection }; -- } -- return { type: a.type, 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: PendingMessage): 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 { -- 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, this._workingDirectory); -- } -- -- 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, reasoningEffort?: SessionConfig['reasoningEffort']): Promise { -- this._logService.info(`[Copilot:${this.sessionId}] Changing model to: ${model}`); -- await this._wrapper.session.setModel(model, { reasoningEffort }); -- } -- -- // ---- 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: ITypedPermissionRequest, -- ): Promise { -- this._logService.info(`[Copilot:${this.sessionId}] Permission request: kind=${request.kind}`); -- -- try { -- 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' }; -- } -- -- const sessionResourcePath = this._getInternalSessionResourcePath(request); -- if (sessionResourcePath) { -- this._logService.info(`[Copilot:${this.sessionId}] Auto-approving internal session resource ${sessionResourcePath}`); -- return { kind: 'approved' }; -- } -- -- 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, permissionPath } = getPermissionDisplay(request, this._workingDirectory); -- -- // For write permission requests, build an FileEdit preview so the -- // client can show a diff before the user approves or denies. This -- // awaits async filesystem operations; the SDK already calls -- // `handlePermissionRequest` from an arbitrary async context, so the -- // extra await here is fine. -- const edits = await this._buildEditsForPermission(request, toolCallId); -- -- // If the session was aborted/disposed while we were building the -- // preview, the deferred has already been resolved and the -- // `pending-edit-content:` entry has been cleaned up. Bail without -- // firing tool_ready. -- if (!this._pendingPermissions.has(toolCallId)) { -- return { kind: 'denied-interactively-by-user' }; -- } -- -- // 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, -- edits, -- }); -- -- 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' }; -- } catch (error) { -- this._logService.error(error, `[Copilot:${this.sessionId}] Failed to handle permission request: kind=${request.kind}, toolCallId=${request.toolCallId ?? 'missing'}`); -- throw error; -- } -- } -- -- private _getInternalSessionResourcePath(request: ITypedPermissionRequest): string | undefined { -- let permissionPath: string | undefined; -- if (request.kind === 'read') { -- permissionPath = typeof request.path === 'string' ? request.path : undefined; -- } else if (request.kind === 'write') { -- permissionPath = typeof request.fileName === 'string' ? request.fileName : undefined; -- } -- -- if (!permissionPath) { -- return undefined; -- } -- -- const sessionStateDir = normalizePath(URI.file(getCopilotCLISessionStateDir(this._environmentService.userHome.fsPath))); -- const sessionDir = normalizePath(URI.joinPath(sessionStateDir, this.sessionId)); -- if (!extUriBiasedIgnorePathCase.isEqualOrParent(sessionDir, sessionStateDir)) { -- return undefined; -- } -- -- const permissionUri = normalizePath(URI.file(permissionPath)); -- return extUriBiasedIgnorePathCase.isEqualOrParent(permissionUri, sessionDir) ? permissionPath : undefined; -- } -- -- /** -- * Builds an {@link FileEdit} preview for a write permission request. -- * -- * The `before` side references the existing file on disk directly (if it -- * exists); the `after` side is written to the `pending-edit-content:` -- * in-memory filesystem so the client can fetch it via `resourceRead`. -- * -- * Returns `undefined` for permission kinds that don't describe file -- * edits or when the request is missing the fields needed to build a -- * preview. If the permission request is no longer pending by the time -- * the in-memory write completes (e.g. the session was aborted), the -- * just-written entry is deleted so it cannot leak. -- */ -- private async _buildEditsForPermission(request: ITypedPermissionRequest, toolCallId: string): Promise<{ items: FileEdit[] } | undefined> { -- if (request.kind !== 'write') { -- return undefined; -- } -- const filePath = typeof request.fileName === 'string' ? request.fileName : undefined; -- const newFileContents = typeof request.newFileContents === 'string' ? request.newFileContents : undefined; -- if (!filePath || newFileContents === undefined) { -- return undefined; -- } -- -- const fileUri = URI.file(filePath); -- const fileUriStr = fileUri.toString(); -- -- let beforeExists = false; -- try { -- beforeExists = await this._fileService.exists(fileUri); -- } catch (err) { -- this._logService.warn(`[Copilot:${this.sessionId}] Failed to check file for edit preview: ${filePath}`, err); -- } -- -- const afterUri = buildPendingEditContentUri(this.sessionUri.toString(), toolCallId, filePath); -- try { -- await this._fileService.writeFile(afterUri, VSBuffer.fromString(newFileContents)); -- } catch (err) { -- this._logService.warn(`[Copilot:${this.sessionId}] Failed to write pending edit content for ${filePath}`, err); -- return undefined; -- } -- -- // If the request was already resolved (aborted/disposed) while we -- // were awaiting the write, drop the in-memory entry immediately; -- // `_deletePendingEditContent` has already run and won't run again. -- if (!this._pendingPermissions.has(toolCallId)) { -- this._fileService.del(afterUri).catch(err => { -- this._logService.warn(`[Copilot:${this.sessionId}] Failed to delete orphaned pending edit content: ${afterUri.toString()}`, err); -- }); -- return undefined; -- } -- this._pendingEditContentUris.set(toolCallId, afterUri); -- -- const diffCounts = typeof request.diff === 'string' ? countUnifiedDiffLines(request.diff) : undefined; -- -- const edit: FileEdit = { -- ...(beforeExists ? { before: { uri: fileUriStr, content: { uri: fileUriStr } } } : {}), -- after: { uri: fileUriStr, content: { uri: afterUri.toString() } }, -- ...(diffCounts ? { diff: diffCounts } : {}), -- }; -- return { items: [edit] }; -- } -- -- respondToPermissionRequest(requestId: string, approved: boolean): boolean { -- const deferred = this._pendingPermissions.get(requestId); -- if (deferred) { -- this._pendingPermissions.delete(requestId); -- this._deletePendingEditContent(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: UserInputRequest, -- _invocation: { sessionId: string }, -- ): Promise { -- const questionPreview = request.question.substring(0, 100); -- try { -- const requestId = generateUuid(); -- const questionId = generateUuid(); -- this._logService.info(`[Copilot:${this.sessionId}] User input request: requestId=${requestId}, question="${questionPreview}"`); -- -- const deferred = new DeferredPromise<{ response: SessionInputResponseKind; answers?: Record }>(); -- this._pendingUserInputs.set(requestId, { deferred, questionId }); -- -- // Build the protocol SessionInputRequest from the SDK's simple format -- const inputRequest: SessionInputRequest = { -- 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 }; -- } catch (error) { -- this._logService.error(error, `[Copilot:${this.sessionId}] Failed to handle user input request: question="${questionPreview}"`); -- throw error; -- } -- } -- -- 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; -- } -- -- private async _handlePreToolUse(input: PreToolUseHookInput): Promise { -- try { -- if (isEditTool(input.toolName)) { -- const filePath = getEditFilePath(input.toolArgs); -- if (filePath) { -- await this._editTracker.trackEditStart(filePath); -- } -- } -- } catch (error) { -- this._logService.error(error, `[Copilot:${this.sessionId}] Failed in onPreToolUse: tool=${input.toolName}`); -- throw error; -- } -- } -- -- private async _handlePostToolUse(input: PostToolUseHookInput): Promise { -- try { -- if (isEditTool(input.toolName)) { -- const filePath = getEditFilePath(input.toolArgs); -- if (filePath) { -- await this._editTracker.completeEdit(filePath); -- } -- } -- } catch (error) { -- this._logService.error(error, `[Copilot:${this.sessionId}] Failed in onPostToolUse: tool=${input.toolName}`); -- throw error; -- } -- } -- -- // ---- event wiring ------------------------------------------------------- -- -- private _subscribeToEvents(): void { -- const wrapper = this._wrapper; -- const sessionId = this.sessionId; -- const session = this.sessionUri; -- -- // Capture SDK event IDs for each user.message event so we can map -- // protocol turn indices to the event IDs needed by the SDK's -- // history.truncate and sessions.fork RPCs. -- this._register(wrapper.onUserMessage(e => { -- if (this._turnId) { -- this._databaseRef.object.setTurnEventId(this._turnId, e.id); -- } -- })); -- -- 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}`); -- let 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 */ } -- } -- // Strip redundant `cd && …` prefixes from shell tool -- // commands so clients see the simplified form. Mirrors the logic in -- // mapSessionEvents (which handles the history-replay path). -- if (stripRedundantCdPrefix(e.data.toolName, parameters, this._workingDirectory)) { -- toolArgs = tryStringify(parameters); -- } -- const displayName = getToolDisplayName(e.data.toolName); -- this._activeToolCalls.set(e.data.toolCallId, { toolName: e.data.toolName, displayName, parameters, content: [] }); -- const toolKind = getToolKind(e.data.toolName); -- const subagentMeta = toolKind === 'subagent' ? getSubagentMetadata(parameters) : undefined; -- -- 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, -- subagentAgentName: subagentMeta?.agentName, -- subagentDescription: subagentMeta?.description, -- mcpServerName: e.data.mcpServerName, -- mcpToolName: e.data.mcpToolName, -- parentToolCallId: e.data.parentToolCallId, -- toolClientId: this._clientToolNames.has(e.data.toolName) ? this._appliedSnapshot.clientId : undefined, -- }); -- })); -- -- 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: ToolResultContent[] = [...tracked.content]; -- 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 (skip if already -- // added during onDidAssociateTerminal while the tool was running) -- if (isShellTool(tracked.toolName) && this._shellManager) { -- const terminalUri = this._shellManager.getTerminalUriForToolCall(e.data.toolCallId); -- if (terminalUri && !content.some(c => c.type === ToolResultContentType.Terminal && c.resource === 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' }); -- })); -- -- // The SDK emits a `skill` tool call (which we hide) and a richer -- // `skill.invoked` event with the resolved SKILL.md path. Synthesize a -- // tool-start/complete pair from the latter so the UI can render a -- // clickable file link, matching the `view`-tool display style. -- this._register(wrapper.onSkillInvoked(e => { -- this._logService.info(`[Copilot:${sessionId}] Skill invoked: ${e.data.name} (${e.data.path})`); -- const { start, complete } = synthesizeSkillToolEvents(session, e.data, e.id); -- this._onDidSessionProgress.fire(start); -- this._onDidSessionProgress.fire(complete); -- })); -- -- 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`); -- })); -- } -- -- // ---- SDK event ID tracking & truncation --------------------------------- -- -- /** -- * Returns the SDK event ID for the turn inserted after the given turn, -- * or `undefined` if it's the last turn. -- */ -- getNextTurnEventId(turnId: string): Promise { -- return this._databaseRef.object.getNextTurnEventId(turnId); -- } -- -- /** -- * Returns the SDK event ID of the earliest turn. -- */ -- getFirstTurnEventId(): Promise { -- return this._databaseRef.object.getFirstTurnEventId(); -- } -- -- /** -- * Truncates the session history via the SDK's RPC and cleans up -- * stale turns from the session database. -- * -- * @param eventId The SDK event ID at which to truncate. This event -- * and all events after it are removed. -- * @param keepTurnId If provided, turns inserted after this turn are -- * deleted from the DB. If omitted, all turns are deleted. -- */ -- async truncateAtEventId(eventId: string, keepTurnId?: string): Promise { -- this._logService.info(`[Copilot:${this.sessionId}] Truncating via SDK RPC at eventId=${eventId}`); -- const result = await this._wrapper.session.rpc.history.truncate({ eventId }); -- this._logService.info(`[Copilot:${this.sessionId}] SDK truncation removed ${result.eventsRemoved} events`); -- -- // Clean up stale turns from our DB so getNextTurnEventId doesn't -- // return event IDs for turns that no longer exist in the SDK. -- if (keepTurnId) { -- await this._databaseRef.object.deleteTurnsAfter(keepTurnId); -- } else { -- await this._databaseRef.object.deleteAllTurns(); -- } -- } -- -- /** -- * Bulk-remaps turn IDs in this session's database. -- * Used after file-copying a source session's database for a fork. -- */ -- async remapTurnIds(mapping: ReadonlyMap): Promise { -- await this._databaseRef.object.remapTurnIds(mapping); -- } -- -- // ---- cleanup ------------------------------------------------------------ -- -- private _denyPendingPermissions(): void { -- for (const [toolCallId, deferred] of this._pendingPermissions) { -- this._deletePendingEditContent(toolCallId); -- deferred.complete(false); -- } -- this._pendingPermissions.clear(); -- } -- -- /** -- * Removes any `pending-edit-content:` entries associated with a resolved -- * (approved, denied, or cancelled) permission request. -- */ -- private _deletePendingEditContent(toolCallId: string): void { -- const uri = this._pendingEditContentUris.get(toolCallId); -- if (!uri) { -- return; -- } -- this._pendingEditContentUris.delete(toolCallId); -- this._fileService.del(uri).catch(err => { -- this._logService.warn(`[Copilot:${this.sessionId}] Failed to delete pending edit content: ${uri.toString()}`, err); -- }); -- } -- -- private _cancelPendingUserInputs(): void { -- for (const [, pending] of this._pendingUserInputs) { -- pending.deferred.complete({ response: SessionInputResponseKind.Cancel }); -- } -- this._pendingUserInputs.clear(); -- } -- -- private _cancelPendingClientToolCalls(): void { -- for (const [, deferred] of this._pendingClientToolCalls) { -- deferred.complete({ textResultForLlm: 'Tool call cancelled: session ended', resultType: 'failure', error: 'Session ended' }); -- } -- this._pendingClientToolCalls.clear(); -- } --} -- --/** -- * Counts added/removed lines in a unified diff string. Ignores the `+++` and -- * `---` header rows and any non-hunk context. -- */ --function countUnifiedDiffLines(diff: string): { added: number; removed: number } | undefined { -- let added = 0; -- let removed = 0; -- for (const line of diff.split('\n')) { -- if (line.startsWith('+++') || line.startsWith('---')) { -- continue; -- } -- if (line.startsWith('+')) { -- added++; -- } else if (line.startsWith('-')) { -- removed++; -- } -- } -- if (added === 0 && removed === 0) { -- return undefined; -- } -- return { added, removed }; --} -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 e45ca942..00000000 ---- a/src/vs/platform/agentHost/node/copilot/copilotGitProject.ts -+++ /dev/null -@@ -1,58 +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 { 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'; --import type { IAgentHostGitService } from '../agentHostGitService.js'; -- --export interface ICopilotSessionContext { -- readonly cwd?: string; -- readonly gitRoot?: string; -- readonly repository?: string; --} -- --export async function resolveGitProject(workingDirectory: URI | undefined, gitService: IAgentHostGitService): Promise { -- if (!workingDirectory || workingDirectory.scheme !== Schemas.file) { -- return undefined; -- } -- -- if (!await gitService.isInsideWorkTree(workingDirectory)) { -- return undefined; -- } -- -- const uri = (await gitService.getWorktreeRoots(workingDirectory))[0] -- ?? await gitService.getRepositoryRoot(workingDirectory); -- if (!uri) { -- return undefined; -- } -- 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, gitService: IAgentHostGitService): 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, gitService); -- 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 61e93e5a..00000000 ---- a/src/vs/platform/agentHost/node/copilot/copilotPluginConverters.ts -+++ /dev/null -@@ -1,350 +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; --type PreToolUseHookInput = Parameters>[0]; --type PostToolUseHookInput = Parameters>[0]; --type UserPromptSubmittedHookInput = Parameters>[0]; --type SessionStartHookInput = Parameters>[0]; --type SessionEndHookInput = Parameters>[0]; --type ErrorOccurredHookInput = Parameters>[0]; -- --// --------------------------------------------------------------------------- --// 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}`)); -- } -- }); -- }); --} -- --/** -- * Runs a list of hook commands sequentially, passing `input` as JSON stdin. -- * Returns the parsed output of the first command that emits a valid JSON object, -- * or `undefined` if no command produces parseable JSON output. -- * Command failures are swallowed — hooks are non-fatal. -- */ --async function runHookCommands(commands: readonly IParsedHookCommand[] | undefined, input: unknown): Promise { -- if (!commands) { -- return undefined; -- } -- const stdin = JSON.stringify(input); -- for (const cmd of commands) { -- 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 -- } -- } -- return undefined; --} -- --/** -- * 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: PreToolUseHookInput) => Promise; -- readonly onPostToolUse: (input: PostToolUseHookInput) => 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: PreToolUseHookInput) => { -- await editTrackingHooks?.onPreToolUse(input); -- return runHookCommands(preToolCommands, input); -- }; -- } -- -- // Post-tool-use handler -- const postToolCommands = commandsByKey.get('onPostToolUse'); -- if (postToolCommands?.length || editTrackingHooks) { -- hooks.onPostToolUse = async (input: PostToolUseHookInput) => { -- await editTrackingHooks?.onPostToolUse(input); -- return runHookCommands(postToolCommands, input); -- }; -- } -- -- // User-prompt-submitted handler -- const promptCommands = commandsByKey.get('onUserPromptSubmitted'); -- if (promptCommands?.length) { -- hooks.onUserPromptSubmitted = async (input: UserPromptSubmittedHookInput) => { -- 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: SessionStartHookInput) => { -- 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: SessionEndHookInput) => { -- 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: ErrorOccurredHookInput) => { -- 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 c46d2c75..00000000 ---- a/src/vs/platform/agentHost/node/copilot/copilotShellTools.ts -+++ /dev/null -@@ -1,728 +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 { Emitter, Event } from '../../../../base/common/event.js'; --import { ILogService } from '../../../log/common/log.js'; --import { TerminalClaimKind, type TerminalSessionClaim } 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(); -- -- private readonly _onDidAssociateTerminal = new Emitter<{ toolCallId: string; terminalUri: string; displayName: string }>(); -- readonly onDidAssociateTerminal: Event<{ toolCallId: string; terminalUri: string; displayName: string }> = this._onDidAssociateTerminal.event; -- -- constructor( -- private readonly _sessionUri: URI, -- private readonly _workingDirectory: URI | undefined, -- @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: TerminalSessionClaim = { -- 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: cwd ?? this._workingDirectory?.fsPath, -- }, { shell: getShellExecutable(shellType), preventShellHistory: true, nonInteractive: true }); -- -- 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); -- const shell = this._shells.get(shellId); -- if (shell) { -- const displayName = shell.shellType === 'bash' ? 'Bash' : 'PowerShell'; -- this._onDidAssociateTerminal.fire({ toolCallId, terminalUri: shell.terminalUri, displayName }); -- } -- } -- -- 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_$?>>>"`; --} -- --/** -- * For POSIX shells (bash/zsh) that honor `HISTCONTROL=ignorespace` / -- * `HIST_IGNORE_SPACE`, prepending a single space prevents the command from -- * being recorded in shell history. The shell integration scripts opt these -- * settings in via the `VSCODE_PREVENT_SHELL_HISTORY` env var (set when the -- * terminal is created with `preventShellHistory: true`). PowerShell -- * suppresses history through PSReadLine instead, so no prefix is needed. -- * -- * Exported for tests. -- */ --export function prefixForHistorySuppression(shellType: ShellType): string { -- return shellType === 'powershell' ? '' : ' '; --} -- --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 result = terminalManager.supportsCommandDetection(shell.terminalUri) -- ? await executeCommandWithShellIntegration(shell, command, timeoutMs, terminalManager, logService) -- : await executeCommandWithSentinel(shell, command, timeoutMs, terminalManager, logService); -- return { -- ...result, -- textResultForLlm: `Shell ID: ${shell.id}\n${result.textResultForLlm}`, -- }; --} -- --/** -- * Execute a command using shell integration (OSC 633) for completion detection. -- * No sentinel echo is injected — the shell's own command-finished signal -- * provides the exit code and cleanly delineated output. -- */ --async function executeCommandWithShellIntegration( -- shell: IManagedShell, -- command: string, -- timeoutMs: number, -- terminalManager: IAgentHostTerminalManager, -- logService: ILogService, --): Promise { -- const disposables = new DisposableStore(); -- -- terminalManager.writeInput(shell.terminalUri, `${prefixForHistorySuppression(shell.shellType)}${command}\r`); -- -- return new Promise(resolve => { -- let resolved = false; -- const finish = (result: ToolResultObject) => { -- if (resolved) { -- return; -- } -- resolved = true; -- disposables.dispose(); -- resolve(result); -- }; -- -- disposables.add(terminalManager.onCommandFinished(shell.terminalUri, event => { -- const output = prepareOutputForModel(event.output); -- const exitCode = event.exitCode ?? 0; -- logService.info(`[ShellTool] Command completed (shell integration) with exit code ${exitCode}`); -- if (exitCode === 0) { -- finish(makeSuccessResult(`Exit code: ${exitCode}\n${output}`)); -- } else { -- finish(makeFailureResult(`Exit code: ${exitCode}\n${output}`)); -- } -- })); -- -- disposables.add(terminalManager.onExit(shell.terminalUri, (exitCode: number) => { -- logService.info(`[ShellTool] Shell exited unexpectedly with code ${exitCode}`); -- const fullContent = terminalManager.getContent(shell.terminalUri) ?? ''; -- const output = prepareOutputForModel(fullContent); -- 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 output = prepareOutputForModel(fullContent); -- finish(makeFailureResult( -- `Command timed out after ${Math.round(timeoutMs / 1000)}s. Partial output:\n${output}`, -- 'timeout', -- )); -- }, timeoutMs); -- disposables.add(toDisposable(() => clearTimeout(timer))); -- }); --} -- --/** -- * Fallback: execute a command using a sentinel echo to detect completion. -- * Used when shell integration is not available. -- */ --async function executeCommandWithSentinel( -- 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 = `${prefixForHistorySuppression(shell.shellType)}${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: shellType === 'bash' ? createBashModelDescription(false) : createPowerShellModelDescription(shellType, 'pwsh.exe', false), -- 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, -- skipPermission: 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, -- skipPermission: 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, -- skipPermission: 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, -- skipPermission: 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]; --} --interface ITerminalSandboxResolvedNetworkDomains { -- allowedDomains: string[]; -- deniedDomains: string[]; --} -- --function isWindowsPowerShell(envShell: string): boolean { -- return envShell.endsWith('System32\\WindowsPowerShell\\v1.0\\powershell.exe'); --} -- --function createPowerShellModelDescription(shellType: string, shellPath: string, isSandboxEnabled: boolean, networkDomains?: ITerminalSandboxResolvedNetworkDomains): string { -- const isWinPwsh = isWindowsPowerShell(shellPath); -- const parts = [ -- `This tool allows you to execute ${isWinPwsh ? 'Windows PowerShell 5.1' : 'PowerShell'} commands in a persistent terminal session, preserving environment variables, working directory, and other context across multiple commands.`, -- '', -- 'Command Execution:', -- // IMPORTANT: PowerShell 5 does not support `&&` so always re-write them to `;`. Note that -- // the behavior of `&&` differs a little from `;` but in general it's fine -- isWinPwsh ? '- Use semicolons ; to chain commands on one line, NEVER use && even when asked explicitly' : '- Prefer ; when chaining commands on one line', -- '- Prefer pipelines | for object-based data flow', -- '- Never create a sub-shell (eg. powershell -c "command") unless explicitly asked', -- '', -- 'Directory Management:', -- '- Prefer relative paths when navigating directories, only use absolute when the path is far away or the current cwd is not expected', -- '- By default (mode=sync), shell and cwd are reused by subsequent sync commands', -- '- Use $PWD or Get-Location for current directory', -- '- Use Push-Location/Pop-Location for directory stack', -- '', -- 'Program Execution:', -- '- Supports .NET, Python, Node.js, and other executables', -- '- Install modules via Install-Module, Install-Package', -- '- Use Get-Command to verify cmdlet/function availability', -- '', -- 'Async Mode:', -- '- For long-running tasks (e.g., servers), use mode=async', -- '- Returns a terminal ID for checking status and runtime later', -- '- Use Start-Job for background PowerShell jobs', -- '', -- `Use write_${shellType} to send commands or input to a terminal session.`, -- ]; -- -- if (isSandboxEnabled) { -- parts.push(...createSandboxLines(networkDomains)); -- } -- -- parts.push( -- '', -- 'Output Management:', -- '- Output is automatically truncated if longer than 60KB to prevent context overflow', -- '- Use Select-Object, Where-Object, Format-Table to filter output', -- '- Use -First/-Last parameters to limit results', -- '- For pager commands, add | Out-String or | Format-List', -- '', -- 'Best Practices:', -- '- Use proper cmdlet names instead of aliases in scripts', -- '- Quote paths with spaces: "C:\\Path With Spaces"', -- '- Prefer PowerShell cmdlets over external commands when available', -- '- Prefer idiomatic PowerShell like Get-ChildItem instead of dir or ls for file listings', -- '- Use Test-Path to check file/directory existence', -- '- Be specific with Select-Object properties to avoid excessive output', -- '- Avoid printing credentials unless absolutely required', -- '', -- 'Interactive Input Handling:', -- '- When a terminal command is waiting for interactive input, do NOT suggest alternatives or ask the user whether to proceed. Instead, use the ask_user tool to collect the needed values from the user, then send them.', -- `- Send exactly one answer per prompt using write_${shellType}. Never send multiple answers in a single send.`, -- `- After each send, call read_${shellType} to read the next prompt before sending the next answer.`, -- '- Continue one prompt at a time until the command finishes.', -- ); -- -- return parts.join('\n'); --} -- --function createSandboxLines(networkDomains?: ITerminalSandboxResolvedNetworkDomains): string[] { -- const lines = [ -- '', -- 'Sandboxing:', -- '- ATTENTION: Terminal sandboxing is enabled, commands run in a sandbox by default', -- '- When executing commands within the sandboxed environment, all operations requiring a temporary directory must utilize the $TMPDIR environment variable. The /tmp directory is not guaranteed to be accessible or writable and must be avoided', -- '- Tools and scripts should respect the TMPDIR environment variable, which is automatically set to an appropriate path within the sandbox', -- '- When a command fails due to sandbox restrictions, immediately re-run it with requestUnsandboxedExecution=true. Do NOT ask the user for permission — setting this flag automatically shows a confirmation prompt to the user', -- '- Only set requestUnsandboxedExecution=true when there is evidence of failures caused by the sandbox, e.g. \'Operation not permitted\' errors, network failures, or file access errors, etc', -- '- Do NOT set requestUnsandboxedExecution=true without first executing the command in sandbox mode. Always try the command in the sandbox first, and only set requestUnsandboxedExecution=true when retrying after that sandboxed execution failed due to sandbox restrictions.', -- '- When setting requestUnsandboxedExecution=true, also provide requestUnsandboxedExecutionReason explaining why the command needs unsandboxed access', -- ]; -- if (networkDomains) { -- const deniedSet = new Set(networkDomains.deniedDomains); -- const effectiveAllowed = networkDomains.allowedDomains.filter(d => !deniedSet.has(d)); -- if (effectiveAllowed.length === 0) { -- lines.push('- All network access is blocked in the sandbox'); -- } else { -- lines.push(`- Only the following domains are accessible in the sandbox (all other network access is blocked): ${effectiveAllowed.join(', ')}`); -- } -- if (networkDomains.deniedDomains.length > 0) { -- lines.push(`- The following domains are explicitly blocked in the sandbox: ${networkDomains.deniedDomains.join(', ')}`); -- } -- } -- return lines; --} -- --function createGenericDescription(shellType: string, isSandboxEnabled: boolean, networkDomains?: ITerminalSandboxResolvedNetworkDomains): string { -- const parts = [` --Command Execution: --- Use && to chain simple commands on one line --- Prefer pipelines | over temporary files for data flow --- Never create a sub-shell (eg. bash -c "command") unless explicitly asked -- --Directory Management: --- Prefer relative paths when navigating directories, only use absolute when the path is far away or the current cwd is not expected --- By default (mode=sync), shell and cwd are reused by subsequent sync commands --- Use $PWD for current directory references --- Consider using pushd/popd for directory stack management --- Supports directory shortcuts like ~ and - -- --Program Execution: --- Supports Python, Node.js, and other executables --- Install packages via package managers (brew, apt, etc.) --- Use which or command -v to verify command availability -- --Async Mode: --- For long-running tasks (e.g., servers), use mode=async --- Returns a terminal ID for checking status and runtime later -- --Use write_${shellType} to send commands or input to a terminal session.`]; -- -- if (isSandboxEnabled) { -- parts.push(createSandboxLines(networkDomains).join('\n')); -- } -- -- parts.push(` -- --Output Management: --- Output is automatically truncated if longer than 60KB to prevent context overflow --- Use head, tail, grep, awk to filter and limit output size --- For pager commands, disable paging: git --no-pager or add | cat --- Use wc -l to count lines before displaying large outputs -- --Best Practices: --- Quote variables: "$var" instead of $var to handle spaces --- Use find with -exec or xargs for file operations --- Be specific with commands to avoid excessive output --- Avoid printing credentials unless absolutely required --- NEVER run sleep or similar wait commands in a terminal. You will be automatically notified on your next turn when async terminal commands or timed-out sync commands complete or need input. Use read_${shellType} to check output before then -- --Interactive Input Handling: --- When a terminal command is waiting for interactive input, do NOT suggest alternatives or ask the user whether to proceed. Instead, use the ask_user tool to collect the needed values from the user, then send them. --- Send exactly one answer per prompt using write_${shellType}. Never send multiple answers in a single send. --- After each send, call read_${shellType} to read the next prompt before sending the next answer. --- Continue one prompt at a time until the command finishes.`); -- -- return parts.join(''); --} -- --function createBashModelDescription(isSandboxEnabled: boolean, networkDomains?: ITerminalSandboxResolvedNetworkDomains): string { -- return [ -- 'This tool allows you to execute shell commands in a persistent bash terminal session, preserving environment variables, working directory, and other context across multiple commands.', -- createGenericDescription('bash', isSandboxEnabled, networkDomains), -- '- Use [[ ]] for conditional tests instead of [ ]', -- '- Prefer $() over backticks for command substitution', -- '- Use set -e at start of complex commands to exit on errors' -- ].join('\n'); --} -diff --git a/src/vs/platform/agentHost/node/copilot/copilotToolDisplay.ts b/src/vs/platform/agentHost/node/copilot/copilotToolDisplay.ts -deleted file mode 100644 -index 2d061529..00000000 ---- a/src/vs/platform/agentHost/node/copilot/copilotToolDisplay.ts -+++ /dev/null -@@ -1,735 +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 } from '@github/copilot-sdk'; --import { hasKey } from '../../../../base/common/types.js'; --import { URI } from '../../../../base/common/uri.js'; --import { appendEscapedMarkdownInlineCode, escapeMarkdownLinkLabel } from '../../../../base/common/htmlContent.js'; --import { hash } from '../../../../base/common/hash.js'; --import { localize } from '../../../../nls.js'; --import type { IAgentToolCompleteEvent, IAgentToolReadyEvent, IAgentToolStartEvent } from '../../common/agentService.js'; --import { stripRedundantCdPrefix } from '../../common/commandLineHelpers.js'; --import { StringOrMarkdown } from '../../common/state/protocol/state.js'; --import { basename } from '../../../../base/common/resources.js'; -- --// ============================================================================= --// Copilot CLI built-in tool interfaces --// --// The Copilot CLI (via @github/copilot) exposes these built-in tools. Tool names --// and parameter shapes are not typed in the SDK -- they come from the CLI server --// as plain strings. These interfaces are derived from observing the CLI's actual --// tool events and the ShellConfig class in @github/copilot. --// --// Shell tool names follow a pattern per ShellConfig: --// shellToolName, readShellToolName, writeShellToolName, --// stopShellToolName, listShellsToolName --// For bash: bash, read_bash, write_bash, bash_shutdown, list_bash --// For powershell: powershell, read_powershell, write_powershell, list_powershell --// ============================================================================= -- --/** -- * Known Copilot CLI tool names. These are the `toolName` values that appear -- * in `tool.execution_start` events from the SDK. -- */ --const enum CopilotToolName { -- Bash = 'bash', -- ReadBash = 'read_bash', -- WriteBash = 'write_bash', -- BashShutdown = 'bash_shutdown', -- ListBash = 'list_bash', -- -- PowerShell = 'powershell', -- ReadPowerShell = 'read_powershell', -- WritePowerShell = 'write_powershell', -- ListPowerShell = 'list_powershell', -- -- View = 'view', -- Edit = 'edit', -- Create = 'create', -- Grep = 'grep', -- Glob = 'glob', -- ApplyPatch = 'apply_patch', -- GitApplyPatch = 'git_apply_patch', -- WebSearch = 'web_search', -- WebFetch = 'web_fetch', -- AskUser = 'ask_user', -- ReportIntent = 'report_intent', -- Skill = 'skill', --} -- --/** Parameters for the `bash` / `powershell` shell tools. */ --interface ICopilotShellToolArgs { -- command: string; -- timeout?: number; --} -- --/** Parameters for file tools (`view`, `edit`, `create`). */ --interface ICopilotFileToolArgs { -- path: string; --} -- --/** -- * Parameters for the `view` tool. The Copilot CLI accepts an optional -- * `view_range: [startLine, endLine]` (1-based, inclusive). `endLine` may be -- * `-1` to mean "to end of file". -- */ --interface ICopilotViewToolArgs extends ICopilotFileToolArgs { -- view_range?: number[]; --} -- --/** -- * Normalizes a `view_range` array. Returns `undefined` unless the array has -- * exactly two integer elements with `startLine >= 0`. `endLine === -1` is -- * preserved as the "to end of file" sentinel; otherwise `endLine` must be -- * `>= startLine`. -- */ --function formatViewRange(view_range: number[] | undefined): { startLine: number; endLine: number } | undefined { -- if (!Array.isArray(view_range) || view_range.length !== 2) { -- return undefined; -- } -- const [startLine, endLine] = view_range; -- if (!Number.isInteger(startLine) || !Number.isInteger(endLine)) { -- return undefined; -- } -- if (startLine < 0) { -- return undefined; -- } -- if (endLine !== -1 && endLine < startLine) { -- return undefined; -- } -- return { startLine, endLine }; --} -- --/** Parameters for the `grep` tool. */ --interface ICopilotGrepToolArgs { -- pattern: string; -- path?: string; -- include?: string; --} -- --/** Parameters for the `glob` tool. */ --interface ICopilotGlobToolArgs { -- pattern: string; -- path?: string; --} -- --/** Set of tool names that perform file edits. */ --const EDIT_TOOL_NAMES: ReadonlySet = new Set([ -- CopilotToolName.Edit, -- CopilotToolName.Create, -- CopilotToolName.ApplyPatch, -- CopilotToolName.GitApplyPatch, --]); -- --/** -- * Returns true if the tool modifies files on disk. -- */ --export function isEditTool(toolName: string): boolean { -- return EDIT_TOOL_NAMES.has(toolName); --} -- --/** -- * Extracts the target file path from an edit tool's parameters, if available. -- */ --export function getEditFilePath(parameters: unknown): string | undefined { -- if (typeof parameters === 'string') { -- try { -- parameters = JSON.parse(parameters); -- } catch { -- return undefined; -- } -- } -- -- const args = parameters as ICopilotFileToolArgs | undefined; -- return args?.path; --} -- --/** Set of tool names that execute shell commands (bash or powershell). */ --const SHELL_TOOL_NAMES: ReadonlySet = new Set([ -- CopilotToolName.Bash, -- CopilotToolName.PowerShell, --]); -- --/** Set of tool names that write input to an interactive shell session. */ --const WRITE_SHELL_TOOL_NAMES: ReadonlySet = new Set([ -- CopilotToolName.WriteBash, -- CopilotToolName.WritePowerShell, --]); -- --/** Set of tool names that read output from an interactive shell session. */ --const READ_SHELL_TOOL_NAMES: ReadonlySet = new Set([ -- CopilotToolName.ReadBash, -- CopilotToolName.ReadPowerShell, --]); -- --/** Set of tool names that spawn subagent sessions. */ --const SUBAGENT_TOOL_NAMES: ReadonlySet = new Set([ -- 'task', --]); -- --/** -- * Tools that should not be shown to the user. These are internal tools -- * used by the CLI for its own purposes (e.g., reporting intent to the model). -- * -- * `skill` is hidden because the SDK already emits a richer `skill.invoked` -- * lifecycle event with the resolved skill file path; the agent session -- * synthesizes a tool-start/complete pair from that event so the UI can -- * render a clickable file link instead of just the skill name. See -- * {@link synthesizeSkillToolEvents}. -- */ --const HIDDEN_TOOL_NAMES: ReadonlySet = new Set([ -- CopilotToolName.ReportIntent, -- CopilotToolName.Skill, --]); -- --/** -- * Returns true if the tool should be hidden from the UI. -- */ --export function isHiddenTool(toolName: string): boolean { -- return HIDDEN_TOOL_NAMES.has(toolName); --} -- --/** -- * Returns true if the tool executes shell commands. -- */ --export function isShellTool(toolName: string): boolean { -- return SHELL_TOOL_NAMES.has(toolName); --} -- --// ============================================================================= --// Display helpers --// --// These functions translate Copilot CLI tool names and arguments into --// human-readable display strings. This logic lives here -- in the agent-host --// process -- so the IPC protocol stays agent-agnostic; the renderer never needs --// to know about specific tool names. --// ============================================================================= -- --function truncate(text: string, maxLength: number): string { -- return text.length > maxLength ? text.substring(0, maxLength - 3) + '...' : text; --} -- --/** -- * Formats a file path as a markdown link `[](file-uri)` so it renders -- * as a clickable file widget in the chat UI. -- */ --function formatPathAsMarkdownLink(path: string): string { -- const uri = URI.file(path); -- return `[${basename(uri)}](${uri})`; --} -- --/** -- * Wraps a localized message containing a markdown file link into a -- * `StringOrMarkdown` object so the renderer treats it as markdown. -- */ --function md(value: string): StringOrMarkdown { -- return { markdown: value }; --} -- --export function getToolDisplayName(toolName: string): string { -- switch (toolName) { -- case CopilotToolName.Bash: return localize('toolName.bash', "Bash"); -- case CopilotToolName.PowerShell: return localize('toolName.powershell', "PowerShell"); -- case CopilotToolName.ReadBash: -- case CopilotToolName.ReadPowerShell: return localize('toolName.readShell', "Read Shell Output"); -- case CopilotToolName.WriteBash: -- case CopilotToolName.WritePowerShell: return localize('toolName.writeShell', "Write Shell Input"); -- case CopilotToolName.BashShutdown: return localize('toolName.bashShutdown', "Stop Shell"); -- case CopilotToolName.ListBash: -- case CopilotToolName.ListPowerShell: return localize('toolName.listShells', "List Shells"); -- case CopilotToolName.View: return localize('toolName.view', "View File"); -- case CopilotToolName.Edit: return localize('toolName.edit', "Edit File"); -- case CopilotToolName.Create: return localize('toolName.create', "Create File"); -- case CopilotToolName.Grep: return localize('toolName.grep', "Search"); -- case CopilotToolName.Glob: return localize('toolName.glob', "Find Files"); -- case CopilotToolName.ApplyPatch: -- case CopilotToolName.GitApplyPatch: return localize('toolName.patch', "Patch"); -- case CopilotToolName.WebSearch: return localize('toolName.webSearch', "Web Search"); -- case CopilotToolName.WebFetch: return localize('toolName.webFetch', "Web Fetch"); -- case CopilotToolName.AskUser: return localize('toolName.askUser', "Ask User"); -- default: return toolName; -- } --} -- --export function getInvocationMessage(toolName: string, displayName: string, parameters: Record | undefined): StringOrMarkdown { -- if (SHELL_TOOL_NAMES.has(toolName)) { -- const args = parameters as ICopilotShellToolArgs | undefined; -- if (args?.command) { -- const firstLine = args.command.split('\n')[0]; -- return md(localize('toolInvoke.shellCmd', "Running {0}", appendEscapedMarkdownInlineCode(truncate(firstLine, 80)))); -- } -- return localize('toolInvoke.shell', "Running {0} command", displayName); -- } -- -- if (WRITE_SHELL_TOOL_NAMES.has(toolName)) { -- const args = parameters as ICopilotShellToolArgs | undefined; -- if (args?.command) { -- const firstLine = args.command.split('\n')[0]; -- return md(localize('toolInvoke.writeShellCmd', "Sending {0} to shell", appendEscapedMarkdownInlineCode(truncate(firstLine, 80)))); -- } -- return localize('toolInvoke.writeShell', "Sending input to shell"); -- } -- -- if (READ_SHELL_TOOL_NAMES.has(toolName)) { -- return localize('toolInvoke.readShell', "Reading shell output"); -- } -- -- switch (toolName) { -- case CopilotToolName.View: { -- const args = parameters as ICopilotViewToolArgs | undefined; -- if (args?.path) { -- const link = formatPathAsMarkdownLink(args.path); -- const range = formatViewRange(args.view_range); -- if (range) { -- if (range.endLine === -1) { -- return md(localize('toolInvoke.viewFileFromLine', "Reading {0}, line {1} to the end", link, range.startLine)); -- } -- if (range.endLine !== range.startLine) { -- return md(localize('toolInvoke.viewFileRange', "Reading {0}, lines {1} to {2}", link, range.startLine, range.endLine)); -- } -- return md(localize('toolInvoke.viewFileLine', "Reading {0}, line {1}", link, range.startLine)); -- } -- return md(localize('toolInvoke.viewFile', "Reading {0}", link)); -- } -- return localize('toolInvoke.view', "Reading file"); -- } -- case CopilotToolName.Edit: { -- const args = parameters as ICopilotFileToolArgs | undefined; -- if (args?.path) { -- return md(localize('toolInvoke.editFile', "Editing {0}", formatPathAsMarkdownLink(args.path))); -- } -- return localize('toolInvoke.edit', "Editing file"); -- } -- case CopilotToolName.Create: { -- const args = parameters as ICopilotFileToolArgs | undefined; -- if (args?.path) { -- return md(localize('toolInvoke.createFile', "Creating {0}", formatPathAsMarkdownLink(args.path))); -- } -- return localize('toolInvoke.create', "Creating file"); -- } -- case CopilotToolName.Grep: { -- const args = parameters as ICopilotGrepToolArgs | undefined; -- if (args?.pattern) { -- return md(localize('toolInvoke.grepPattern', "Searching for {0}", appendEscapedMarkdownInlineCode(truncate(args.pattern, 80)))); -- } -- return localize('toolInvoke.grep', "Searching files"); -- } -- case CopilotToolName.Glob: { -- const args = parameters as ICopilotGlobToolArgs | undefined; -- if (args?.pattern) { -- return md(localize('toolInvoke.globPattern', "Finding files matching {0}", appendEscapedMarkdownInlineCode(truncate(args.pattern, 80)))); -- } -- return localize('toolInvoke.glob', "Finding files"); -- } -- default: -- return localize('toolInvoke.generic', "Using \"{0}\"", displayName); -- } --} -- --export function getPastTenseMessage(toolName: string, displayName: string, parameters: Record | undefined, success: boolean): StringOrMarkdown { -- if (!success) { -- return localize('toolComplete.failed', "\"{0}\" failed", displayName); -- } -- -- if (SHELL_TOOL_NAMES.has(toolName)) { -- const args = parameters as ICopilotShellToolArgs | undefined; -- if (args?.command) { -- const firstLine = args.command.split('\n')[0]; -- return md(localize('toolComplete.shellCmd', "Ran {0}", appendEscapedMarkdownInlineCode(truncate(firstLine, 80)))); -- } -- return localize('toolComplete.shell', "Ran {0} command", displayName); -- } -- -- if (WRITE_SHELL_TOOL_NAMES.has(toolName)) { -- const args = parameters as ICopilotShellToolArgs | undefined; -- if (args?.command) { -- const firstLine = args.command.split('\n')[0]; -- return md(localize('toolComplete.writeShellCmd', "Sent {0} to shell", appendEscapedMarkdownInlineCode(truncate(firstLine, 80)))); -- } -- return localize('toolComplete.writeShell', "Sent input to shell"); -- } -- -- if (READ_SHELL_TOOL_NAMES.has(toolName)) { -- return localize('toolComplete.readShell', "Read shell output"); -- } -- -- switch (toolName) { -- case CopilotToolName.View: { -- const args = parameters as ICopilotViewToolArgs | undefined; -- if (args?.path) { -- const link = formatPathAsMarkdownLink(args.path); -- const range = formatViewRange(args.view_range); -- if (range) { -- if (range.endLine === -1) { -- return md(localize('toolComplete.viewFileFromLine', "Read {0}, line {1} to the end", link, range.startLine)); -- } -- if (range.endLine !== range.startLine) { -- return md(localize('toolComplete.viewFileRange', "Read {0}, lines {1} to {2}", link, range.startLine, range.endLine)); -- } -- return md(localize('toolComplete.viewFileLine', "Read {0}, line {1}", link, range.startLine)); -- } -- return md(localize('toolComplete.viewFile', "Read {0}", link)); -- } -- return localize('toolComplete.view', "Read file"); -- } -- case CopilotToolName.Edit: { -- const args = parameters as ICopilotFileToolArgs | undefined; -- if (args?.path) { -- return md(localize('toolComplete.editFile', "Edited {0}", formatPathAsMarkdownLink(args.path))); -- } -- return localize('toolComplete.edit', "Edited file"); -- } -- case CopilotToolName.Create: { -- const args = parameters as ICopilotFileToolArgs | undefined; -- if (args?.path) { -- return md(localize('toolComplete.createFile', "Created {0}", formatPathAsMarkdownLink(args.path))); -- } -- return localize('toolComplete.create', "Created file"); -- } -- case CopilotToolName.Grep: { -- const args = parameters as ICopilotGrepToolArgs | undefined; -- if (args?.pattern) { -- return md(localize('toolComplete.grepPattern', "Searched for {0}", appendEscapedMarkdownInlineCode(truncate(args.pattern, 80)))); -- } -- return localize('toolComplete.grep', "Searched files"); -- } -- case CopilotToolName.Glob: { -- const args = parameters as ICopilotGlobToolArgs | undefined; -- if (args?.pattern) { -- return md(localize('toolComplete.globPattern', "Found files matching {0}", appendEscapedMarkdownInlineCode(truncate(args.pattern, 80)))); -- } -- return localize('toolComplete.glob', "Found files"); -- } -- default: -- return localize('toolComplete.generic', "Used \"{0}\"", displayName); -- } --} -- --// ============================================================================= --// Skill event synthesis --// --// The Copilot SDK emits a `skill` tool call (which we hide) and, separately, a --// `skill.invoked` lifecycle event with the resolved skill file path. We turn --// the latter into a synthesized tool-start/complete pair so clients can render --// a clickable file link to the SKILL.md the agent loaded -- matching the --// existing `view`-tool display style. Live and replay paths share this helper --// so they stay in lock-step (see also the mirrored-pair gotcha for tool-call --// display in this file). --// ============================================================================= -- --/** Subset of the SDK's `skill.invoked` payload that the synth helper needs. */ --export interface ICopilotSkillInvokedData { -- readonly name: string; -- readonly path?: string; -- readonly description?: string; --} -- --/** -- * Builds a stable synthetic tool call id for a `skill.invoked` event so -- * reconnect/replay produces the same id as the original live emit. The id -- * is used unencoded as a path segment (e.g. by `ChatResponseResource.createUri`), -- * so it must not contain characters like `/` -- we hash any fallback values -- * that could carry filesystem paths or arbitrary text. -- */ --export function getSkillSyntheticToolCallId(eventId: string | undefined, data: ICopilotSkillInvokedData): string { -- if (eventId) { -- return `synth-skill-${eventId}`; -- } -- const seed = data.path ?? data.name; -- return `synth-skill-${hash(seed).toString(16)}`; --} -- --/** -- * Synthesizes the `tool_start` and `tool_complete` agent progress events that -- * represent a successful `skill.invoked` lifecycle event. Used by both the -- * live session handler and the history-replay mapper so the two paths render -- * identically. -- */ --export function synthesizeSkillToolEvents( -- session: URI, -- data: ICopilotSkillInvokedData, -- eventId: string | undefined, --): { start: IAgentToolStartEvent; complete: IAgentToolCompleteEvent } { -- const toolCallId = getSkillSyntheticToolCallId(eventId, data); -- const displayName = localize('toolName.skill', "Read Skill"); -- // Use the skill name as the link text rather than the basename: every skill -- // file is named SKILL.md, so `Reading skill [plan]` reads better than the -- // always-identical `Reading skill [SKILL.md]`. The client may further upgrade -- // this link to a rich pill based on the `SKILL.md` basename. Skill names and -- // paths come from the SDK / agent host and are escaped to prevent markdown -- // injection from a malicious skill author. -- // Escape only the characters that would break out of markdown link text -- // syntax (`\` and `]`); a full markdown escape would leave visible -- // backslashes in renderers (like the skill pill) that extract link text -- // without re-parsing markdown. -- const escapedName = escapeMarkdownLinkLabel(data.name); -- const skillLink = data.path ? `[${escapedName}](${URI.file(data.path)})` : undefined; -- const invocationMessage: StringOrMarkdown = skillLink -- ? md(localize('toolInvoke.skill', "Reading skill {0}", skillLink)) -- : localize('toolInvoke.skillName', "Reading skill {0}", data.name); -- const pastTenseMessage: StringOrMarkdown = skillLink -- ? md(localize('toolComplete.skill', "Read skill {0}", skillLink)) -- : localize('toolComplete.skillName', "Read skill {0}", data.name); -- const start: IAgentToolStartEvent = { -- session, -- type: 'tool_start', -- toolCallId, -- toolName: CopilotToolName.Skill, -- displayName, -- invocationMessage, -- }; -- const complete: IAgentToolCompleteEvent = { -- session, -- type: 'tool_complete', -- toolCallId, -- result: { -- success: true, -- pastTenseMessage, -- }, -- }; -- return { start, complete }; --} -- --export function getToolInputString(toolName: string, parameters: Record | undefined, rawArguments: string | undefined): string | undefined { -- if (!parameters && !rawArguments) { -- return undefined; -- } -- -- if (SHELL_TOOL_NAMES.has(toolName) || WRITE_SHELL_TOOL_NAMES.has(toolName)) { -- const args = parameters as ICopilotShellToolArgs | undefined; -- // Custom tool overrides may wrap the args: { kind: 'custom-tool', args: { command: '...' } } -- const command = args?.command ?? (args as Record | undefined)?.args; -- if (typeof command === 'string') { -- return command; -- } -- if (typeof command === 'object' && command !== null && hasKey(command, { command: true })) { -- return (command as ICopilotShellToolArgs).command; -- } -- return rawArguments; -- } -- -- switch (toolName) { -- case CopilotToolName.Grep: { -- const args = parameters as ICopilotGrepToolArgs | undefined; -- return args?.pattern ?? rawArguments; -- } -- default: -- // For other tools, show the formatted JSON arguments -- if (parameters) { -- try { -- return JSON.stringify(parameters, null, 2); -- } catch { -- return rawArguments; -- } -- } -- return rawArguments; -- } --} -- --/** -- * Returns a rendering hint for the given tool. Currently only 'terminal' is -- * supported, which tells the renderer to display the tool as a terminal command -- * block. -- */ --export function getToolKind(toolName: string): 'terminal' | 'subagent' | undefined { -- if (SHELL_TOOL_NAMES.has(toolName)) { -- return 'terminal'; -- } -- if (SUBAGENT_TOOL_NAMES.has(toolName)) { -- return 'subagent'; -- } -- return undefined; --} -- --/** -- * Extracts subagent metadata (agent name, description) from the parsed -- * arguments of a Copilot SDK subagent tool call. The Copilot `task` tool -- * uses `agent_type` (snake_case), which this normalizes into the generic -- * `subagentAgentName` / `subagentDescription` shape used by the rest of the -- * agent host code. -- * -- * Only call this for tools where {@link getToolKind} returned `'subagent'`. -- */ --export function getSubagentMetadata(parameters: Record | undefined): { agentName?: string; description?: string } { -- if (!parameters) { -- return {}; -- } -- const agentName = typeof parameters.agent_type === 'string' && parameters.agent_type.length > 0 -- ? parameters.agent_type -- : undefined; -- const description = typeof parameters.description === 'string' && parameters.description.length > 0 -- ? parameters.description -- : undefined; -- return { agentName, description }; --} -- --/** -- * Returns the shell language identifier for syntax highlighting. -- * Used when creating terminal tool-specific data for the renderer. -- */ --export function getShellLanguage(toolName: string): string { -- switch (toolName) { -- case CopilotToolName.PowerShell: -- case CopilotToolName.WritePowerShell: -- case CopilotToolName.ReadPowerShell: return 'powershell'; -- default: return 'shellscript'; -- } --} -- --// ============================================================================= --// Permission display --// --// Derives display fields from SDK permission requests for the tool --// confirmation UI. Colocated with the tool-start display helpers above so --// that formatting utilities (formatPathAsMarkdownLink, md, etc.) are shared. --// ============================================================================= -- --export function tryStringify(value: unknown): string | undefined { -- try { -- return JSON.stringify(value); -- } catch { -- return undefined; -- } --} -- --/** -- * Extends the SDK's {@link PermissionRequest} with the known extra properties -- * that arrive on the index-signature. The SDK defines these as `[key: string]: unknown` -- * so this interface adds proper types for the fields we actually use. -- */ --export interface ITypedPermissionRequest extends PermissionRequest { -- /** File path — set for `read` permission requests. */ -- path?: string; -- /** File path — set for `write` permission requests. */ -- fileName?: string; -- /** Full shell command text — set for `shell` permission requests. */ -- fullCommandText?: string; -- /** Human-readable intention describing the operation. */ -- intention?: string; -- /** MCP server name — set for `mcp` permission requests. */ -- serverName?: string; -- /** Tool name — set for `mcp` and `custom-tool` permission requests. */ -- toolName?: string; -- /** Tool arguments — set for `custom-tool` permission requests. */ -- args?: Record; -- /** URL — set for `url` permission requests. */ -- url?: string; -- /** Unified diff of the proposed change — set for `write` permission requests. */ -- diff?: string; -- /** New file contents that will be written — set for `write` permission requests. */ -- newFileContents?: string; --} -- --/** Safely extract a string value from an SDK field that may be `unknown` at runtime. */ --function str(value: unknown): string | undefined { -- return typeof value === 'string' ? value : undefined; --} -- --/** -- * Derives display fields from a permission request for the tool confirmation UI. -- */ --export function getPermissionDisplay(request: ITypedPermissionRequest, workingDirectory?: URI): { -- confirmationTitle: string; -- invocationMessage: StringOrMarkdown; -- toolInput?: string; -- /** Normalized permission kind for auto-approval routing. */ -- permissionKind: IAgentToolReadyEvent['permissionKind']; -- /** File path extracted from the request. */ -- permissionPath?: string; --} { -- const path = str(request.path) ?? str(request.fileName); -- const fullCommandText = str(request.fullCommandText); -- const intention = str(request.intention); -- const serverName = str(request.serverName); -- const toolName = str(request.toolName); -- -- switch (request.kind) { -- case 'shell': { -- // Strip a redundant `cd && …` prefix so the -- // confirmation dialog shows the simplified command. -- const shellParams: Record | undefined = fullCommandText ? { command: fullCommandText } : undefined; -- stripRedundantCdPrefix(CopilotToolName.Bash, shellParams, workingDirectory); -- const cleanedCommand = typeof shellParams?.command === 'string' ? shellParams.command : fullCommandText; -- return { -- confirmationTitle: localize('copilot.permission.shell.title', "Run in terminal?"), -- invocationMessage: intention ?? getInvocationMessage(CopilotToolName.Bash, getToolDisplayName(CopilotToolName.Bash), cleanedCommand ? { command: cleanedCommand } : undefined), -- toolInput: cleanedCommand, -- permissionKind: 'shell', -- permissionPath: path, -- }; -- } -- 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 sdkToolName = str(request.toolName); -- if (args && sdkToolName && isShellTool(sdkToolName) && typeof args.command === 'string') { -- stripRedundantCdPrefix(sdkToolName, args, workingDirectory); -- const command = args.command as string; -- return { -- confirmationTitle: localize('copilot.permission.shell.title', "Run in terminal?"), -- invocationMessage: getInvocationMessage(sdkToolName, getToolDisplayName(sdkToolName), { command }), -- toolInput: command, -- permissionKind: 'shell', -- permissionPath: path, -- }; -- } -- return { -- confirmationTitle: localize('copilot.permission.default.title', "Allow tool call?"), -- invocationMessage: md(localize('copilot.permission.default.message', "Allow the model to call {0}?", appendEscapedMarkdownInlineCode(toolName ?? request.kind))), -- toolInput: args ? tryStringify(args) : tryStringify(request), -- permissionKind: request.kind, -- permissionPath: path, -- }; -- } -- case 'write': -- return { -- confirmationTitle: localize('copilot.permission.write.title', "Write file?"), -- invocationMessage: getInvocationMessage(CopilotToolName.Edit, getToolDisplayName(CopilotToolName.Edit), path ? { path } : undefined), -- toolInput: tryStringify(path ? { path } : request) ?? undefined, -- permissionKind: 'write', -- permissionPath: path, -- }; -- case 'mcp': { -- const title = toolName ?? localize('copilot.permission.mcp.defaultTool', "MCP Tool"); -- return { -- confirmationTitle: serverName -- ? localize('copilot.permission.mcp.title', "Allow tool from {0}?", serverName) -- : localize('copilot.permission.default.title', "Allow tool call?"), -- invocationMessage: serverName ? `${serverName}: ${title}` : title, -- toolInput: tryStringify({ serverName, toolName }) ?? undefined, -- permissionKind: 'mcp', -- permissionPath: path, -- }; -- } -- case 'read': -- return { -- confirmationTitle: localize('copilot.permission.read.title', "Read file?"), -- invocationMessage: intention ?? getInvocationMessage(CopilotToolName.View, getToolDisplayName(CopilotToolName.View), path ? { path } : undefined), -- toolInput: tryStringify(path ? { path, intention } : request) ?? undefined, -- permissionKind: 'read', -- permissionPath: path, -- }; -- case 'url': { -- const url = str(request.url); -- // Parse through URL for punycode escaping, but preserve the raw value if parsing fails. -- const normalizedUrl = url ? (URL.canParse(url) ? new URL(url).href : url) : undefined; -- return { -- confirmationTitle: localize('copilot.permission.url.title', "Fetch URL?"), -- invocationMessage: md(localize('copilot.permission.url.message', "Allow fetching web content?")), -- toolInput: normalizedUrl ? JSON.stringify({ url: normalizedUrl }) : undefined, -- permissionKind: 'url', -- }; -- } -- default: -- return { -- confirmationTitle: localize('copilot.permission.default.title', "Allow tool call?"), -- invocationMessage: md(localize('copilot.permission.default.message', "Allow the model to call {0}?", appendEscapedMarkdownInlineCode(toolName ?? request.kind))), -- toolInput: tryStringify(request) ?? undefined, -- permissionKind: request.kind, -- permissionPath: path, -- }; -- } --} -diff --git a/src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts b/src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts -deleted file mode 100644 -index 5a306612..00000000 ---- a/src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts -+++ /dev/null -@@ -1,300 +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 { URI } from '../../../../base/common/uri.js'; --import { SessionHistoryEvent } from '../../common/agentService.js'; --import { stripRedundantCdPrefix } from '../../common/commandLineHelpers.js'; --import { IFileEditRecord, ISessionDatabase } from '../../common/sessionDataService.js'; --import { ToolResultContentType, type ToolResultContent } from '../../common/state/sessionState.js'; --import { getInvocationMessage, getPastTenseMessage, getShellLanguage, getSubagentMetadata, getToolDisplayName, getToolInputString, getToolKind, isEditTool, isHiddenTool, synthesizeSkillToolEvents } from './copilotToolDisplay.js'; --import { buildSessionDbUri } from './fileEditTracker.js'; -- --function tryStringify(value: unknown): string | undefined { -- try { -- return JSON.stringify(value); -- } catch { -- return undefined; -- } --} -- --// ---- Minimal event shapes matching the SDK's SessionEvent union --------- --// Defined here so tests can construct events without importing the SDK. -- --export interface ISessionEventToolStart { -- type: 'tool.execution_start'; -- data: { -- toolCallId: string; -- toolName: string; -- arguments?: unknown; -- mcpServerName?: string; -- mcpToolName?: string; -- parentToolCallId?: string; -- }; --} -- --export interface ISessionEventToolComplete { -- type: 'tool.execution_complete'; -- data: { -- toolCallId: string; -- success: boolean; -- result?: { content?: string }; -- error?: { message: string; code?: string }; -- isUserRequested?: boolean; -- toolTelemetry?: unknown; -- parentToolCallId?: string; -- }; --} -- --export interface ISessionEventMessage { -- type: 'assistant.message' | 'user.message'; -- data?: { -- messageId?: string; -- interactionId?: string; -- content?: string; -- toolRequests?: readonly { toolCallId: string; name: string; arguments?: unknown; type?: 'function' | 'custom' }[]; -- reasoningOpaque?: string; -- reasoningText?: string; -- encryptedContent?: string; -- parentToolCallId?: string; -- /** -- * Origin of this message. The SDK sets this to a non-`'user'` value -- * (e.g. `'skill-pdf'`) for messages it injects on behalf of a skill or -- * other internal mechanism. We filter those out so they don't render -- * as user turns. -- */ -- source?: string; -- }; --} -- --/** Minimal event shape for `skill.invoked`, used to synthesize a tool-style render. */ --export interface ISessionEventSkillInvoked { -- type: 'skill.invoked'; -- id?: string; -- data: { -- name: string; -- path?: string; -- description?: string; -- }; --} -- --/** -- * Returns true if the event is a SDK-injected `user.message` that should not -- * be shown to the user (e.g. skill-content injection). -- * -- * The SDK marks these via a non-`'user'` `source` field. Older sessions -- * persisted before `source` existed will not be filtered; that is accepted -- * leakage rather than guessed-at content sniffing. -- */ --export function isSyntheticUserMessage(event: ISessionEvent): boolean { -- if (event.type !== 'user.message') { -- return false; -- } -- const source = (event as ISessionEventMessage).data?.source; -- return !!source && source.toLowerCase() !== 'user'; --} -- --/** Minimal event shape for session history mapping. */ --export type ISessionEvent = ISessionEventToolStart | ISessionEventToolComplete | ISessionEventMessage | ISessionEventSubagentStarted | ISessionEventSkillInvoked | { type: string; data?: unknown }; -- --export interface ISessionEventSubagentStarted { -- type: 'subagent.started'; -- data: { -- toolCallId: string; -- agentName: string; -- agentDisplayName: string; -- agentDescription: string; -- }; --} -- --/** -- * Maps raw SDK session events into agent protocol events, restoring -- * stored file-edit metadata from the session database when available. -- * -- * If `workingDirectory` is provided, redundant `cd &&` -- * (or PowerShell equivalent) prefixes are stripped from shell tool -- * commands so clients see the simplified form. -- * -- * Extracted as a standalone function so it can be tested without the -- * full CopilotAgent or SDK dependencies. -- */ --export async function mapSessionEvents( -- session: URI, -- db: ISessionDatabase | undefined, -- events: readonly ISessionEvent[], -- workingDirectory?: URI, --): Promise { -- const result: SessionHistoryEvent[] = []; -- const toolInfoByCallId = new Map | undefined; rewrittenArgs?: string }>(); -- -- // Collect all tool call IDs for edit tools so we can batch-query the database -- const editToolCallIds: string[] = []; -- -- // First pass: collect tool info and identify edit tool calls -- for (const e of events) { -- if (e.type === 'tool.execution_start') { -- const d = (e as ISessionEventToolStart).data; -- if (isHiddenTool(d.toolName)) { -- continue; -- } -- const toolArgs = d.arguments !== undefined ? tryStringify(d.arguments) : undefined; -- let parameters: Record | undefined; -- if (toolArgs) { -- try { parameters = JSON.parse(toolArgs) as Record; } catch { /* ignore */ } -- } -- const rewrittenArgs = stripRedundantCdPrefix(d.toolName, parameters, workingDirectory) ? tryStringify(parameters) : undefined; -- toolInfoByCallId.set(d.toolCallId, { toolName: d.toolName, parameters, rewrittenArgs }); -- if (isEditTool(d.toolName)) { -- editToolCallIds.push(d.toolCallId); -- } -- } -- } -- -- // Query the database for stored file edits (metadata only) -- let storedEdits: Map | undefined; -- if (db && editToolCallIds.length > 0) { -- try { -- const records = await db.getFileEdits(editToolCallIds); -- if (records.length > 0) { -- storedEdits = new Map(); -- for (const r of records) { -- let list = storedEdits.get(r.toolCallId); -- if (!list) { -- list = []; -- storedEdits.set(r.toolCallId, list); -- } -- list.push(r); -- } -- } -- } catch (_e) { -- // Database may not exist yet for new sessions — that's fine -- } -- } -- -- const sessionUriStr = session.toString(); -- -- // Second pass: build result events -- for (const e of events) { -- if (e.type === 'assistant.message' || e.type === 'user.message') { -- if (isSyntheticUserMessage(e)) { -- continue; -- } -- const d = (e as ISessionEventMessage).data; -- result.push({ -- session, -- type: 'message', -- role: e.type === 'user.message' ? 'user' : 'assistant', -- messageId: d?.messageId ?? d?.interactionId ?? '', -- content: d?.content ?? '', -- toolRequests: d?.toolRequests?.map((tr) => ({ -- toolCallId: tr.toolCallId, -- name: tr.name, -- arguments: tr.arguments !== undefined ? tryStringify(tr.arguments) : undefined, -- type: tr.type, -- })), -- reasoningOpaque: d?.reasoningOpaque, -- reasoningText: d?.reasoningText, -- encryptedContent: d?.encryptedContent, -- parentToolCallId: d?.parentToolCallId, -- }); -- } else if (e.type === 'tool.execution_start') { -- const d = (e as ISessionEventToolStart).data; -- if (isHiddenTool(d.toolName)) { -- continue; -- } -- const info = toolInfoByCallId.get(d.toolCallId); -- const displayName = getToolDisplayName(d.toolName); -- const toolKind = getToolKind(d.toolName); -- const toolArgs = info?.rewrittenArgs ?? (d.arguments !== undefined ? tryStringify(d.arguments) : undefined); -- const subagentMeta = toolKind === 'subagent' ? getSubagentMetadata(info?.parameters) : undefined; -- result.push({ -- session, -- type: 'tool_start', -- toolCallId: d.toolCallId, -- toolName: d.toolName, -- displayName, -- invocationMessage: getInvocationMessage(d.toolName, displayName, info?.parameters), -- toolInput: getToolInputString(d.toolName, info?.parameters, toolArgs), -- toolKind, -- language: toolKind === 'terminal' ? getShellLanguage(d.toolName) : undefined, -- toolArguments: toolArgs, -- subagentAgentName: subagentMeta?.agentName, -- subagentDescription: subagentMeta?.description, -- mcpServerName: d.mcpServerName, -- mcpToolName: d.mcpToolName, -- parentToolCallId: d.parentToolCallId, -- }); -- } else if (e.type === 'tool.execution_complete') { -- const d = (e as ISessionEventToolComplete).data; -- const info = toolInfoByCallId.get(d.toolCallId); -- if (!info) { -- continue; -- } -- toolInfoByCallId.delete(d.toolCallId); -- const displayName = getToolDisplayName(info.toolName); -- const toolOutput = d.error?.message ?? d.result?.content; -- const content: ToolResultContent[] = []; -- if (toolOutput !== undefined) { -- content.push({ type: ToolResultContentType.Text, text: toolOutput }); -- } -- -- // Restore file edit content references from the database -- const edits = storedEdits?.get(d.toolCallId); -- if (edits) { -- for (const edit of edits) { -- const beforeUri = edit.kind === 'rename' && edit.originalPath -- ? URI.file(edit.originalPath).toString() -- : URI.file(edit.filePath).toString(); -- const afterUri = URI.file(edit.filePath).toString(); -- const hasBefore = edit.kind !== 'create'; -- const hasAfter = edit.kind !== 'delete'; -- content.push({ -- type: ToolResultContentType.FileEdit, -- before: hasBefore ? { -- uri: beforeUri, -- content: { uri: buildSessionDbUri(sessionUriStr, edit.toolCallId, edit.filePath, 'before') }, -- } : undefined, -- after: hasAfter ? { -- uri: afterUri, -- content: { uri: buildSessionDbUri(sessionUriStr, edit.toolCallId, edit.filePath, 'after') }, -- } : undefined, -- diff: (edit.addedLines !== undefined || edit.removedLines !== undefined) -- ? { added: edit.addedLines, removed: edit.removedLines } -- : undefined, -- }); -- } -- } -- -- result.push({ -- session, -- type: 'tool_complete', -- toolCallId: d.toolCallId, -- result: { -- success: d.success, -- pastTenseMessage: getPastTenseMessage(info.toolName, displayName, info.parameters, d.success), -- content: content.length > 0 ? content : undefined, -- error: d.error, -- }, -- isUserRequested: d.isUserRequested, -- toolTelemetry: d.toolTelemetry !== undefined ? tryStringify(d.toolTelemetry) : undefined, -- parentToolCallId: d.parentToolCallId, -- }); -- } else if (e.type === 'subagent.started') { -- const d = (e as ISessionEventSubagentStarted).data; -- result.push({ -- session, -- type: 'subagent_started', -- toolCallId: d.toolCallId, -- agentName: d.agentName, -- agentDisplayName: d.agentDisplayName, -- agentDescription: d.agentDescription, -- }); -- } else if (e.type === 'skill.invoked') { -- const skillEvent = e as ISessionEventSkillInvoked; -- const { start, complete } = synthesizeSkillToolEvents(session, skillEvent.data, skillEvent.id); -- result.push(start, complete); -- } -- } -- return result; --} -diff --git a/src/vs/platform/agentHost/test/node/agentService.test.ts b/src/vs/platform/agentHost/test/node/agentService.test.ts -deleted file mode 100644 -index 8cb64838..00000000 ---- a/src/vs/platform/agentHost/test/node/agentService.test.ts -+++ /dev/null -@@ -1,905 +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 { readFileSync } from 'fs'; --import { fileURLToPath } from 'url'; --import { VSBuffer } from '../../../../base/common/buffer.js'; --import { DisposableStore, IReference, toDisposable } from '../../../../base/common/lifecycle.js'; --import { Schemas } from '../../../../base/common/network.js'; --import { URI } from '../../../../base/common/uri.js'; --import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; --import { hasKey } from '../../../../base/common/types.js'; --import { NullLogService } from '../../../log/common/log.js'; --import { FileService } from '../../../files/common/fileService.js'; --import { InMemoryFileSystemProvider } from '../../../files/common/inMemoryFilesystemProvider.js'; --import { AgentSession } from '../../common/agentService.js'; --import { ISessionDatabase, ISessionDataService } from '../../common/sessionDataService.js'; --import { SessionDatabase } from '../../node/sessionDatabase.js'; --import { ActionType, ActionEnvelope } from '../../common/state/sessionActions.js'; --import { SessionActiveClient, ResponsePartKind, SessionLifecycle, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, TurnState, buildSubagentSessionUri, type MarkdownResponsePart, type ToolCallCompletedState, type ToolCallResponsePart } from '../../common/state/sessionState.js'; --import { IProductService } from '../../../product/common/productService.js'; --import { AgentService } from '../../node/agentService.js'; --import { MockAgent, ScriptedMockAgent } from './mockAgent.js'; --import { mapSessionEvents, type ISessionEvent } from '../../node/copilot/mapSessionEvents.js'; --import { createNoopGitService, createSessionDataService } from '../common/sessionTestHelpers.js'; -- --/** -- * Loads a JSONL fixture of raw Copilot SDK events, runs them through -- * {@link mapSessionEvents}, and returns the result suitable for setting -- * on {@link MockAgent.sessionMessages}. This tests the full pipeline: -- * SDK events → mapSessionEvents → _buildTurnsFromMessages → Turn[]. -- * -- * Fixture files live in `test-cases/` and are sanitized copies of real -- * `events.jsonl` files from `~/.copilot/session-state/`. -- */ --async function loadFixtureMessages(fixtureName: string, session: URI) { -- // Resolve the fixture from the source tree (test-cases/ is not compiled to out/) -- const thisFile = fileURLToPath(import.meta.url); -- // Navigate from out/vs/... to src/vs/... by replacing the out/ prefix. -- // Use a regex that handles both / and \ separators for Windows compat. -- const srcFile = thisFile.replace(/[/\\]out[/\\]/, (m) => m.replace('out', 'src')); -- const lastSep = Math.max(srcFile.lastIndexOf('/'), srcFile.lastIndexOf('\\')); -- const fixtureDir = srcFile.substring(0, lastSep); -- const sep = srcFile.includes('\\') ? '\\' : '/'; -- const raw = readFileSync(`${fixtureDir}${sep}test-cases${sep}${fixtureName}`, 'utf-8'); -- const events: ISessionEvent[] = raw.trim().split('\n').map(line => JSON.parse(line)); -- return mapSessionEvents(session, undefined, events); --} -- --suite('AgentService (node dispatcher)', () => { -- -- const disposables = new DisposableStore(); -- let service: AgentService; -- let copilotAgent: MockAgent; -- let fileService: FileService; -- let nullSessionDataService: ISessionDataService; -- -- setup(async () => { -- nullSessionDataService = { -- _serviceBrand: undefined, -- getSessionDataDir: () => URI.parse('inmemory:/session-data'), -- getSessionDataDirById: () => URI.parse('inmemory:/session-data'), -- openDatabase: () => { throw new Error('not implemented'); }, -- tryOpenDatabase: async () => undefined, -- deleteSessionData: async () => { }, -- cleanupOrphanedData: async () => { }, -- whenIdle: async () => { }, -- }; -- fileService = disposables.add(new FileService(new NullLogService())); -- disposables.add(fileService.registerProvider(Schemas.inMemory, disposables.add(new InMemoryFileSystemProvider()))); -- -- // Seed a directory for browseDirectory tests -- await fileService.createFolder(URI.from({ scheme: Schemas.inMemory, path: '/testDir' })); -- await fileService.writeFile(URI.from({ scheme: Schemas.inMemory, path: '/testDir/file.txt' }), VSBuffer.fromString('hello')); -- -- service = disposables.add(new AgentService(new NullLogService(), fileService, nullSessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); -- copilotAgent = new MockAgent('copilot'); -- disposables.add(toDisposable(() => copilotAgent.dispose())); -- }); -- -- teardown(() => disposables.clear()); -- ensureNoDisposablesAreLeakedInTestSuite(); -- -- // ---- Provider registration ------------------------------------------ -- -- suite('registerProvider', () => { -- -- test('registers a provider successfully', () => { -- service.registerProvider(copilotAgent); -- // No throw - success -- }); -- -- test('throws on duplicate provider registration', () => { -- service.registerProvider(copilotAgent); -- const duplicate = new MockAgent('copilot'); -- disposables.add(toDisposable(() => duplicate.dispose())); -- assert.throws(() => service.registerProvider(duplicate), /already registered/); -- }); -- -- test('maps progress events to protocol actions via onDidAction', async () => { -- service.registerProvider(copilotAgent); -- const session = await service.createSession({ provider: 'copilot' }); -- -- // Start a turn so there's an active turn to map events to -- service.dispatchAction( -- { type: ActionType.SessionTurnStarted, session: session.toString(), turnId: 'turn-1', userMessage: { text: 'hello' } }, -- 'test-client', 1, -- ); -- -- const envelopes: ActionEnvelope[] = []; -- disposables.add(service.onDidAction(e => envelopes.push(e))); -- -- copilotAgent.fireProgress({ session, type: 'delta', messageId: 'msg-1', content: 'hello' }); -- assert.ok(envelopes.some(e => e.action.type === ActionType.SessionResponsePart)); -- }); -- }); -- -- // ---- createSession -------------------------------------------------- -- -- suite('createSession', () => { -- -- test('creates session via specified provider', async () => { -- service.registerProvider(copilotAgent); -- -- const session = await service.createSession({ provider: 'copilot' }); -- assert.strictEqual(AgentSession.provider(session), 'copilot'); -- }); -- -- test('honors requested session URI', async () => { -- service.registerProvider(copilotAgent); -- -- const requestedSession = AgentSession.uri('copilot', 'requested-session'); -- const session = await service.createSession({ provider: 'copilot', session: requestedSession }); -- assert.strictEqual(session.toString(), requestedSession.toString()); -- }); -- -- test('scripted mock agent honors requested session URI', async () => { -- const agent = new ScriptedMockAgent(); -- disposables.add(toDisposable(() => agent.dispose())); -- -- const requestedSession = AgentSession.uri('mock', 'requested-session'); -- const result = await agent.createSession({ session: requestedSession }); -- const sessions = await agent.listSessions(); -- -- assert.deepStrictEqual({ -- created: result.session.toString(), -- listed: sessions.some(s => s.session.toString() === requestedSession.toString()), -- }, { -- created: requestedSession.toString(), -- listed: true, -- }); -- }); -- -- test('uses default provider when none specified', async () => { -- service.registerProvider(copilotAgent); -- -- const session = await service.createSession(); -- assert.strictEqual(AgentSession.provider(session), 'copilot'); -- }); -- -- test('throws when no providers are registered at all', async () => { -- await assert.rejects(() => service.createSession(), /No agent provider/); -- }); -- }); -- -- // ---- disposeSession ------------------------------------------------- -- -- suite('disposeSession', () => { -- -- test('dispatches to the correct provider and cleans up tracking', async () => { -- service.registerProvider(copilotAgent); -- -- const session = await service.createSession({ provider: 'copilot' }); -- await service.disposeSession(session); -- -- assert.strictEqual(copilotAgent.disposeSessionCalls.length, 1); -- }); -- -- test('is a no-op for unknown sessions', async () => { -- service.registerProvider(copilotAgent); -- const unknownSession = URI.from({ scheme: 'unknown', path: '/nope' }); -- -- // Should not throw -- await service.disposeSession(unknownSession); -- }); -- }); -- -- // ---- listSessions / listModels -------------------------------------- -- -- suite('aggregation', () => { -- -- test('listSessions aggregates sessions from all providers', async () => { -- service.registerProvider(copilotAgent); -- -- await service.createSession({ provider: 'copilot' }); -- -- const sessions = await service.listSessions(); -- assert.strictEqual(sessions.length, 1); -- }); -- -- test('listSessions overlays custom title from session database', async () => { -- // Pre-seed a custom title in an in-memory database -- const db = disposables.add(await SessionDatabase.open(':memory:')); -- await db.setMetadata('customTitle', 'My Custom Title'); -- -- const sessionId = 'test-session-abc'; -- const sessionUri = AgentSession.uri('copilot', sessionId); -- -- const sessionDataService: ISessionDataService = { -- _serviceBrand: undefined, -- getSessionDataDir: () => URI.parse('inmemory:/session-data'), -- getSessionDataDirById: () => URI.parse('inmemory:/session-data'), -- openDatabase: (): IReference => ({ -- object: db, -- dispose: () => { }, -- }), -- tryOpenDatabase: async (): Promise | undefined> => ({ -- object: db, -- dispose: () => { }, -- }), -- deleteSessionData: async () => { }, -- cleanupOrphanedData: async () => { }, -- whenIdle: async () => { }, -- }; -- -- // Create a mock that returns a session with that ID -- const agent = new MockAgent('copilot'); -- disposables.add(toDisposable(() => agent.dispose())); -- agent.sessionMetadataOverrides = { summary: 'SDK Title' }; -- // Manually add the session to the mock -- (agent as unknown as { _sessions: Map })._sessions.set(sessionId, sessionUri); -- -- const svc = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); -- svc.registerProvider(agent); -- -- const sessions = await svc.listSessions(); -- assert.strictEqual(sessions.length, 1); -- assert.strictEqual(sessions[0].summary, 'My Custom Title'); -- }); -- -- test('listSessions uses SDK title when no custom title exists', async () => { -- service.registerProvider(copilotAgent); -- copilotAgent.sessionMetadataOverrides = { summary: 'Auto-generated Title' }; -- -- await service.createSession({ provider: 'copilot' }); -- -- const sessions = await service.listSessions(); -- assert.strictEqual(sessions.length, 1); -- assert.strictEqual(sessions[0].summary, 'Auto-generated Title'); -- }); -- -- test('listSessions overlays live state manager title over SDK title', async () => { -- service.registerProvider(copilotAgent); -- -- const session = await service.createSession({ provider: 'copilot' }); -- -- // Simulate immediate title change via state manager -- service.stateManager.dispatchServerAction({ -- type: ActionType.SessionTitleChanged, -- session: session.toString(), -- title: 'User first message', -- }); -- -- const sessions = await service.listSessions(); -- assert.strictEqual(sessions.length, 1); -- assert.strictEqual(sessions[0].summary, 'User first message'); -- }); -- -- test('createSession attaches git state into state _meta when working directory is present', async () => { -- const workingDirectory = URI.file('/workspace/repo'); -- const gitState = { -- hasGitHubRemote: true, -- branchName: 'feature/x', -- baseBranchName: 'main', -- upstreamBranchName: 'origin/feature/x', -- incomingChanges: 1, -- outgoingChanges: 2, -- uncommittedChanges: 3, -- }; -- const calls: string[] = []; -- const gitService = { -- _serviceBrand: undefined, -- isInsideWorkTree: async () => true, -- getCurrentBranch: async () => undefined, -- getDefaultBranch: async () => undefined, -- getBranches: async () => [], -- getRepositoryRoot: async () => undefined, -- getWorktreeRoots: async () => [], -- addWorktree: async () => { }, -- removeWorktree: async () => { }, -- getSessionGitState: async (uri: URI) => { calls.push(uri.fsPath); return gitState; }, -- computeSessionFileDiffs: async () => undefined, -- showBlob: async () => undefined, -- }; -- const localService = disposables.add(new AgentService(new NullLogService(), fileService, nullSessionDataService, { _serviceBrand: undefined } as IProductService, gitService)); -- const agent = new MockAgent('copilot'); -- disposables.add(toDisposable(() => agent.dispose())); -- agent.resolvedWorkingDirectory = workingDirectory; -- agent.sessionMetadataOverrides = { workingDirectory }; -- localService.registerProvider(agent); -- -- const session = await localService.createSession({ provider: 'copilot' }); -- -- // _attachGitState is fire-and-forget; drain microtasks until the -- // git service's promise has resolved and setSessionMeta has run. -- for (let i = 0; i < 5; i++) { -- await Promise.resolve(); -- } -- -- const sessions = await localService.listSessions(); -- assert.strictEqual(sessions.length, 1); -- assert.deepStrictEqual(calls, [workingDirectory.fsPath]); -- assert.deepStrictEqual( -- localService.stateManager.getSessionState(session.toString())?._meta, -- { git: gitState }, -- ); -- }); -- -- test('createSession skips git overlay when no working directory or no git state', async () => { -- const gitService = { -- _serviceBrand: undefined, -- isInsideWorkTree: async () => false, -- getCurrentBranch: async () => undefined, -- getDefaultBranch: async () => undefined, -- getBranches: async () => [], -- getRepositoryRoot: async () => undefined, -- getWorktreeRoots: async () => [], -- addWorktree: async () => { }, -- removeWorktree: async () => { }, -- getSessionGitState: async () => undefined, -- computeSessionFileDiffs: async () => undefined, -- showBlob: async () => undefined, -- }; -- const localService = disposables.add(new AgentService(new NullLogService(), fileService, nullSessionDataService, { _serviceBrand: undefined } as IProductService, gitService)); -- const agent = new MockAgent('copilot'); -- disposables.add(toDisposable(() => agent.dispose())); -- // No resolvedWorkingDirectory set on the mock. -- localService.registerProvider(agent); -- -- const session = await localService.createSession({ provider: 'copilot' }); -- for (let i = 0; i < 5; i++) { -- await Promise.resolve(); -- } -- const sessions = await localService.listSessions(); -- -- assert.strictEqual(sessions.length, 1); -- assert.strictEqual(localService.stateManager.getSessionState(session.toString())?._meta, undefined); -- }); -- -- test('subscribe lazily attaches git state when an existing session has no _meta.git', async () => { -- // Regression test: previously AgentService was constructed without -- // a git service, so _attachGitState always bailed and `_meta.git` -- // was never populated. This test ensures the lazy-fire path on -- // subscribe() actually invokes the git service and writes git -- // state into the session's `_meta`. -- const workingDirectory = URI.file('/workspace/repo'); -- const gitState = { -- hasGitHubRemote: false, -- branchName: 'feature/lazy', -- baseBranchName: 'main', -- upstreamBranchName: undefined, -- incomingChanges: 0, -- outgoingChanges: 0, -- uncommittedChanges: 0, -- }; -- const calls: string[] = []; -- const gitService = createNoopGitService(); -- gitService.getSessionGitState = async (uri: URI) => { calls.push(uri.fsPath); return gitState; }; -- const localService = disposables.add(new AgentService(new NullLogService(), fileService, nullSessionDataService, { _serviceBrand: undefined } as IProductService, gitService)); -- const agent = new MockAgent('copilot'); -- disposables.add(toDisposable(() => agent.dispose())); -- agent.resolvedWorkingDirectory = workingDirectory; -- agent.sessionMetadataOverrides = { workingDirectory }; -- localService.registerProvider(agent); -- -- // Seed a session and clear its _meta so subscribe must lazily -- // recompute git state. -- const session = await localService.createSession({ provider: 'copilot' }); -- for (let i = 0; i < 5; i++) { -- await Promise.resolve(); -- } -- localService.stateManager.setSessionMeta(session.toString(), undefined); -- calls.length = 0; -- -- await localService.subscribe(session); -- for (let i = 0; i < 5; i++) { -- await Promise.resolve(); -- } -- -- assert.deepStrictEqual(calls, [workingDirectory.fsPath]); -- assert.deepStrictEqual( -- localService.stateManager.getSessionState(session.toString())?._meta, -- { git: gitState }, -- ); -- }); -- -- test('createSession stores live session config', async () => { -- service.registerProvider(copilotAgent); -- -- const config = { isolation: 'worktree', branch: 'feature/config' }; -- const session = await service.createSession({ provider: 'copilot', config }); -- -- assert.deepStrictEqual(service.stateManager.getSessionState(session.toString())?.config?.values, config); -- }); -- -- test('seeds activeClient into the initial session state when provided', async () => { -- service.registerProvider(copilotAgent); -- -- const envelopes: ActionEnvelope[] = []; -- disposables.add(service.onDidAction(env => envelopes.push(env))); -- -- const activeClient: SessionActiveClient = { -- clientId: 'client-eager', -- tools: [{ name: 't1', description: 'd', inputSchema: { type: 'object' } }], -- customizations: [{ uri: 'file:///plugin-a', displayName: 'A' }], -- }; -- const session = await service.createSession({ provider: 'copilot', activeClient }); -- -- assert.deepStrictEqual({ -- activeClient: service.stateManager.getSessionState(session.toString())?.activeClient, -- dispatchedActiveClientChanged: envelopes.some(e => e.action.type === ActionType.SessionActiveClientChanged), -- }, { -- activeClient, -- dispatchedActiveClientChanged: false, -- }); -- }); -- -- test('omits activeClient from the initial session state when not provided', async () => { -- service.registerProvider(copilotAgent); -- -- const session = await service.createSession({ provider: 'copilot' }); -- -- assert.strictEqual(service.stateManager.getSessionState(session.toString())?.activeClient, undefined); -- }); -- }); -- -- // ---- authenticate --------------------------------------------------- -- -- suite('authenticate', () => { -- -- test('routes token to provider matching the resource', async () => { -- service.registerProvider(copilotAgent); -- -- const result = await service.authenticate({ resource: 'https://api.github.com', token: 'ghp_test123' }); -- -- assert.deepStrictEqual(result, { authenticated: true }); -- assert.deepStrictEqual(copilotAgent.authenticateCalls, [{ resource: 'https://api.github.com', token: 'ghp_test123' }]); -- }); -- -- test('returns not authenticated for unknown resource', async () => { -- service.registerProvider(copilotAgent); -- -- const result = await service.authenticate({ resource: 'https://unknown.example.com', token: 'tok' }); -- -- assert.deepStrictEqual(result, { authenticated: false }); -- assert.strictEqual(copilotAgent.authenticateCalls.length, 0); -- }); -- }); -- -- // ---- shutdown ------------------------------------------------------- -- -- suite('shutdown', () => { -- -- test('shuts down all providers', async () => { -- let copilotShutdown = false; -- copilotAgent.shutdown = async () => { copilotShutdown = true; }; -- -- service.registerProvider(copilotAgent); -- -- await service.shutdown(); -- assert.ok(copilotShutdown); -- }); -- }); -- -- // ---- restoreSession ------------------------------------------------- -- -- suite('restoreSession', () => { -- -- test('restores a session with message history', async () => { -- service.registerProvider(copilotAgent); -- const { session } = await copilotAgent.createSession(); -- const sessions = await copilotAgent.listSessions(); -- const sessionResource = sessions[0].session; -- -- copilotAgent.sessionMessages = [ -- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Hello', toolRequests: [] }, -- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: 'Hi there!', toolRequests: [] }, -- ]; -- -- await service.restoreSession(sessionResource); -- -- const state = service.stateManager.getSessionState(sessionResource.toString()); -- assert.ok(state, 'session should be in state manager'); -- assert.strictEqual(state!.lifecycle, SessionLifecycle.Ready); -- assert.strictEqual(state!.turns.length, 1); -- assert.strictEqual(state!.turns[0].userMessage.text, 'Hello'); -- const mdPart = state!.turns[0].responseParts.find((p): p is MarkdownResponsePart => p.kind === ResponsePartKind.Markdown); -- assert.ok(mdPart); -- assert.strictEqual(mdPart.content, 'Hi there!'); -- assert.strictEqual(state!.turns[0].state, TurnState.Complete); -- }); -- -- test('restores a session with tool calls', async () => { -- service.registerProvider(copilotAgent); -- const { session } = await copilotAgent.createSession(); -- const sessions = await copilotAgent.listSessions(); -- const sessionResource = sessions[0].session; -- -- copilotAgent.sessionMessages = [ -- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Run a command', toolRequests: [] }, -- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: 'I will run a command.', toolRequests: [{ toolCallId: 'tc-1', name: 'shell' }] }, -- { type: 'tool_start', session, toolCallId: 'tc-1', toolName: 'shell', displayName: 'Shell', invocationMessage: 'Running command...' }, -- { type: 'tool_complete', session, toolCallId: 'tc-1', result: { success: true, pastTenseMessage: 'Ran command', content: [{ type: ToolResultContentType.Text, text: 'output' }] } }, -- { type: 'message', session, role: 'assistant', messageId: 'msg-3', content: 'Done!', toolRequests: [] }, -- ]; -- -- await service.restoreSession(sessionResource); -- -- const state = service.stateManager.getSessionState(sessionResource.toString()); -- assert.ok(state); -- const turn = state!.turns[0]; -- const toolCallParts = turn.responseParts.filter((p): p is ToolCallResponsePart => p.kind === ResponsePartKind.ToolCall); -- assert.strictEqual(toolCallParts.length, 1); -- const tc = toolCallParts[0].toolCall as ToolCallCompletedState; -- assert.strictEqual(tc.status, ToolCallStatus.Completed); -- assert.strictEqual(tc.toolCallId, 'tc-1'); -- assert.strictEqual(tc.confirmed, ToolCallConfirmationReason.NotNeeded); -- }); -- -- test('interleaves reasoning, markdown, and tool calls in stream order on resume', async () => { -- service.registerProvider(copilotAgent); -- const { session } = await copilotAgent.createSession(); -- const sessions = await copilotAgent.listSessions(); -- const sessionResource = sessions[0].session; -- -- copilotAgent.sessionMessages = [ -- { type: 'message', session, role: 'user', messageId: 'u-1', content: 'Hello', toolRequests: [] }, -- { type: 'message', session, role: 'assistant', messageId: 'a-1', content: 'Reply A', reasoningText: 'Thinking A', toolRequests: [{ toolCallId: 'tc-1', name: 'shell' }] }, -- { type: 'tool_start', session, toolCallId: 'tc-1', toolName: 'shell', displayName: 'Shell', invocationMessage: 'Running...' }, -- { type: 'tool_complete', session, toolCallId: 'tc-1', result: { success: true, pastTenseMessage: 'Ran', content: [{ type: ToolResultContentType.Text, text: 'ok' }] } }, -- { type: 'message', session, role: 'assistant', messageId: 'a-2', content: 'Reply B', reasoningText: 'Thinking B', toolRequests: [] }, -- ]; -- -- await service.restoreSession(sessionResource); -- -- const state = service.stateManager.getSessionState(sessionResource.toString()); -- assert.ok(state); -- const turn = state!.turns[0]; -- const summary = turn.responseParts.map(p => { -- if (p.kind === ResponsePartKind.Reasoning) { return ['reasoning', p.content]; } -- if (p.kind === ResponsePartKind.Markdown) { return ['markdown', p.content]; } -- if (p.kind === ResponsePartKind.ToolCall) { return ['toolCall', p.toolCall.toolCallId]; } -- return ['other']; -- }); -- assert.deepStrictEqual(summary, [ -- ['reasoning', 'Thinking A'], -- ['markdown', 'Reply A'], -- ['toolCall', 'tc-1'], -- ['reasoning', 'Thinking B'], -- ['markdown', 'Reply B'], -- ]); -- }); -- -- test('flushes interrupted turns', async () => { -- service.registerProvider(copilotAgent); -- const { session } = await copilotAgent.createSession(); -- const sessions = await copilotAgent.listSessions(); -- const sessionResource = sessions[0].session; -- -- copilotAgent.sessionMessages = [ -- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Interrupted', toolRequests: [] }, -- { type: 'message', session, role: 'user', messageId: 'msg-2', content: 'Retried', toolRequests: [] }, -- { type: 'message', session, role: 'assistant', messageId: 'msg-3', content: 'Answer', toolRequests: [] }, -- ]; -- -- await service.restoreSession(sessionResource); -- -- const state = service.stateManager.getSessionState(sessionResource.toString()); -- assert.ok(state); -- assert.strictEqual(state!.turns.length, 2); -- assert.strictEqual(state!.turns[0].state, TurnState.Cancelled); -- assert.strictEqual(state!.turns[1].state, TurnState.Complete); -- }); -- -- test('throws when session is not found on backend', async () => { -- service.registerProvider(copilotAgent); -- await assert.rejects( -- () => service.restoreSession(AgentSession.uri('copilot', 'nonexistent')), -- /Session not found on backend/, -- ); -- }); -- -- test('restores a session with subagent tool calls', async () => { -- service.registerProvider(copilotAgent); -- const { session } = await copilotAgent.createSession(); -- const sessions = await copilotAgent.listSessions(); -- const sessionResource = sessions[0].session; -- -- copilotAgent.sessionMessages = [ -- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Review this code', toolRequests: [] }, -- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: '', toolRequests: [{ toolCallId: 'tc-sub', name: 'task' }] }, -- { type: 'tool_start', session, toolCallId: 'tc-sub', toolName: 'task', displayName: 'Task', invocationMessage: 'Delegating...', toolKind: 'subagent' as const, subagentDescription: 'Find related files', subagentAgentName: 'explore' }, -- { type: 'subagent_started', session, toolCallId: 'tc-sub', agentName: 'explore', agentDisplayName: 'Explore', agentDescription: 'Explores the codebase' }, -- // Inner tool calls from the subagent (have parentToolCallId) -- { type: 'tool_start', session, toolCallId: 'tc-inner-1', toolName: 'bash', displayName: 'Bash', invocationMessage: 'Running ls...', parentToolCallId: 'tc-sub' }, -- { type: 'tool_complete', session, toolCallId: 'tc-inner-1', result: { success: true, pastTenseMessage: 'Ran ls', content: [{ type: ToolResultContentType.Text, text: 'file1.ts' }] }, parentToolCallId: 'tc-sub' }, -- { type: 'tool_start', session, toolCallId: 'tc-inner-2', toolName: 'view', displayName: 'View File', invocationMessage: 'Reading file1.ts', parentToolCallId: 'tc-sub' }, -- { type: 'tool_complete', session, toolCallId: 'tc-inner-2', result: { success: true, pastTenseMessage: 'Read file1.ts' }, parentToolCallId: 'tc-sub' }, -- // Parent tool completes -- { type: 'tool_complete', session, toolCallId: 'tc-sub', result: { success: true, pastTenseMessage: 'Delegated task', content: [{ type: ToolResultContentType.Text, text: 'Found 3 issues' }] } }, -- { type: 'message', session, role: 'assistant', messageId: 'msg-3', content: 'The review found 3 issues.', toolRequests: [] }, -- ]; -- -- await service.restoreSession(sessionResource); -- -- const state = service.stateManager.getSessionState(sessionResource.toString()); -- assert.ok(state); -- -- // Should produce exactly one turn -- assert.strictEqual(state!.turns.length, 1, `Expected 1 turn but got ${state!.turns.length}`); -- -- const turn = state!.turns[0]; -- assert.strictEqual(turn.userMessage.text, 'Review this code'); -- -- // The parent turn should only have the parent tool call — inner -- // tool calls are excluded from the parent and belong to the -- // child subagent session instead. -- const toolCallParts = turn.responseParts.filter((p): p is ToolCallResponsePart => p.kind === ResponsePartKind.ToolCall); -- assert.strictEqual(toolCallParts.length, 1, `Expected 1 tool call (parent only) but got ${toolCallParts.length}`); -- -- // Parent subagent tool call -- const parentTc = toolCallParts[0].toolCall as ToolCallCompletedState; -- assert.strictEqual(parentTc.toolCallId, 'tc-sub'); -- assert.strictEqual(parentTc.status, ToolCallStatus.Completed); -- assert.strictEqual(parentTc._meta?.toolKind, 'subagent'); -- assert.strictEqual(parentTc._meta?.subagentDescription, 'Find related files'); -- assert.strictEqual(parentTc._meta?.subagentAgentName, 'explore'); -- -- // Parent tool should have subagent content entry -- const content = parentTc.content ?? []; -- const subagentEntry = content.find(c => hasKey(c, { type: true }) && c.type === ToolResultContentType.Subagent); -- assert.ok(subagentEntry, 'Completed tool call should have subagent content entry'); -- -- // Subscribing to the child session should restore it with inner tool calls -- const childSessionUri = buildSubagentSessionUri(sessionResource.toString(), 'tc-sub'); -- const snapshot = await service.subscribe(URI.parse(childSessionUri)); -- const childState = service.stateManager.getSessionState(childSessionUri); -- assert.ok(snapshot?.state, 'Child session snapshot should exist'); -- assert.ok(childState, 'Child session state should exist'); -- assert.strictEqual(childState!.turns.length, 1, 'Child session should have 1 turn'); -- const childToolParts = childState!.turns[0].responseParts.filter((p): p is ToolCallResponsePart => p.kind === ResponsePartKind.ToolCall); -- assert.strictEqual(childToolParts.length, 2, `Child session should have 2 inner tool calls but got ${childToolParts.length}`); -- assert.ok(childToolParts.some(p => p.toolCall.toolCallId === 'tc-inner-1'), 'Should have tc-inner-1'); -- assert.ok(childToolParts.some(p => p.toolCall.toolCallId === 'tc-inner-2'), 'Should have tc-inner-2'); -- -- // The turn should also have the final markdown -- const mdParts = turn.responseParts.filter((p): p is MarkdownResponsePart => p.kind === ResponsePartKind.Markdown); -- assert.ok(mdParts.some(p => p.content.includes('3 issues')), 'Should have the final markdown response'); -- }); -- -- test('inner assistant messages from subagent do not create extra turns (fixture)', async () => { -- service.registerProvider(copilotAgent); -- const { session } = await copilotAgent.createSession(); -- const sessions = await copilotAgent.listSessions(); -- const sessionResource = sessions[0].session; -- -- // Load real SDK events from fixture (sanitized from ~/.copilot/session-state/) -- copilotAgent.sessionMessages = await loadFixtureMessages('subagent-session.jsonl', session); -- -- await service.restoreSession(sessionResource); -- -- const state = service.stateManager.getSessionState(sessionResource.toString()); -- assert.ok(state); -- assert.strictEqual(state!.turns.length, 1, `Expected 1 turn but got ${state!.turns.length}: ${state!.turns.map(t => `"${t.userMessage.text.substring(0, 40)}"`).join(', ')}`); -- assert.strictEqual(state!.turns[0].userMessage.text, 'Run a sync subagent to do some searches, just testing subagent rendering'); -- assert.strictEqual(state!.turns[0].state, TurnState.Complete); -- -- // Should have the parent subagent tool call with subagent content -- const toolCallParts = state!.turns[0].responseParts.filter((p): p is ToolCallResponsePart => p.kind === ResponsePartKind.ToolCall); -- const parentTc = toolCallParts.find(p => p.toolCall.toolName === 'task'); -- assert.ok(parentTc, 'Should have a task tool call'); -- assert.strictEqual(parentTc!.toolCall._meta?.toolKind, 'subagent'); -- -- // Inner tool calls should NOT be in the parent turn — they belong -- // to the child subagent session. -- const parentToolCallId = parentTc!.toolCall.toolCallId; -- const nonParentTools = toolCallParts.filter(p => p.toolCall.toolCallId !== parentToolCallId); -- assert.strictEqual(nonParentTools.length, 0, `Parent turn should only contain the task tool call, but found ${nonParentTools.length} extra tool calls`); -- -- // Subscribe to the child subagent session and verify inner tools -- const childSessionUri = buildSubagentSessionUri(sessionResource.toString(), parentToolCallId); -- const snapshot = await service.subscribe(URI.parse(childSessionUri)); -- assert.ok(snapshot?.state, 'Child session snapshot should exist'); -- const childState = service.stateManager.getSessionState(childSessionUri); -- assert.ok(childState, 'Child session state should exist'); -- assert.strictEqual(childState!.turns.length, 1, 'Child session should have 1 turn'); -- const childToolParts = childState!.turns[0].responseParts.filter((p): p is ToolCallResponsePart => p.kind === ResponsePartKind.ToolCall); -- assert.ok(childToolParts.length > 0, `Child session should have inner tool calls but got ${childToolParts.length}`); -- -- // Should have the final markdown -- const mdParts = state!.turns[0].responseParts.filter((p): p is MarkdownResponsePart => p.kind === ResponsePartKind.Markdown); -- assert.ok(mdParts.length > 0, 'Should have markdown content'); -- }); -- }); -- -- // ---- session config persistence ------------------------------------- -- -- suite('session config persistence', () => { -- -- test('createSession persists initial config values to the session DB', async () => { -- const sessionDb = disposables.add(await SessionDatabase.open(':memory:')); -- const sessionDataService = createSessionDataService(sessionDb); -- const localAgent = new MockAgent('copilot'); -- disposables.add(toDisposable(() => localAgent.dispose())); -- const localService = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); -- localService.registerProvider(localAgent); -- -- await localService.createSession({ provider: 'copilot', config: { autoApprove: 'autoApprove' } }); -- -- // Persistence is fire-and-forget; wait for it to flush -- await new Promise(r => setTimeout(r, 50)); -- -- const persisted = await sessionDb.getMetadata('configValues'); -- assert.ok(persisted, 'configValues should be persisted'); -- assert.deepStrictEqual(JSON.parse(persisted!), { autoApprove: 'autoApprove' }); -- }); -- -- test('createSession does not write configValues when there are no values', async () => { -- const sessionDb = disposables.add(await SessionDatabase.open(':memory:')); -- const sessionDataService = createSessionDataService(sessionDb); -- const localAgent = new MockAgent('copilot'); -- disposables.add(toDisposable(() => localAgent.dispose())); -- const localService = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); -- localService.registerProvider(localAgent); -- -- await localService.createSession({ provider: 'copilot' }); -- -- await new Promise(r => setTimeout(r, 50)); -- -- const persisted = await sessionDb.getMetadata('configValues'); -- assert.strictEqual(persisted, undefined); -- }); -- -- test('restoreSession overlays persisted config values onto the resolved config', async () => { -- const sessionDb = disposables.add(await SessionDatabase.open(':memory:')); -- const sessionDataService = createSessionDataService(sessionDb); -- const localAgent = new MockAgent('copilot'); -- disposables.add(toDisposable(() => localAgent.dispose())); -- const localService = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); -- localService.registerProvider(localAgent); -- -- // Create a session on the agent backend (no config) so listSessions can find it -- const { session } = await localAgent.createSession(); -- const sessions = await localAgent.listSessions(); -- const sessionResource = sessions[0].session; -- -- // Pre-seed persisted config values -- await sessionDb.setMetadata('configValues', JSON.stringify({ autoApprove: 'autoApprove' })); -- -- localAgent.sessionMessages = [ -- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Hello', toolRequests: [] }, -- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: 'Hi', toolRequests: [] }, -- ]; -- -- await localService.restoreSession(sessionResource); -- -- const state = localService.stateManager.getSessionState(sessionResource.toString()); -- assert.ok(state); -- // MockAgent.resolveSessionConfig echoes params.config back as values, so the -- // persisted values are forwarded through and end up on state.config.values. -- assert.deepStrictEqual(state!.config?.values, { autoApprove: 'autoApprove' }); -- }); -- -- test('createSession + restoreSession round-trip restores initial config without any mid-session changes', async () => { -- // Regression test: when a session is created with initial config but no -- // mid-session SessionConfigChanged actions are dispatched, restoring it -- // must still rehydrate the initial values. -- const sessionDb = disposables.add(await SessionDatabase.open(':memory:')); -- const sessionDataService = createSessionDataService(sessionDb); -- const localAgent = new MockAgent('copilot'); -- disposables.add(toDisposable(() => localAgent.dispose())); -- const localService = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); -- localService.registerProvider(localAgent); -- -- const session = await localService.createSession({ provider: 'copilot', config: { autoApprove: 'autoApprove' } }); -- -- // Wait for the fire-and-forget persistence to flush -- await new Promise(r => setTimeout(r, 50)); -- -- // Simulate a server restart: drop the in-memory state -- localService.stateManager.removeSession(session.toString()); -- -- localAgent.sessionMessages = [ -- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Hello', toolRequests: [] }, -- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: 'Hi', toolRequests: [] }, -- ]; -- await localService.restoreSession(session); -- -- const state = localService.stateManager.getSessionState(session.toString()); -- assert.ok(state); -- assert.deepStrictEqual(state!.config?.values, { autoApprove: 'autoApprove' }); -- }); -- -- test('restoreSession ignores malformed persisted configValues', async () => { -- const sessionDb = disposables.add(await SessionDatabase.open(':memory:')); -- const sessionDataService = createSessionDataService(sessionDb); -- const localAgent = new MockAgent('copilot'); -- disposables.add(toDisposable(() => localAgent.dispose())); -- const localService = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService())); -- localService.registerProvider(localAgent); -- -- const { session } = await localAgent.createSession(); -- const sessions = await localAgent.listSessions(); -- const sessionResource = sessions[0].session; -- -- await sessionDb.setMetadata('configValues', '{not json'); -- -- localAgent.sessionMessages = [ -- { type: 'message', session, role: 'user', messageId: 'msg-1', content: 'Hello', toolRequests: [] }, -- { type: 'message', session, role: 'assistant', messageId: 'msg-2', content: 'Hi', toolRequests: [] }, -- ]; -- -- // Should not throw despite the malformed JSON -- await localService.restoreSession(sessionResource); -- -- const state = localService.stateManager.getSessionState(sessionResource.toString()); -- assert.ok(state); -- // MockAgent has a workingDirectory? No — but the metadata supplies it as undefined. -- // _resolveCreatedSessionConfig bails when both .config and .workingDirectory are -- // missing, so state.config is undefined here. The key point is: no throw. -- assert.strictEqual(state!.config, undefined); -- }); -- }); -- -- // ---- resourceList ------------------------------------------------ -- -- suite('resourceList', () => { -- -- test('throws when the directory does not exist', async () => { -- await assert.rejects( -- () => service.resourceList(URI.from({ scheme: Schemas.inMemory, path: '/nonexistent' })), -- /Directory not found/, -- ); -- }); -- -- test('throws when the target is not a directory', async () => { -- await assert.rejects( -- () => service.resourceList(URI.from({ scheme: Schemas.inMemory, path: '/testDir/file.txt' })), -- /Not a directory/, -- ); -- }); -- }); -- -- // ---- worktree working directory ------------------------------------- -- -- suite('worktree working directory', () => { -- -- test('createSession uses agent-resolved working directory in state', async () => { -- // Simulate an agent that resolves a worktree path different from the input -- const worktreeDir = URI.file('/source/repo.worktrees/agents-xyz'); -- copilotAgent.resolvedWorkingDirectory = worktreeDir; -- service.registerProvider(copilotAgent); -- -- const sourceDir = URI.file('/source/repo'); -- const session = await service.createSession({ provider: 'copilot', workingDirectory: sourceDir }); -- -- // The state manager should have the worktree path, not the source path -- const state = service.stateManager.getSessionState(session.toString()); -- assert.strictEqual(state?.summary.workingDirectory, worktreeDir.toString()); -- }); -- -- test('createSession falls back to config working directory when agent does not resolve', async () => { -- // Agent does not override the working directory (e.g. folder isolation) -- copilotAgent.resolvedWorkingDirectory = undefined; -- service.registerProvider(copilotAgent); -- -- const sourceDir = URI.file('/source/repo'); -- const session = await service.createSession({ provider: 'copilot', workingDirectory: sourceDir }); -- -- const state = service.stateManager.getSessionState(session.toString()); -- assert.strictEqual(state?.summary.workingDirectory, sourceDir.toString()); -- }); -- -- test('restoreSession uses agent working directory in state', async () => { -- // Agent returns the worktree path through listSessions -- const worktreeDir = URI.file('/source/repo.worktrees/agents-xyz'); -- copilotAgent.sessionMetadataOverrides = { workingDirectory: worktreeDir }; -- service.registerProvider(copilotAgent); -- -- const session = await service.createSession({ provider: 'copilot' }); -- -- // Delete from state to simulate a server restart -- service.stateManager.deleteSession(session.toString()); -- assert.strictEqual(service.stateManager.getSessionState(session.toString()), undefined); -- -- // Restore the session (simulates a client subscribing after restart) -- await service.restoreSession(session); -- -- const state = service.stateManager.getSessionState(session.toString()); -- assert.strictEqual(state?.summary.workingDirectory, worktreeDir.toString()); -- }); -- }); --}); -diff --git a/src/vs/platform/agentHost/test/node/copilotAgent.test.ts b/src/vs/platform/agentHost/test/node/copilotAgent.test.ts -deleted file mode 100644 -index 3185e0b4..00000000 ---- a/src/vs/platform/agentHost/test/node/copilotAgent.test.ts -+++ /dev/null -@@ -1,603 +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 { CopilotSession, SessionEventPayload, SessionEventType, TypedSessionEventHandler } from '@github/copilot-sdk'; --import assert from 'assert'; --import * as fs from 'fs/promises'; --import * as os from 'os'; --import { Disposable, type DisposableStore, type IDisposable, type IReference } from '../../../../base/common/lifecycle.js'; --import { URI } from '../../../../base/common/uri.js'; --import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; --import { INativeEnvironmentService } from '../../../environment/common/environment.js'; --import { FileService } from '../../../files/common/fileService.js'; --import { IFileService } from '../../../files/common/files.js'; --import { IInstantiationService } from '../../../instantiation/common/instantiation.js'; --import { InstantiationService } from '../../../instantiation/common/instantiationService.js'; --import { ServiceCollection } from '../../../instantiation/common/serviceCollection.js'; --import { ILogService, NullLogService } from '../../../log/common/log.js'; --import { IAgentPluginManager, ISyncedCustomization } from '../../common/agentPluginManager.js'; --import { AgentSession, type IAgentDeltaEvent, type IAgentMessageEvent, type IAgentProgressEvent, type IAgentSessionMetadata, type IAgentToolStartEvent } from '../../common/agentService.js'; --import { ISessionDataService } from '../../common/sessionDataService.js'; --import { AHP_AUTH_REQUIRED, ProtocolError } from '../../common/state/sessionProtocol.js'; --import { SessionCustomization, CustomizationRef } from '../../common/state/sessionState.js'; --import { IAgentHostGitService } from '../../node/agentHostGitService.js'; --import { IAgentHostTerminalManager } from '../../node/agentHostTerminalManager.js'; --import { CopilotAgent, getCopilotWorktreeBranchName, getCopilotWorktreeName, getCopilotWorktreesRoot, type ICopilotClient } from '../../node/copilot/copilotAgent.js'; --import { CopilotAgentSession, type SessionWrapperFactory } from '../../node/copilot/copilotAgentSession.js'; --import { CopilotSessionWrapper } from '../../node/copilot/copilotSessionWrapper.js'; --import { ShellManager } from '../../node/copilot/copilotShellTools.js'; --import { SessionDatabase } from '../../node/sessionDatabase.js'; --import { createNullSessionDataService } from '../common/sessionTestHelpers.js'; -- --class TestAgentPluginManager implements IAgentPluginManager { -- declare readonly _serviceBrand: undefined; -- -- async syncCustomizations(_clientId: string, _customizations: CustomizationRef[], _progress?: (status: SessionCustomization[]) => void): Promise { -- return []; -- } --} -- --class TestAgentHostGitService implements IAgentHostGitService { -- declare readonly _serviceBrand: undefined; -- -- repositoryRoot: URI | undefined = undefined; -- addedWorktrees: { repositoryRoot: URI; worktree: URI; branchName: string; startPoint: string }[] = []; -- -- async isInsideWorkTree(): Promise { return false; } -- async getCurrentBranch(): Promise { return undefined; } -- async getDefaultBranch(): Promise { return undefined; } -- async getBranches(): Promise { return []; } -- async getRepositoryRoot(): Promise { return this.repositoryRoot; } -- async getWorktreeRoots(): Promise { return []; } -- async addWorktree(repositoryRoot: URI, worktree: URI, branchName: string, startPoint: string): Promise { -- this.addedWorktrees.push({ repositoryRoot, worktree, branchName, startPoint }); -- } -- async removeWorktree(): Promise { } -- async getSessionGitState(): Promise { return undefined; } -- async computeSessionFileDiffs(): Promise { return undefined; } -- async showBlob(): Promise { return undefined; } --} -- --class TestAgentHostTerminalManager implements IAgentHostTerminalManager { -- declare readonly _serviceBrand: undefined; -- -- async createTerminal(): Promise { } -- writeInput(): void { } -- onData(): IDisposable { return Disposable.None; } -- onExit(): IDisposable { return Disposable.None; } -- onClaimChanged(): IDisposable { return Disposable.None; } -- onCommandFinished(): IDisposable { return Disposable.None; } -- getContent(): string | undefined { return undefined; } -- getClaim(): undefined { return undefined; } -- hasTerminal(): boolean { return false; } -- getExitCode(): number | undefined { return undefined; } -- supportsCommandDetection(): boolean { return false; } -- disposeTerminal(): void { } -- getTerminalInfos(): [] { return []; } -- getTerminalState(): undefined { return undefined; } --} -- --class TestSessionDataService extends Disposable implements ISessionDataService { -- declare readonly _serviceBrand: undefined; -- -- private readonly _databases = new Map(); -- readonly openedSessions: string[] = []; -- -- getSessionDataDir(session: URI): URI { return URI.from({ scheme: 'test', path: `/session-data/${AgentSession.id(session)}` }); } -- getSessionDataDirById(sessionId: string): URI { return URI.from({ scheme: 'test', path: `/session-data/${sessionId}` }); } -- -- openDatabase(session: URI): IReference { -- const sessionId = AgentSession.id(session); -- this.openedSessions.push(sessionId); -- let db = this._databases.get(sessionId); -- if (!db) { -- db = this._register(new SessionDatabase(':memory:')); -- this._databases.set(sessionId, db); -- } -- return { object: db, dispose: () => { } }; -- } -- -- async tryOpenDatabase(session: URI): Promise | undefined> { -- const db = this._databases.get(AgentSession.id(session)); -- return db ? { object: db, dispose: () => { } } : undefined; -- } -- -- deleteSessionData(): Promise { return Promise.resolve(); } -- cleanupOrphanedData(): Promise { return Promise.resolve(); } -- whenIdle(): Promise { return Promise.resolve(); } --} -- --class TestCopilotClient implements ICopilotClient { -- readonly rpc: ICopilotClient['rpc'] = { sessions: { fork: async () => ({ sessionId: 'forked-session' }) } }; -- -- constructor( -- private readonly _sessions: Awaited>, -- ) { } -- -- async start(): Promise { } -- async stop(): ReturnType { return []; } -- async listSessions(): ReturnType { return this._sessions; } -- async listModels(): ReturnType { return []; } -- async getSessionMetadata(): ReturnType { return undefined; } -- createSession: ICopilotClient['createSession'] = async () => { throw new Error('not implemented'); }; -- resumeSession: ICopilotClient['resumeSession'] = async () => { throw new Error('not implemented'); }; --} -- --interface IFakeAgentSession { -- send: (prompt: string, attachments?: unknown, turnId?: string) => Promise; -- getMessages: () => Promise; -- dispose: () => void; --} -- --class MockCopilotSession { -- readonly sessionId = 'test-session-1'; -- -- on(_eventType: K, _handler: TypedSessionEventHandler): () => void { -- return () => { }; -- } -- -- async send(): Promise { return ''; } -- async abort(): Promise { } -- async setModel(): Promise { } -- async getMessages(): Promise[]> { return []; } -- async destroy(): Promise { } --} -- --class TestableCopilotAgent extends CopilotAgent { -- private readonly _fakeSessions = new Map(); -- readonly resumeCalls: string[] = []; -- -- constructor( -- private readonly _copilotClient: ICopilotClient, -- @ILogService logService: ILogService, -- @IInstantiationService instantiationService: IInstantiationService, -- @IFileService fileService: IFileService, -- @ISessionDataService sessionDataService: ISessionDataService, -- @IAgentHostGitService gitService: IAgentHostGitService, -- @IAgentHostTerminalManager terminalManager: IAgentHostTerminalManager, -- ) { -- super(logService, instantiationService, fileService, sessionDataService, gitService, terminalManager); -- } -- -- protected override _createCopilotClient(): ICopilotClient { -- return this._copilotClient; -- } -- -- registerFakeSession(sessionId: string, fake: IFakeAgentSession): void { -- this._fakeSessions.set(sessionId, fake); -- } -- -- protected override async _resumeSession(sessionId: string): Promise { -- this.resumeCalls.push(sessionId); -- const fake = this._fakeSessions.get(sessionId); -- if (!fake) { -- throw new Error(`No fake session registered for '${sessionId}'`); -- } -- // `_sessions` is a DisposableMap, so it will dispose() the entry on -- // teardown. The fields below are the only ones touched by sendMessage -- // and getSessionMessages in the code under test. -- const stub = { -- send: fake.send, -- getMessages: fake.getMessages, -- appliedSnapshot: undefined, -- dispose: fake.dispose, -- } as unknown as CopilotAgentSession; -- return stub; -- } -- -- resolveWorktreeForTest(config: Parameters[0], sessionId: string): Promise { -- return this._resolveSessionWorkingDirectory(config, sessionId); -- } --} -- --function createTestAgentContext(disposables: Pick, options?: { sessionDataService?: ISessionDataService; copilotClient?: ICopilotClient; gitService?: TestAgentHostGitService; environmentServiceRegistration?: 'native' | 'none'; pluginManager?: IAgentPluginManager }): { agent: CopilotAgent; instantiationService: IInstantiationService } { -- const services = new ServiceCollection(); -- const logService = new NullLogService(); -- const fileService = disposables.add(new FileService(logService)); -- services.set(ILogService, logService); -- services.set(IFileService, fileService); -- services.set(ISessionDataService, options?.sessionDataService ?? createNullSessionDataService()); -- services.set(IAgentPluginManager, options?.pluginManager ?? new TestAgentPluginManager()); -- services.set(IAgentHostGitService, options?.gitService ?? new TestAgentHostGitService()); -- services.set(IAgentHostTerminalManager, new TestAgentHostTerminalManager()); -- if (options?.environmentServiceRegistration !== 'none') { -- const environmentService = { -- _serviceBrand: undefined, -- userHome: URI.file('/mock-home'), -- } as INativeEnvironmentService; -- services.set(INativeEnvironmentService, environmentService); -- } -- const instantiationService: IInstantiationService = disposables.add(new InstantiationService(services)); -- services.set(IInstantiationService, instantiationService); -- const agent = options?.copilotClient -- ? instantiationService.createInstance(TestableCopilotAgent, options.copilotClient) -- : instantiationService.createInstance(CopilotAgent); -- return { agent, instantiationService }; --} -- --function createTestAgent(disposables: Pick, options?: { sessionDataService?: ISessionDataService; copilotClient?: ICopilotClient; gitService?: TestAgentHostGitService; environmentServiceRegistration?: 'native' | 'none'; pluginManager?: IAgentPluginManager }): CopilotAgent { -- return createTestAgentContext(disposables, options).agent; --} -- --function createAgentSessionThroughAgent(agent: CopilotAgent, instantiationService: IInstantiationService): CopilotAgentSession { -- const sessionUri = AgentSession.uri('copilotcli', 'test-session-1'); -- const shellManager = instantiationService.createInstance(ShellManager, sessionUri, undefined); -- const wrapperFactory: SessionWrapperFactory = async () => new CopilotSessionWrapper(new MockCopilotSession() as unknown as CopilotSession); -- return (agent as unknown as { -- _createAgentSession: (wrapperFactory: SessionWrapperFactory, sessionId: string, shellManager: ShellManager) => CopilotAgentSession; -- })._createAgentSession(wrapperFactory, 'test-session-1', shellManager); --} -- --function withoutUndefinedProperties(metadata: IAgentSessionMetadata): Record { -- const result: Record = {}; -- for (const [key, value] of Object.entries(metadata)) { -- if (value !== undefined) { -- result[key] = value; -- } -- } -- return result; --} -- --function sdkSession(sessionId: string, cwd?: string): Awaited>[number] { -- return { -- sessionId, -- startTime: new Date(1000), -- modifiedTime: new Date(2000), -- summary: `SDK ${sessionId}`, -- isRemote: false, -- ...(cwd ? { context: { cwd } } : {}), -- }; --} -- --async function disposeAgent(agent: CopilotAgent): Promise { -- await agent.shutdown(); -- agent.dispose(); -- // CopilotAgent.dispose calls super.dispose() from a promise continuation so -- // async shutdown can stop SDK sessions before child disposables are released. -- // Let that continuation run before the disposable leak tracker checks. -- await Promise.resolve(); --} -- --suite('CopilotAgent', () => { -- const disposables = ensureNoDisposablesAreLeakedInTestSuite(); -- -- test('uses the Copilot CLI sibling worktrees root convention', () => { -- assert.strictEqual( -- getCopilotWorktreesRoot(URI.file('/Users/me/src/vscode')).fsPath, -- URI.file('/Users/me/src/vscode.worktrees').fsPath, -- ); -- }); -- -- test('uses Agents-window Copilot CLI branch prefix', () => { -- assert.strictEqual(getCopilotWorktreeBranchName('12345678-aaaa-bbbb-cccc-123456789abc', 'add-agent-host-config'), 'agents/add-agent-host-config-12345678'); -- assert.strictEqual(getCopilotWorktreeBranchName('12345678-aaaa-bbbb-cccc-123456789abc', undefined), 'agents/12345678-aaaa-bbbb-cccc-123456789abc'); -- }); -- -- test('uses Git extension branch-derived worktree folder names', () => { -- assert.strictEqual(getCopilotWorktreeName('agents/add-agent-host-config-12345678'), 'agents-add-agent-host-config-12345678'); -- }); -- -- test('keeps hinted branch names short', () => { -- assert.strictEqual(getCopilotWorktreeBranchName('12345678-aaaa-bbbb-cccc-123456789abc', 'a'.repeat(48)).length, 'agents/'.length + 48 + '-12345678'.length); -- }); -- -- test('returns empty models and throws AuthRequired for sessions before authentication', async () => { -- const agent = createTestAgent(disposables); -- try { -- assert.deepStrictEqual(agent.models.get(), []); -- await assert.rejects( -- () => agent.listSessions(), -- (error: Error) => error instanceof ProtocolError && error.code === AHP_AUTH_REQUIRED, -- ); -- } finally { -- await disposeAgent(agent); -- } -- }); -- -- test('requires authentication before creating a session', async () => { -- const agent = createTestAgent(disposables); -- try { -- await assert.rejects( -- () => agent.createSession({ workingDirectory: URI.file('/workspace') }), -- (error: Error) => error instanceof ProtocolError && error.code === AHP_AUTH_REQUIRED, -- ); -- } finally { -- await disposeAgent(agent); -- } -- }); -- -- test('agent-created sessions can resolve session-state paths via INativeEnvironmentService', async () => { -- const sessionDataService = disposables.add(new TestSessionDataService()); -- const { agent, instantiationService } = createTestAgentContext(disposables, { -- environmentServiceRegistration: 'native', -- sessionDataService, -- }); -- const previousXdgStateHome = process.env['XDG_STATE_HOME']; -- delete process.env['XDG_STATE_HOME']; -- try { -- const agentSession = disposables.add(createAgentSessionThroughAgent(agent, instantiationService)); -- await agentSession.initializeSession(); -- -- const result = await agentSession.handlePermissionRequest({ -- kind: 'read', -- path: URI.file('/mock-home/.copilot/session-state/test-session-1/plan.md').fsPath, -- toolCallId: 'tc-read-plan-agent-composition', -- }); -- -- assert.strictEqual(result.kind, 'approved'); -- } finally { -- if (previousXdgStateHome === undefined) { -- delete process.env['XDG_STATE_HOME']; -- } else { -- process.env['XDG_STATE_HOME'] = previousXdgStateHome; -- } -- await disposeAgent(agent); -- } -- }); -- -- test('listSessions only returns sessions with a database', async () => { -- const sessionDataService = disposables.add(new TestSessionDataService()); -- const ownedSession = AgentSession.uri('copilotcli', 'owned'); -- const ownedDb = sessionDataService.openDatabase(ownedSession); -- ownedDb.dispose(); -- -- const client = new TestCopilotClient([sdkSession('owned'), sdkSession('external')]); -- const agent = createTestAgent(disposables, { sessionDataService, copilotClient: client }); -- try { -- await agent.authenticate('https://api.github.com', 'token'); -- -- assert.deepStrictEqual((await agent.listSessions()).map(s => AgentSession.id(s.session)), ['owned']); -- } finally { -- await disposeAgent(agent); -- } -- }); -- -- test('listSessions reads stored metadata from sessions with a database', async () => { -- const sessionDataService = disposables.add(new TestSessionDataService()); -- const legacySession = AgentSession.uri('copilotcli', 'legacy'); -- const legacyDb = sessionDataService.openDatabase(legacySession); -- await legacyDb.object.setMetadata('copilot.workingDirectory', URI.file('/workspace').toString()); -- legacyDb.dispose(); -- -- const agent = createTestAgent(disposables, { sessionDataService, copilotClient: new TestCopilotClient([sdkSession('legacy')]) }); -- try { -- await agent.authenticate('https://api.github.com', 'token'); -- -- assert.deepStrictEqual((await agent.listSessions()).map(withoutUndefinedProperties), [{ -- session: legacySession, -- startTime: 1000, -- modifiedTime: 2000, -- summary: 'SDK legacy', -- workingDirectory: URI.file('/workspace'), -- }]); -- } finally { -- await disposeAgent(agent); -- } -- }); -- -- test('listSessions does not create databases for unowned SDK sessions', async () => { -- const sessionDataService = disposables.add(new TestSessionDataService()); -- const agent = createTestAgent(disposables, { sessionDataService, copilotClient: new TestCopilotClient([sdkSession('external', '/workspace')]) }); -- try { -- await agent.authenticate('https://api.github.com', 'token'); -- -- assert.deepStrictEqual(await agent.listSessions(), []); -- assert.deepStrictEqual(sessionDataService.openedSessions, []); -- } finally { -- await disposeAgent(agent); -- } -- }); -- -- suite('createSession activeClient eager-claim', () => { -- -- class SpyingPluginManager extends TestAgentPluginManager { -- public readonly calls: { clientId: string; customizations: CustomizationRef[] }[] = []; -- -- override async syncCustomizations(clientId: string, customizations: CustomizationRef[], _progress?: (status: SessionCustomization[]) => void): Promise { -- this.calls.push({ clientId, customizations: [...customizations] }); -- return []; -- } -- } -- -- test('createSession seeds activeClient tools and syncs customizations', async () => { -- const sessionDataService = disposables.add(new TestSessionDataService()); -- const client = new TestCopilotClient([]); -- const pluginManager = new SpyingPluginManager(); -- // Fail fast inside the SDK factory so we don't need to wire up a -- // real raw session. The seeding of activeClient and the plugin -- // sync both happen before `client.createSession` is invoked. -- client.createSession = async () => { throw new Error('sentinel'); }; -- -- const agent = createTestAgent(disposables, { sessionDataService, copilotClient: client, pluginManager }); -- try { -- await agent.authenticate('https://api.github.com', 'token'); -- -- const customizations: CustomizationRef[] = [{ uri: 'file:///plugin-a', displayName: 'Plugin A' }]; -- await assert.rejects( -- agent.createSession({ -- session: AgentSession.uri('copilotcli', 'test-session'), -- workingDirectory: URI.file('/workspace'), -- activeClient: { -- clientId: 'client-1', -- tools: [{ name: 't1', description: 'd', inputSchema: { type: 'object' } }], -- customizations, -- }, -- }), -- (err: Error) => /sentinel/.test(err.message), -- ); -- -- assert.deepStrictEqual(pluginManager.calls, [{ clientId: 'client-1', customizations }]); -- } finally { -- await disposeAgent(agent); -- } -- }); -- -- test('createSession without activeClient does not sync customizations', async () => { -- const sessionDataService = disposables.add(new TestSessionDataService()); -- const client = new TestCopilotClient([]); -- const pluginManager = new SpyingPluginManager(); -- client.createSession = async () => { throw new Error('sentinel'); }; -- -- const agent = createTestAgent(disposables, { sessionDataService, copilotClient: client, pluginManager }); -- try { -- await agent.authenticate('https://api.github.com', 'token'); -- -- await assert.rejects( -- agent.createSession({ -- session: AgentSession.uri('copilotcli', 'test-session-2'), -- workingDirectory: URI.file('/workspace'), -- }), -- (err: Error) => /sentinel/.test(err.message), -- ); -- -- assert.deepStrictEqual(pluginManager.calls, []); -- } finally { -- await disposeAgent(agent); -- } -- }); -- }); -- -- suite('worktree announcement', () => { -- // Drives a real session through worktree creation (calling the -- // agent's _resolveSessionWorkingDirectory via a test seam so we don't -- // need a full Copilot SDK), then exercises both the live path -- // (sendMessage emits a synthetic delta) and the restore path -- // (getSessionMessages prepends to the first assistant message). A -- // stubbed CopilotAgentSession is injected via overriding _resumeSession -- // because the real one requires a full SDK CopilotSession with ~30 -- // event subscriptions. -- -- let tmpDir: string; -- -- setup(async () => { -- tmpDir = await fs.mkdtemp(`${os.tmpdir()}/copilot-agent-worktree-test-`); -- }); -- -- teardown(async () => { -- await fs.rm(tmpDir, { recursive: true, force: true }); -- }); -- -- test('emits announcement live as a delta on first sendMessage and persists it for restore via getSessionMessages', async () => { -- const sessionId = 'wt-session'; -- const session = AgentSession.uri('copilotcli', sessionId); -- const repositoryRoot = URI.joinPath(URI.file(tmpDir), 'repo'); -- await fs.mkdir(repositoryRoot.fsPath, { recursive: true }); -- -- const gitService = new TestAgentHostGitService(); -- gitService.repositoryRoot = repositoryRoot; -- -- const sessionDataService = disposables.add(new TestSessionDataService()); -- const agent = createTestAgent(disposables, { -- sessionDataService, -- copilotClient: new TestCopilotClient([]), -- gitService, -- }) as TestableCopilotAgent; -- -- const fakeMessages: (IAgentMessageEvent | IAgentToolStartEvent)[] = [ -- { session, type: 'message', role: 'user', messageId: 'u1', content: 'hi' }, -- { session, type: 'message', role: 'assistant', messageId: 'a1', content: 'hello back' }, -- ]; -- let sendCalls = 0; -- agent.registerFakeSession(sessionId, { -- send: async () => { sendCalls++; }, -- getMessages: async () => fakeMessages, -- dispose: () => { }, -- }); -- -- try { -- await agent.authenticate('https://api.github.com', 'token'); -- -- // 1. Drive worktree resolution: this is what createSession does -- // before constructing the SDK session. Verifies that the -- // real production code path persists branch metadata and -- // queues the live announcement. -- const branchHint = 'add-feature'; -- const expectedBranchName = getCopilotWorktreeBranchName(sessionId, branchHint); -- const workingDir = await agent.resolveWorktreeForTest({ -- workingDirectory: repositoryRoot, -- config: { isolation: 'worktree', branch: 'main', branchNameHint: branchHint }, -- }, sessionId); -- assert.ok(workingDir, 'resolveWorktreeForTest must return a worktree URI'); -- assert.deepStrictEqual(gitService.addedWorktrees.length, 1, 'addWorktree must be called once'); -- assert.strictEqual(gitService.addedWorktrees[0].branchName, expectedBranchName); -- -- // 2. Live path: sendMessage must fire a synthetic delta event -- // carrying the announcement text before delegating to the SDK. -- const events: IAgentProgressEvent[] = []; -- disposables.add(agent.onDidSessionProgress(e => events.push(e))); -- -- await agent.sendMessage(session, 'hello'); -- assert.strictEqual(sendCalls, 1, 'underlying SDK send must still be called'); -- -- const deltas = events.filter((e): e is IAgentDeltaEvent => e.type === 'delta'); -- assert.strictEqual(deltas.length, 1, 'exactly one delta should be emitted for the worktree announcement'); -- const announcement = deltas[0]; -- assert.ok(announcement.content.includes(expectedBranchName), `announcement should contain branch name '${expectedBranchName}', got '${announcement.content}'`); -- assert.ok(announcement.messageId.startsWith('copilot-announcement-'), `announcement messageId should be synthetic, got '${announcement.messageId}'`); -- -- // 3. Live path is one-shot: a second sendMessage must not re-emit. -- events.length = 0; -- await agent.sendMessage(session, 'follow-up'); -- assert.strictEqual(events.filter(e => e.type === 'delta').length, 0, 'announcement must not be re-emitted on subsequent sends'); -- -- // 4. Restore path: getSessionMessages must prepend the -- // announcement to the first assistant message's content, -- // using the persisted branch metadata. -- const restored = await agent.getSessionMessages(session); -- const assistant = restored.find((m): m is IAgentMessageEvent => m.type === 'message' && m.role === 'assistant'); -- assert.ok(assistant, 'restored messages should include the assistant reply'); -- assert.ok(assistant.content.includes(expectedBranchName), `restored assistant content should include the branch name, got '${assistant.content}'`); -- assert.ok(assistant.content.endsWith('hello back'), `restored assistant content should still end with the original reply, got '${assistant.content}'`); -- } finally { -- await disposeAgent(agent); -- } -- }); -- -- test('does not announce or persist branch metadata when isolation is not worktree', async () => { -- const sessionId = 'no-wt-session'; -- const session = AgentSession.uri('copilotcli', sessionId); -- const repositoryRoot = URI.joinPath(URI.file(tmpDir), 'repo'); -- await fs.mkdir(repositoryRoot.fsPath, { recursive: true }); -- -- const gitService = new TestAgentHostGitService(); -- gitService.repositoryRoot = repositoryRoot; -- -- const sessionDataService = disposables.add(new TestSessionDataService()); -- const agent = createTestAgent(disposables, { -- sessionDataService, -- copilotClient: new TestCopilotClient([]), -- gitService, -- }) as TestableCopilotAgent; -- -- const fakeMessages: IAgentMessageEvent[] = [ -- { session, type: 'message', role: 'user', messageId: 'u1', content: 'hi' }, -- { session, type: 'message', role: 'assistant', messageId: 'a1', content: 'untouched reply' }, -- ]; -- agent.registerFakeSession(sessionId, { -- send: async () => { }, -- getMessages: async () => fakeMessages, -- dispose: () => { }, -- }); -- -- try { -- await agent.authenticate('https://api.github.com', 'token'); -- -- await agent.resolveWorktreeForTest({ workingDirectory: repositoryRoot }, sessionId); -- assert.deepStrictEqual(gitService.addedWorktrees, [], 'addWorktree must not be called without worktree isolation'); -- -- const events: IAgentProgressEvent[] = []; -- disposables.add(agent.onDidSessionProgress(e => events.push(e))); -- await agent.sendMessage(session, 'hello'); -- assert.deepStrictEqual(events.filter(e => e.type === 'delta'), [], 'no announcement should be emitted live'); -- -- const restored = await agent.getSessionMessages(session); -- const assistant = restored.find((m): m is IAgentMessageEvent => m.type === 'message' && m.role === 'assistant'); -- assert.strictEqual(assistant?.content, 'untouched reply', 'restored assistant content must not be modified'); -- } finally { -- await disposeAgent(agent); -- } -- }); -- }); --}); -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 93b83312..00000000 ---- a/src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts -+++ /dev/null -@@ -1,1178 +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 { CopilotSession, SessionEvent, SessionEventPayload, SessionEventType, Tool, ToolResultObject, TypedSessionEventHandler } from '@github/copilot-sdk'; --import assert from 'assert'; --import { DeferredPromise } from '../../../../base/common/async.js'; --import { Emitter } from '../../../../base/common/event.js'; --import { DisposableStore } from '../../../../base/common/lifecycle.js'; --import { join, sep } from '../../../../base/common/path.js'; --import { URI } from '../../../../base/common/uri.js'; --import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; --import { INativeEnvironmentService } from '../../../environment/common/environment.js'; --import { IFileService } from '../../../files/common/files.js'; --import { InstantiationService } from '../../../instantiation/common/instantiationService.js'; --import { ServiceCollection } from '../../../instantiation/common/serviceCollection.js'; --import { ILogService, NullLogService } from '../../../log/common/log.js'; --import { AgentSession, IAgentProgressEvent, IAgentUserInputRequestEvent } from '../../common/agentService.js'; --import { IDiffComputeService } from '../../common/diffComputeService.js'; --import { ISessionDataService } from '../../common/sessionDataService.js'; --import { AttachmentType, SessionInputAnswerState, SessionInputAnswerValueKind, SessionInputQuestionKind, SessionInputResponseKind, ToolResultContentType } from '../../common/state/sessionState.js'; --import { CopilotAgentSession, IActiveClientSnapshot, SessionWrapperFactory } from '../../node/copilot/copilotAgentSession.js'; --import { CopilotSessionWrapper } from '../../node/copilot/copilotSessionWrapper.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'; -- readonly sendRequests: unknown[] = []; -- -- 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(request: unknown) { this.sendRequests.push(request); return ''; } -- async abort() { } -- async setModel() { } -- async getMessages() { return []; } -- async destroy() { } --} -- --class CapturingLogService extends NullLogService { -- readonly errors: Array<{ first: string | Error; args: unknown[] }> = []; -- -- override error(message: string | Error, ...args: unknown[]): void { -- this.errors.push({ first: message, args }); -- super.error(message, ...args); -- } --} -- --// ---- Helpers ---------------------------------------------------------------- -- --/** -- * Invokes a client-SDK tool's handler with the minimal fields the SDK -- * contract requires, and narrows the `unknown` return type to -- * {@link ToolResultObject} — which is what {@link CopilotAgentSession}'s -- * handler implementation actually returns. -- */ --function invokeClientToolHandler(tool: Pick, toolCallId: string): Promise { -- return Promise.resolve(tool.handler({}, { -- sessionId: 'test-session-1', -- toolCallId, -- toolName: tool.name, -- arguments: {}, -- })) as Promise; --} -- --type ISessionInternalsForTest = { -- _onDidSessionProgress: { fire(event: IAgentProgressEvent): void }; -- _editTracker: { -- trackEditStart(path: string): Promise; -- completeEdit(path: string): Promise; -- }; -- _pendingClientToolCalls: { -- get(toolCallId: string): DeferredPromise | undefined; -- set(toolCallId: string, value: DeferredPromise): Map>; -- delete(toolCallId: string): boolean; -- }; --}; -- --async function createAgentSession(disposables: DisposableStore, options?: { -- clientSnapshot?: IActiveClientSnapshot; -- environmentServiceRegistration?: 'native' | 'none'; -- logService?: ILogService; -- captureWrapperCallbacks?: { current?: Parameters[0] }; -- workingDirectory?: URI; --}): Promise<{ -- session: CopilotAgentSession; -- mockSession: MockCopilotSession; -- progressEvents: IAgentProgressEvent[]; -- waitForProgress: (predicate: (event: IAgentProgressEvent) => boolean) => Promise; --}> { -- const progressEmitter = disposables.add(new Emitter()); -- const progressEvents: IAgentProgressEvent[] = []; -- const waiters: { predicate: (event: IAgentProgressEvent) => boolean; deferred: DeferredPromise }[] = []; -- disposables.add(progressEmitter.event(e => { -- progressEvents.push(e); -- for (let i = waiters.length - 1; i >= 0; i--) { -- if (waiters[i].predicate(e)) { -- const { deferred } = waiters[i]; -- waiters.splice(i, 1); -- deferred.complete(e); -- } -- } -- })); -- -- const waitForProgress = (predicate: (event: IAgentProgressEvent) => boolean): Promise => { -- const existing = progressEvents.find(predicate); -- if (existing) { -- return Promise.resolve(existing); -- } -- const deferred = new DeferredPromise(); -- waiters.push({ predicate, deferred }); -- return deferred.p; -- }; -- -- const sessionUri = AgentSession.uri('copilot', 'test-session-1'); -- const mockSession = new MockCopilotSession(); -- -- const factory: SessionWrapperFactory = async callbacks => { -- if (options?.captureWrapperCallbacks) { -- options.captureWrapperCallbacks.current = callbacks; -- } -- return new CopilotSessionWrapper(mockSession as unknown as CopilotSession); -- }; -- -- const services = new ServiceCollection(); -- services.set(ILogService, options?.logService ?? new NullLogService()); -- services.set(IFileService, { _serviceBrand: undefined } as IFileService); -- services.set(ISessionDataService, createSessionDataService()); -- services.set(IDiffComputeService, createZeroDiffComputeService()); -- const environmentService = { -- _serviceBrand: undefined, -- userHome: URI.file('/mock-home'), -- } as INativeEnvironmentService; -- if (options?.environmentServiceRegistration !== 'none') { -- services.set(INativeEnvironmentService, environmentService); -- } -- const instantiationService = disposables.add(new InstantiationService(services)); -- -- const session = disposables.add(instantiationService.createInstance( -- CopilotAgentSession, -- { -- sessionUri, -- rawSessionId: 'test-session-1', -- onDidSessionProgress: progressEmitter, -- wrapperFactory: factory, -- shellManager: undefined, -- clientSnapshot: options?.clientSnapshot, -- workingDirectory: options?.workingDirectory, -- }, -- )); -- -- await session.initializeSession(); -- -- return { session, mockSession, progressEvents, waitForProgress }; --} -- --// ---- Tests ------------------------------------------------------------------ -- --suite('CopilotAgentSession', () => { -- -- const disposables = new DisposableStore(); -- -- teardown(() => disposables.clear()); -- ensureNoDisposablesAreLeakedInTestSuite(); -- -- test('maps internal attachment URIs to Copilot SDK path fields', async () => { -- const { session, mockSession } = await createAgentSession(disposables); -- const fileUri = URI.file('/workspace/file.ts'); -- const selectionUri = URI.file('/workspace/selection.ts'); -- -- await session.send('hello', [ -- { type: AttachmentType.File, uri: fileUri, displayName: 'file.ts' }, -- { type: AttachmentType.Selection, uri: selectionUri, displayName: 'selection.ts' }, -- ]); -- -- assert.deepStrictEqual(mockSession.sendRequests, [{ -- prompt: 'hello', -- attachments: [ -- { type: 'file', path: fileUri.fsPath, displayName: 'file.ts' }, -- { type: 'selection', filePath: selectionUri.fsPath, displayName: 'selection.ts', text: undefined, selection: undefined }, -- ], -- }]); -- }); -- -- // ---- permission handling ---- -- -- suite('permission handling', () => { -- -- test('read permission fires tool_ready (deferred to side effects)', async () => { -- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); -- const resultPromise = session.handlePermissionRequest({ -- kind: 'read', -- path: '/workspace/src/file.ts', -- toolCallId: 'tc-1', -- }); -- -- await waitForProgress(e => e.type === 'tool_ready'); -- assert.strictEqual(progressEvents.length, 1); -- -- assert.ok(session.respondToPermissionRequest('tc-1', true)); -- const result = await resultPromise; -- assert.strictEqual(result.kind, 'approved'); -- }); -- -- test('auto-approves read permission for session-state plan files', async () => { -- const previousXdgStateHome = process.env['XDG_STATE_HOME']; -- process.env['XDG_STATE_HOME'] = '/mock-state-home'; -- try { -- const { session, progressEvents } = await createAgentSession(disposables); -- const result = await session.handlePermissionRequest({ -- kind: 'read', -- path: join('/mock-state-home', '.copilot', 'session-state', 'test-session-1', 'plan.md'), -- toolCallId: 'tc-read-plan', -- }); -- -- assert.strictEqual(result.kind, 'approved'); -- assert.strictEqual(progressEvents.length, 0); -- } finally { -- if (previousXdgStateHome === undefined) { -- delete process.env['XDG_STATE_HOME']; -- } else { -- process.env['XDG_STATE_HOME'] = previousXdgStateHome; -- } -- } -- }); -- -- test('resolves native environment through INativeEnvironmentService registration', async () => { -- const previousXdgStateHome = process.env['XDG_STATE_HOME']; -- delete process.env['XDG_STATE_HOME']; -- try { -- const { session, progressEvents } = await createAgentSession(disposables, { environmentServiceRegistration: 'native' }); -- const result = await session.handlePermissionRequest({ -- kind: 'read', -- path: join('/mock-home', '.copilot', 'session-state', 'test-session-1', 'plan.md'), -- toolCallId: 'tc-read-plan-native-env', -- }); -- -- assert.strictEqual(result.kind, 'approved'); -- assert.strictEqual(progressEvents.length, 0); -- } finally { -- if (previousXdgStateHome === undefined) { -- delete process.env['XDG_STATE_HOME']; -- } else { -- process.env['XDG_STATE_HOME'] = previousXdgStateHome; -- } -- } -- }); -- -- test('logs and rethrows permission failures', async () => { -- const previousXdgStateHome = process.env['XDG_STATE_HOME']; -- delete process.env['XDG_STATE_HOME']; -- const logService = new CapturingLogService(); -- try { -- const { session } = await createAgentSession(disposables, { -- environmentServiceRegistration: 'none', -- logService, -- }); -- -- await assert.rejects( -- session.handlePermissionRequest({ -- kind: 'read', -- path: join('/mock-home', '.copilot', 'session-state', 'test-session-1', 'plan.md'), -- toolCallId: 'tc-read-plan-missing-env', -- }), -- ); -- -- assert.strictEqual(logService.errors.length, 1); -- const [entry] = logService.errors; -- assert.ok(entry.first instanceof TypeError); -- assert.strictEqual(entry.args[0], '[Copilot:test-session-1] Failed to handle permission request: kind=read, toolCallId=tc-read-plan-missing-env'); -- } finally { -- if (previousXdgStateHome === undefined) { -- delete process.env['XDG_STATE_HOME']; -- } else { -- process.env['XDG_STATE_HOME'] = previousXdgStateHome; -- } -- } -- }); -- -- test('write permission fires tool_ready (deferred to side effects)', async () => { -- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); -- const resultPromise = session.handlePermissionRequest({ -- kind: 'write', -- fileName: '/workspace/src/file.ts', -- toolCallId: 'tc-1', -- }); -- -- await waitForProgress(e => e.type === 'tool_ready'); -- assert.strictEqual(progressEvents.length, 1); -- -- assert.ok(session.respondToPermissionRequest('tc-1', true)); -- const result = await resultPromise; -- assert.strictEqual(result.kind, 'approved'); -- }); -- -- test('auto-approves write permission for session-state plan files', async () => { -- const previousXdgStateHome = process.env['XDG_STATE_HOME']; -- process.env['XDG_STATE_HOME'] = '/mock-state-home'; -- try { -- const { session, progressEvents } = await createAgentSession(disposables); -- const result = await session.handlePermissionRequest({ -- kind: 'write', -- fileName: join('/mock-state-home', '.copilot', 'session-state', 'test-session-1', 'plan.md'), -- toolCallId: 'tc-write-plan', -- }); -- -- assert.strictEqual(result.kind, 'approved'); -- assert.strictEqual(progressEvents.length, 0); -- } finally { -- if (previousXdgStateHome === undefined) { -- delete process.env['XDG_STATE_HOME']; -- } else { -- process.env['XDG_STATE_HOME'] = previousXdgStateHome; -- } -- } -- }); -- -- test('does not auto-approve session-state files from another session', async () => { -- const previousXdgStateHome = process.env['XDG_STATE_HOME']; -- process.env['XDG_STATE_HOME'] = '/mock-state-home'; -- try { -- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); -- const resultPromise = session.handlePermissionRequest({ -- kind: 'write', -- fileName: join('/mock-state-home', '.copilot', 'session-state', 'different-session', 'plan.md'), -- toolCallId: 'tc-write-other-plan', -- }); -- -- await waitForProgress(e => e.type === 'tool_ready'); -- assert.strictEqual(progressEvents.length, 1); -- -- assert.ok(session.respondToPermissionRequest('tc-write-other-plan', true)); -- const result = await resultPromise; -- assert.strictEqual(result.kind, 'approved'); -- } finally { -- if (previousXdgStateHome === undefined) { -- delete process.env['XDG_STATE_HOME']; -- } else { -- process.env['XDG_STATE_HOME'] = previousXdgStateHome; -- } -- } -- }); -- -- test('does not auto-approve traversal paths that escape the session-state directory', async () => { -- const previousXdgStateHome = process.env['XDG_STATE_HOME']; -- process.env['XDG_STATE_HOME'] = '/mock-state-home'; -- try { -- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); -- const sessionDir = join('/mock-state-home', '.copilot', 'session-state', 'test-session-1'); -- const resultPromise = session.handlePermissionRequest({ -- kind: 'write', -- fileName: `${sessionDir}${sep}..${sep}outside.md`, -- toolCallId: 'tc-write-traversal', -- }); -- -- await waitForProgress(e => e.type === 'tool_ready'); -- assert.strictEqual(progressEvents.length, 1); -- -- assert.ok(session.respondToPermissionRequest('tc-write-traversal', true)); -- const result = await resultPromise; -- assert.strictEqual(result.kind, 'approved'); -- } finally { -- if (previousXdgStateHome === undefined) { -- delete process.env['XDG_STATE_HOME']; -- } else { -- process.env['XDG_STATE_HOME'] = previousXdgStateHome; -- } -- } -- }); -- -- test('write permission outside working directory fires tool_ready', async () => { -- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); -- -- const resultPromise = session.handlePermissionRequest({ -- kind: 'write', -- fileName: '/other/file.ts', -- toolCallId: 'tc-write-outside', -- }); -- -- await waitForProgress(e => e.type === 'tool_ready'); -- assert.strictEqual(progressEvents.length, 1); -- -- assert.ok(session.respondToPermissionRequest('tc-write-outside', true)); -- const result = await resultPromise; -- assert.strictEqual(result.kind, 'approved'); -- }); -- -- test('read permission outside working directory fires tool_ready', async () => { -- const { session, progressEvents, waitForProgress } = await createAgentSession(disposables); -- -- // 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 -- await waitForProgress(e => e.type === 'tool_ready'); -- assert.strictEqual(progressEvents.length, 1); -- -- // 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, waitForProgress } = await createAgentSession(disposables); -- const resultPromise = session.handlePermissionRequest({ -- kind: 'shell', -- toolCallId: 'tc-3', -- }); -- -- await waitForProgress(e => e.type === 'tool_ready'); -- 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('live tool_start strips redundant cd prefix matching workingDirectory', async () => { -- const wd = URI.file('/repo/project'); -- const { mockSession, progressEvents } = await createAgentSession(disposables, { workingDirectory: wd }); -- mockSession.fire('tool.execution_start', { -- toolCallId: 'tc-cd', -- toolName: 'bash', -- arguments: { command: 'cd /repo/project && npm test' }, -- } as SessionEventPayload<'tool.execution_start'>['data']); -- -- assert.strictEqual(progressEvents.length, 1); -- const ev = progressEvents[0]; -- assert.strictEqual(ev.type, 'tool_start'); -- if (ev.type === 'tool_start') { -- assert.strictEqual(ev.toolInput, 'npm test'); -- assert.ok(ev.toolArguments && ev.toolArguments.includes('"npm test"'), `toolArguments should contain rewritten command, was: ${ev.toolArguments}`); -- assert.ok(!ev.toolArguments?.includes('cd /repo/project'), 'toolArguments should not contain stripped prefix'); -- } -- }); -- -- test('live tool_complete past-tense message reflects the rewritten command', async () => { -- const wd = URI.file('/repo/project'); -- const { mockSession, progressEvents } = await createAgentSession(disposables, { workingDirectory: wd }); -- -- mockSession.fire('tool.execution_start', { -- toolCallId: 'tc-cd-complete', -- toolName: 'bash', -- arguments: { command: 'cd /repo/project && npm test' }, -- } as SessionEventPayload<'tool.execution_start'>['data']); -- -- mockSession.fire('tool.execution_complete', { -- toolCallId: 'tc-cd-complete', -- success: true, -- result: { content: 'all tests passed' }, -- } as SessionEventPayload<'tool.execution_complete'>['data']); -- -- assert.strictEqual(progressEvents.length, 2); -- const completeEv = progressEvents[1]; -- assert.strictEqual(completeEv.type, 'tool_complete'); -- if (completeEv.type === 'tool_complete') { -- const past = completeEv.result.pastTenseMessage; -- const pastStr = typeof past === 'string' ? past : (past?.markdown ?? ''); -- assert.ok(!pastStr.includes('cd /repo/project'), `past-tense message should not contain stripped prefix, got: ${pastStr}`); -- assert.ok(pastStr.includes('npm test'), `past-tense message should contain the rewritten command, got: ${pastStr}`); -- } -- }); -- -- test('live tool_start does not rewrite when cd target differs from workingDirectory', async () => { -- const wd = URI.file('/repo/project'); -- const { mockSession, progressEvents } = await createAgentSession(disposables, { workingDirectory: wd }); -- mockSession.fire('tool.execution_start', { -- toolCallId: 'tc-cd-other', -- toolName: 'bash', -- arguments: { command: 'cd /tmp && ls' }, -- } as SessionEventPayload<'tool.execution_start'>['data']); -- -- assert.strictEqual(progressEvents.length, 1); -- const ev = progressEvents[0]; -- assert.strictEqual(ev.type, 'tool_start'); -- if (ev.type === 'tool_start') { -- assert.strictEqual(ev.toolInput, 'cd /tmp && ls'); -- } -- }); -- -- test('live tool_start without workingDirectory passes command through', async () => { -- const { mockSession, progressEvents } = await createAgentSession(disposables); -- mockSession.fire('tool.execution_start', { -- toolCallId: 'tc-cd-nowd', -- toolName: 'bash', -- arguments: { command: 'cd /repo/project && npm test' }, -- } as SessionEventPayload<'tool.execution_start'>['data']); -- -- assert.strictEqual(progressEvents.length, 1); -- const ev = progressEvents[0]; -- assert.strictEqual(ev.type, 'tool_start'); -- if (ev.type === 'tool_start') { -- assert.strictEqual(ev.toolInput, 'cd /repo/project && npm test'); -- } -- }); -- -- 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('handleUserInputRequest returns empty answer on skipped question', async () => { -- const { session, progressEvents } = await createAgentSession(disposables); -- -- const resultPromise = session.handleUserInputRequest( -- { question: 'Skip me' }, -- { sessionId: 'test-session-1' } -- ); -- -- const event = progressEvents[0]; -- assertUserInputEvent(event); -- const questionId = event.request.questions![0].id; -- session.respondToUserInputRequest(event.request.id, SessionInputResponseKind.Accept, { -- [questionId]: { -- state: SessionInputAnswerState.Skipped, -- } -- }); -- -- const result = await resultPromise; -- assert.strictEqual(result.answer, ''); -- assert.strictEqual(result.wasFreeform, true); -- }); -- -- 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); -- }); -- }); -- -- suite('SDK callback logging', () => { -- -- test('logs and rethrows user input callback failures', async () => { -- const logService = new CapturingLogService(); -- const { session } = await createAgentSession(disposables, { logService }); -- const sessionInternals = session as unknown as ISessionInternalsForTest; -- sessionInternals._onDidSessionProgress.fire = () => { -- throw new Error('user input boom'); -- }; -- -- await assert.rejects( -- session.handleUserInputRequest( -- { question: 'Need input' }, -- { sessionId: 'test-session-1' }, -- ), -- /user input boom/, -- ); -- -- assert.strictEqual(logService.errors.length, 1); -- const [entry] = logService.errors; -- assert.ok(entry.first instanceof Error); -- assert.strictEqual((entry.first as Error).message, 'user input boom'); -- assert.strictEqual(entry.args[0], '[Copilot:test-session-1] Failed to handle user input request: question="Need input"'); -- }); -- -- test('logs and rethrows onPreToolUse failures', async () => { -- const logService = new CapturingLogService(); -- const capturedCallbacks: { current?: Parameters[0] } = {}; -- const { session } = await createAgentSession(disposables, { logService, captureWrapperCallbacks: capturedCallbacks }); -- const sessionInternals = session as unknown as ISessionInternalsForTest; -- sessionInternals._editTracker.trackEditStart = async () => { -- throw new Error('pre tool boom'); -- }; -- -- await assert.rejects( -- capturedCallbacks.current!.hooks.onPreToolUse({ -- timestamp: 0, -- cwd: '/tmp', -- toolName: 'edit', -- toolArgs: { path: '/tmp/file.ts' }, -- }), -- /pre tool boom/, -- ); -- -- assert.strictEqual(logService.errors.length, 1); -- const [entry] = logService.errors; -- assert.ok(entry.first instanceof Error); -- assert.strictEqual((entry.first as Error).message, 'pre tool boom'); -- assert.strictEqual(entry.args[0], '[Copilot:test-session-1] Failed in onPreToolUse: tool=edit'); -- }); -- -- test('logs and rethrows onPostToolUse failures', async () => { -- const logService = new CapturingLogService(); -- const capturedCallbacks: { current?: Parameters[0] } = {}; -- const { session } = await createAgentSession(disposables, { logService, captureWrapperCallbacks: capturedCallbacks }); -- const sessionInternals = session as unknown as ISessionInternalsForTest; -- sessionInternals._editTracker.completeEdit = async () => { -- throw new Error('post tool boom'); -- }; -- -- await assert.rejects( -- capturedCallbacks.current!.hooks.onPostToolUse({ -- timestamp: 0, -- cwd: '/tmp', -- toolName: 'edit', -- toolArgs: { path: '/tmp/file.ts' }, -- toolResult: { textResultForLlm: '', resultType: 'success' }, -- }), -- /post tool boom/, -- ); -- -- assert.strictEqual(logService.errors.length, 1); -- const [entry] = logService.errors; -- assert.ok(entry.first instanceof Error); -- assert.strictEqual((entry.first as Error).message, 'post tool boom'); -- assert.strictEqual(entry.args[0], '[Copilot:test-session-1] Failed in onPostToolUse: tool=edit'); -- }); -- }); -- -- // ---- client tool calls ---- -- -- suite('client tool calls', () => { -- -- const snapshot: IActiveClientSnapshot = { -- clientId: 'test-client', -- tools: [{ -- name: 'my_tool', -- description: 'A test tool', -- inputSchema: { type: 'object', properties: {} }, -- }], -- plugins: [], -- }; -- -- test('tool_start fires immediately for client tools', async () => { -- const { session, mockSession, progressEvents } = await createAgentSession(disposables, { clientSnapshot: snapshot }); -- -- // SDK emits tool.execution_start — tool_start fires immediately -- mockSession.fire('tool.execution_start', { -- toolCallId: 'tc-client-1', -- toolName: 'my_tool', -- arguments: {}, -- } as SessionEventPayload<'tool.execution_start'>['data']); -- -- // tool_start fires immediately -- assert.strictEqual(progressEvents.filter(e => e.type === 'tool_start').length, 1); -- if (progressEvents[0].type === 'tool_start') { -- assert.strictEqual(progressEvents[0].toolClientId, 'test-client'); -- } -- -- // SDK invokes the handler -- const tools = session.createClientSdkTools(); -- const handlerPromise = invokeClientToolHandler(tools[0], 'tc-client-1'); -- -- // Complete the tool call -- session.handleClientToolCallComplete('tc-client-1', { -- success: true, -- pastTenseMessage: 'did it', -- content: [{ type: ToolResultContentType.Text, text: 'result text' }], -- }); -- -- const result = await handlerPromise; -- assert.strictEqual(result.resultType, 'success'); -- assert.strictEqual(result.textResultForLlm, 'result text'); -- }); -- -- test('permission request consumes pending auto-ready for client tools', async () => { -- const { session, mockSession, progressEvents, waitForProgress } = await createAgentSession(disposables, { clientSnapshot: snapshot }); -- -- // SDK emits tool.execution_start — tool_start fires immediately -- mockSession.fire('tool.execution_start', { -- toolCallId: 'tc-client-perm', -- toolName: 'my_tool', -- arguments: {}, -- } as SessionEventPayload<'tool.execution_start'>['data']); -- -- // tool_start fired, no tool_ready yet -- assert.strictEqual(progressEvents.filter(e => e.type === 'tool_start').length, 1); -- assert.strictEqual(progressEvents.filter(e => e.type === 'tool_ready').length, 0); -- -- // Permission request fires — tool_ready from permission flow -- // (with confirmationTitle) replaces the auto-ready -- const resultPromise = session.handlePermissionRequest({ -- kind: 'custom-tool', -- toolCallId: 'tc-client-perm', -- toolName: 'my_tool', -- }); -- -- // tool_ready from permission flow should have fired (with confirmationTitle) -- await waitForProgress(e => e.type === 'tool_ready'); -- const toolReadys = progressEvents.filter(e => e.type === 'tool_ready'); -- assert.strictEqual(toolReadys.length, 1); -- if (toolReadys[0].type === 'tool_ready') { -- assert.strictEqual(toolReadys[0].toolCallId, 'tc-client-perm'); -- assert.ok(toolReadys[0].confirmationTitle); -- } -- -- // Approve and clean up -- session.respondToPermissionRequest('tc-client-perm', true); -- const permResult = await resultPromise; -- assert.strictEqual(permResult.kind, 'approved'); -- }); -- -- test('handleClientToolCallComplete pre-completes when no handler is waiting yet', async () => { -- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); -- -- // Completion arrives before handler — pre-creates deferred -- session.handleClientToolCallComplete('tc-unknown', { -- success: true, -- pastTenseMessage: 'done', -- }); -- -- // Handler picks up the pre-completed result -- const tools = session.createClientSdkTools(); -- const result = await invokeClientToolHandler(tools[0], 'tc-unknown'); -- assert.strictEqual(result.resultType, 'success'); -- }); -- -- test('handleClientToolCallComplete with failure result', async () => { -- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); -- -- const tools = session.createClientSdkTools(); -- const handlerPromise = invokeClientToolHandler(tools[0], 'tc-client-3'); -- -- session.handleClientToolCallComplete('tc-client-3', { -- success: false, -- pastTenseMessage: 'failed', -- error: { message: 'something broke' }, -- }); -- -- const result = await handlerPromise; -- assert.strictEqual(result.resultType, 'failure'); -- assert.strictEqual(result.error, 'something broke'); -- }); -- -- test('pending client tool calls are cancelled on dispose', async () => { -- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); -- -- const tools = session.createClientSdkTools(); -- const handlerPromise = invokeClientToolHandler(tools[0], 'tc-client-4'); -- -- session.dispose(); -- const result = await handlerPromise; -- assert.strictEqual(result.resultType, 'failure'); -- assert.ok(result.error); -- }); -- -- test('multiple concurrent client tool calls resolve independently', async () => { -- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); -- -- const tools = session.createClientSdkTools(); -- const promise1 = invokeClientToolHandler(tools[0], 'tc-multi-1'); -- const promise2 = invokeClientToolHandler(tools[0], 'tc-multi-2'); -- -- // Complete in reverse order -- session.handleClientToolCallComplete('tc-multi-2', { -- success: true, -- pastTenseMessage: 'second done', -- content: [{ type: ToolResultContentType.Text, text: 'result-2' }], -- }); -- session.handleClientToolCallComplete('tc-multi-1', { -- success: true, -- pastTenseMessage: 'first done', -- content: [{ type: ToolResultContentType.Text, text: 'result-1' }], -- }); -- -- const [result1, result2] = await Promise.all([promise1, promise2]); -- assert.strictEqual(result1.textResultForLlm, 'result-1'); -- assert.strictEqual(result2.textResultForLlm, 'result-2'); -- }); -- -- test('handler cleans up deferred after consuming result', async () => { -- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); -- -- const tools = session.createClientSdkTools(); -- const handlerPromise = invokeClientToolHandler(tools[0], 'tc-cleanup'); -- -- session.handleClientToolCallComplete('tc-cleanup', { -- success: true, -- pastTenseMessage: 'done', -- content: [{ type: ToolResultContentType.Text, text: 'ok' }], -- }); -- -- await handlerPromise; -- -- // A second complete for the same toolCallId should create a new -- // deferred (not fail). This tests the cleanup path. -- session.handleClientToolCallComplete('tc-cleanup', { -- success: true, -- pastTenseMessage: 'done again', -- }); -- }); -- -- test('client tool handler logs and rethrows failures', async () => { -- const logService = new CapturingLogService(); -- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot, logService }); -- const tools = session.createClientSdkTools(); -- const sessionInternals = session as unknown as ISessionInternalsForTest; -- sessionInternals._pendingClientToolCalls.get = () => { -- throw new Error('client tool boom'); -- }; -- -- await assert.rejects( -- invokeClientToolHandler(tools[0], 'tc-client-error'), -- /client tool boom/, -- ); -- -- assert.strictEqual(logService.errors.length, 1); -- const [entry] = logService.errors; -- assert.ok(entry.first instanceof Error); -- assert.strictEqual((entry.first as Error).message, 'client tool boom'); -- assert.strictEqual(entry.args[0], '[Copilot:test-session-1] Failed in client tool handler: tool=my_tool, toolCallId=tc-client-error'); -- }); -- -- test('tool_start stores pending auto-ready data for client tools', async () => { -- const { session, mockSession, progressEvents, waitForProgress } = await createAgentSession(disposables, { clientSnapshot: snapshot }); -- -- mockSession.fire('tool.execution_start', { -- toolCallId: 'tc-ready-data', -- toolName: 'my_tool', -- arguments: { file: 'test.ts' }, -- } as SessionEventPayload<'tool.execution_start'>['data']); -- -- // tool_start should have fired -- assert.strictEqual(progressEvents.filter(e => e.type === 'tool_start').length, 1); -- -- // The session should have stored pending auto-ready data. -- // We verify this indirectly: if we now fire a permission request -- // for the same toolCallId, the pending auto-ready is consumed -- // (tested by the permission request test above), and we get -- // tool_ready with confirmationTitle instead. -- const resultPromise = session.handlePermissionRequest({ -- kind: 'custom-tool', -- toolCallId: 'tc-ready-data', -- toolName: 'my_tool', -- }); -- -- await waitForProgress(e => e.type === 'tool_ready'); -- const toolReadys = progressEvents.filter(e => e.type === 'tool_ready'); -- assert.strictEqual(toolReadys.length, 1); -- if (toolReadys[0].type === 'tool_ready') { -- assert.ok(toolReadys[0].confirmationTitle); -- } -- -- session.respondToPermissionRequest('tc-ready-data', true); -- await resultPromise; -- }); -- -- test('handleClientToolCallComplete with content containing embedded resources', async () => { -- const { session } = await createAgentSession(disposables, { clientSnapshot: snapshot }); -- -- const tools = session.createClientSdkTools(); -- const handlerPromise = invokeClientToolHandler(tools[0], 'tc-embedded'); -- -- session.handleClientToolCallComplete('tc-embedded', { -- success: true, -- pastTenseMessage: 'done', -- content: [ -- { type: ToolResultContentType.Text, text: 'text part' }, -- { type: ToolResultContentType.EmbeddedResource, data: 'base64data', contentType: 'image/png' }, -- ], -- }); -- -- const result = await handlerPromise; -- assert.strictEqual(result.resultType, 'success'); -- // Text content should be extracted -- assert.strictEqual(result.textResultForLlm, 'text part'); -- }); -- }); --}); -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 4f581253..00000000 ---- a/src/vs/platform/agentHost/test/node/copilotGitProject.test.ts -+++ /dev/null -@@ -1,100 +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 { URI } from '../../../../base/common/uri.js'; --import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; --import type { IAgentHostGitService } from '../../node/agentHostGitService.js'; --import { projectFromCopilotContext, projectFromRepository, resolveGitProject } from '../../node/copilot/copilotGitProject.js'; -- --class TestAgentHostGitService implements IAgentHostGitService { -- declare readonly _serviceBrand: undefined; -- -- insideWorkTree = true; -- repositoryRoot: URI | undefined; -- worktreeRoots: URI[] = []; -- -- async isInsideWorkTree(): Promise { return this.insideWorkTree; } -- async getCurrentBranch(): Promise { return undefined; } -- async getDefaultBranch(): Promise { return undefined; } -- async getBranches(): Promise { return []; } -- async getRepositoryRoot(): Promise { return this.repositoryRoot; } -- async getWorktreeRoots(): Promise { return this.worktreeRoots; } -- async addWorktree(): Promise { } -- async removeWorktree(): Promise { } -- async getSessionGitState(): Promise { return undefined; } -- async computeSessionFileDiffs(): Promise { return undefined; } -- async showBlob(): Promise { return undefined; } --} -- --suite('Copilot Git Project', () => { -- ensureNoDisposablesAreLeakedInTestSuite(); -- -- let gitService: TestAgentHostGitService; -- -- setup(() => { -- gitService = new TestAgentHostGitService(); -- }); -- -- test('resolves a repository project from a worktree working directory', async () => { -- gitService.worktreeRoots = [URI.file('/workspace/source-repo')]; -- -- const project = await resolveGitProject(URI.file('/workspace/worktree-checkout'), gitService); -- -- assert.deepStrictEqual({ -- uri: project?.uri.toString(), -- displayName: project?.displayName, -- }, { -- uri: URI.file('/workspace/source-repo').toString(), -- displayName: 'source-repo', -- }); -- }); -- -- test('resolves the repository itself for a normal git working directory', async () => { -- gitService.repositoryRoot = URI.file('/workspace/normal-repo'); -- -- const project = await resolveGitProject(URI.file('/workspace/normal-repo'), gitService); -- -- assert.deepStrictEqual({ -- uri: project?.uri.toString(), -- displayName: project?.displayName, -- }, { -- uri: URI.file('/workspace/normal-repo').toString(), -- displayName: 'normal-repo', -- }); -- }); -- -- test('returns undefined outside a git working tree', async () => { -- gitService.insideWorkTree = false; -- -- assert.strictEqual(await resolveGitProject(URI.file('/workspace/plain-folder'), gitService), undefined); -- }); -- -- test('falls back to repository context when no git project is available', async () => { -- gitService.insideWorkTree = false; -- -- const project = await projectFromCopilotContext({ repository: 'microsoft/vscode' }, gitService); -- -- 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 990a15bb..00000000 ---- a/src/vs/platform/agentHost/test/node/copilotPluginConverters.test.ts -+++ /dev/null -@@ -1,349 +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 { writeFileSync, unlinkSync } from 'fs'; --import { fileURLToPath } from 'url'; --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, toSdkHooks } from '../../node/copilot/copilotPluginConverters.js'; --import type { IMcpServerDefinition, INamedPluginResource, IParsedHookGroup, 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, []); -- }); -- }); -- -- // ---- toSdkHooks ------------------------------------------------------- -- -- suite('toSdkHooks', () => { -- -- function makeHookGroup(type: string, command: string): IParsedHookGroup { -- return { -- type, -- commands: [{ command }], -- uri: URI.file('/plugin/hooks.json'), -- originalId: type, -- }; -- } -- -- /** -- * Writes a temp JS script that outputs JSON to stdout and returns -- * a `node ` command. Works on both bash (/bin/sh -c) and -- * cmd.exe without any shell-quoting issues. -- * The script is written alongside the compiled test file which is -- * guaranteed to exist, be writable, and have no spaces in CI. -- */ -- function echoJsonCmd(value: object): { command: string; cleanup: () => void } { -- const json = JSON.stringify(value); -- // fileURLToPath(new URL('.', import.meta.url)) is the Node ESM equivalent -- // of __dirname and works on Node 12+, unlike import.meta.dirname (Node 21.2+). -- const dir = fileURLToPath(new URL('.', import.meta.url)).replace(/[\\/]$/, ''); -- const filePath = `${dir}/vscode-test-hook-${Date.now()}.js`; -- writeFileSync(filePath, `process.stdout.write(${JSON.stringify(json)});\n`); -- // Do NOT quote the path: cmd.exe /c "node path" strips the outer quotes, -- // leaving "node path" without inner quoting which cmd.exe handles cleanly. -- const command = `node ${filePath}`; -- return { command, cleanup: () => { try { unlinkSync(filePath); } catch { /* ignore */ } } }; -- } -- -- test('onPostToolUse returns parsed JSON output as hook result', async () => { -- const expectedOutput = { additionalContext: 'Before presenting the plan, run review-plan skill' }; -- const { command, cleanup } = echoJsonCmd(expectedOutput); -- try { -- const hookGroup = makeHookGroup('PostToolUse', command); -- const hooks = toSdkHooks([hookGroup]); -- const toolResult = { textResultForLlm: 'ok', resultType: 'success' as const }; -- const result = await hooks.onPostToolUse!({ toolName: 'memory', toolArgs: {}, toolResult, timestamp: 0, cwd: '/' }, { sessionId: 'test' }); -- assert.deepStrictEqual(result, expectedOutput); -- } finally { -- cleanup(); -- } -- }); -- -- test('onPostToolUse returns undefined when output is non-JSON', async () => { -- // Use a script file so there are no cmd.exe quoting issues on Windows. -- const dir = fileURLToPath(new URL('.', import.meta.url)).replace(/[\\/]$/, ''); -- const filePath = `${dir}/vscode-test-hook-nonjson-${Date.now()}.js`; -- writeFileSync(filePath, `process.stdout.write('not-json');\n`); -- try { -- const hookGroup = makeHookGroup('PostToolUse', `node ${filePath}`); -- const hooks = toSdkHooks([hookGroup]); -- const toolResult = { textResultForLlm: 'ok', resultType: 'success' as const }; -- const result = await hooks.onPostToolUse!({ toolName: 'memory', toolArgs: {}, toolResult, timestamp: 0, cwd: '/' }, { sessionId: 'test' }); -- assert.strictEqual(result, undefined); -- } finally { -- try { unlinkSync(filePath); } catch { /* ignore */ } -- } -- }); -- -- test('onPostToolUse returns undefined when command fails', async () => { -- const dir = fileURLToPath(new URL('.', import.meta.url)).replace(/[\\/]$/, ''); -- const filePath = `${dir}/vscode-test-hook-fail-${Date.now()}.js`; -- writeFileSync(filePath, `process.exit(1);\n`); -- try { -- const hookGroup = makeHookGroup('PostToolUse', `node ${filePath}`); -- const hooks = toSdkHooks([hookGroup]); -- const toolResult = { textResultForLlm: 'ok', resultType: 'success' as const }; -- const result = await hooks.onPostToolUse!({ toolName: 'memory', toolArgs: {}, toolResult, timestamp: 0, cwd: '/' }, { sessionId: 'test' }); -- assert.strictEqual(result, undefined); -- } finally { -- try { unlinkSync(filePath); } catch { /* ignore */ } -- } -- }); -- -- test('onPostToolUse returns undefined when no commands', async () => { -- const hooks = toSdkHooks([]); -- assert.strictEqual(hooks.onPostToolUse, undefined); -- }); -- -- test('onPostToolUse calls editTrackingHooks and returns command output', async () => { -- const expectedOutput = { additionalContext: 'context from hook' }; -- const { command, cleanup } = echoJsonCmd(expectedOutput); -- try { -- const hookGroup = makeHookGroup('PostToolUse', command); -- let trackingInput: unknown; -- const editTrackingHooks = { -- onPreToolUse: async () => { }, -- onPostToolUse: async (input: unknown) => { trackingInput = input; }, -- }; -- const hooks = toSdkHooks([hookGroup], editTrackingHooks); -- const toolResult = { textResultForLlm: 'ok', resultType: 'success' as const }; -- const callInput = { toolName: 'memory', toolArgs: {}, toolResult, timestamp: 0, cwd: '/' }; -- const result = await hooks.onPostToolUse!(callInput, { sessionId: 'test' }); -- assert.deepStrictEqual(result, expectedOutput); -- assert.deepStrictEqual(trackingInput, callInput); -- } finally { -- cleanup(); -- } -- }); -- }); -- -- // ---- 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/copilotShellTools.test.ts b/src/vs/platform/agentHost/test/node/copilotShellTools.test.ts -deleted file mode 100644 -index 698ab70d..00000000 ---- a/src/vs/platform/agentHost/test/node/copilotShellTools.test.ts -+++ /dev/null -@@ -1,89 +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 { URI } from '../../../../base/common/uri.js'; --import { Disposable, DisposableStore, type IDisposable } from '../../../../base/common/lifecycle.js'; --import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; --import { IInstantiationService } from '../../../instantiation/common/instantiation.js'; --import { InstantiationService } from '../../../instantiation/common/instantiationService.js'; --import { ServiceCollection } from '../../../instantiation/common/serviceCollection.js'; --import { ILogService, NullLogService } from '../../../log/common/log.js'; --import type { CreateTerminalParams } from '../../common/state/protocol/commands.js'; --import type { TerminalClaim, TerminalInfo } from '../../common/state/protocol/state.js'; --import { IAgentHostTerminalManager } from '../../node/agentHostTerminalManager.js'; --import { ShellManager, prefixForHistorySuppression } from '../../node/copilot/copilotShellTools.js'; -- --class TestAgentHostTerminalManager implements IAgentHostTerminalManager { -- declare readonly _serviceBrand: undefined; -- -- readonly created: { params: CreateTerminalParams; options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean } }[] = []; -- -- async createTerminal(params: CreateTerminalParams, options?: { shell?: string; preventShellHistory?: boolean; nonInteractive?: boolean }): Promise { -- this.created.push({ params, options }); -- } -- writeInput(): void { } -- onData(): IDisposable { return Disposable.None; } -- onExit(): IDisposable { return Disposable.None; } -- onClaimChanged(): IDisposable { return Disposable.None; } -- onCommandFinished(): IDisposable { return Disposable.None; } -- getContent(): string | undefined { return undefined; } -- getClaim(): TerminalClaim | undefined { return undefined; } -- hasTerminal(): boolean { return false; } -- getExitCode(): number | undefined { return undefined; } -- supportsCommandDetection(): boolean { return false; } -- disposeTerminal(): void { } -- getTerminalInfos(): TerminalInfo[] { return []; } -- getTerminalState(): undefined { return undefined; } --} -- --suite('CopilotShellTools', () => { -- -- const disposables = new DisposableStore(); -- -- teardown(() => disposables.clear()); -- ensureNoDisposablesAreLeakedInTestSuite(); -- -- test('uses session working directory for created shells', async () => { -- const terminalManager = new TestAgentHostTerminalManager(); -- const services = new ServiceCollection(); -- services.set(ILogService, new NullLogService()); -- services.set(IAgentHostTerminalManager, terminalManager); -- const instantiationService: IInstantiationService = disposables.add(new InstantiationService(services)); -- services.set(IInstantiationService, instantiationService); -- const worktreePath = URI.file('/workspace/worktree').fsPath; -- const explicitCwd = URI.file('/explicit/cwd').fsPath; -- const shellManager = disposables.add(instantiationService.createInstance(ShellManager, URI.parse('copilot:/session-1'), URI.file(worktreePath))); -- -- await shellManager.getOrCreateShell('bash', 'turn-1', 'tool-1'); -- await shellManager.getOrCreateShell('bash', 'turn-2', 'tool-2', explicitCwd); -- -- assert.deepStrictEqual(terminalManager.created.map(c => c.params.cwd), [ -- worktreePath, -- explicitCwd, -- ]); -- }); -- -- test('opts every managed shell into shell-history suppression and non-interactive mode', async () => { -- const terminalManager = new TestAgentHostTerminalManager(); -- const services = new ServiceCollection(); -- services.set(ILogService, new NullLogService()); -- services.set(IAgentHostTerminalManager, terminalManager); -- const instantiationService: IInstantiationService = disposables.add(new InstantiationService(services)); -- services.set(IInstantiationService, instantiationService); -- const shellManager = disposables.add(instantiationService.createInstance(ShellManager, URI.parse('copilot:/session-1'), undefined)); -- -- await shellManager.getOrCreateShell('bash', 'turn-1', 'tool-1'); -- -- assert.strictEqual(terminalManager.created.length, 1); -- assert.strictEqual(terminalManager.created[0].options?.preventShellHistory, true); -- assert.strictEqual(terminalManager.created[0].options?.nonInteractive, true); -- }); -- -- test('prefixForHistorySuppression prepends a space for POSIX shells, no-op for PowerShell', () => { -- assert.strictEqual(prefixForHistorySuppression('bash'), ' '); -- assert.strictEqual(prefixForHistorySuppression('powershell'), ''); -- }); --}); -diff --git a/src/vs/platform/agentHost/test/node/copilotToolDisplay.test.ts b/src/vs/platform/agentHost/test/node/copilotToolDisplay.test.ts -deleted file mode 100644 -index 07d9471f..00000000 ---- a/src/vs/platform/agentHost/test/node/copilotToolDisplay.test.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 assert from 'assert'; --import { URI } from '../../../../base/common/uri.js'; --import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; --import { getInvocationMessage, getPastTenseMessage, getPermissionDisplay, getShellLanguage, getToolInputString, getToolKind, isHiddenTool, synthesizeSkillToolEvents, type ITypedPermissionRequest } from '../../node/copilot/copilotToolDisplay.js'; -- --suite('getPermissionDisplay — cd-prefix stripping', () => { -- -- ensureNoDisposablesAreLeakedInTestSuite(); -- -- const wd = URI.file('/repo/project'); -- -- test('strips redundant cd from shell permission request fullCommandText', () => { -- const request: ITypedPermissionRequest = { -- kind: 'shell', -- fullCommandText: 'cd /repo/project && npm test', -- } as ITypedPermissionRequest; -- const display = getPermissionDisplay(request, wd); -- assert.strictEqual(display.toolInput, 'npm test'); -- assert.strictEqual(display.permissionKind, 'shell'); -- }); -- -- test('leaves shell command alone when cd target differs from working directory', () => { -- const request: ITypedPermissionRequest = { -- kind: 'shell', -- fullCommandText: 'cd /tmp && ls', -- } as ITypedPermissionRequest; -- const display = getPermissionDisplay(request, wd); -- assert.strictEqual(display.toolInput, 'cd /tmp && ls'); -- }); -- -- test('leaves shell command alone when no working directory provided', () => { -- const request: ITypedPermissionRequest = { -- kind: 'shell', -- fullCommandText: 'cd /repo/project && npm test', -- } as ITypedPermissionRequest; -- const display = getPermissionDisplay(request, undefined); -- assert.strictEqual(display.toolInput, 'cd /repo/project && npm test'); -- }); -- -- test('strips redundant cd from custom-tool shell permission request', () => { -- const request: ITypedPermissionRequest = { -- kind: 'custom-tool', -- toolName: 'bash', -- args: { command: 'cd /repo/project && echo hi' }, -- } as ITypedPermissionRequest; -- const display = getPermissionDisplay(request, wd); -- assert.strictEqual(display.toolInput, 'echo hi'); -- assert.strictEqual(display.permissionKind, 'shell'); -- }); -- -- test('does not affect non-shell custom-tool requests', () => { -- const request: ITypedPermissionRequest = { -- kind: 'custom-tool', -- toolName: 'some_other_tool', -- args: { command: 'cd /repo/project && echo hi' }, -- } as ITypedPermissionRequest; -- const display = getPermissionDisplay(request, wd); -- // Falls through to the generic branch — toolInput is the JSON-stringified args. -- assert.ok(display.toolInput?.includes('cd /repo/project'), `expected unrewritten args, got: ${display.toolInput}`); -- assert.strictEqual(display.permissionKind, 'custom-tool'); -- }); -- -- test('handles powershell custom-tool with semicolon separator', () => { -- const request: ITypedPermissionRequest = { -- kind: 'custom-tool', -- toolName: 'powershell', -- args: { command: 'cd /repo/project; dir' }, -- } as ITypedPermissionRequest; -- const display = getPermissionDisplay(request, wd); -- assert.strictEqual(display.toolInput, 'dir'); -- }); --}); -- --suite('view tool — view_range display', () => { -- -- ensureNoDisposablesAreLeakedInTestSuite(); -- -- function invocation(parameters: Record | undefined): string { -- const result = getInvocationMessage('view', 'View File', parameters); -- return typeof result === 'string' ? result : result.markdown; -- } -- -- function pastTense(parameters: Record | undefined): string { -- const result = getPastTenseMessage('view', 'View File', parameters, true); -- return typeof result === 'string' ? result : result.markdown; -- } -- -- test('renders path-only when view_range is absent', () => { -- assert.ok(invocation({ path: '/repo/file.ts' }).startsWith('Reading [')); -- assert.ok(pastTense({ path: '/repo/file.ts' }).startsWith('Read [')); -- }); -- -- test('renders "lines X to Y" for a valid two-element range', () => { -- assert.ok(invocation({ path: '/repo/file.ts', view_range: [10, 20] }).endsWith(', lines 10 to 20')); -- assert.ok(pastTense({ path: '/repo/file.ts', view_range: [10, 20] }).endsWith(', lines 10 to 20')); -- }); -- -- test('renders "line X" when start === end', () => { -- assert.ok(invocation({ path: '/repo/file.ts', view_range: [10, 10] }).endsWith(', line 10')); -- assert.ok(pastTense({ path: '/repo/file.ts', view_range: [10, 10] }).endsWith(', line 10')); -- }); -- -- test('renders "line X to the end" for the -1 EOF sentinel', () => { -- assert.ok(invocation({ path: '/repo/file.ts', view_range: [10, -1] }).endsWith(', line 10 to the end')); -- assert.ok(pastTense({ path: '/repo/file.ts', view_range: [10, -1] }).endsWith(', line 10 to the end')); -- }); -- -- test('falls back to path-only for invalid ranges', () => { -- // end < start (and not -1) -- assert.ok(!invocation({ path: '/repo/file.ts', view_range: [20, 10] }).includes(',')); -- // negative start -- assert.ok(!invocation({ path: '/repo/file.ts', view_range: [-5, 10] }).includes(',')); -- // non-integer -- assert.ok(!invocation({ path: '/repo/file.ts', view_range: [1.5, 10] }).includes(',')); -- // wrong arity -- assert.ok(!invocation({ path: '/repo/file.ts', view_range: [10] }).includes(',')); -- assert.ok(!invocation({ path: '/repo/file.ts', view_range: [10, 20, 30] }).includes(',')); -- // non-array -- assert.ok(!invocation({ path: '/repo/file.ts', view_range: 'whatever' }).includes(',')); -- }); --}); -- --// ---- write_/read_ shell tool display --------------------------------------- --// --// Coverage for the secondary shell helpers (write_bash, read_bash, and their --// powershell siblings). These never appear in a permission dialog (they're --// registered with `skipPermission: true` — see copilotShellTools.ts), but they --// still flow through the tool-execution display pipeline. -- --suite('copilotToolDisplay — write_/read_ shell tools', () => { -- -- ensureNoDisposablesAreLeakedInTestSuite(); -- -- suite('getToolKind', () => { -- -- test('returns terminal for bash', () => { -- assert.strictEqual(getToolKind('bash'), 'terminal'); -- }); -- -- test('returns terminal for powershell', () => { -- assert.strictEqual(getToolKind('powershell'), 'terminal'); -- }); -- -- test('returns undefined for write_bash (sending input to a running program, not launching a terminal)', () => { -- assert.strictEqual(getToolKind('write_bash'), undefined); -- }); -- -- test('returns undefined for write_powershell', () => { -- assert.strictEqual(getToolKind('write_powershell'), undefined); -- }); -- -- test('returns undefined for read_bash (reading output, not launching a terminal)', () => { -- assert.strictEqual(getToolKind('read_bash'), undefined); -- }); -- -- test('returns undefined for read_powershell', () => { -- assert.strictEqual(getToolKind('read_powershell'), undefined); -- }); -- -- test('returns subagent for task', () => { -- assert.strictEqual(getToolKind('task'), 'subagent'); -- }); -- -- test('returns undefined for view', () => { -- assert.strictEqual(getToolKind('view'), undefined); -- }); -- }); -- -- suite('getShellLanguage', () => { -- -- test('bash returns shellscript', () => { -- assert.strictEqual(getShellLanguage('bash'), 'shellscript'); -- }); -- -- test('powershell returns powershell', () => { -- assert.strictEqual(getShellLanguage('powershell'), 'powershell'); -- }); -- -- test('write_bash returns shellscript', () => { -- assert.strictEqual(getShellLanguage('write_bash'), 'shellscript'); -- }); -- -- test('write_powershell returns powershell', () => { -- assert.strictEqual(getShellLanguage('write_powershell'), 'powershell'); -- }); -- -- test('read_bash returns shellscript', () => { -- assert.strictEqual(getShellLanguage('read_bash'), 'shellscript'); -- }); -- -- test('read_powershell returns powershell', () => { -- assert.strictEqual(getShellLanguage('read_powershell'), 'powershell'); -- }); -- }); -- -- suite('getInvocationMessage', () => { -- -- function getText(msg: ReturnType): string { -- return typeof msg === 'string' ? msg : msg.markdown; -- } -- -- test('write_bash with command includes the command text', () => { -- const msg = getInvocationMessage('write_bash', 'Write Shell Input', { command: 'echo hello' }); -- assert.ok(getText(msg).includes('echo hello'), `expected 'echo hello' in: ${getText(msg)}`); -- }); -- -- test('write_bash without command returns a non-empty fallback message', () => { -- const msg = getInvocationMessage('write_bash', 'Write Shell Input', undefined); -- assert.ok(getText(msg).length > 0); -- assert.ok(!getText(msg).includes('undefined')); -- }); -- -- test('write_powershell with command includes the command text', () => { -- const msg = getInvocationMessage('write_powershell', 'Write Shell Input', { command: 'Get-Date' }); -- assert.ok(getText(msg).includes('Get-Date'), `expected 'Get-Date' in: ${getText(msg)}`); -- }); -- -- test('read_bash returns a non-empty message', () => { -- const msg = getInvocationMessage('read_bash', 'Read Shell Output', undefined); -- assert.ok(getText(msg).length > 0); -- }); -- -- test('read_powershell returns a non-empty message', () => { -- const msg = getInvocationMessage('read_powershell', 'Read Shell Output', undefined); -- assert.ok(getText(msg).length > 0); -- }); -- -- test('write_bash message differs from bash message (distinct wording)', () => { -- const writeBashMsg = getText(getInvocationMessage('write_bash', 'Write Shell Input', { command: 'echo hi' })); -- const bashMsg = getText(getInvocationMessage('bash', 'Bash', { command: 'echo hi' })); -- // Both include the command, but the surrounding text should differ -- assert.notStrictEqual(writeBashMsg, bashMsg); -- }); -- }); -- -- suite('getPastTenseMessage', () => { -- -- function getText(msg: ReturnType): string { -- return typeof msg === 'string' ? msg : msg.markdown; -- } -- -- test('write_bash with command includes the command text', () => { -- const msg = getPastTenseMessage('write_bash', 'Write Shell Input', { command: 'echo hello' }, true); -- assert.ok(getText(msg).includes('echo hello'), `expected 'echo hello' in: ${getText(msg)}`); -- }); -- -- test('write_bash without command returns a non-empty fallback message', () => { -- const msg = getPastTenseMessage('write_bash', 'Write Shell Input', undefined, true); -- assert.ok(getText(msg).length > 0); -- }); -- -- test('write_powershell with command includes the command text', () => { -- const msg = getPastTenseMessage('write_powershell', 'Write Shell Input', { command: 'Get-Date' }, true); -- assert.ok(getText(msg).includes('Get-Date'), `expected 'Get-Date' in: ${getText(msg)}`); -- }); -- -- test('read_bash success returns a non-empty message', () => { -- const msg = getPastTenseMessage('read_bash', 'Read Shell Output', undefined, true); -- assert.ok(getText(msg).length > 0); -- }); -- -- test('write_bash failure returns a non-empty error message', () => { -- const msg = getPastTenseMessage('write_bash', 'Write Shell Input', { command: 'echo hello' }, false); -- assert.ok(getText(msg).length > 0); -- }); -- }); -- -- suite('getToolInputString', () => { -- -- test('write_bash extracts command field', () => { -- assert.strictEqual(getToolInputString('write_bash', { command: 'echo hello' }, undefined), 'echo hello'); -- }); -- -- test('write_powershell extracts command field', () => { -- assert.strictEqual(getToolInputString('write_powershell', { command: 'Get-Date' }, undefined), 'Get-Date'); -- }); -- -- test('write_bash falls back to rawArguments when no command field', () => { -- assert.strictEqual(getToolInputString('write_bash', {}, '{"command":"echo hello"}'), '{"command":"echo hello"}'); -- }); -- -- test('write_bash returns undefined when both parameters and rawArguments are absent', () => { -- assert.strictEqual(getToolInputString('write_bash', undefined, undefined), undefined); -- }); -- -- test('read_bash with no parameters returns undefined', () => { -- assert.strictEqual(getToolInputString('read_bash', undefined, undefined), undefined); -- }); -- }); --}); -- --suite('skill events', () => { -- -- ensureNoDisposablesAreLeakedInTestSuite(); -- -- const session = URI.parse('agent://copilot/test'); -- -- test('hides the raw `skill` tool call and synthesizes a tool-start/complete pair from `skill.invoked`', () => { -- const withPath = synthesizeSkillToolEvents( -- session, -- { name: 'plan', path: '/abs/repo/skills/plan/SKILL.md' }, -- 'evt-123', -- ); -- const noPath = synthesizeSkillToolEvents( -- session, -- { name: 'plan' }, -- undefined, -- ); -- -- assert.deepStrictEqual({ -- skillIsHidden: isHiddenTool('skill'), -- withPathToolCallId: withPath.start.toolCallId, -- withPathSameIdOnComplete: withPath.start.toolCallId === withPath.complete.toolCallId, -- withPathToolName: withPath.start.toolName, -- withPathDisplayName: withPath.start.displayName, -- withPathInvocation: withPath.start.invocationMessage, -- withPathPastTense: withPath.complete.result.pastTenseMessage, -- withPathSuccess: withPath.complete.result.success, -- noPathToolCallId: noPath.start.toolCallId, -- noPathInvocation: noPath.start.invocationMessage, -- noPathPastTense: noPath.complete.result.pastTenseMessage, -- }, { -- skillIsHidden: true, -- withPathToolCallId: 'synth-skill-evt-123', -- withPathSameIdOnComplete: true, -- withPathToolName: 'skill', -- withPathDisplayName: 'Read Skill', -- withPathInvocation: { markdown: 'Reading skill [plan](file:///abs/repo/skills/plan/SKILL.md)' }, -- withPathPastTense: { markdown: 'Read skill [plan](file:///abs/repo/skills/plan/SKILL.md)' }, -- withPathSuccess: true, -- noPathToolCallId: 'synth-skill-2108d652', -- noPathInvocation: 'Reading skill plan', -- noPathPastTense: 'Read skill plan', -- }); -- }); --}); -diff --git a/src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts b/src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts -deleted file mode 100644 -index d5105061..00000000 ---- a/src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts -+++ /dev/null -@@ -1,401 +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 { DisposableStore } from '../../../../base/common/lifecycle.js'; --import { URI } from '../../../../base/common/uri.js'; --import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; --import { AgentSession } from '../../common/agentService.js'; --import { FileEditKind, ToolResultContentType } from '../../common/state/sessionState.js'; --import { SessionDatabase } from '../../node/sessionDatabase.js'; --import { parseSessionDbUri } from '../../node/copilot/fileEditTracker.js'; --import { mapSessionEvents, type ISessionEvent } from '../../node/copilot/mapSessionEvents.js'; -- --suite('mapSessionEvents', () => { -- -- const disposables = new DisposableStore(); -- let db: SessionDatabase | undefined; -- const session = AgentSession.uri('copilot', 'test-session'); -- -- teardown(async () => { -- disposables.clear(); -- await db?.close(); -- }); -- ensureNoDisposablesAreLeakedInTestSuite(); -- -- // ---- Basic event mapping -------------------------------------------- -- -- test('maps user and assistant messages', async () => { -- const events: ISessionEvent[] = [ -- { type: 'user.message', data: { messageId: 'msg-1', content: 'hello' } }, -- { type: 'assistant.message', data: { messageId: 'msg-2', content: 'world' } }, -- ]; -- -- const result = await mapSessionEvents(session, undefined, events); -- assert.strictEqual(result.length, 2); -- assert.deepStrictEqual(result[0], { -- session, -- type: 'message', -- role: 'user', -- messageId: 'msg-1', -- content: 'hello', -- toolRequests: undefined, -- reasoningOpaque: undefined, -- reasoningText: undefined, -- encryptedContent: undefined, -- parentToolCallId: undefined, -- }); -- assert.strictEqual(result[1].type, 'message'); -- assert.strictEqual((result[1] as { role: string }).role, 'assistant'); -- }); -- -- test('maps tool start and complete events', async () => { -- const events: ISessionEvent[] = [ -- { -- type: 'tool.execution_start', -- data: { toolCallId: 'tc-1', toolName: 'shell', arguments: { command: 'echo hi' } }, -- }, -- { -- type: 'tool.execution_complete', -- data: { toolCallId: 'tc-1', success: true, result: { content: 'hi\n' } }, -- }, -- ]; -- -- const result = await mapSessionEvents(session, undefined, events); -- assert.strictEqual(result.length, 2); -- assert.strictEqual(result[0].type, 'tool_start'); -- assert.strictEqual(result[1].type, 'tool_complete'); -- -- const complete = result[1] as { result: { content?: readonly { type: string; text?: string }[] } }; -- assert.ok(complete.result.content); -- assert.strictEqual(complete.result.content[0].type, ToolResultContentType.Text); -- }); -- -- test('skips tool_complete without matching tool_start', async () => { -- const events: ISessionEvent[] = [ -- { type: 'tool.execution_complete', data: { toolCallId: 'orphan', success: true } }, -- ]; -- -- const result = await mapSessionEvents(session, undefined, events); -- assert.strictEqual(result.length, 0); -- }); -- -- test('ignores unknown event types', async () => { -- const events: ISessionEvent[] = [ -- { type: 'some.unknown.event', data: {} }, -- { type: 'user.message', data: { messageId: 'msg-1', content: 'test' } }, -- ]; -- -- const result = await mapSessionEvents(session, undefined, events); -- assert.strictEqual(result.length, 1); -- }); -- -- // ---- File edit restoration ------------------------------------------ -- -- suite('file edit restoration', () => { -- -- test('restores file edits from database for edit tools', async () => { -- db = disposables.add(await SessionDatabase.open(':memory:')); -- await db.createTurn('turn-1'); -- await db.storeFileEdit({ -- turnId: 'turn-1', -- toolCallId: 'tc-edit', -- filePath: '/workspace/file.ts', -- kind: FileEditKind.Edit, -- beforeContent: new TextEncoder().encode('before'), -- afterContent: new TextEncoder().encode('after'), -- addedLines: 3, -- removedLines: 1, -- }); -- -- const events: ISessionEvent[] = [ -- { -- type: 'tool.execution_start', -- data: { toolCallId: 'tc-edit', toolName: 'edit', arguments: { filePath: '/workspace/file.ts' } }, -- }, -- { -- type: 'tool.execution_complete', -- data: { toolCallId: 'tc-edit', success: true, result: { content: 'Edited file.ts' } }, -- }, -- ]; -- -- const result = await mapSessionEvents(session, db, events); -- const complete = result[1]; -- assert.strictEqual(complete.type, 'tool_complete'); -- -- const content = (complete as { result: { content?: readonly Record[] } }).result.content; -- assert.ok(content); -- // Should have text content + file edit -- assert.strictEqual(content.length, 2); -- assert.strictEqual(content[0].type, ToolResultContentType.Text); -- assert.strictEqual(content[1].type, ToolResultContentType.FileEdit); -- -- // 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 beforeFields = parseSessionDbUri(fileEdit.before.content.uri); -- assert.ok(beforeFields); -- assert.strictEqual(beforeFields.toolCallId, 'tc-edit'); -- assert.strictEqual(beforeFields.filePath, '/workspace/file.ts'); -- assert.strictEqual(beforeFields.part, 'before'); -- assert.deepStrictEqual(fileEdit.diff, { added: 3, removed: 1 }); -- }); -- -- test('handles multiple file edits for one tool call', async () => { -- db = disposables.add(await SessionDatabase.open(':memory:')); -- await db.createTurn('turn-1'); -- await db.storeFileEdit({ -- turnId: 'turn-1', -- toolCallId: 'tc-multi', -- filePath: '/workspace/a.ts', -- kind: FileEditKind.Edit, -- beforeContent: new Uint8Array(0), -- afterContent: new TextEncoder().encode('a'), -- addedLines: undefined, -- removedLines: undefined, -- }); -- await db.storeFileEdit({ -- turnId: 'turn-1', -- toolCallId: 'tc-multi', -- filePath: '/workspace/b.ts', -- kind: FileEditKind.Edit, -- beforeContent: new Uint8Array(0), -- afterContent: new TextEncoder().encode('b'), -- addedLines: undefined, -- removedLines: undefined, -- }); -- -- const events: ISessionEvent[] = [ -- { -- type: 'tool.execution_start', -- data: { toolCallId: 'tc-multi', toolName: 'edit' }, -- }, -- { -- type: 'tool.execution_complete', -- data: { toolCallId: 'tc-multi', success: true }, -- }, -- ]; -- -- const result = await mapSessionEvents(session, db, events); -- const content = (result[1] as { result: { content?: readonly Record[] } }).result.content; -- assert.ok(content); -- // Two file edits (no text since result had no content) -- const fileEdits = content.filter(c => c.type === ToolResultContentType.FileEdit); -- assert.strictEqual(fileEdits.length, 2); -- }); -- -- test('works without database (no file edits restored)', async () => { -- const events: ISessionEvent[] = [ -- { -- type: 'tool.execution_start', -- data: { toolCallId: 'tc-1', toolName: 'edit', arguments: { filePath: '/workspace/file.ts' } }, -- }, -- { -- type: 'tool.execution_complete', -- data: { toolCallId: 'tc-1', success: true, result: { content: 'done' } }, -- }, -- ]; -- -- const result = await mapSessionEvents(session, undefined, events); -- const content = (result[1] as { result: { content?: readonly Record[] } }).result.content; -- assert.ok(content); -- // Only text content, no file edits -- assert.strictEqual(content.length, 1); -- assert.strictEqual(content[0].type, ToolResultContentType.Text); -- }); -- -- test('non-edit tools do not get file edits even if db has data', async () => { -- db = disposables.add(await SessionDatabase.open(':memory:')); -- -- const events: ISessionEvent[] = [ -- { -- type: 'tool.execution_start', -- data: { toolCallId: 'tc-1', toolName: 'shell', arguments: { command: 'ls' } }, -- }, -- { -- type: 'tool.execution_complete', -- data: { toolCallId: 'tc-1', success: true, result: { content: 'files' } }, -- }, -- ]; -- -- const result = await mapSessionEvents(session, db, events); -- const content = (result[1] as { result: { content?: readonly Record[] } }).result.content; -- assert.ok(content); -- assert.strictEqual(content.length, 1); -- assert.strictEqual(content[0].type, ToolResultContentType.Text); -- }); -- }); -- -- // ---- Subagent events ------------------------------------------------ -- -- suite('subagent events', () => { -- -- test('maps subagent.started event to subagent_started progress event', async () => { -- const events: ISessionEvent[] = [ -- { -- type: 'subagent.started', -- data: { -- toolCallId: 'tc-1', -- agentName: 'code-reviewer', -- agentDisplayName: 'Code Reviewer', -- agentDescription: 'Reviews code', -- }, -- }, -- ]; -- -- const result = await mapSessionEvents(session, undefined, events); -- assert.strictEqual(result.length, 1); -- assert.strictEqual(result[0].type, 'subagent_started'); -- const event = result[0] as { type: string; toolCallId: string; agentName: string; agentDisplayName: string }; -- assert.strictEqual(event.toolCallId, 'tc-1'); -- assert.strictEqual(event.agentName, 'code-reviewer'); -- assert.strictEqual(event.agentDisplayName, 'Code Reviewer'); -- }); -- }); -- -- // ---- Skill events --------------------------------------------------- -- -- suite('skill events', () => { -- -- test('synthesizes tool start/complete from skill.invoked and filters synthetic skill-injected user messages', async () => { -- const events: ISessionEvent[] = [ -- { -- type: 'tool.execution_start', -- data: { toolCallId: 'tc-skill', toolName: 'skill', arguments: { skill: 'plan' } }, -- }, -- { -- type: 'tool.execution_complete', -- data: { toolCallId: 'tc-skill', success: true }, -- }, -- { -- type: 'skill.invoked', -- id: 'evt-42', -- data: { name: 'plan', path: '/abs/repo/skills/plan/SKILL.md' }, -- }, -- { -- type: 'user.message', -- data: { messageId: 'msg-skill', content: '', source: 'skill-plan' }, -- }, -- { -- type: 'assistant.message', -- data: { messageId: 'msg-1', content: 'ok' }, -- }, -- ]; -- -- const result = await mapSessionEvents(session, undefined, events); -- -- assert.deepStrictEqual({ -- count: result.length, -- types: result.map(r => r.type), -- skillStart: result[0], -- skillComplete: result[1], -- assistantRole: (result[2] as { role: string }).role, -- }, { -- count: 3, -- types: ['tool_start', 'tool_complete', 'message'], -- skillStart: { -- session, -- type: 'tool_start', -- toolCallId: 'synth-skill-evt-42', -- toolName: 'skill', -- displayName: 'Read Skill', -- invocationMessage: { markdown: 'Reading skill [plan](file:///abs/repo/skills/plan/SKILL.md)' }, -- }, -- skillComplete: { -- session, -- type: 'tool_complete', -- toolCallId: 'synth-skill-evt-42', -- result: { -- success: true, -- pastTenseMessage: { markdown: 'Read skill [plan](file:///abs/repo/skills/plan/SKILL.md)' }, -- }, -- }, -- assistantRole: 'assistant', -- }); -- }); -- }); -- -- // ---- cd-prefix rewriting -------------------------------------------- -- -- suite('cd-prefix rewriting', () => { -- -- const cwd = URI.file('/workspace/proj'); -- -- function makeBashEvent(command: string, toolCallId = 'tc-1'): ISessionEvent { -- return { -- type: 'tool.execution_start', -- data: { toolCallId, toolName: 'bash', arguments: { command } }, -- }; -- } -- -- function getStart(events: ReturnType extends Promise ? R : never) { -- return events[0] as { toolInput: string; toolArguments?: string }; -- } -- -- test('strips redundant bash cd prefix matching workingDirectory', async () => { -- const result = await mapSessionEvents(session, undefined, [ -- makeBashEvent('cd /workspace/proj && ls -la'), -- ], cwd); -- const start = getStart(result); -- assert.strictEqual(start.toolInput, 'ls -la'); -- assert.deepStrictEqual(JSON.parse(start.toolArguments!), { command: 'ls -la' }); -- }); -- -- test('leaves command unchanged when cd dir does not match', async () => { -- const result = await mapSessionEvents(session, undefined, [ -- makeBashEvent('cd /other && ls'), -- ], cwd); -- const start = getStart(result); -- assert.strictEqual(start.toolInput, 'cd /other && ls'); -- }); -- -- test('leaves command unchanged when no workingDirectory provided', async () => { -- const result = await mapSessionEvents(session, undefined, [ -- makeBashEvent('cd /workspace/proj && ls'), -- ]); -- const start = getStart(result); -- assert.strictEqual(start.toolInput, 'cd /workspace/proj && ls'); -- }); -- -- test('non-shell tools are not rewritten even with matching command field', async () => { -- const result = await mapSessionEvents(session, undefined, [ -- { -- type: 'tool.execution_start', -- data: { toolCallId: 'tc-1', toolName: 'edit', arguments: { command: 'cd /workspace/proj && ls' } }, -- }, -- ], cwd); -- const start = getStart(result); -- // edit tool's toolInput is derived from filePath, not command — but toolArguments preserves original -- assert.deepStrictEqual(JSON.parse(start.toolArguments!), { command: 'cd /workspace/proj && ls' }); -- }); -- -- test('handles trailing slash on workingDirectory', async () => { -- const result = await mapSessionEvents(session, undefined, [ -- makeBashEvent('cd /workspace/proj && ls'), -- ], URI.file('/workspace/proj/')); -- const start = getStart(result); -- assert.strictEqual(start.toolInput, 'ls'); -- }); -- -- test('handles quoted directory in cd prefix', async () => { -- const cwdWithSpaces = URI.file('/workspace/my proj'); -- const result = await mapSessionEvents(session, undefined, [ -- makeBashEvent('cd "/workspace/my proj" && ls'), -- ], cwdWithSpaces); -- const start = getStart(result); -- assert.strictEqual(start.toolInput, 'ls'); -- }); -- -- test('rewrites powershell commands too', async () => { -- const result = await mapSessionEvents(session, undefined, [ -- { -- type: 'tool.execution_start', -- data: { toolCallId: 'tc-1', toolName: 'powershell', arguments: { command: 'cd /workspace/proj; dir' } }, -- }, -- ], cwd); -- const start = getStart(result); -- assert.strictEqual(start.toolInput, 'dir'); -- }); -- }); --}); diff --git a/patches/52-ext-copilot-remove-it.patch b/patches/52-ext-copilot-remove-it.patch new file mode 100644 index 00000000000..ef9b79bb4d9 --- /dev/null +++ b/patches/52-ext-copilot-remove-it.patch @@ -0,0 +1,491 @@ +diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts +index 3c0e3a16..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, prepareBuiltInCopilotRipgrepShim } 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,12 +463,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { + +-function prepareCopilotRipgrepShimTaskREH(platform: string, arch: string, destinationFolderName: string) { +- return async () => { +- const outputDir = path.join(BUILD_ROOT, destinationFolderName); +- const nodeModulesDir = path.join(outputDir, 'node_modules'); +- +- const builtInCopilotExtensionDir = path.join(outputDir, 'extensions', 'copilot'); +- prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, nodeModulesDir); +- }; +-} +- + /** +@@ -524,3 +512,2 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { + packageTask(type, platform, arch, sourceFolderName, destinationFolderName), +- prepareCopilotRipgrepShimTaskREH(platform, arch, destinationFolderName) + ]; +@@ -538,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 25aa6a57..7615c27a 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, prepareBuiltInCopilotRipgrepShim } from './lib/copilot.ts'; + import { useEsbuildTranspile } from './buildConfig.ts'; +@@ -337,3 +336,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) +@@ -344,3 +342,2 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d + '**/@vscode/ripgrep/bin/*', +- '**/@github/copilot-*/**', + '**/node-pty/build/Release/*', +@@ -559,19 +556,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { + +-function prepareCopilotRipgrepShimTask(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'); +- +- const builtInCopilotExtensionDir = path.join(appBase, 'extensions', 'copilot'); +- prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, appNodeModulesDir); +- }; +-} +- + const buildRoot = path.dirname(root); +@@ -601,3 +581,2 @@ BUILD_TARGETS.forEach(buildTarget => { + packageTask(platform, arch, sourceFolderName, destinationFolderName, opts), +- prepareCopilotRipgrepShimTask(platform, arch, destinationFolderName) + ]; +@@ -628,3 +607,2 @@ BUILD_TARGETS.forEach(buildTarget => { + compileNonNativeExtensionsBuildTask, +- compileCopilotExtensionBuildTask, + compileExtensionMediaBuildTask, +@@ -649,3 +627,2 @@ BUILD_TARGETS.forEach(buildTarget => { + compileNonNativeExtensionsBuildTask, +- compileCopilotExtensionBuildTask, + compileExtensionMediaBuildTask, +diff --git a/build/npm/dirs.ts b/build/npm/dirs.ts +index 468d2df8..64b1c19d 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 dc154d4b..834a4a78 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 96150298..752530f6 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -12,5 +12,2 @@ + "dependencies": { +- "@anthropic-ai/sdk": "^0.82.0", +- "@github/copilot": "1.0.39", +- "@github/copilot-sdk": "^0.3.0", + "@microsoft/1ds-core-js": "^3.2.13", +@@ -340,22 +337,2 @@ + }, +- "node_modules/@anthropic-ai/sdk": { +- "version": "0.82.0", +- "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.82.0.tgz", +- "integrity": "sha512-xdHTjL1GlUlDugHq/I47qdOKp/ROPvuHl7ROJCgUQigbvPu7asf9KcAcU1EqdrP2LuVhEKaTs7Z+ShpZDRzHdQ==", +- "license": "MIT", +- "dependencies": { +- "json-schema-to-ts": "^3.1.1" +- }, +- "bin": { +- "anthropic-ai-sdk": "bin/cli" +- }, +- "peerDependencies": { +- "zod": "^3.25.0 || ^4.0.0" +- }, +- "peerDependenciesMeta": { +- "zod": { +- "optional": true +- } +- } +- }, + "node_modules/@azure-rest/ai-translation-text": { +@@ -880,2 +857,3 @@ + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", ++ "dev": true, + "license": "MIT", +@@ -1152,138 +1130,2 @@ + }, +- "node_modules/@github/copilot": { +- "version": "1.0.39", +- "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.39.tgz", +- "integrity": "sha512-AY0VPYf6QQm88wUcOav2B36iedWKBUaMegKRxxY2uIHESiU6HueEuQR/n7D3U2UdD0zLox3jFRjYbZAsr2CgkQ==", +- "license": "SEE LICENSE IN LICENSE.md", +- "bin": { +- "copilot": "npm-loader.js" +- }, +- "optionalDependencies": { +- "@github/copilot-darwin-arm64": "1.0.39", +- "@github/copilot-darwin-x64": "1.0.39", +- "@github/copilot-linux-arm64": "1.0.39", +- "@github/copilot-linux-x64": "1.0.39", +- "@github/copilot-win32-arm64": "1.0.39", +- "@github/copilot-win32-x64": "1.0.39" +- } +- }, +- "node_modules/@github/copilot-darwin-arm64": { +- "version": "1.0.39", +- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.39.tgz", +- "integrity": "sha512-E8WfNL43NMzMTDDpCiYikaEmYCMAr6mz8LHrJtkaFuVXVkBr/q2NI3hAtwHFy8M11Fac/MeIe3/VEymWwwh3kw==", +- "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.39", +- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.39.tgz", +- "integrity": "sha512-0zbC4lDVX7l8Wvq+JSCMjO0xTN69nWLejTBCl3Ev5bP6P+/7wPURcUvZKoHEaXxOULQ3AGj0DwZNAsvvQkA/6Q==", +- "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.39", +- "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.39.tgz", +- "integrity": "sha512-x88FuByweJlHlAmUZXjq4JlmtqgoM57Fe7nXzQkGr2Y5wnc2EDydBzFYEOlYDSWozQreimaJIm0KEMAA5T8/Fg==", +- "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.39", +- "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.39.tgz", +- "integrity": "sha512-ssahg8r7a0VCsHVXPRmFFXx70xNAxaTM2SZfG7qPRfFB2OM8gHrW26F2oikTklDF6D+A2MfSAMpzJLBUZbPnhw==", +- "cpu": [ +- "x64" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linux-x64": "copilot" +- } +- }, +- "node_modules/@github/copilot-sdk": { +- "version": "0.3.0", +- "resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-0.3.0.tgz", +- "integrity": "sha512-SUo35k56pzzgYgwmDPHcu7kZxPrzXbH66IWXaEf6pmb94DlA709F82HrrDeja087TL4djJ9OuvRFWWOKCosAsg==", +- "license": "MIT", +- "dependencies": { +- "@github/copilot": "^1.0.36-0", +- "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.39", +- "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.39.tgz", +- "integrity": "sha512-hhBWGZQIywbp6MBxlqMX2GSmHqtUAOGwpo9b0igscecL4i0kz89QNasC+mKiN+zFEHP6I8gggOu87XPI17Io8Q==", +- "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.39", +- "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.39.tgz", +- "integrity": "sha512-0ehlMtBiwKjmfEY3hVZggdn7qrmPMC8ueBQv/b+6UY3SMRS/M/1Y7xkOCwG84NvJsktdSsk3SlQnE2LbkTVpSA==", +- "cpu": [ +- "x64" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "win32" +- ], +- "bin": { +- "copilot-win32-x64": "copilot.exe" +- } +- }, + "node_modules/@gulp-sourcemaps/identity-map": { +@@ -12671,2 +12513,3 @@ + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", ++ "dev": true, + "license": "MIT", +@@ -18557,2 +18400,3 @@ + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", ++ "dev": true, + "license": "MIT" +@@ -19596,11 +19440,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": { +diff --git a/package.json b/package.json +index 79f619ca..5c2bcea8 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:chat-leak": "node scripts/chat-simulation/test-chat-mem-leaks.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/sdk": "^0.82.0", +- "@github/copilot": "1.0.39", +- "@github/copilot-sdk": "^0.3.0", + "@microsoft/1ds-core-js": "^3.2.13", +diff --git a/remote/package.json b/remote/package.json +index 1544b4c9..b3c51d2f 100644 +--- a/remote/package.json ++++ b/remote/package.json +@@ -5,4 +5,2 @@ + "dependencies": { +- "@github/copilot": "1.0.39", +- "@github/copilot-sdk": "^0.3.0", + "@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 0232f81b..e6758b6b 100644 +--- a/src/vs/platform/agentHost/node/agentHostMain.ts ++++ b/src/vs/platform/agentHost/node/agentHostMain.ts +@@ -17,3 +17,3 @@ import * as os from 'os'; + import * as inspector from 'inspector'; +-import { AgentHostClaudeSdkPathEnvVar, AgentHostIpcChannels, IAgentHostInspectInfo, IAgentHostSocketInfo, IConnectionTrackerService } from '../common/agentService.js'; ++import { AgentHostIpcChannels, IAgentHostInspectInfo, IAgentHostSocketInfo, IConnectionTrackerService } from '../common/agentService.js'; + import { AgentService } from './agentService.js'; +@@ -21,7 +21,2 @@ import { IAgentConfigurationService } from './agentConfigurationService.js'; + import { IAgentHostTerminalManager } from './agentHostTerminalManager.js'; +-import { CopilotAgent } from './copilot/copilotAgent.js'; +-import { CopilotApiService, ICopilotApiService } from './shared/copilotApiService.js'; +-import { ClaudeAgent } from './claude/claudeAgent.js'; +-import { ClaudeAgentSdkService, IClaudeAgentSdkService } from './claude/claudeAgentSdkService.js'; +-import { ClaudeProxyService, IClaudeProxyService } from './claude/claudeProxyService.js'; + import { ProtocolServerHandler } from './protocolServerHandler.js'; +@@ -115,8 +110,2 @@ function startAgentHost(): void { + diServices.set(IAgentHostGitService, gitService); +- const copilotApiService = instantiationService.createInstance(CopilotApiService, undefined); +- diServices.set(ICopilotApiService, copilotApiService); +- const claudeProxyService = disposables.add(instantiationService.createInstance(ClaudeProxyService)); +- diServices.set(IClaudeProxyService, claudeProxyService); +- const claudeAgentSdkService = instantiationService.createInstance(ClaudeAgentSdkService); +- diServices.set(IClaudeAgentSdkService, claudeAgentSdkService); + agentService = new AgentService(logService, fileService, sessionDataService, productService, gitService, rootConfigResource); +@@ -129,11 +118,2 @@ function startAgentHost(): void { + diServices.set(IAgentConfigurationService, agentService.configurationService); +- agentService.registerProvider(instantiationService.createInstance(CopilotAgent)); +- // The Claude agent provider is opt-in. Gated on the +- // `chat.agentHost.claudeAgent.path` workbench setting being non-empty, +- // forwarded by the agent host starters as `VSCODE_AGENT_HOST_CLAUDE_SDK_PATH`. +- // The SDK is intentionally not bundled with VS Code; the env var holds the +- // absolute path to a locally-installed `@anthropic-ai/claude-agent-sdk` package. +- if (process.env[AgentHostClaudeSdkPathEnvVar]) { +- agentService.registerProvider(instantiationService.createInstance(ClaudeAgent)); +- } + } catch (err) { +diff --git a/src/vs/platform/agentHost/node/agentHostServerMain.ts b/src/vs/platform/agentHost/node/agentHostServerMain.ts +index 26cbf179..bd83ddf3 100644 +--- a/src/vs/platform/agentHost/node/agentHostServerMain.ts ++++ b/src/vs/platform/agentHost/node/agentHostServerMain.ts +@@ -33,7 +33,2 @@ import { InstantiationService } from '../../instantiation/common/instantiationSe + import { ServiceCollection } from '../../instantiation/common/serviceCollection.js'; +-import { CopilotAgent } from './copilot/copilotAgent.js'; +-import { CopilotApiService, ICopilotApiService } from './shared/copilotApiService.js'; +-import { ClaudeAgent } from './claude/claudeAgent.js'; +-import { ClaudeAgentSdkService, IClaudeAgentSdkService } from './claude/claudeAgentSdkService.js'; +-import { ClaudeProxyService, IClaudeProxyService } from './claude/claudeProxyService.js'; + import { AgentService } from './agentService.js'; +@@ -207,21 +202,2 @@ async function main(): Promise { + diServices.set(IAgentHostGitService, gitService); +- // Register `ICopilotApiService` BEFORE `IClaudeProxyService` — +- // the proxy service constructor requires it. +- const copilotApiService = instantiationService.createInstance(CopilotApiService, undefined); +- diServices.set(ICopilotApiService, copilotApiService); +- const claudeProxyService = disposables.add(instantiationService.createInstance(ClaudeProxyService)); +- diServices.set(IClaudeProxyService, claudeProxyService); +- const claudeAgentSdkService = instantiationService.createInstance(ClaudeAgentSdkService); +- diServices.set(IClaudeAgentSdkService, claudeAgentSdkService); +- const copilotAgent = disposables.add(instantiationService.createInstance(CopilotAgent)); +- agentService.registerProvider(copilotAgent); +- log('CopilotAgent registered'); +- if (options.claudeSdkPath) { +- // `ClaudeAgentSdkService` reads `AgentHostClaudeSdkPathEnvVar` directly, +- // so make sure it is set even if the path was provided via CLI flag. +- process.env[AgentHostClaudeSdkPathEnvVar] = options.claudeSdkPath; +- const claudeAgent = disposables.add(instantiationService.createInstance(ClaudeAgent)); +- agentService.registerProvider(claudeAgent); +- log('ClaudeAgent registered'); +- } + } +diff --git a/src/vs/platform/agentHost/test/node/mockAgent.ts b/src/vs/platform/agentHost/test/node/mockAgent.ts +index 1c48abc3..71258ee8 100644 +--- a/src/vs/platform/agentHost/test/node/mockAgent.ts ++++ b/src/vs/platform/agentHost/test/node/mockAgent.ts +@@ -12,3 +12,2 @@ import { type ISyncedCustomization } from '../../common/agentPluginManager.js'; + import { AgentSession, type AgentProvider, type AgentSignal, type IAgent, type IAgentActionSignal, type IAgentCreateSessionConfig, type IAgentCreateSessionResult, type IAgentDescriptor, type IAgentModelInfo, type IAgentResolveSessionConfigParams, type IAgentSessionConfigCompletionsParams, type IAgentSessionMetadata, type IAgentToolPendingConfirmationSignal } from '../../common/agentService.js'; +-import { buildSubagentTurnsFromHistory, buildTurnsFromHistory, type IHistoryRecord } from './historyRecordFixtures.js'; + import { ProtectedResourceMetadata, type MessageAttachment, type ModelSelection } from '../../common/state/protocol/state.js'; +@@ -16,3 +15,3 @@ import type { ResolveSessionConfigResult, SessionConfigCompletionsResult } from + import { ActionType } from '../../common/state/sessionActions.js'; +-import { CustomizationStatus, ResponsePartKind, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, parseSubagentSessionUri, type CustomizationRef, type PendingMessage, type SessionCustomization, type StringOrMarkdown, type ToolCallResult, type Turn } from '../../common/state/sessionState.js'; ++import { CustomizationStatus, ResponsePartKind, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, type CustomizationRef, type PendingMessage, type SessionCustomization, type StringOrMarkdown, type ToolCallResult, type Turn } from '../../common/state/sessionState.js'; + import { hasKey } from '../../../../base/common/types.js'; +@@ -65,10 +64,2 @@ export class MockAgent implements IAgent { + +- /** +- * Configurable session history. Tests construct {@link IHistoryRecord} +- * entries (the agent-internal intermediate shape) and the mock converts +- * them to {@link Turn}s on demand. Subagent URIs are routed to filtered +- * subagent turns via {@link buildSubagentTurnsFromHistory}. +- */ +- sessionMessages: IHistoryRecord[] = []; +- + /** Optional overrides applied to session metadata from listSessions. */ +@@ -134,7 +125,3 @@ export class MockAgent implements IAgent { + async getSessionMessages(session: URI): Promise { +- const subagentInfo = parseSubagentSessionUri(session); +- if (subagentInfo) { +- return buildSubagentTurnsFromHistory(this.sessionMessages, subagentInfo.toolCallId, session.toString()); +- } +- return buildTurnsFromHistory(this.sessionMessages); ++ return [] + } +@@ -245,9 +232,2 @@ export class ScriptedMockAgent implements IAgent { + */ +- private readonly _preExistingMessages: IHistoryRecord[] = [ +- { type: 'message', role: 'user', session: PRE_EXISTING_SESSION_URI, messageId: 'h-msg-1', content: 'What files are here?' }, +- { type: 'tool_start', session: PRE_EXISTING_SESSION_URI, toolCallId: 'h-tc-1', toolName: 'list_files', displayName: 'List Files', invocationMessage: 'Listing files...' }, +- { type: 'tool_complete', session: PRE_EXISTING_SESSION_URI, toolCallId: 'h-tc-1', result: { pastTenseMessage: 'Listed files', content: [{ type: ToolResultContentType.Text, text: 'file1.ts\nfile2.ts' }], success: true } satisfies ToolCallResult }, +- { type: 'message', role: 'assistant', session: PRE_EXISTING_SESSION_URI, messageId: 'h-msg-2', content: 'Here are the files: file1.ts and file2.ts' }, +- ]; +- + // Track pending permission requests +@@ -684,9 +664,2 @@ export class ScriptedMockAgent implements IAgent { + async getSessionMessages(session: URI): Promise { +- const subagentInfo = parseSubagentSessionUri(session); +- if (subagentInfo) { +- return buildSubagentTurnsFromHistory(this._preExistingMessages, subagentInfo.toolCallId, session.toString()); +- } +- if (session.toString() === PRE_EXISTING_SESSION_URI.toString()) { +- return buildTurnsFromHistory(this._preExistingMessages); +- } + return []; diff --git a/patches/build-replace-unicode.patch b/patches/build-replace-unicode.patch new file mode 100644 index 00000000000..8f710e6bac6 --- /dev/null +++ b/patches/build-replace-unicode.patch @@ -0,0 +1,9 @@ +diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/monitoring/outputMonitor.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/monitoring/outputMonitor.ts +index 3de86060..875de670 100644 +--- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/monitoring/outputMonitor.ts ++++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/monitoring/outputMonitor.ts +@@ -657,3 +657,3 @@ export function detectsHighConfidenceInputPattern(cursorLine: string): boolean { + // allow-any-unicode-next-line +- /^(?:\s|\x1b\[[0-9;]*m)*\?.*[›❯▸▶]\s*$/, ++ /^(?:\s|\x1b\[[0-9;]*m)*\?.*[\u{203A}\u{276F}\u{25B8}\u{25B6}]\s*$/u, + ].some(e => e.test(cursorLine)); diff --git a/patches/linux/00-log-use-state-home.patch b/patches/linux/00-log-use-state-home.patch index cdf6a4e85c6..cc49e37d96c 100644 --- a/patches/linux/00-log-use-state-home.patch +++ b/patches/linux/00-log-use-state-home.patch @@ -1,13 +1,13 @@ diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts -index 4a14c83..59c963b 100644 +index 4fdf7227..fa05f304 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts -@@ -137,2 +137,3 @@ export interface INativeEnvironmentService extends IEnvironmentService { +@@ -141,2 +141,3 @@ export interface INativeEnvironmentService extends IEnvironmentService { userDataPath: string; + userStatePath: string; diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts -index 535132f..edd76bc 100644 +index 004d0614..7f0af774 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -28,2 +28,4 @@ export interface INativeEnvironmentPaths { @@ -27,22 +27,20 @@ index 535132f..edd76bc 100644 + this.args.logsPath = join(this.userStatePath, key); } diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts -index ae9e7e1..3c6c6c5 100644 +index 8652144b..62fc4ea6 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -11,2 +11,3 @@ import { getUserDataPath } from './userDataPath.js'; import { IProductService } from '../../product/common/productService.js'; +import { getUserStatePath } from './userStatePath.js'; -@@ -18,3 +19,4 @@ export class NativeEnvironmentService extends AbstractNativeEnvironmentService { - tmpDir: tmpdir(), -- userDataDir: getUserDataPath(args, productService.nameShort) -+ userDataDir: getUserDataPath(args, productService.nameShort), -+ userStateDir: getUserStatePath(args, productService.nameShort) +@@ -20,2 +21,3 @@ export class NativeEnvironmentService extends AbstractNativeEnvironmentService { + userDataDir: getUserDataPath(args, productService.nameShort), ++ userStateDir: getUserStatePath(args, productService.nameShort), }, productService); diff --git a/src/vs/platform/environment/node/userStatePath.ts b/src/vs/platform/environment/node/userStatePath.ts new file mode 100644 -index 0000000..53f9e2f +index 00000000..53f9e2f1 --- /dev/null +++ b/src/vs/platform/environment/node/userStatePath.ts @@ -0,0 +1,42 @@ @@ -88,36 +86,57 @@ index 0000000..53f9e2f + + return join(appStatePath, productName); +} +diff --git a/src/vs/platform/userDataProfile/test/common/userDataProfileService.test.ts b/src/vs/platform/userDataProfile/test/common/userDataProfileService.test.ts +index 37cd505a..9ae46919 100644 +--- a/src/vs/platform/userDataProfile/test/common/userDataProfileService.test.ts ++++ b/src/vs/platform/userDataProfile/test/common/userDataProfileService.test.ts +@@ -24,4 +24,6 @@ class TestEnvironmentService extends AbstractNativeEnvironmentService { + const userDataDir = _appSettingsHome.fsPath.replace(/\/User$/, ''); ++ const userStateDir = _appSettingsHome.fsPath.replace(/\/State/, ''); + const paths: INativeEnvironmentPaths = { + userDataDir, ++ userStateDir, + homeDir: userDataDir, +diff --git a/src/vs/platform/userDataProfile/test/electron-main/userDataProfileMainService.test.ts b/src/vs/platform/userDataProfile/test/electron-main/userDataProfileMainService.test.ts +index c8ec2ae6..e6ad98a3 100644 +--- a/src/vs/platform/userDataProfile/test/electron-main/userDataProfileMainService.test.ts ++++ b/src/vs/platform/userDataProfile/test/electron-main/userDataProfileMainService.test.ts +@@ -25,4 +25,6 @@ class TestEnvironmentService extends AbstractNativeEnvironmentService { + const userDataDir = _appSettingsHome.fsPath.replace(/\/User$/, ''); ++ const userStateDir = _appSettingsHome.fsPath.replace(/\/State/, ''); + const paths: INativeEnvironmentPaths = { + userDataDir, ++ userStateDir, + homeDir: userDataDir, diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts -index fa297d1..839fd60 100644 +index 291648bc..29c28d21 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts -@@ -443,2 +443,3 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native +@@ -444,2 +444,3 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native userDataDir: string; + userStateDir: string; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts -index 117dfd2..6b0458b 100644 +index 9feeb83e..53975c9f 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts -@@ -1511,2 +1511,3 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic +@@ -1562,2 +1562,3 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic userDataDir: this.environmentMainService.userDataPath, + userStateDir: this.environmentMainService.userStatePath, diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts -index 6cfa517..46286c6 100644 +index 1abd21a9..eafaaa1f 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts -@@ -153,3 +153,3 @@ export class NativeWorkbenchEnvironmentService extends AbstractNativeEnvironment - ) { -- super(configuration, { homeDir: configuration.homeDir, tmpDir: configuration.tmpDir, userDataDir: configuration.userDataDir }, productService); -+ super(configuration, { homeDir: configuration.homeDir, tmpDir: configuration.tmpDir, userDataDir: configuration.userDataDir, userStateDir: configuration.userStateDir }, productService); - } +@@ -166,2 +166,3 @@ export class NativeWorkbenchEnvironmentService extends AbstractNativeEnvironment + userDataDir: configuration.userDataDir, ++ userStateDir: configuration.userStateDir, + }, diff --git a/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts b/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts -index 6b7307e..c74476a 100644 +index 7420d5e5..767502b0 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts -@@ -74,2 +74,3 @@ const TestNativeWindowConfiguration: INativeWindowConfiguration = { +@@ -76,2 +76,3 @@ const TestNativeWindowConfiguration: INativeWindowConfiguration = { userDataDir: joinPath(homeDir, product.nameShort).fsPath, + userStateDir: joinPath(homeDir, product.nameShort).fsPath, profiles: { profile: NULL_PROFILE, all: [NULL_PROFILE], home: homeDir }, diff --git a/patches/linux/31-build-fix-dependencies.patch b/patches/linux/31-build-fix-dependencies.patch index 3aafb3a6645..055e6527fe2 100644 --- a/patches/linux/31-build-fix-dependencies.patch +++ b/patches/linux/31-build-fix-dependencies.patch @@ -18,7 +18,7 @@ index 874c8026..04731cfd 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 507ec216..a7440867 100644 +index 6025771a..b0637f1f 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -17,3 +17,2 @@ @@ -189,7 +189,7 @@ index 507ec216..a7440867 100644 - } - }, "node_modules/@esbuild/aix-ppc64": { -@@ -1344,5 +1297,5 @@ +@@ -1357,5 +1310,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 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.4.1.tgz", + "integrity": "sha512-XifMpBMdo0E1Fuh85YdcYAgy+okNg9WKBzIPIO4JUDnSWUVFihnogrM4cjDapeHkgzSgulwR8oJVJ17eyxI1bA==", "dev": true, -@@ -1351,5 +1304,5 @@ +@@ -1364,5 +1317,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 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.4.1.tgz", + "integrity": "sha512-kAV7Sup3vwvqxKvBbf9lx/JaPHkRybQp/LLvA73U1AorPZE6XyfBAFG24BbMiCs4OX1ax4g7kXRuFPgMLWRf+g==", "dev": true, -@@ -1359,8 +1312,8 @@ +@@ -1372,8 +1325,8 @@ "@azu/style-format": "^1.0.1", - "@textlint/module-interop": "15.2.2", - "@textlint/resolver": "15.2.2", @@ -221,7 +221,7 @@ index 507ec216..a7440867 100644 + "debug": "^4.4.3", + "js-yaml": "^4.1.0", "lodash": "^4.17.21", -@@ -1436,2 +1389,9 @@ +@@ -1449,2 +1402,9 @@ }, + "node_modules/@textlint/linter-formatter/node_modules/emoji-regex": { + "version": "8.0.0", @@ -231,7 +231,7 @@ index 507ec216..a7440867 100644 + "license": "MIT" + }, "node_modules/@textlint/linter-formatter/node_modules/has-flag": { -@@ -1453,2 +1413,17 @@ +@@ -1466,2 +1426,17 @@ }, + "node_modules/@textlint/linter-formatter/node_modules/string-width": { + "version": "4.2.3", @@ -249,7 +249,7 @@ index 507ec216..a7440867 100644 + } + }, "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { -@@ -1480,5 +1455,5 @@ +@@ -1493,5 +1468,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 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.4.1.tgz", + "integrity": "sha512-jHtM2E5CR68P3z/+FGrEU5pml2fQVzEo2sez9FEjrVHSPCrHtqHcPaKfsYbQJjc9C48ObwaWrCzRNaL3KedNCQ==", "dev": true, -@@ -1487,5 +1462,5 @@ +@@ -1500,5 +1475,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 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.4.1.tgz", + "integrity": "sha512-uVssyG3XXXKNY+O7NOajGvQZTyOuhPviwlq7Xek6ZT9K1eDQtA8074cPkAQoLMYhi/TUyOE5P5kpz42UF8Lmdw==", "dev": true, -@@ -1494,5 +1469,5 @@ +@@ -1507,5 +1482,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 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.4.1.tgz", + "integrity": "sha512-WByVZ3zblbvuI+voWQplUP7seSTKXI9z6TMVXEB3dY3JFrZCIXWKNfLbETX5lZV7fYkCMaDtILO1l6s11wdbQA==", "dev": true, -@@ -1500,3 +1475,3 @@ +@@ -1513,3 +1488,3 @@ "dependencies": { - "@textlint/ast-node-types": "15.2.2" + "@textlint/ast-node-types": "15.4.1" } -@@ -1594,12 +1569,2 @@ +@@ -1607,12 +1582,2 @@ }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", @@ -294,7 +294,7 @@ index 507ec216..a7440867 100644 - } - }, "node_modules/@types/gulp": { -@@ -1921,2 +1886,17 @@ +@@ -1934,2 +1899,17 @@ }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.2", @@ -312,7 +312,7 @@ index 507ec216..a7440867 100644 + } + }, "node_modules/@vscode/iconv-lite-umd": { -@@ -2233,5 +2213,5 @@ +@@ -2361,5 +2341,5 @@ "node_modules/ansi-escapes": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.1.tgz", @@ -321,7 +321,11 @@ index 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", "dev": true, -@@ -2308,17 +2288,7 @@ +@@ -2406,2 +2386,3 @@ + "dev": true, ++ "license": "BSD-2-Clause", + "dependencies": { +@@ -2436,17 +2417,7 @@ "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -343,25 +347,25 @@ index 507ec216..a7440867 100644 - "license": "BSD-3-Clause" + "license": "Python-2.0" }, -@@ -2366,2 +2336,3 @@ +@@ -2494,2 +2465,3 @@ "dev": true, + "license": "BSD-2-Clause", "dependencies": { -@@ -2450,2 +2421,3 @@ +@@ -2578,2 +2550,3 @@ "dev": true, + "license": "MIT", "optional": true, -@@ -2461,3 +2433,4 @@ +@@ -2589,3 +2562,4 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "dev": true, + "license": "ISC" }, -@@ -2563,2 +2536,3 @@ +@@ -2691,2 +2665,3 @@ "dev": true, -+ "license": "BSD-2-Clause", ++ "license": "MIT", "dependencies": { -@@ -2622,6 +2596,7 @@ +@@ -2750,6 +2725,7 @@ "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", @@ -372,7 +376,7 @@ index 507ec216..a7440867 100644 "dev": true, + "license": "MIT", "dependencies": { -@@ -2630,9 +2605,13 @@ +@@ -2758,9 +2734,13 @@ "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", @@ -391,11 +395,7 @@ index 507ec216..a7440867 100644 - "node": ">= 6" + "node": ">=20.18.1" }, -@@ -2647,2 +2626,3 @@ - "dev": true, -+ "license": "MIT", - "dependencies": { -@@ -2847,6 +2827,7 @@ +@@ -2975,6 +2955,7 @@ "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -406,13 +406,13 @@ index 507ec216..a7440867 100644 "dev": true, + "license": "BSD-2-Clause", "engines": { -@@ -3005,3 +2986,4 @@ +@@ -3133,3 +3114,4 @@ } - ] + ], + "license": "BSD-2-Clause" }, -@@ -3023,6 +3005,7 @@ +@@ -3151,6 +3133,7 @@ "node_modules/domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", @@ -423,12 +423,12 @@ index 507ec216..a7440867 100644 "dev": true, + "license": "BSD-2-Clause", "dependencies": { -@@ -3030,3 +3013,3 @@ +@@ -3158,3 +3141,3 @@ "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" + "domhandler": "^5.0.3" }, -@@ -3097,5 +3080,5 @@ +@@ -3225,5 +3208,5 @@ "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -437,7 +437,7 @@ index 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, -@@ -3103,2 +3086,16 @@ +@@ -3231,2 +3214,16 @@ }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", @@ -454,7 +454,7 @@ index 507ec216..a7440867 100644 + } + }, "node_modules/end-of-stream": { -@@ -3113,6 +3110,7 @@ +@@ -3241,6 +3238,7 @@ "node_modules/entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -465,7 +465,7 @@ index 507ec216..a7440867 100644 "dev": true, + "license": "BSD-2-Clause", "engines": { -@@ -3253,16 +3251,2 @@ +@@ -3381,16 +3379,2 @@ }, - "node_modules/esprima": { - "version": "4.0.1", @@ -482,19 +482,19 @@ index 507ec216..a7440867 100644 - } - }, "node_modules/events": { -@@ -3433,2 +3417,3 @@ +@@ -3432,2 +3416,3 @@ "dev": true, + "license": "ISC", "dependencies": { -@@ -3625,2 +3610,3 @@ +@@ -3650,2 +3635,3 @@ "dev": true, + "license": "ISC", "optional": true -@@ -3828,2 +3814,3 @@ +@@ -3957,2 +3943,3 @@ "dev": true, + "license": "MIT", "engines": { -@@ -3887,5 +3874,5 @@ +@@ -4016,5 +4003,5 @@ "node_modules/htmlparser2": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", @@ -503,7 +503,7 @@ index 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", "dev": true, -@@ -3898,7 +3885,21 @@ +@@ -4027,7 +4014,21 @@ ], + "license": "MIT", "dependencies": { @@ -528,7 +528,7 @@ index 507ec216..a7440867 100644 + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } -@@ -3952,2 +3953,15 @@ +@@ -4081,2 +4082,15 @@ }, + "node_modules/iconv-lite": { + "version": "0.6.3", @@ -544,7 +544,7 @@ index 507ec216..a7440867 100644 + } + }, "node_modules/ieee754": { -@@ -3984,5 +3998,5 @@ +@@ -4113,5 +4127,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", @@ -553,15 +553,15 @@ index 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", "dev": true, -@@ -4018,2 +4032,3 @@ +@@ -4147,2 +4161,3 @@ "dev": true, + "license": "MIT", "optional": true -@@ -4037,2 +4052,3 @@ +@@ -4166,2 +4181,3 @@ "dev": true, + "license": "ISC", "bin": { -@@ -4192,5 +4208,5 @@ +@@ -4321,5 +4337,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 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, -@@ -4198,4 +4214,3 @@ +@@ -4327,4 +4343,3 @@ "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, -@@ -4342,2 +4357,3 @@ - "hasInstallScript": true, -+ "license": "MIT", - "optional": true, -@@ -4353,2 +4369,3 @@ +@@ -4393,2 +4408,3 @@ "dev": true, + "license": "BSD-2-Clause", "dependencies": { -@@ -4410,2 +4427,3 @@ +@@ -4432,2 +4448,3 @@ "dev": true, + "license": "MIT", "dependencies": { -@@ -4435,9 +4453,2 @@ +@@ -4471,2 +4488,3 @@ + "hasInstallScript": true, ++ "license": "MIT", + "optional": true, +@@ -4564,9 +4582,2 @@ }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", @@ -598,11 +598,19 @@ index 507ec216..a7440867 100644 - "license": "Python-2.0" - }, "node_modules/matcher": { -@@ -4598,2 +4609,3 @@ +@@ -4576,2 +4587,3 @@ + "dev": true, ++ "license": "MIT", + "optional": true, +@@ -4589,2 +4601,3 @@ + "dev": true, ++ "license": "(MIT OR WTFPL)", + "optional": true, +@@ -4727,2 +4740,3 @@ "dev": true, + "license": "MIT", "optional": true -@@ -4614,6 +4626,7 @@ +@@ -4743,6 +4757,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", @@ -613,19 +621,11 @@ index 507ec216..a7440867 100644 "dev": true, + "license": "MIT", "optional": true -@@ -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 @@ +@@ -4789,2 +4804,3 @@ "dev": true, + "license": "ISC", "optional": true -@@ -4675,5 +4691,5 @@ +@@ -4804,5 +4820,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 507ec216..a7440867 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, -@@ -4685,3 +4701,3 @@ +@@ -4814,3 +4830,3 @@ "engines": { - "node": ">=18" + "node": ">=20" } -@@ -4762,5 +4778,5 @@ +@@ -4891,5 +4907,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 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, -@@ -4890,5 +4906,5 @@ +@@ -5019,5 +5035,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 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, -@@ -4968,8 +4984,9 @@ +@@ -5097,8 +5113,9 @@ "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -671,7 +671,7 @@ index 507ec216..a7440867 100644 - "entities": "^4.4.0" + "entities": "^6.0.0" }, -@@ -4980,8 +4997,9 @@ +@@ -5109,8 +5126,22 @@ "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", @@ -680,26 +680,26 @@ index 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", "dev": true, -+ "license": "MIT", - "dependencies": { -- "domhandler": "^5.0.2", -+ "domhandler": "^5.0.3", - "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.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, ++ "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" +@@ -5121,2 +5152,15 @@ + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -714,7 +714,7 @@ index 507ec216..a7440867 100644 + } + }, "node_modules/path-expression-matcher": { -@@ -5027,5 +5071,5 @@ +@@ -5156,5 +5200,5 @@ "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", @@ -723,16 +723,20 @@ index 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "dev": true, -@@ -5165,3 +5209,3 @@ +@@ -5287,2 +5331,3 @@ + "dev": true, ++ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, +@@ -5294,3 +5339,3 @@ "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", + "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", -@@ -5282,2 +5326,3 @@ +@@ -5342,2 +5387,3 @@ "dev": true, -+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "optional": true, -@@ -5306,22 +5351,2 @@ ++ "license": "ISC", + "dependencies": { +@@ -5435,22 +5481,2 @@ }, - "node_modules/rc-config-loader/node_modules/argparse": { - "version": "2.0.1", @@ -755,11 +759,11 @@ index 507ec216..a7440867 100644 - } - }, "node_modules/read": { -@@ -5331,2 +5356,3 @@ +@@ -5598,2 +5624,3 @@ "dev": true, -+ "license": "ISC", - "dependencies": { -@@ -5512,7 +5538,15 @@ ++ "license": "MIT", + "optional": true, +@@ -5641,7 +5668,15 @@ }, + "node_modules/safer-buffer": { + "version": "2.1.2", @@ -779,27 +783,23 @@ index 507ec216..a7440867 100644 + "dev": true, + "license": "ISC" }, -@@ -5563,2 +5597,3 @@ +@@ -5692,2 +5727,3 @@ "dev": true, + "license": "MIT", "optional": true -@@ -5570,2 +5605,3 @@ - "dev": true, -+ "license": "MIT", - "optional": true, -@@ -5710,2 +5746,3 @@ +@@ -5839,2 +5875,3 @@ ], + "license": "BSD-3-Clause", "optional": true -@@ -5731,2 +5768,3 @@ +@@ -5860,2 +5897,3 @@ ], + "license": "MIT", "optional": true, -@@ -5862,2 +5900,3 @@ +@@ -5939,2 +5977,3 @@ "dev": true, + "license": "MIT", "engines": { -@@ -5889,5 +5928,5 @@ +@@ -6018,5 +6057,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 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, -@@ -5911,31 +5950,11 @@ +@@ -6040,31 +6079,11 @@ "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", @@ -846,7 +846,7 @@ index 507ec216..a7440867 100644 + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } -@@ -5952,2 +5971,9 @@ +@@ -6081,2 +6100,9 @@ }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", @@ -856,18 +856,18 @@ index 507ec216..a7440867 100644 + "license": "MIT" + }, "node_modules/string-width/node_modules/strip-ansi": { -@@ -6045,4 +6071,5 @@ +@@ -6174,4 +6200,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, -@@ -6092,2 +6119,3 @@ +@@ -6209,2 +6236,3 @@ "dev": true, + "license": "MIT", "dependencies": { -@@ -6166,2 +6194,24 @@ +@@ -6295,2 +6323,24 @@ }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", @@ -892,7 +892,7 @@ index 507ec216..a7440867 100644 + } + }, "node_modules/table/node_modules/strip-ansi": { -@@ -6297,5 +6347,5 @@ +@@ -6426,5 +6476,5 @@ "node_modules/tmp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", @@ -901,14 +901,14 @@ index 507ec216..a7440867 100644 + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, -@@ -6421,4 +6471,5 @@ +@@ -6550,4 +6600,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, -@@ -6470,2 +6521,12 @@ +@@ -6599,2 +6650,12 @@ }, + "node_modules/undici": { + "version": "7.16.0", @@ -921,13 +921,13 @@ index 507ec216..a7440867 100644 + } + }, "node_modules/undici-types": { -@@ -6509,3 +6570,4 @@ +@@ -6638,3 +6699,4 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true + "dev": true, + "license": "MIT" }, -@@ -6690,2 +6752,25 @@ +@@ -6819,2 +6881,25 @@ }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", @@ -953,7 +953,7 @@ index 507ec216..a7440867 100644 + } + }, "node_modules/which": { -@@ -6794,2 +6879,24 @@ +@@ -6923,2 +7008,24 @@ }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", @@ -978,7 +978,7 @@ index 507ec216..a7440867 100644 + } + }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { -@@ -6820,27 +6927,2 @@ +@@ -6949,27 +7056,2 @@ }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", @@ -1006,7 +1006,7 @@ index 507ec216..a7440867 100644 - } - }, "node_modules/wrappy": { -@@ -6897,2 +6979,47 @@ +@@ -7026,2 +7108,47 @@ }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", @@ -1055,60 +1055,27 @@ index 507ec216..a7440867 100644 + }, "node_modules/yauzl": { diff --git a/build/package.json b/build/package.json -index 3493ef4e..a3fa30b0 100644 +index 3b3d1db3..99a77e5a 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 +index 79f619ca..948b9313 100644 --- a/package.json +++ b/package.json -@@ -255,3 +255,3 @@ +@@ -260,3 +260,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 +index 1544b4c9..bed3fa7f 100644 --- a/remote/package.json +++ b/remote/package.json -@@ -55,3 +55,3 @@ +@@ -56,3 +56,3 @@ "ssh2": { - "cpu-features": "0.0.0" + "cpu-features": "0.0.10" diff --git a/patches/windows/71-build-disable-appx.patch b/patches/windows/71-build-disable-appx.patch index bc342fd0e5d..e348e26740d 100644 --- a/patches/windows/71-build-disable-appx.patch +++ b/patches/windows/71-build-disable-appx.patch @@ -1,8 +1,8 @@ diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index b01d710a..303d2f68 100644 +index 2412dd32..681e49e7 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -621,18 +621,18 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d +@@ -488,18 +488,18 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d - if (quality === 'stable' || quality === 'insider') { - result = es.merge(result, gulp.src('.build/win32/appx/**', { base: '.build/win32' })); @@ -38,19 +38,27 @@ index b01d710a..303d2f68 100644 + // } } else if (platform === 'linux') { diff --git a/build/gulpfile.vscode.win32.ts b/build/gulpfile.vscode.win32.ts -index 2675bd85..51723721 100644 +index 3f168c42..d4bbfc26 100644 --- a/build/gulpfile.vscode.win32.ts +++ b/build/gulpfile.vscode.win32.ts -@@ -133,7 +133,7 @@ function buildWin32Setup(arch: string, target: string): task.CallbackTask { +@@ -113,11 +113,11 @@ function buildWin32Setup(arch: string, target: string): task.CallbackTask { - if (quality === 'stable' || quality === 'insider') { - definitions['AppxPackage'] = `${product.applicationName.replaceAll('-', '_')}_${arch}.appx`; - definitions['AppxPackageDll'] = `${product.applicationName.replaceAll('-', '_')}_explorer_command_${arch}.dll`; - definitions['AppxPackageName'] = `${product.win32AppUserModelId}`; +- const ctxMenu = (product as { win32ContextMenu?: Record }).win32ContextMenu; +- if (ctxMenu && ctxMenu[arch]) { +- definitions['FileExplorerContextMenuCLSID'] = ctxMenu[arch].clsid; +- } - } + // if (quality === 'stable' || quality === 'insider') { + // definitions['AppxPackage'] = `${product.applicationName.replaceAll('-', '_')}_${arch}.appx`; + // definitions['AppxPackageDll'] = `${product.applicationName.replaceAll('-', '_')}_explorer_command_${arch}.dll`; + // definitions['AppxPackageName'] = `${product.win32AppUserModelId}`; ++ // const ctxMenu = (product as { win32ContextMenu?: Record }).win32ContextMenu; ++ // if (ctxMenu && ctxMenu[arch]) { ++ // definitions['FileExplorerContextMenuCLSID'] = ctxMenu[arch].clsid; ++ // } + // } diff --git a/prepare_vscode.sh b/prepare_vscode.sh index 6dc2973e3e4..2f5febcf2b4 100755 --- a/prepare_vscode.sh +++ b/prepare_vscode.sh @@ -13,7 +13,7 @@ cp -f LICENSE vscode/LICENSE.txt cd vscode || { echo "'vscode' dir not found"; exit 1; } -rm -rf extensions/copilot +# rm -rf extensions/copilot { set +x; } 2>/dev/null @@ -149,6 +149,12 @@ if [[ "${DISABLE_UPDATE}" == "yes" ]]; then mv ../patches/00-update-disable.patch.yet ../patches/00-update-disable.patch fi +for file in ../patches/*.json; do + if [[ -f "${file}" ]]; then + apply_actions "${file}" + fi +done + for file in ../patches/*.patch; do if [[ -f "${file}" ]]; then apply_patch "${file}" diff --git a/product.json b/product.json index 46a631a96b5..293d6695cc6 100644 --- a/product.json +++ b/product.json @@ -254,6 +254,7 @@ ], "GitHub.copilot-chat": [ "agentSessionsWorkspace", + "agentsWindowConfiguration", "interactive", "terminalDataWriteEvent", "terminalExecuteCommandEvent", @@ -315,7 +316,9 @@ "chatHooks", "chatDebug", "environmentPower", - "terminalTitle" + "terminalTitle", + "languageModelPricing", + "chatInputNotification" ], "GitHub.remotehub": [ "contribRemoteHelp", diff --git a/upstream/insider.json b/upstream/insider.json index d394f1c84a2..22716e1fc86 100644 --- a/upstream/insider.json +++ b/upstream/insider.json @@ -1,4 +1,4 @@ { - "tag": "1.118.1", - "commit": "034f571df509819cc10b0c8129f66ef77a542f0e" + "tag": "1.120.0", + "commit": "0958016b2af9f09bb4257e0df4a95e2f90590f9f" } diff --git a/utils.sh b/utils.sh index 595aaba3adf..132812c116c 100755 --- a/utils.sh +++ b/utils.sh @@ -16,6 +16,32 @@ fi # All common functions can be added to this file +apply_actions() { + jq -c '.[]' "$1" | while IFS= read -r ENTRY; do + ENTRY_ACTION=$( jq -r '.action // empty' <<< "${ENTRY}" ) + + case "${ENTRY_ACTION}" in + remove) + jq -r '.paths[]' <<< "${ENTRY}" | while IFS= read -r ENTRY_PATH; do + ENTRY_PATH="${ENTRY_PATH%$'\r'}" + + if [[ -e "${ENTRY_PATH}" ]]; then + if rm -rf -- "${ENTRY_PATH}"; then + echo "Removed: ${ENTRY_PATH}" + else + echo "Failed to remove: ${ENTRY_PATH}" >&2 + exit 4 + fi + else + echo "Not found: ${ENTRY_PATH}" >&2 + exit 4 + fi + done + ;; + esac + done +} + apply_patch() { if [[ -z "$2" ]]; then echo applying patch: "$1"; From d0f84b28aefca47ed7c2849578e3b467476cee18 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 16 May 2026 10:54:49 +0200 Subject: [PATCH 40/79] fix(update): set state to idle (#2840) --- patches/11-update-use-github-release.patch | 46 ++++++++++++---------- patches/12-update-add-cooldown.patch | 10 ++--- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/patches/11-update-use-github-release.patch b/patches/11-update-use-github-release.patch index af2df0f8456..734b122c810 100644 --- a/patches/11-update-use-github-release.patch +++ b/patches/11-update-use-github-release.patch @@ -49,7 +49,7 @@ index 92ae7b96..c3fe6214 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 e13d1ba5..3767c907 100644 +index 8cf5dceb..49720f6a 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -17,3 +17,3 @@ import { ILogService } from '../../log/common/log.js'; @@ -81,12 +81,12 @@ index e13d1ba5..3767c907 100644 - - return url.toString(); } -@@ -434,3 +431,3 @@ export abstract class AbstractUpdateService implements IUpdateService { +@@ -419,3 +416,3 @@ export abstract class AbstractUpdateService implements IUpdateService { - if (mode === 'none') { + if (mode === 'none' || mode === 'manual') { return undefined; -@@ -444,18 +441,37 @@ export abstract class AbstractUpdateService implements IUpdateService { +@@ -429,18 +426,37 @@ export abstract class AbstractUpdateService implements IUpdateService { + return this._isLatestVersion(url, false) + .then((result) => { @@ -138,7 +138,7 @@ index e13d1ba5..3767c907 100644 + }) } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts -index 3ddb310e..26738a64 100644 +index e92c54e2..c4354cd1 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'; @@ -146,7 +146,7 @@ index 3ddb310e..26738a64 100644 -import { asJson, IRequestService } from '../../request/common/request.js'; +import { asJson, IRequestService, NO_FETCH_TELEMETRY } from '../../request/common/request.js'; import { IApplicationStorageMainService } from '../../storage/electron-main/storageMainService.js'; -@@ -101,15 +101,4 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau +@@ -93,15 +93,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'); @@ -164,7 +164,7 @@ index 3ddb310e..26738a64 100644 + protected buildUpdateFeedUrl(quality: string, _commit: string, _options?: IUpdateURLOptions): string | undefined { + return createUpdateURL(this.productService, quality, process.platform, process.arch); } -@@ -145,4 +134,30 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau +@@ -131,4 +120,32 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau - this.logService.trace('update#doCheckForUpdates - using Electron autoUpdater', { url, explicit, background }); - electron.autoUpdater.checkForUpdates(); @@ -173,6 +173,8 @@ index 3ddb310e..26738a64 100644 + this._isLatestVersion(url, explicit) + .then((result) => { + if(!result) { ++ this.setState(State.Idle(UpdateType.Archive)); ++ + return Promise.resolve(null); + } + @@ -194,16 +196,16 @@ index 3ddb310e..26738a64 100644 + // only show message when explicitly checking for updates + const message: string | undefined = explicit ? (error.message || error) : undefined; + -+ this.setState(State.Idle(UpdateType.Setup, message)); ++ this.setState(State.Idle(UpdateType.Archive, message)); + }); } -@@ -159,3 +174,3 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau +@@ -145,3 +162,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 2be53f61..8776a7f6 100644 +index 2be53f61..53904dab 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 @@ @@ -231,7 +233,7 @@ index 2be53f61..8776a7f6 100644 + this.setState(State.CheckingForUpdates(explicit)); + const internalOrg = this.getInternalOrg(); -@@ -48,17 +49,26 @@ export class LinuxUpdateService extends AbstractUpdateService { +@@ -48,17 +49,28 @@ export class LinuxUpdateService extends AbstractUpdateService { const url = this.buildUpdateFeedUrl(this.quality, this.productService.commit!, { background, internalOrg }); - this.setState(State.CheckingForUpdates(explicit)); @@ -244,6 +246,8 @@ index 2be53f61..8776a7f6 100644 + this._isLatestVersion(url, explicit) + .then((result) => { + if(!result) { ++ this.setState(State.Idle(UpdateType.Archive)); ++ + return Promise.resolve(null); + } + @@ -269,14 +273,14 @@ index 2be53f61..8776a7f6 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 222db559..0037e002 100644 +index a7851933..b49db325 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'; -@@ -32,7 +31,7 @@ import { INativeHostMainService } from '../../native/electron-main/nativeHostMai +@@ -31,7 +30,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'; @@ -287,7 +291,7 @@ index 222db559..0037e002 100644 +import { AvailableForDownload, DisablementReason, IUpdate, State, StateType, Target, UpdateType } from '../common/update.js'; +import { AbstractUpdateService, createUpdateURL, IUpdateURLOptions } from './abstractUpdateService.js'; -@@ -50,5 +49,9 @@ function getUpdateType(): UpdateType { +@@ -49,5 +48,9 @@ function getUpdateType(): UpdateType { if (typeof _updateType === 'undefined') { - _updateType = existsSync(path.join(path.dirname(process.execPath), 'unins000.exe')) - ? UpdateType.Setup @@ -300,12 +304,12 @@ index 222db559..0037e002 100644 + _updateType = UpdateType.Archive; + } } -@@ -158,3 +161,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -157,3 +160,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 -@@ -178,12 +181,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -215,12 +218,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - protected buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined { - let platform = `win32-${process.arch}`; @@ -336,14 +340,14 @@ index 222db559..0037e002 100644 - return createUpdateURL(this.productService.updateUrl!, platform, quality, commit, options); + return createUpdateURL(this.productService, quality, process.platform, process.arch, target); } -@@ -195,6 +208,2 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -232,6 +245,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 -@@ -204,9 +213,13 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -241,9 +250,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) @@ -362,7 +366,7 @@ index 222db559..0037e002 100644 - if (!update || !update.url || !update.version || !update.productVersion) { + if(!result) { // If we were checking for an overwrite update and found nothing newer, -@@ -222,2 +235,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -259,2 +272,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + const { lastest, update } = result; + @@ -372,12 +376,12 @@ index 222db559..0037e002 100644 + } + if (updateType === UpdateType.Archive) { -@@ -247,3 +267,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -284,3 +304,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 => { -@@ -292,8 +312,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -329,8 +349,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun }) - .then(undefined, err => { - this.telemetryService.publicLog2<{ messageHash: string }, UpdateErrorClassification>('update:error', { messageHash: String(hash(String(err))) }); @@ -389,7 +393,7 @@ index 222db559..0037e002 100644 - const message: string | undefined = explicit ? (err.message || err) : undefined; + const message: string | undefined = explicit ? (error.message || error) : undefined; -@@ -357,20 +376,31 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -394,20 +413,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/12-update-add-cooldown.patch b/patches/12-update-add-cooldown.patch index c52a18a69da..f24af52f6a2 100644 --- a/patches/12-update-add-cooldown.patch +++ b/patches/12-update-add-cooldown.patch @@ -1,5 +1,5 @@ diff --git a/src/vs/platform/update/common/update.config.contribution.ts b/src/vs/platform/update/common/update.config.contribution.ts -index 6061e15b..84246033 100644 +index 2c63e9e6..556f2403 100644 --- a/src/vs/platform/update/common/update.config.contribution.ts +++ b/src/vs/platform/update/common/update.config.contribution.ts @@ -65,2 +65,8 @@ configurationRegistry.registerConfiguration({ @@ -12,15 +12,15 @@ index 6061e15b..84246033 100644 + }, 'update.enableWindowsBackgroundUpdates': { diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts -index 3767c907..9b3c17bf 100644 +index 49720f6a..7ce182cd 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts -@@ -470,3 +470,3 @@ export abstract class AbstractUpdateService implements IUpdateService { +@@ -455,3 +455,3 @@ export abstract class AbstractUpdateService implements IUpdateService { - this.logService.info('update#isLatestVersion() - found version', fetchedVersion, currentVersion); + this.logService.info(`update#isLatestVersion() - found: ${fetchedVersion}, current: ${currentVersion}`); -@@ -474,3 +474,28 @@ export abstract class AbstractUpdateService implements IUpdateService { +@@ -459,3 +459,26 @@ export abstract class AbstractUpdateService implements IUpdateService { - return Promise.resolve({ lastest, update }); + const minReleaseAge = this.configurationService.getValue('update.minReleaseAge'); @@ -29,8 +29,6 @@ index 3767c907..9b3c17bf 100644 + return Promise.resolve({ lastest, update }); + } + -+ this.logService.info(`update#isLatestVersion() - ${update.timestamp} ${typeof update.timestamp}`); -+ + const releaseDate = update.timestamp ? new Date(Number.parseInt(String(update.timestamp), 10)) : null; + + this.logService.info(`update#isLatestVersion() - releaseDate: ${releaseDate}`); From 5b6504a1a126cf50f56e7d193bd3a87e43ff70f3 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 16 May 2026 11:20:21 +0200 Subject: [PATCH 41/79] fix: remove all references to onboarding screen (#2841) --- patches/00-brand-remove-branding.patch | 9 ---- patches/00-ui-disable-onboarding.patch | 35 --------------- patches/80-ui-disable-onboarding.json | 8 ++++ patches/81-ui-disable-onboarding.patch | 61 ++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 44 deletions(-) delete mode 100644 patches/00-ui-disable-onboarding.patch create mode 100644 patches/80-ui-disable-onboarding.json create mode 100644 patches/81-ui-disable-onboarding.patch diff --git a/patches/00-brand-remove-branding.patch b/patches/00-brand-remove-branding.patch index 8d6cc5fc823..3cb972da3b9 100644 --- a/patches/00-brand-remove-branding.patch +++ b/patches/00-brand-remove-branding.patch @@ -1401,15 +1401,6 @@ index 79eae7a1..7c0ff9dd 100644 - type: 'svg', altText: 'VS Code extension marketplace with featured language extensions', path: 'extensions.svg' + type: 'svg', altText: '!!APP_NAME!! extension marketplace with featured language extensions', path: 'extensions.svg' }, -diff --git a/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts b/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts -index a77b2cdb..9e2f08d3 100644 ---- a/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts -+++ b/src/vs/workbench/contrib/welcomeOnboarding/browser/onboardingVariationA.ts -@@ -173,3 +173,3 @@ export class OnboardingVariationA extends Disposable implements IOnboardingServi - this.overlay.setAttribute('aria-modal', 'true'); -- this.overlay.setAttribute('aria-label', localize('onboarding.a.aria', "Welcome to Visual Studio Code")); -+ this.overlay.setAttribute('aria-label', localize('onboarding.a.aria', "Welcome to !!APP_NAME!!")); - 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 bdd30bf2..317d11c8 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/editor/vs_code_editor_walkthrough.ts diff --git a/patches/00-ui-disable-onboarding.patch b/patches/00-ui-disable-onboarding.patch deleted file mode 100644 index 42807172ae2..00000000000 --- a/patches/00-ui-disable-onboarding.patch +++ /dev/null @@ -1,35 +0,0 @@ -diff --git a/src/vs/workbench/contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.ts b/src/vs/workbench/contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.ts -index d725507e..a4a092d8 100644 ---- a/src/vs/workbench/contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.ts -+++ b/src/vs/workbench/contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.ts -@@ -4,30 +4 @@ - *--------------------------------------------------------------------------------------------*/ -- --// Load styles for the remaining onboarding variant. --import './media/variationA.css'; -- --import { localize2 } from '../../../../nls.js'; --import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; --import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; --import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; --import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; --import { IOnboardingService } from '../common/onboardingService.js'; --import { OnboardingVariationA } from './onboardingVariationA.js'; -- --registerSingleton(IOnboardingService, OnboardingVariationA, InstantiationType.Delayed); -- --registerAction2(class extends Action2 { -- constructor() { -- super({ -- id: 'workbench.action.welcomeOnboarding2026', -- title: localize2('welcomeOnboarding2026', "Welcome Onboarding 2026"), -- category: Categories.Developer, -- f1: true, -- }); -- } -- -- run(accessor: ServicesAccessor): void { -- const onboardingService = accessor.get(IOnboardingService); -- onboardingService.show(); -- } --}); diff --git a/patches/80-ui-disable-onboarding.json b/patches/80-ui-disable-onboarding.json new file mode 100644 index 00000000000..fc28593a19f --- /dev/null +++ b/patches/80-ui-disable-onboarding.json @@ -0,0 +1,8 @@ +[ + { + "action": "remove", + "paths": [ + "src/vs/workbench/contrib/welcomeOnboarding" + ] + } +] diff --git a/patches/81-ui-disable-onboarding.patch b/patches/81-ui-disable-onboarding.patch new file mode 100644 index 00000000000..caa83250b3a --- /dev/null +++ b/patches/81-ui-disable-onboarding.patch @@ -0,0 +1,61 @@ +diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts +index 6f785eaa..4172f07f 100644 +--- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts ++++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts +@@ -33,5 +33,2 @@ import { mainWindow } from '../../../../base/browser/window.js'; + import { getActiveElement } from '../../../../base/browser/dom.js'; +-import { isWeb } from '../../../../base/common/platform.js'; +-import { IOnboardingService } from '../../welcomeOnboarding/common/onboardingService.js'; +-import { ONBOARDING_STORAGE_KEY } from '../../welcomeOnboarding/common/onboardingTypes.js'; + +@@ -96,3 +93,2 @@ export class StartupPageRunnerContribution extends Disposable implements IWorkbe + @IContextKeyService private readonly contextKeyService: IContextKeyService, +- @IOnboardingService private readonly onboardingService: IOnboardingService, + ) { +@@ -100,3 +96,2 @@ export class StartupPageRunnerContribution extends Disposable implements IWorkbe + +- this.tryShowOnboarding(); + this.run().then(undefined, onUnexpectedError); +@@ -232,32 +227,2 @@ export class StartupPageRunnerContribution extends Disposable implements IWorkbe + } +- +- private tryShowOnboarding(): void { +- if (this.environmentService.skipWelcome) { +- return; // skip welcome flag is set +- } +- +- if (isWeb) { +- return; // not supported on web (e.g. codespaces, github.dev) +- } +- +- if (!this.configurationService.getValue('workbench.welcomePage.experimentalOnboarding')) { +- return; // experimental onboarding is disabled +- } +- +- if (!this.storageService.isNew(StorageScope.APPLICATION)) { +- return; // only show onboarding for new users who have never used the product before +- } +- +- if (this.storageService.getBoolean(ONBOARDING_STORAGE_KEY, StorageScope.APPLICATION)) { +- return; // onboarding already completed +- } +- +- // Show the onboarding overlay on top of the welcome page +- this.onboardingService.show(); +- +- // Mark onboarding as completed when dismissed +- this._register(this.onboardingService.onDidDismiss(() => { +- this.storageService.store(ONBOARDING_STORAGE_KEY, true, StorageScope.APPLICATION, StorageTarget.USER); +- })); +- } + } +diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts +index 2c3be688..0ab9b329 100644 +--- a/src/vs/workbench/workbench.common.main.ts ++++ b/src/vs/workbench/workbench.common.main.ts +@@ -379,5 +379,2 @@ import './contrib/welcomeViews/common/newFile.contribution.js'; + +-// Welcome Onboarding +-import './contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.js'; +- + // Call Hierarchy From 809d10dac010e14ec9f072ce7a7639e25e5736df Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 16 May 2026 11:20:48 +0200 Subject: [PATCH 42/79] feat: make sure that ai-coauthor is off (#2842) --- patches/00-ext-git-ai-coauthor-off.patch | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 patches/00-ext-git-ai-coauthor-off.patch diff --git a/patches/00-ext-git-ai-coauthor-off.patch b/patches/00-ext-git-ai-coauthor-off.patch new file mode 100644 index 00000000000..e18abc514e5 --- /dev/null +++ b/patches/00-ext-git-ai-coauthor-off.patch @@ -0,0 +1,8 @@ +diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts +index 77f1acf2..53d694ba 100644 +--- a/extensions/git/src/repository.ts ++++ b/extensions/git/src/repository.ts +@@ -1505,2 +1505,3 @@ export class Repository implements Disposable { + const config = workspace.getConfiguration('git', Uri.file(this.root)); ++ // Make sure that AI CoAuthor is off + const addAICoAuthor = config.get<'off' | 'chatAndAgent' | 'all'>('addAICoAuthor', 'off'); From 92141f2c75f4657bda05b5c2939e7a45c062d67a Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 16 May 2026 12:06:33 +0200 Subject: [PATCH 43/79] fix(msi): use .msi as extension for updater file (#2843) --- patches/11-update-use-github-release.patch | 46 +++++++++++++++++----- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/patches/11-update-use-github-release.patch b/patches/11-update-use-github-release.patch index 734b122c810..e6a8bf91803 100644 --- a/patches/11-update-use-github-release.patch +++ b/patches/11-update-use-github-release.patch @@ -273,7 +273,7 @@ index 2be53f61..53904dab 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 a7851933..b49db325 100644 +index a7851933..c9157f60 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 @@ -304,12 +304,20 @@ index a7851933..b49db325 100644 + _updateType = UpdateType.Archive; + } } -@@ -157,3 +160,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -144,5 +147,6 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + try { ++ const updateType = getUpdateType(); + const updatingVersion = (await readFile(updatingVersionPath, 'utf8')).trim(); + this.logService.info(`update#doCheckForUpdates - application was updating to version ${updatingVersion}`); +- const updatePackagePath = await this.getUpdatePackagePath(updatingVersion); ++ const updatePackagePath = await this.getUpdatePackagePath(updatingVersion, updateType); + if (await pfs.Promises.exists(updatePackagePath)) { +@@ -157,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 -@@ -215,12 +218,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -215,12 +219,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - protected buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined { - let platform = `win32-${process.arch}`; @@ -340,14 +348,14 @@ index a7851933..b49db325 100644 - return createUpdateURL(this.productService.updateUrl!, platform, quality, commit, options); + return createUpdateURL(this.productService, quality, process.platform, process.arch, target); } -@@ -232,6 +245,2 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -232,6 +246,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 -@@ -241,9 +250,13 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -241,9 +251,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) @@ -366,7 +374,7 @@ index a7851933..b49db325 100644 - if (!update || !update.url || !update.version || !update.productVersion) { + if(!result) { // If we were checking for an overwrite update and found nothing newer, -@@ -259,2 +272,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -259,2 +273,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + const { lastest, update } = result; + @@ -376,12 +384,17 @@ index a7851933..b49db325 100644 + } + if (updateType === UpdateType.Archive) { -@@ -284,3 +304,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -276,3 +297,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + return this.cleanup(update.version).then(() => { +- return this.getUpdatePackagePath(update.version).then(updatePackagePath => { ++ return this.getUpdatePackagePath(update.version, updateType).then(updatePackagePath => { + return pfs.Promises.exists(updatePackagePath).then(exists => { +@@ -284,3 +305,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 => { -@@ -329,8 +349,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -329,8 +350,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun }) - .then(undefined, err => { - this.telemetryService.publicLog2<{ messageHash: string }, UpdateErrorClassification>('update:error', { messageHash: String(hash(String(err))) }); @@ -393,7 +406,22 @@ index a7851933..b49db325 100644 - const message: string | undefined = explicit ? (err.message || err) : undefined; + const message: string | undefined = explicit ? (error.message || error) : undefined; -@@ -394,20 +413,31 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -354,5 +374,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + +- private async getUpdatePackagePath(version: string): Promise { ++ private async getUpdatePackagePath(version: string, type: UpdateType): Promise { + const cachePath = await this.cachePath; +- return path.join(cachePath, `CodeSetup-${this.productService.quality}-${version}.exe`); ++ const extension = type == UpdateType.WindowsInstaller ? 'msi' : 'exe' ++ ++ return path.join(cachePath, `${this.productService.nameShort.replaceAll(/\s/g, '')}-${this.productService.quality}-${version}.${extension}`); + } +@@ -360,3 +382,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + private async cleanup(exceptVersion: string | null = null): Promise { +- const filter = exceptVersion ? (one: string) => !(new RegExp(`${this.productService.quality}-${exceptVersion}\\.exe$`).test(one)) : () => true; ++ const filter = exceptVersion ? (one: string) => !(new RegExp(`${this.productService.quality}-${exceptVersion}\\.(exe|msi)$`).test(one)) : () => true; + +@@ -394,20 +416,31 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun await pfs.Promises.writeFile(this.availableUpdate.updateFilePath, 'flag'); - const child = spawn(this.availableUpdate.packagePath, - [ From d70f6aea1815172368032751c29c5a1f9ad94706 Mon Sep 17 00:00:00 2001 From: Sahil Jain Date: Sat, 16 May 2026 10:17:19 +0000 Subject: [PATCH 44/79] fix(wsl): use jeanp413.open-remote-wsl as default wsl extension (#2789) --- patches/windows/00-remote-use-open-ext.patch | 73 ++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 patches/windows/00-remote-use-open-ext.patch diff --git a/patches/windows/00-remote-use-open-ext.patch b/patches/windows/00-remote-use-open-ext.patch new file mode 100644 index 00000000000..986f98777ef --- /dev/null +++ b/patches/windows/00-remote-use-open-ext.patch @@ -0,0 +1,73 @@ +diff --git i/resources/win32/bin/code.sh w/resources/win32/bin/code.sh +index bcf31892c23..f19cde6b9e1 100644 +--- i/resources/win32/bin/code.sh ++++ w/resources/win32/bin/code.sh +@@ -11,6 +11,7 @@ APP_NAME="@@APPNAME@@" + QUALITY="@@QUALITY@@" + NAME="@@NAME@@" + SERVERDATAFOLDER="@@SERVERDATAFOLDER@@" ++VERSION="@@VERSION@@" + VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")" + ELECTRON="$VSCODE_PATH/$NAME.exe" + +@@ -41,7 +42,7 @@ if [ $IN_WSL = true ]; then + CLI=$(wslpath -m "$VSCODE_PATH/resources/app/out/cli.js") + + # use the Remote WSL extension if installed +- WSL_EXT_ID="ms-vscode-remote.remote-wsl" ++ WSL_EXT_ID="${VSCODE_WSL_EXTENSION_ID:-jeanp413.open-remote-wsl}" + + ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt 2>/dev/null /tmp/remote-wsl-loc.txt 2>/dev/null Date: Sat, 16 May 2026 14:44:51 +0200 Subject: [PATCH 45/79] fix: missing executable flag [skip ci] --- update_version.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 update_version.sh diff --git a/update_version.sh b/update_version.sh old mode 100644 new mode 100755 From 63a2dddc54e10fb841df5428af6e831d91c2b797 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 18 May 2026 10:05:37 +0200 Subject: [PATCH 46/79] feat(reh): add flag to to disable client validation (#2847) --- .../00-remote-disable-client-validation.patch | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 patches/00-remote-disable-client-validation.patch diff --git a/patches/00-remote-disable-client-validation.patch b/patches/00-remote-disable-client-validation.patch new file mode 100644 index 00000000000..820b42a5856 --- /dev/null +++ b/patches/00-remote-disable-client-validation.patch @@ -0,0 +1,46 @@ +diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts +index 5c4f3c2a..3c5d8268 100644 +--- a/src/vs/server/node/remoteExtensionHostAgentServer.ts ++++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts +@@ -77,2 +77,3 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { + serverBasePath: string | undefined, ++ private readonly _disableClientValidation: boolean | undefined, + @IServerEnvironmentService private readonly _environmentService: IServerEnvironmentService, +@@ -334,8 +335,10 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { + +- const rendererCommit = msg2.commit; +- const myCommit = this._productService.commit; +- if (rendererCommit && myCommit) { +- // Running in the built version where commits are defined +- if (rendererCommit !== myCommit) { +- return rejectWebSocketConnection(`Client refused: version mismatch`); ++ if(!this._disableClientValidation) { ++ const rendererCommit = msg2.commit; ++ const myCommit = this._productService.commit; ++ if (rendererCommit && myCommit) { ++ // Running in the built version where commits are defined ++ if (rendererCommit !== myCommit) { ++ return rejectWebSocketConnection(`Client refused: version mismatch`); ++ } + } +@@ -700,3 +703,5 @@ export async function createServer(address: string | net.AddressInfo | null, arg + +- const remoteExtensionHostAgentServer = instantiationService.createInstance(RemoteExtensionHostAgentServer, socketServer, connectionToken, vsdaMod, hasWebClient, serverBasePath); ++ let disableClientValidation = args['disable-client-validation']; ++ ++ const remoteExtensionHostAgentServer = instantiationService.createInstance(RemoteExtensionHostAgentServer, socketServer, connectionToken, vsdaMod, hasWebClient, serverBasePath, disableClientValidation); + +diff --git a/src/vs/server/node/serverEnvironmentService.ts b/src/vs/server/node/serverEnvironmentService.ts +index bacdd289..194b5485 100644 +--- a/src/vs/server/node/serverEnvironmentService.ts ++++ b/src/vs/server/node/serverEnvironmentService.ts +@@ -35,2 +35,4 @@ export const serverOptions: OptionDescriptions> = { + ++ 'disable-client-validation': { type: 'boolean' }, ++ + /* ----- vs code options --- -- */ +@@ -227,2 +229,4 @@ export interface ServerParsedArgs { + ++ 'disable-client-validation'?: boolean; ++ + /* ----- server cli ----- */ From e7428323efe54cd72fe75dcdc7afb0403257940e Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 18 May 2026 10:05:50 +0200 Subject: [PATCH 47/79] refactor(update): move and merge log (#2848) --- patches/11-update-use-github-release.patch | 2 +- patches/12-update-add-cooldown.patch | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/patches/11-update-use-github-release.patch b/patches/11-update-use-github-release.patch index e6a8bf91803..9f512fcb540 100644 --- a/patches/11-update-use-github-release.patch +++ b/patches/11-update-use-github-release.patch @@ -130,7 +130,7 @@ index 8cf5dceb..49720f6a 100644 + const fetchedVersion = /\d+\.\d+\.\d+\.\d+/.test(update.productVersion) ? update.productVersion.replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') : update.productVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); + const currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); + -+ this.logService.info('update#isLatestVersion() - found version', fetchedVersion, currentVersion); ++ this.logService.info(`update#isLatestVersion() - found: ${fetchedVersion}, current: ${currentVersion}`); + + const lastest = semver.compareBuild(currentVersion, fetchedVersion) >= 0; + diff --git a/patches/12-update-add-cooldown.patch b/patches/12-update-add-cooldown.patch index f24af52f6a2..8c0a59b42b1 100644 --- a/patches/12-update-add-cooldown.patch +++ b/patches/12-update-add-cooldown.patch @@ -15,11 +15,6 @@ diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src index 49720f6a..7ce182cd 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts -@@ -455,3 +455,3 @@ export abstract class AbstractUpdateService implements IUpdateService { - -- this.logService.info('update#isLatestVersion() - found version', fetchedVersion, currentVersion); -+ this.logService.info(`update#isLatestVersion() - found: ${fetchedVersion}, current: ${currentVersion}`); - @@ -459,3 +459,26 @@ export abstract class AbstractUpdateService implements IUpdateService { - return Promise.resolve({ lastest, update }); From a6a4322e82aaaa792fee6bbdfdf97913cd0a7828 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 18 May 2026 12:22:04 +0200 Subject: [PATCH 48/79] docs: update macos min version closes: - #2786 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 412dfdb8540..9dfae9e8715 100644 --- a/README.md +++ b/README.md @@ -179,8 +179,8 @@ The builds are run every day, but exit early if there isn't a new release from M The minimal version is limited by the core component Electron, you may want to check its [platform prerequisites](https://www.electronjs.org/docs/latest/development/build-instructions-gn#platform-prerequisites). -- [x] macOS (`zip`, `dmg`) macOS 10.15 or newer x64 -- [x] macOS (`zip`, `dmg`) macOS 11.0 or newer arm64 +- [x] macOS (`zip`, `dmg`) macOS 12 or newer x64 +- [x] macOS (`zip`, `dmg`) macOS 12 or newer arm64 - [x] GNU/Linux x64 (`deb`, `rpm`, `AppImage`, `snap`, `tar.gz`) - [x] GNU/Linux arm64 (`deb`, `rpm`, `snap`, `tar.gz`) - [x] GNU/Linux armhf (`deb`, `rpm`, `tar.gz`) From 27ffab98492866f32dcbc6947587dfe6f5493636 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 18 May 2026 15:00:10 +0200 Subject: [PATCH 49/79] fix(ui): resize find widget (#2849) --- font-size/.artifactrc.yml | 6 +- font-size/.tazerc.json | 8 +- font-size/.xo-config.json | 9 +- font-size/generate-css.ts | 32 +++- font-size/package-lock.json | 266 +++++++++++++++++++------- font-size/package.json | 22 ++- patches/00-ui-custom-font.patch | 321 +++++++++++++++++++++++++++++--- 7 files changed, 545 insertions(+), 119 deletions(-) diff --git a/font-size/.artifactrc.yml b/font-size/.artifactrc.yml index b2069690dc5..1810e6b47dc 100644 --- a/font-size/.artifactrc.yml +++ b/font-size/.artifactrc.yml @@ -1,8 +1,8 @@ -$schema: https://raw.githubusercontent.com/zokugun/artifact/v0.6.0/schemas/v1/install.json +$schema: https://raw.githubusercontent.com/zokugun/artifact/v0.9.0/schemas/v2/install.json artifacts: "@daiyam/artifact-lang-js": - version: 0.11.3 + version: 0.12.3 requires: - "22" "@daiyam/artifact-lang-ts": - version: 0.8.0 + version: 0.9.1 diff --git a/font-size/.tazerc.json b/font-size/.tazerc.json index 52b7a03b709..3b849486f95 100644 --- a/font-size/.tazerc.json +++ b/font-size/.tazerc.json @@ -2,5 +2,11 @@ "force": true, "includeLocked": true, "maturityPeriod": 7, - "write": true + "packageMode": { + "@types/node": "minor", + "typescript": "minor" + }, + "exclude": [ + "xo" + ] } diff --git a/font-size/.xo-config.json b/font-size/.xo-config.json index e2f442786f9..d96bb651440 100644 --- a/font-size/.xo-config.json +++ b/font-size/.xo-config.json @@ -89,6 +89,12 @@ "always" ], "@typescript-eslint/parameter-properties": "off", + "@typescript-eslint/prefer-nullish-coalescing": [ + "error", + { + "ignoreConditionalTests": true + } + ], "@typescript-eslint/prefer-promise-reject-errors": "off", "@typescript-eslint/return-await": "off", "arrow-parens": [ @@ -162,7 +168,8 @@ "mod": false, "num": false, "pkg": false, - "str": false + "str": false, + "temp": false } } ] diff --git a/font-size/generate-css.ts b/font-size/generate-css.ts index 6e0f5841659..8f865dfcee4 100755 --- a/font-size/generate-css.ts +++ b/font-size/generate-css.ts @@ -4,7 +4,7 @@ import path from 'node:path'; import process from 'node:process'; import fse from '@zokugun/fs-extra-plus/async'; import { err, OK, type Result, stringifyError, xtry } from '@zokugun/xtry'; -import postcss, { Root, type Rule } from 'postcss'; +import postcss, { type Root, type Rule } from 'postcss'; type Area = { name: string; @@ -16,7 +16,7 @@ type Area = { const PX_REGEX = /(-?\d+(\.\d+)?)px\b/g; const COEFF_PRECISION = 6; const HEADER = '/*** Generated for Custom Font Size ***/'; -const ZEROS = ['margin', 'padding']; +const ZEROS = new Set(['margin', 'padding']); const AREAS: Record = { activitybar: { @@ -43,7 +43,9 @@ const AREAS: Record = { files: [ 'src/vs/base/browser/ui/actionbar/actionbar.css', 'src/vs/base/browser/ui/button/button.css', + 'src/vs/base/browser/ui/codicons/codicon/codicon.css', 'src/vs/base/browser/ui/inputbox/inputBox.css', + 'src/vs/base/browser/ui/toggle/toggle.css', 'src/vs/workbench/contrib/debug/browser/media/debugToolBar.css', 'src/vs/workbench/contrib/debug/browser/media/debugViewlet.css', 'src/vs/workbench/contrib/extensions/browser/media/extension.css', @@ -59,10 +61,20 @@ const AREAS: Record = { files: [ 'src/vs/workbench/browser/parts/editor/media/editortabscontrol.css', 'src/vs/workbench/browser/parts/editor/media/editortitlecontrol.css', - 'src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css' + 'src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css', ], prefixes: ['.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs'], }, + workbench: { + name: 'workbench', + defaultSize: 13, + files: [ + 'src/vs/base/browser/ui/codicons/codicon/codicon.css', + 'src/vs/base/browser/ui/toggle/toggle.css', + 'src/vs/editor/contrib/find/browser/findWidget.css', + ], + prefixes: ['.monaco-workbench .part'], + }, }; function formatCoefficient(n: number): string { // {{{ @@ -79,8 +91,9 @@ function replacePx(area: Area) { // {{{ } const coeff = formatCoefficient(pxValue / area.defaultSize); + const varname = area.name === 'workbench' ? '--vscode-workbench-font-size' : `--vscode-workbench-${area.name}-font-size`; - return `calc(var(--vscode-workbench-${area.name}-font-size) * ${coeff})`; + return `calc(var(${varname}) * ${coeff})`; }; } // }}} @@ -104,7 +117,7 @@ async function processFile(filePath: string, areas: Area[]): Promise 0) { @@ -135,7 +148,7 @@ function processFileArea(postcssResult: Root, generatedRoot: Root, area: Area): else if(declaration.value === 'auto' && (declaration.prop === 'height' || declaration.prop === 'width')) { declarationsToAdd.push({ prop: declaration.prop, value: 'auto' }); } - else if(declaration.value === '0' && ZEROS.includes(declaration.prop)) { + else if(declaration.value === '0' && ZEROS.has(declaration.prop)) { declarationsToAdd.push({ prop: declaration.prop, value: '0' }); } }); @@ -205,7 +218,7 @@ function mergeSelector(selectors: string[], prefixes: string[], index: number): mergeSelector(selectors, prefixes, index + 1); } else if(index === 0) { - selectors.unshift(...prefixes) + selectors.unshift(...prefixes); } else { selectors.splice(index + 1, 0, ...prefixes.slice(index)); @@ -245,10 +258,10 @@ async function main(): Promise { // {{{ for(const area of Object.values(AREAS)) { for(const file of area.files) { if(files[file]) { - files[file].push(area) + files[file].push(area); } else { - files[file] = [area] + files[file] = [area]; } } } @@ -263,7 +276,6 @@ async function main(): Promise { // {{{ else { console.log(`No area found for ${name}`); console.log(`\nAvailable areas:\n- ${Object.keys(AREAS).join('\n- ')}`); - return; } } // }}} diff --git a/font-size/package-lock.json b/font-size/package-lock.json index 097f1ad72cf..2cdbfbba6b7 100644 --- a/font-size/package-lock.json +++ b/font-size/package-lock.json @@ -9,15 +9,16 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@zokugun/fs-extra-plus": "0.3.8", - "@zokugun/xtry": "0.10.1", + "@zokugun/fs-extra-plus": "0.4.1", + "@zokugun/xtry": "0.11.6", "fast-glob": "3.3.3", - "postcss": "8.5.10" + "postcss": "8.5.14" }, "devDependencies": { - "@types/node": "22.19.17", + "@types/node": "22.19.18", "fixpack": "4.0.0", - "taze": "19.11.0", + "rimraf": "6.1.3", + "taze": "19.12.0", "typescript": "5.9.3", "xo": "0.60.0" } @@ -406,9 +407,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", - "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", + "version": "22.19.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.18.tgz", + "integrity": "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ==", "dev": true, "license": "MIT", "dependencies": { @@ -872,22 +873,22 @@ "peer": true }, "node_modules/@zokugun/fs-extra-plus": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@zokugun/fs-extra-plus/-/fs-extra-plus-0.3.8.tgz", - "integrity": "sha512-EijO0TJdupzTQN8a2ZsoWZNG7PuuKsyuYwr0XJDpgfWCVofuabxLSJPRiT5uzdTrTpsyEC4tk4RTXp9A5ZPeXg==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@zokugun/fs-extra-plus/-/fs-extra-plus-0.4.1.tgz", + "integrity": "sha512-aLwHhiBg7mqmhxCat/mOoM1zdzrqOVnxCXGMzs6I3XiPaTlNczwlO8WHGw/111DSlYuGvrX2zagBXHVpqVHGGQ==", "license": "MIT", "dependencies": { "@zokugun/is-it-type": "0.7.0", - "@zokugun/xtry": "0.11.4" + "@zokugun/xtry": "0.11.5" }, "optionalDependencies": { "mime-types": "*" } }, "node_modules/@zokugun/fs-extra-plus/node_modules/@zokugun/xtry": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/@zokugun/xtry/-/xtry-0.11.4.tgz", - "integrity": "sha512-Skkb/roP7cLWOo2uei66C63B39FeGqaaR/HU68at46X6ObTvYVMvvqEDr2AhmETVQF74wIOxSTMY6NPPhOCzEA==", + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/@zokugun/xtry/-/xtry-0.11.5.tgz", + "integrity": "sha512-C6g/48K05hLMHAlx0cJoSoOiAcKL/1qJRFxeoDao1dywry2XO+CnhE8BdDkQEVsgkZrqob7B2dQSP7N1piEPSg==", "license": "MIT" }, "node_modules/@zokugun/is-it-type": { @@ -897,9 +898,9 @@ "license": "MIT" }, "node_modules/@zokugun/xtry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@zokugun/xtry/-/xtry-0.10.1.tgz", - "integrity": "sha512-nB3KpyjpFdK69EFTT9SLcIpec9EFA9ysiOuWS1B8lpnbJtzm4woMxHT22ZEEDKl9ykIhHKfpFQc2emaJMUi5oQ==", + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@zokugun/xtry/-/xtry-0.11.6.tgz", + "integrity": "sha512-hEODJ0ZUsC8o/9vZA1XEIxXx89oPHgswM7T6Q6r3A4+rx7bbLoDXSIB6Ar1TcwI94BgbcPC1Y4t3AtU3UOOGiQ==", "license": "MIT" }, "node_modules/acorn": { @@ -1519,9 +1520,9 @@ } }, "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3274,9 +3275,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -3487,6 +3488,45 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flat-cache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/flatted": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", @@ -3699,22 +3739,18 @@ } }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3740,6 +3776,45 @@ "license": "BSD-2-Clause", "peer": true }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -4977,6 +5052,16 @@ "node": ">=0.10.0" } }, + "node_modules/lru-cache": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz", + "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4994,6 +5079,19 @@ "dev": true, "license": "MIT" }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -5100,6 +5198,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5547,6 +5655,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/package-manager-detector": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", @@ -5636,6 +5751,23 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-type": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", @@ -5832,9 +5964,9 @@ } }, "node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "funding": [ { "type": "opencollective", @@ -6273,17 +6405,20 @@ } }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "glob": "^7.1.3" + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" }, "bin": { - "rimraf": "bin.js" + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -6861,13 +6996,13 @@ } }, "node_modules/taze": { - "version": "19.11.0", - "resolved": "https://registry.npmjs.org/taze/-/taze-19.11.0.tgz", - "integrity": "sha512-BlfH8Z6JdoIsrUptnz4P4YuEqdYsa/bSNNDOMhTlsHZ7Bbg1/0NyYh6uPkoRREjrt/kVovV+HYdi1ilHxvChfw==", + "version": "19.12.0", + "resolved": "https://registry.npmjs.org/taze/-/taze-19.12.0.tgz", + "integrity": "sha512-qe9mTHwyJUNgWyqx62chVcJN0bfel2AJHsqlFVVxVto0GiBhKjNsPo0FOnFxpKin1Nh0uBooZHgNAjcsP8t2Fw==", "dev": true, "license": "MIT", "dependencies": { - "@antfu/ni": "^30.0.0", + "@antfu/ni": "^30.1.0", "@henrygd/queue": "^1.2.0", "cac": "^7.0.0", "find-up-simple": "^1.0.1", @@ -6876,10 +7011,10 @@ "pathe": "^2.0.3", "pnpm-workspace-yaml": "^1.6.0", "restore-cursor": "^5.1.0", - "tinyexec": "^1.0.4", - "tinyglobby": "^0.2.15", + "tinyexec": "^1.1.2", + "tinyglobby": "^0.2.16", "unconfig": "^7.5.0", - "yaml": "^2.8.3" + "yaml": "^2.9.0" }, "bin": { "taze": "bin/taze.mjs" @@ -6959,9 +7094,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", "dev": true, "license": "MIT", "engines": { @@ -7683,23 +7818,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/xo/node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "dev": true, "license": "ISC", "bin": { diff --git a/font-size/package.json b/font-size/package.json index 4e223b9fa3d..61c17a62828 100644 --- a/font-size/package.json +++ b/font-size/package.json @@ -11,25 +11,27 @@ "main": "generate-css.ts", "scripts": { "audit:fix": "npm audit fix --min-release-age=0", - "ci:lint": "zizmor .", - "ci:lint:fix": "zizmor . --fix=all", + "clean": "rimraf lib", + "compile:src": "tsc -p src", "lint": "xo", - "lint:all": "fixpack && npm audit && npm run ci:lint && npm run lint", + "lint:all": "npm audit && npm run lint:package && npm run lint", "lint:fix": "xo --fix", - "prepare": "fixpack || true", + "lint:package": "fixpack || true", + "run": "node generate-css.ts", "update:artifacts": "artifact update", - "update:deps": "taze" + "update:deps": "taze --all" }, "dependencies": { - "@zokugun/fs-extra-plus": "0.3.8", - "@zokugun/xtry": "0.10.1", + "@zokugun/fs-extra-plus": "0.4.1", + "@zokugun/xtry": "0.11.6", "fast-glob": "3.3.3", - "postcss": "8.5.10" + "postcss": "8.5.14" }, "devDependencies": { - "@types/node": "22.19.17", + "@types/node": "22.19.18", "fixpack": "4.0.0", - "taze": "19.11.0", + "rimraf": "6.1.3", + "taze": "19.12.0", "typescript": "5.9.3", "xo": "0.60.0" }, diff --git a/patches/00-ui-custom-font.patch b/patches/00-ui-custom-font.patch index adedf118e28..b12d59a1a1b 100644 --- a/patches/00-ui-custom-font.patch +++ b/patches/00-ui-custom-font.patch @@ -77,10 +77,10 @@ index e9e55ad9..0ce9147c 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 0ec4cbb4..0fc77fae 100644 +index 0ec4cbb4..704ed8a8 100644 --- a/src/vs/base/browser/ui/button/button.css +++ b/src/vs/base/browser/ui/button/button.css -@@ -184 +184,43 @@ +@@ -184 +184,46 @@ } + + @@ -117,6 +117,9 @@ index 0ec4cbb4..0fc77fae 100644 +.monaco-workbench .part.sidebar .monaco-button-dropdown .monaco-button-dropdown-separator > div, .monaco-workbench .part.auxiliarybar .monaco-button-dropdown .monaco-button-dropdown-separator > div { + width: 1px +} ++.monaco-workbench .part.sidebar .monaco-button-dropdown > .monaco-button.monaco-dropdown-button, .monaco-workbench .part.auxiliarybar .monaco-button-dropdown > .monaco-button.monaco-dropdown-button { ++ padding: 0 calc(var(--vscode-workbench-sidebar-font-size) * 0.307692) ++} +.monaco-workbench .part.sidebar .monaco-description-button, .monaco-workbench .part.auxiliarybar .monaco-description-button { + margin: calc(var(--vscode-workbench-sidebar-font-size) * 0.307692) calc(var(--vscode-workbench-sidebar-font-size) * 0.384615) +} @@ -126,17 +129,28 @@ index 0ec4cbb4..0fc77fae 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 d7f257db..af55bddb 100644 +index d7f257db..4d7c6499 100644 --- a/src/vs/base/browser/ui/codicons/codicon/codicon.css +++ b/src/vs/base/browser/ui/codicons/codicon/codicon.css -@@ -25 +25,7 @@ +@@ -25 +25,18 @@ /* icon rules are dynamically created by the platform theme service (see iconsStyleSheet.ts) */ + + + ++/*** Handcrafted for Custom Font Size ***/ ++.monaco-workbench .part.titlebar .codicon[class*='codicon-'] { ++ font: normal normal normal 16px/1 codicon; ++} ++ ++ ++/*** Generated for Custom Font Size ***/ ++ +.monaco-workbench .part.sidebar .codicon[class*='codicon-'], .monaco-workbench .part.auxiliarybar .codicon[class*='codicon-'] { + font: normal normal normal calc(var(--vscode-workbench-sidebar-font-size) * 1.230769)/1 codicon +} ++.monaco-workbench .part .codicon[class*='codicon-'] { ++ font: normal normal normal calc(var(--vscode-workbench-font-size) * 1.230769)/1 codicon ++} \ 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 d3dfd9a5..cf596272 100644 @@ -302,19 +316,21 @@ index fb2e1f40..3b1e23f2 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 e2d206d3..e69352c8 100644 +index e2d206d3..e8dfa5a9 100644 --- a/src/vs/base/browser/ui/toggle/toggle.css +++ b/src/vs/base/browser/ui/toggle/toggle.css -@@ -69 +69,27 @@ +@@ -69 +69,52 @@ } + + + ++/*** Generated for Custom Font Size ***/ ++ +.monaco-workbench .part.sidebar .monaco-custom-toggle, .monaco-workbench .part.auxiliarybar .monaco-custom-toggle { + margin-left: calc(var(--vscode-workbench-sidebar-font-size) * 0.153846); + width: calc(var(--vscode-workbench-sidebar-font-size) * 1.538462); + height: calc(var(--vscode-workbench-sidebar-font-size) * 1.538462); -+ padding: calc(var(--vscode-workbench-sidebar-font-size) * 0.076923) ++ padding: 1px +} +.monaco-workbench .part.sidebar .monaco-custom-toggle.monaco-checkbox, .monaco-workbench .part.auxiliarybar .monaco-custom-toggle.monaco-checkbox { + height: calc(var(--vscode-workbench-sidebar-font-size) * 1.384615); @@ -333,6 +349,29 @@ index e2d206d3..e69352c8 100644 +.monaco-workbench .part.sidebar .monaco-action-bar .checkbox-action-item > .checkbox-label, .monaco-workbench .part.auxiliarybar .monaco-action-bar .checkbox-action-item > .checkbox-label { + font-size: calc(var(--vscode-workbench-sidebar-font-size) * 0.923077) +} ++.monaco-workbench .part .monaco-custom-toggle { ++ margin-left: calc(var(--vscode-workbench-font-size) * 0.153846); ++ width: calc(var(--vscode-workbench-font-size) * 1.538462); ++ height: calc(var(--vscode-workbench-font-size) * 1.538462); ++ padding: 1px ++} ++.monaco-workbench .part .monaco-custom-toggle.monaco-checkbox { ++ height: calc(var(--vscode-workbench-font-size) * 1.384615); ++ width: calc(var(--vscode-workbench-font-size) * 1.384615); ++ margin-right: calc(var(--vscode-workbench-font-size) * 0.692308); ++ margin-left: calc(var(--vscode-workbench-font-size) * 0); ++ padding: calc(var(--vscode-workbench-font-size) * 0); ++ background-size: calc(var(--vscode-workbench-font-size) * 1.230769) ++} ++.monaco-workbench .part .monaco-action-bar .checkbox-action-item { ++ padding-right: calc(var(--vscode-workbench-font-size) * 0.153846) ++} ++.monaco-workbench .part .monaco-action-bar .checkbox-action-item > .monaco-custom-toggle.monaco-checkbox { ++ margin-right: calc(var(--vscode-workbench-font-size) * 0.307692) ++} ++.monaco-workbench .part .monaco-action-bar .checkbox-action-item > .checkbox-label { ++ font-size: calc(var(--vscode-workbench-font-size) * 0.923077) ++} \ 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 0b2fcbbb..6370e6f7 100644 @@ -350,10 +389,10 @@ index 0b2fcbbb..6370e6f7 100644 } diff --git a/src/vs/base/common/font.ts b/src/vs/base/common/font.ts new file mode 100644 -index 00000000..8b9689c7 +index 00000000..20d0b27e --- /dev/null +++ b/src/vs/base/common/font.ts -@@ -0,0 +1,187 @@ +@@ -0,0 +1,191 @@ +import { IConfigurationService } from '../../platform/configuration/common/configuration.js'; + +export const FONT = { @@ -402,6 +441,8 @@ index 00000000..8b9689c7 + tabsSize38: 38, + tabsSize80: 80, + tabsSize120: 120, ++ ++ workbenchCoefficient: 1, +}; + +// Activity bar coefficients (base 16) @@ -500,6 +541,8 @@ index 00000000..8b9689c7 + FONT.defaultSidebarSize = size + FONT.defaultStatusBarSize = size * DEFAULT_COEFF_12 + FONT.defaultTabsSize = size ++ ++ FONT.workbenchCoefficient = size / 13 +} + +export function updatePanelSize(size: number): void { @@ -1030,6 +1073,240 @@ index 00000000..62a49d53 + assert.strictEqual(FONT.tabsSize35, 35); + }); +}); +diff --git a/src/vs/editor/browser/widget/codeEditor/editor.css b/src/vs/editor/browser/widget/codeEditor/editor.css +index 638055a0..713d8a6c 100644 +--- a/src/vs/editor/browser/widget/codeEditor/editor.css ++++ b/src/vs/editor/browser/widget/codeEditor/editor.css +@@ -17,2 +17,6 @@ + ++/* .monaco-editor .editor-widget { ++ font-size: var(--vscode-workbench-font-size, 13px); ++} */ ++ + /* -------------------- Editor -------------------- */ +diff --git a/src/vs/editor/contrib/find/browser/findWidget.css b/src/vs/editor/contrib/find/browser/findWidget.css +index 62c6056c..3d893baa 100644 +--- a/src/vs/editor/contrib/find/browser/findWidget.css ++++ b/src/vs/editor/contrib/find/browser/findWidget.css +@@ -281 +281,104 @@ + } ++ ++ ++ ++/*** Handcrafted for Custom Font Size ***/ ++ ++.monaco-workbench .part .monaco-editor .find-widget.visible { ++ transform: translateY(0); ++} ++ ++.monaco-workbench .part .monaco-editor .find-widget .codicon[class*='codicon-'] { ++ font-size: calc(var(--vscode-workbench-font-size) * 1.230769); ++} ++ ++ ++ ++/*** Generated for Custom Font Size ***/ ++ ++.monaco-workbench .part .monaco-editor .find-widget { ++ height: calc(var(--vscode-workbench-font-size) * 2.615385); ++ line-height: calc(var(--vscode-workbench-font-size) * 1.461538); ++ padding: 0 calc(var(--vscode-workbench-font-size) * 0.307692) 0 calc(var(--vscode-workbench-font-size) * 0.692308); ++ margin-top: calc(var(--vscode-workbench-font-size) * 0.307692); ++ transform: translateY(calc(-100% - calc(var(--vscode-workbench-font-size) * 0.769231))) ++} ++.monaco-workbench .part .monaco-editor .find-widget textarea { ++ margin: calc(var(--vscode-workbench-font-size) * 0) ++} ++.monaco-workbench .part .monaco-editor .find-widget .monaco-inputbox.synthetic-focus { ++ outline: 1px solid -webkit-focus-ring-color; ++ outline-offset: calc(var(--vscode-workbench-font-size) * -0.076923) ++} ++.monaco-workbench .part .monaco-editor .find-widget .monaco-findInput .input { ++ font-size: calc(var(--vscode-workbench-font-size) * 1) ++} ++.monaco-workbench .part .monaco-editor .find-widget > .find-part, .monaco-workbench .part .monaco-editor .find-widget > .replace-part { ++ margin: calc(var(--vscode-workbench-font-size) * 0.230769) calc(var(--vscode-workbench-font-size) * 1.923077) 0 calc(var(--vscode-workbench-font-size) * 1.307692); ++ font-size: calc(var(--vscode-workbench-font-size) * 0.923077) ++} ++.monaco-workbench .part .monaco-editor .find-widget > .find-part .monaco-inputbox, .monaco-workbench .part .monaco-editor .find-widget > .replace-part .monaco-inputbox { ++ min-height: calc(var(--vscode-workbench-font-size) * 1.923077) ++} ++.monaco-workbench .part .monaco-editor .find-widget > .replace-part .monaco-inputbox > .ibwrapper > .mirror { ++ padding-right: calc(var(--vscode-workbench-font-size) * 1.692308) ++} ++.monaco-workbench .part .monaco-editor .find-widget > .find-part .monaco-inputbox > .ibwrapper > .input, .monaco-workbench .part .monaco-editor .find-widget > .find-part .monaco-inputbox > .ibwrapper > .mirror, .monaco-workbench .part .monaco-editor .find-widget > .replace-part .monaco-inputbox > .ibwrapper > .input, .monaco-workbench .part .monaco-editor .find-widget > .replace-part .monaco-inputbox > .ibwrapper > .mirror { ++ padding-top: calc(var(--vscode-workbench-font-size) * 0.153846); ++ padding-bottom: calc(var(--vscode-workbench-font-size) * 0.153846) ++} ++.monaco-workbench .part .monaco-editor .find-widget > .find-part .find-actions { ++ height: calc(var(--vscode-workbench-font-size) * 1.923077) ++} ++.monaco-workbench .part .monaco-editor .find-widget > .replace-part .replace-actions { ++ height: calc(var(--vscode-workbench-font-size) * 1.923077) ++} ++.monaco-workbench .part .monaco-editor .find-widget .matchesCount { ++ margin: 0 0 0 calc(var(--vscode-workbench-font-size) * 0.230769); ++ padding: calc(var(--vscode-workbench-font-size) * 0.153846) 0 0 calc(var(--vscode-workbench-font-size) * 0.153846); ++ height: calc(var(--vscode-workbench-font-size) * 1.923077); ++ line-height: calc(var(--vscode-workbench-font-size) * 1.769231) ++} ++.monaco-workbench .part .monaco-editor .find-widget .button { ++ width: calc(var(--vscode-workbench-font-size) * 1.230769); ++ height: calc(var(--vscode-workbench-font-size) * 1.230769); ++ padding: calc(var(--vscode-workbench-font-size) * 0.230769); ++ margin-left: calc(var(--vscode-workbench-font-size) * 0.230769) ++} ++.monaco-workbench .part .monaco-editor .find-widget .codicon-find-selection { ++ width: calc(var(--vscode-workbench-font-size) * 1.692308); ++ height: calc(var(--vscode-workbench-font-size) * 1.692308); ++ padding: calc(var(--vscode-workbench-font-size) * 0.230769) ++} ++.monaco-workbench .part .monaco-editor .find-widget .button.left { ++ margin: calc(var(--vscode-workbench-font-size) * 0.307692) 0 calc(var(--vscode-workbench-font-size) * 0.307692) calc(var(--vscode-workbench-font-size) * 0.384615) ++} ++.monaco-workbench .part .monaco-editor .find-widget .button.wide { ++ width: auto; ++ padding: 1px calc(var(--vscode-workbench-font-size) * 0.461538); ++ top: calc(var(--vscode-workbench-font-size) * -0.076923) ++} ++.monaco-workbench .part .monaco-editor .find-widget .button.toggle { ++ width: calc(var(--vscode-workbench-font-size) * 1.384615) ++} ++.monaco-workbench .part .monaco-editor .find-widget > .replace-part > .monaco-findInput > .controls { ++ top: calc(var(--vscode-workbench-font-size) * 0.230769); ++ right: calc(var(--vscode-workbench-font-size) * 0.153846) ++} ++.monaco-workbench .part .monaco-editor .find-widget.narrow-find-widget { ++ max-width: calc(var(--vscode-workbench-font-size) * 19.769231) ++} ++.monaco-workbench .part .monaco-editor .find-widget.collapsed-find-widget { ++ max-width: calc(var(--vscode-workbench-font-size) * 13.076923) ++} ++.monaco-workbench .part .monaco-editor .currentFindMatch { ++ padding: 1px ++} ++.monaco-workbench .part .monaco-editor.hc-black .find-widget .button:before { ++ top: 1px; ++ left: calc(var(--vscode-workbench-font-size) * 0.153846) ++} ++.monaco-workbench .part .monaco-editor .find-widget > .button.codicon-widget-close { ++ top: calc(var(--vscode-workbench-font-size) * 0.384615); ++ right: calc(var(--vscode-workbench-font-size) * 0.307692) ++} +\ No newline at end of file +diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts +index 03e698c3..7d6f5afa 100644 +--- a/src/vs/editor/contrib/find/browser/findWidget.ts ++++ b/src/vs/editor/contrib/find/browser/findWidget.ts +@@ -47,2 +47,3 @@ import { HoverStyle, type IHoverLifecycleOptions } from '../../../../base/browse + import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; ++import { FONT } from '../../../../base/common/font.js'; + +@@ -82,2 +83,3 @@ const PART_WIDTH = 275; + const FIND_INPUT_AREA_WIDTH = PART_WIDTH - 54; ++const FLEXIBLE_MAX_HEIGHT = 118; + +@@ -98,3 +100,3 @@ export class FindWidgetViewZone implements IViewZone { + +- this.heightInPx = FIND_INPUT_AREA_HEIGHT; ++ this.heightInPx = FIND_INPUT_AREA_HEIGHT * FONT.workbenchCoefficient; + this.suppressMouseDown = false; +@@ -767,3 +769,3 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + +- if (widgetWidth > FIND_WIDGET_INITIAL_WIDTH) { ++ if (widgetWidth > (FIND_WIDGET_INITIAL_WIDTH * FONT.workbenchCoefficient)) { + // as the widget is resized by users, we may need to change the max width of the widget as the editor width changes. +@@ -775,9 +777,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + +- if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth >= editorWidth) { ++ const initialWidth = (FIND_WIDGET_INITIAL_WIDTH + 28) * FONT.workbenchCoefficient ++ const maxMatchesWidth = MAX_MATCHES_COUNT_WIDTH * FONT.workbenchCoefficient ++ ++ if (initialWidth + minimapWidth >= editorWidth) { + reducedFindWidget = true; + } +- if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth - MAX_MATCHES_COUNT_WIDTH >= editorWidth) { ++ if (initialWidth * FONT.workbenchCoefficient + minimapWidth - maxMatchesWidth >= editorWidth) { + narrowFindWidget = true; + } +- if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth - MAX_MATCHES_COUNT_WIDTH >= editorWidth + 50) { ++ if (initialWidth + minimapWidth - maxMatchesWidth >= editorWidth + 50) { + collapsedFindWidget = true; +@@ -805,2 +810,5 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + private _getHeight(): number { ++ const size2 = 2 * FONT.workbenchCoefficient ++ const size4 = 4 * FONT.workbenchCoefficient ++ + let totalheight = 0; +@@ -808,6 +816,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + // find input margin top +- totalheight += 4; ++ totalheight += size4; + + // find input height +- totalheight += this._findInput.inputBox.height + 2 /** input box border */; ++ totalheight += this._findInput.inputBox.height + size2 /** input box border */; + +@@ -815,5 +823,5 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + // replace input margin +- totalheight += 4; ++ totalheight += size4; + +- totalheight += this._replaceInput.inputBox.height + 2 /** input box border */; ++ totalheight += this._replaceInput.inputBox.height + size2 /** input box border */; + } +@@ -821,3 +829,3 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + // margin bottom +- totalheight += 4; ++ totalheight += size4; + return totalheight; +@@ -990,3 +998,3 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewProvider, { +- width: FIND_INPUT_AREA_WIDTH, ++ width: FIND_INPUT_AREA_WIDTH * FONT.workbenchCoefficient, + label: NLS_FIND_INPUT_LABEL, +@@ -1010,3 +1018,3 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + flexibleWidth, +- flexibleMaxHeight: 118, ++ flexibleMaxHeight: FLEXIBLE_MAX_HEIGHT * FONT.workbenchCoefficient, + showCommonFindToggles: true, +@@ -1166,3 +1174,3 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + flexibleWidth, +- flexibleMaxHeight: 118, ++ flexibleMaxHeight: FLEXIBLE_MAX_HEIGHT, + showHistoryHint: () => showHistoryKeybindingHint(this._keybindingService), +@@ -1262,4 +1270,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + ++ let initialWidth = FIND_WIDGET_INITIAL_WIDTH * FONT.workbenchCoefficient; ++ + // We need to set this explicitly, otherwise on IE11, the width inheritence of flex doesn't work. +- this._domNode.style.width = `${FIND_WIDGET_INITIAL_WIDTH}px`; ++ this._domNode.style.width = `${initialWidth}px`; + +@@ -1272,3 +1282,4 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + this._resized = false; +- let originalWidth = FIND_WIDGET_INITIAL_WIDTH; ++ ++ let originalWidth = initialWidth; + +@@ -1282,3 +1293,3 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + +- if (width < FIND_WIDGET_INITIAL_WIDTH) { ++ if (width < initialWidth) { + // narrow down the find widget should be handled by CSS. +@@ -1304,3 +1315,3 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + +- if (currentWidth < FIND_WIDGET_INITIAL_WIDTH) { ++ if (currentWidth < initialWidth) { + // The editor is narrow and the width of the find widget is controlled fully by CSS. +@@ -1309,5 +1320,5 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL + +- let width = FIND_WIDGET_INITIAL_WIDTH; ++ let width = initialWidth; + +- if (!this._resized || currentWidth === FIND_WIDGET_INITIAL_WIDTH) { ++ if (!this._resized || currentWidth === initialWidth) { + // 1. never resized before, double click should maximizes it diff --git a/src/vs/platform/quickinput/browser/tree/quickInputDelegate.ts b/src/vs/platform/quickinput/browser/tree/quickInputDelegate.ts index 328285f0..0735dfa2 100644 --- a/src/vs/platform/quickinput/browser/tree/quickInputDelegate.ts @@ -1532,7 +1809,7 @@ index a24f7613..6b15b9c2 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 d57abaaf..2cdf3103 100644 +index d57abaaf..4928dac9 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 @@ @@ -1609,14 +1886,14 @@ index d57abaaf..2cdf3103 100644 +.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .tabs-container > .tab.dirty-border-top:not(:focus) > .tab-border-top-container { + height: calc(var(--vscode-workbench-tabs-font-size) * 0.153846) +} -+.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .tabs-container > .tab.sizing-shrink > .tab-label > .monaco-icon-label-container::after, .monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .tabs-container > .tab.sizing-fixed > .tab-label > .monaco-icon-label-container::after { ++.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .tabs-container > .tab.sizing-shrink > .tab-label > .monaco-icon-label-container::after, .monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .tabs-container > .tab.sizing-fixed > .tab-label > .monaco-icon-label-container::after { + width: calc(var(--vscode-workbench-tabs-font-size) * 0.384615); + padding: 0; + top: 1px; + bottom: 1px; + height: calc(100% - calc(var(--vscode-workbench-tabs-font-size) * 0.153846)) +} -+.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .tabs-container > .tab.sizing-shrink > .tab-label.tab-label-has-badge::after, .monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .tabs-container > .tab.sizing-fixed > .tab-label.tab-label-has-badge::after { ++.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .tabs-container > .tab.sizing-shrink > .tab-label.tab-label-has-badge::after, .monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .tabs-container > .tab.sizing-fixed > .tab-label.tab-label-has-badge::after { + margin-right: calc(var(--vscode-workbench-tabs-font-size) * 0.384615) +} +.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .tabs-container > .tab.sizing-shrink:not(.tab-actions-left):not(.close-action-off) .tab-label { @@ -2273,7 +2550,7 @@ index aca98deb..5bf9bf71 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 1c9305bd..6471a0d3 100644 +index 487cf079..438f1cc7 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 +2567,10 @@ index 1c9305bd..6471a0d3 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 bdc603ff..4bc6d153 100644 +index eeddba00..4a0ef488 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts -@@ -698,2 +698,85 @@ const registry = Registry.as(ConfigurationExtensions.Con +@@ -716,2 +716,85 @@ const registry = Registry.as(ConfigurationExtensions.Con }, + 'workbench.experimental.fontFamily': { + type: 'string', @@ -2523,7 +2800,7 @@ index 15ae7996..06a95d59 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 84939387..50a19e4f 100644 +index 6893c58f..0d8f7abc 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 @@ -2683,14 +2960,14 @@ index 5162553b..5adbed8f 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts -index 6f82df16..6c8ae0d6 100644 +index d6dec6a1..3fb52d98 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -63,2 +63,3 @@ import { equals } from '../../../../base/common/arrays.js'; import { hasKey } from '../../../../base/common/types.js'; +import { FONT } from '../../../../base/common/font.js'; -@@ -578,3 +579,3 @@ class BreakpointsDelegate implements IListVirtualDelegate +@@ -593,3 +594,3 @@ class BreakpointsDelegate implements IListVirtualDelegate getHeight(_element: BreakpointTreeElement): number { - return 22; + return FONT.sidebarSize22; @@ -3852,7 +4129,7 @@ index e9c0fcd4..f3e23de0 100644 + this.replaceInput.width = width - FONT.sidebarSize28; this.replaceInput.inputBox.layout(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts -index 21118dd7..b5e53a1d 100644 +index 013ee562..8c0ac05a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -58,2 +58,3 @@ import { TerminalStorageKeys } from '../common/terminalStorageKeys.js'; @@ -3868,12 +4145,12 @@ index 21118dd7..b5e53a1d 100644 - getHeight: () => TerminalTabsListSizes.TabHeight, + getHeight: () => FONT.bottomPaneSize22, getTemplateId: () => 'terminal.tabs' -@@ -113,3 +113,3 @@ export class TerminalTabList extends WorkbenchList { +@@ -116,3 +116,3 @@ export class TerminalTabList extends WorkbenchList { multipleSelectionSupport: true, - paddingBottom: TerminalTabsListSizes.TabHeight, + paddingBottom: FONT.bottomPaneSize22, dnd: instantiationService.createInstance(TerminalTabsDragAndDrop), -@@ -458,3 +458,3 @@ class TerminalTabsRenderer implements IListRenderer Date: Tue, 19 May 2026 00:47:04 +0200 Subject: [PATCH 50/79] ci: use github runner for PR --- .github/repo-starter-kit.yml | 5 +++++ .github/workflows/ci-build-macos.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .github/repo-starter-kit.yml diff --git a/.github/repo-starter-kit.yml b/.github/repo-starter-kit.yml new file mode 100644 index 00000000000..187279397cf --- /dev/null +++ b/.github/repo-starter-kit.yml @@ -0,0 +1,5 @@ +package: "@daiyam/default" +repository: https://github.com/VSCodium/vscodium +resources: + - label +keep: true diff --git a/.github/workflows/ci-build-macos.yml b/.github/workflows/ci-build-macos.yml index 37af0951217..0663b367dc0 100644 --- a/.github/workflows/ci-build-macos.yml +++ b/.github/workflows/ci-build-macos.yml @@ -43,7 +43,7 @@ jobs: include: - runner: macos-15-intel vscode_arch: x64 - - runner: [self-hosted, macOS, ARM64] + - runner: macos-14 vscode_arch: arm64 steps: From 39ea1f75fa8d0a00e7719e8d1aba2a1ad5c39f3c Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 19 May 2026 01:02:32 +0200 Subject: [PATCH 51/79] style: rename patch --- ...build-replace-unicode.patch => 00-build-replace-unicode.patch} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename patches/{build-replace-unicode.patch => 00-build-replace-unicode.patch} (100%) diff --git a/patches/build-replace-unicode.patch b/patches/00-build-replace-unicode.patch similarity index 100% rename from patches/build-replace-unicode.patch rename to patches/00-build-replace-unicode.patch From 2dd828fba0ecb2017aa1a5e073a817e14ef59d73 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 19 May 2026 02:21:20 +0200 Subject: [PATCH 52/79] fix(ui): show icons correctly (#2851) --- font-size/generate-css.ts | 2 -- patches/00-ui-custom-font.patch | 14 +++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/font-size/generate-css.ts b/font-size/generate-css.ts index 8f865dfcee4..274aabaf949 100755 --- a/font-size/generate-css.ts +++ b/font-size/generate-css.ts @@ -43,7 +43,6 @@ const AREAS: Record = { files: [ 'src/vs/base/browser/ui/actionbar/actionbar.css', 'src/vs/base/browser/ui/button/button.css', - 'src/vs/base/browser/ui/codicons/codicon/codicon.css', 'src/vs/base/browser/ui/inputbox/inputBox.css', 'src/vs/base/browser/ui/toggle/toggle.css', 'src/vs/workbench/contrib/debug/browser/media/debugToolBar.css', @@ -69,7 +68,6 @@ const AREAS: Record = { name: 'workbench', defaultSize: 13, files: [ - 'src/vs/base/browser/ui/codicons/codicon/codicon.css', 'src/vs/base/browser/ui/toggle/toggle.css', 'src/vs/editor/contrib/find/browser/findWidget.css', ], diff --git a/patches/00-ui-custom-font.patch b/patches/00-ui-custom-font.patch index b12d59a1a1b..9defc27ebc6 100644 --- a/patches/00-ui-custom-font.patch +++ b/patches/00-ui-custom-font.patch @@ -129,27 +129,23 @@ index 0ec4cbb4..704ed8a8 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 d7f257db..4d7c6499 100644 +index d7f257db..8d21f859 100644 --- a/src/vs/base/browser/ui/codicons/codicon/codicon.css +++ b/src/vs/base/browser/ui/codicons/codicon/codicon.css -@@ -25 +25,18 @@ +@@ -25 +25,14 @@ /* icon rules are dynamically created by the platform theme service (see iconsStyleSheet.ts) */ + + + +/*** Handcrafted for Custom Font Size ***/ +.monaco-workbench .part.titlebar .codicon[class*='codicon-'] { -+ font: normal normal normal 16px/1 codicon; ++ font-size: 16px; +} -+ -+ -+/*** Generated for Custom Font Size ***/ -+ +.monaco-workbench .part.sidebar .codicon[class*='codicon-'], .monaco-workbench .part.auxiliarybar .codicon[class*='codicon-'] { -+ font: normal normal normal calc(var(--vscode-workbench-sidebar-font-size) * 1.230769)/1 codicon ++ font-size: calc(var(--vscode-workbench-sidebar-font-size) * 1.230769) +} +.monaco-workbench .part .codicon[class*='codicon-'] { -+ font: normal normal normal calc(var(--vscode-workbench-font-size) * 1.230769)/1 codicon ++ font-size: calc(var(--vscode-workbench-font-size) * 1.230769) +} \ 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 From 4dcb5ecdec43b99ba06be1460f61e8a3cb70013e Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 19 May 2026 03:20:54 +0200 Subject: [PATCH 53/79] enhance(reh): add log for validation (#2852) --- patches/00-remote-disable-client-validation.patch | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/patches/00-remote-disable-client-validation.patch b/patches/00-remote-disable-client-validation.patch index 820b42a5856..72815f4e8cb 100644 --- a/patches/00-remote-disable-client-validation.patch +++ b/patches/00-remote-disable-client-validation.patch @@ -1,11 +1,16 @@ diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts -index 5c4f3c2a..3c5d8268 100644 +index 5c4f3c2a..91010c0b 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -77,2 +77,3 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { serverBasePath: string | undefined, + private readonly _disableClientValidation: boolean | undefined, @IServerEnvironmentService private readonly _environmentService: IServerEnvironmentService, +@@ -100,3 +101,3 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { + ); +- this._logService.info(`Extension host agent started.`); ++ this._logService.info(`Extension host agent started. (validation: ${!this._disableClientValidation})`); + this._reconnectionGraceTime = this._environmentService.reconnectionGraceTime; @@ -334,8 +335,10 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { - const rendererCommit = msg2.commit; From 3aca4740bec356978011f0644d4209bcbfa2a8bf Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 19 May 2026 03:21:04 +0200 Subject: [PATCH 54/79] fix(msi): install update (#2853) --- patches/11-update-use-github-release.patch | 158 ++++++++++++++++++--- 1 file changed, 141 insertions(+), 17 deletions(-) diff --git a/patches/11-update-use-github-release.patch b/patches/11-update-use-github-release.patch index 9f512fcb540..ffbdb9cb9bb 100644 --- a/patches/11-update-use-github-release.patch +++ b/patches/11-update-use-github-release.patch @@ -49,7 +49,7 @@ index 92ae7b96..c3fe6214 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 8cf5dceb..49720f6a 100644 +index 8cf5dceb..d776f408 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -17,3 +17,3 @@ import { ILogService } from '../../log/common/log.js'; @@ -273,7 +273,7 @@ index 2be53f61..53904dab 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 a7851933..c9157f60 100644 +index a7851933..dafc4447 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 @@ -304,7 +304,16 @@ index a7851933..c9157f60 100644 + _updateType = UpdateType.Archive; + } } -@@ -144,5 +147,6 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -57,2 +60,8 @@ function getUpdateType(): UpdateType { + ++async function pollUntil(fn: () => boolean, millis = 1000): Promise { ++ while (!fn()) { ++ await timeout(millis); ++ } ++} ++ + export class Win32UpdateService extends AbstractUpdateService implements IRelaunchHandler { +@@ -144,5 +153,6 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun try { + const updateType = getUpdateType(); const updatingVersion = (await readFile(updatingVersionPath, 'utf8')).trim(); @@ -312,12 +321,12 @@ index a7851933..c9157f60 100644 - const updatePackagePath = await this.getUpdatePackagePath(updatingVersion); + const updatePackagePath = await this.getUpdatePackagePath(updatingVersion, updateType); if (await pfs.Promises.exists(updatePackagePath)) { -@@ -157,3 +161,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -157,3 +167,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 -@@ -215,12 +219,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -215,12 +225,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - protected buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined { - let platform = `win32-${process.arch}`; @@ -348,14 +357,14 @@ index a7851933..c9157f60 100644 - return createUpdateURL(this.productService.updateUrl!, platform, quality, commit, options); + return createUpdateURL(this.productService, quality, process.platform, process.arch, target); } -@@ -232,6 +246,2 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -232,6 +252,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 -@@ -241,9 +251,13 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -241,9 +257,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) @@ -374,7 +383,7 @@ index a7851933..c9157f60 100644 - if (!update || !update.url || !update.version || !update.productVersion) { + if(!result) { // If we were checking for an overwrite update and found nothing newer, -@@ -259,2 +273,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -259,2 +279,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + const { lastest, update } = result; + @@ -384,17 +393,17 @@ index a7851933..c9157f60 100644 + } + if (updateType === UpdateType.Archive) { -@@ -276,3 +297,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -276,3 +303,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun return this.cleanup(update.version).then(() => { - return this.getUpdatePackagePath(update.version).then(updatePackagePath => { + return this.getUpdatePackagePath(update.version, updateType).then(updatePackagePath => { return pfs.Promises.exists(updatePackagePath).then(exists => { -@@ -284,3 +305,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -284,3 +311,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 => { -@@ -329,8 +350,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -329,8 +356,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun }) - .then(undefined, err => { - this.telemetryService.publicLog2<{ messageHash: string }, UpdateErrorClassification>('update:error', { messageHash: String(hash(String(err))) }); @@ -406,7 +415,7 @@ index a7851933..c9157f60 100644 - const message: string | undefined = explicit ? (err.message || err) : undefined; + const message: string | undefined = explicit ? (error.message || error) : undefined; -@@ -354,5 +374,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -354,5 +380,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - private async getUpdatePackagePath(version: string): Promise { + private async getUpdatePackagePath(version: string, type: UpdateType): Promise { @@ -416,12 +425,12 @@ index a7851933..c9157f60 100644 + + return path.join(cachePath, `${this.productService.nameShort.replaceAll(/\s/g, '')}-${this.productService.quality}-${version}.${extension}`); } -@@ -360,3 +382,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -360,3 +388,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun private async cleanup(exceptVersion: string | null = null): Promise { - const filter = exceptVersion ? (one: string) => !(new RegExp(`${this.productService.quality}-${exceptVersion}\\.exe$`).test(one)) : () => true; + const filter = exceptVersion ? (one: string) => !(new RegExp(`${this.productService.quality}-${exceptVersion}\\.(exe|msi)$`).test(one)) : () => true; -@@ -394,20 +416,31 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -394,77 +422,100 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun await pfs.Promises.writeFile(this.availableUpdate.updateFilePath, 'flag'); - const child = spawn(this.availableUpdate.packagePath, - [ @@ -442,13 +451,32 @@ index a7851933..c9157f60 100644 + if (type == UpdateType.WindowsInstaller) { + child = spawn('msiexec.exe', ['/i', this.availableUpdate.packagePath], { detached: true, -- stdio: ['ignore', 'ignore', 'ignore'], + stdio: ['ignore', 'ignore', 'ignore'], - windowsVerbatimArguments: true, - env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } - } - ); -+ stdio: ['ignore', 'ignore', 'ignore'] + }); + +- // Track the process so we can cancel it if needed +- this.availableUpdate.updateProcess = child; ++ child.once('exit', () => { ++ this.availableUpdate = undefined; ++ this.setState(State.Idle(getUpdateType())); ++ }); + +- child.once('exit', () => { +- this.availableUpdate = undefined; +- this.setState(State.Idle(getUpdateType())); +- }); ++ const readyMutexName = `${this.productService.win32MutexName}-ready`; ++ const mutex = await import('@vscode/windows-mutex'); + +- const readyMutexName = `${this.productService.win32MutexName}-ready`; +- const mutex = await import('@vscode/windows-mutex'); ++ // poll for mutex-ready ++ pollUntil(() => mutex.isActive(readyMutexName)) ++ .then(() => this.setState(State.Ready(update, explicit, this._overwrite))); + } else { + child = spawn(this.availableUpdate.packagePath, + [ @@ -468,5 +496,101 @@ index a7851933..c9157f60 100644 + env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } + } + ); -+ } +- this.updateCancellationTokenSource?.dispose(true); +- const cts = this.updateCancellationTokenSource = new CancellationTokenSource(); +- const token = cts.token; ++ // Track the process so we can cancel it if needed ++ this.availableUpdate.updateProcess = child; + +- const poll = async () => { +- while (this.state.type === StateType.Updating && !token.isCancellationRequested) { +- if (mutex.isActive(readyMutexName)) { +- this.setState(State.Ready(update, explicit, this._overwrite)); +- return; +- } ++ child.once('exit', () => { ++ this.availableUpdate = undefined; ++ this.setState(State.Idle(getUpdateType())); ++ }); + +- try { +- const progressContent = await readFile(progressFilePath, 'utf8'); +- if (!token.isCancellationRequested) { +- const [currentStr, maxStr] = progressContent.split(','); +- const currentProgress = parseInt(currentStr, 10); +- const maxProgress = parseInt(maxStr, 10); +- if (!isNaN(currentProgress) && !isNaN(maxProgress) && this.state.type === StateType.Updating) { +- if (this.state.currentProgress !== currentProgress || this.state.maxProgress !== maxProgress) { +- this.setState(State.Updating(update, explicit, currentProgress, maxProgress)); ++ const readyMutexName = `${this.productService.win32MutexName}-ready`; ++ const mutex = await import('@vscode/windows-mutex'); ++ ++ this.updateCancellationTokenSource?.dispose(true); ++ const cts = this.updateCancellationTokenSource = new CancellationTokenSource(); ++ const token = cts.token; ++ ++ const poll = async () => { ++ while (this.state.type === StateType.Updating && !token.isCancellationRequested) { ++ if (mutex.isActive(readyMutexName)) { ++ this.setState(State.Ready(update, explicit, this._overwrite)); ++ return; ++ } ++ ++ try { ++ const progressContent = await readFile(progressFilePath, 'utf8'); ++ if (!token.isCancellationRequested) { ++ const [currentStr, maxStr] = progressContent.split(','); ++ const currentProgress = parseInt(currentStr, 10); ++ const maxProgress = parseInt(maxStr, 10); ++ if (!isNaN(currentProgress) && !isNaN(maxProgress) && this.state.type === StateType.Updating) { ++ if (this.state.currentProgress !== currentProgress || this.state.maxProgress !== maxProgress) { ++ this.setState(State.Updating(update, explicit, currentProgress, maxProgress)); ++ } + } + } ++ } catch { ++ // Progress file may not exist yet or be locked, ignore + } +- } catch { +- // Progress file may not exist yet or be locked, ignore +- } +- +- await timeout(500); +- } +- }; + +- const cancelTimeout = new ProcessTimeRunOnceScheduler(() => { +- this.logService.warn('update#doApplyUpdate: polling timed out waiting for update to be ready'); +- this.setState(State.Idle(getUpdateType(), 'Update did not complete within expected time')); +- }, 60 * 60 * 1000); +- +- // Poll for progress and ready mutex for 1 hour. +- cancelTimeout.schedule(); +- poll().finally(() => { +- cancelTimeout.dispose(); +- if (this.updateCancellationTokenSource === cts) { +- this.updateCancellationTokenSource = undefined; +- } +- cts.dispose(); +- }); ++ await timeout(500); ++ } ++ }; ++ ++ const cancelTimeout = new ProcessTimeRunOnceScheduler(() => { ++ this.logService.warn('update#doApplyUpdate: polling timed out waiting for update to be ready'); ++ this.setState(State.Idle(getUpdateType(), 'Update did not complete within expected time')); ++ }, 60 * 60 * 1000); ++ ++ // Poll for progress and ready mutex for 1 hour. ++ cancelTimeout.schedule(); ++ poll().finally(() => { ++ cancelTimeout.dispose(); ++ if (this.updateCancellationTokenSource === cts) { ++ this.updateCancellationTokenSource = undefined; ++ } ++ cts.dispose(); ++ }); ++ } + } From 4b4fb3648a2b9a6518c52f5490749f276c70c9cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 21:33:30 +0200 Subject: [PATCH 55/79] build(deps): bump actions-rust-lang/setup-rust-toolchain (#2859) --- .github/workflows/ci-build-linux.yml | 2 +- .github/workflows/publish-insider-linux.yml | 2 +- .github/workflows/publish-stable-linux.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-build-linux.yml b/.github/workflows/ci-build-linux.yml index 62240e13687..22a256c8fab 100644 --- a/.github/workflows/ci-build-linux.yml +++ b/.github/workflows/ci-build-linux.yml @@ -164,7 +164,7 @@ jobs: run: ./build/linux/deps.sh if: env.DISABLED != 'yes' - - uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0 + - uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1.16.1 if: env.DISABLED != 'yes' - name: Download vscode artifact diff --git a/.github/workflows/publish-insider-linux.yml b/.github/workflows/publish-insider-linux.yml index 34137cc2de4..4f09aa50dc4 100644 --- a/.github/workflows/publish-insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -188,7 +188,7 @@ jobs: run: ./build/linux/deps.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - - uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0 + - uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1.16.1 if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Download vscode artifact diff --git a/.github/workflows/publish-stable-linux.yml b/.github/workflows/publish-stable-linux.yml index 423ceba1638..d2f76a630ab 100644 --- a/.github/workflows/publish-stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -189,7 +189,7 @@ jobs: run: ./build/linux/deps.sh if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - - uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0 + - uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1.16.1 if: env.DISABLED != 'yes' && env.SHOULD_BUILD == 'yes' - name: Download vscode artifact From e058400bbab79d3912c0853bb59c300b509b0028 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Thu, 21 May 2026 17:36:40 +0200 Subject: [PATCH 56/79] feat(1.121): update patches (#2860) --- patches/00-brand-remove-branding.patch | 112 ++++++++++-------- patches/00-copilot-fix-action-condition.patch | 38 +++--- patches/20-keymap-use-custom-lib.patch | 10 +- patches/51-ext-copilot-remove-it.json | 27 +++-- patches/52-ext-copilot-remove-it.patch | 42 ++++--- upstream/insider.json | 4 +- 6 files changed, 131 insertions(+), 102 deletions(-) diff --git a/patches/00-brand-remove-branding.patch b/patches/00-brand-remove-branding.patch index 3cb972da3b9..7755070a3d0 100644 --- a/patches/00-brand-remove-branding.patch +++ b/patches/00-brand-remove-branding.patch @@ -320,15 +320,15 @@ index 30199b2b..008aed75 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 dc8f6504..af461f85 100644 +index cb292ba5..1eaf331e 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json -@@ -29,3 +29,3 @@ +@@ -30,3 +30,3 @@ "markdown.trace.extension.desc": "Enable debug logging for the Markdown extension.", - "markdown.trace.server.desc": "Traces the communication between VS Code and the Markdown language server.", + "markdown.trace.server.desc": "Traces the communication between !!APP_NAME!! and the Markdown language server.", "markdown.server.log.desc": "Controls the logging level of the Markdown language server.", -@@ -83,3 +83,3 @@ +@@ -88,3 +88,3 @@ "comment": [ - "This setting is use the user drops or pastes image data into the editor. In this case, VS Code automatically creates a new image file in the workspace containing the dropped/pasted image.", + "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.", @@ -397,7 +397,7 @@ index d52cabb8..eb3a76c1 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 48955c38..0ff70032 100644 +index a697f8c0..2ba1dab8 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -136,5 +136,5 @@ @@ -525,7 +525,7 @@ index 313fe87c..24ba85c7 100644 + "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 0990d7c5..e34d7c4b 100644 +index 02607acc..414b7dc3 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -2,3 +2,3 @@ @@ -568,10 +568,10 @@ index a351aa77..c32e3efd 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 c342647e..17af7aea 100644 +index 373c8809..24568091 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) { +@@ -211,3 +211,3 @@ export function activate(context: vscode.ExtensionContext) { const serverBin = path.join(remoteDataDir, 'bin'); - progress.report({ message: 'Installing VSCode Server' }); + progress.report({ message: 'Installing !!APP_NAME!! Server' }); @@ -806,22 +806,22 @@ index c650e128..611a4282 100644 -export const InAutomationContext = new RawContextKey('inAutomation', false, localize('inAutomation', "Whether VS Code is running under automation/smoke test")); +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 08680900..f1881970 100644 ---- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts -+++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts -@@ -720,3 +720,3 @@ configurationRegistry.registerConfiguration({ +diff --git a/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts +index 74a6b479..198de338 100644 +--- a/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts ++++ b/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts +@@ -725,3 +725,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.") -@@ -747,3 +747,3 @@ configurationRegistry.registerConfiguration({ +@@ -752,3 +752,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 019f314d..ff8318e9 100644 +index 1417e0dc..24d713d2 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts @@ -120,3 +120,3 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation { @@ -837,10 +837,10 @@ index 019f314d..ff8318e9 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 7f0e8c25..24148540 100644 +index d3be4e1e..7390a174 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptValidator.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptValidator.ts -@@ -291,5 +291,5 @@ export class PromptValidator { +@@ -290,5 +290,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.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])); @@ -848,17 +848,17 @@ index 7f0e8c25..24148540 100644 - 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])); } -@@ -299,3 +299,3 @@ export class PromptValidator { +@@ -298,3 +298,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 { -@@ -305,3 +305,3 @@ export class PromptValidator { +@@ -304,3 +304,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; -@@ -1108,3 +1108,3 @@ export function mapClaudeModels(claudeModelNames: readonly string[]): readonly s +@@ -1107,3 +1107,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. @@ -873,30 +873,30 @@ index 0b9ced32..731d9526 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 82c7072f..fe802bb5 100644 +index 464b3948..efd9d24b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts -@@ -383,3 +383,3 @@ CommandsRegistry.registerCommand({ +@@ -382,3 +382,3 @@ CommandsRegistry.registerCommand({ description: '(optional) Options for installing the extension. Object with the following properties: ' + - '`installOnlyNewlyAddedFromExtensionPackVSIX`: When enabled, VS Code installs only newly added extensions from the extension pack VSIX. This option is considered only when installing VSIX. ', + '`installOnlyNewlyAddedFromExtensionPackVSIX`: When enabled, !!APP_NAME!! installs only newly added extensions from the extension pack VSIX. This option is considered only when installing VSIX. ', isOptional: true, -@@ -390,3 +390,3 @@ CommandsRegistry.registerCommand({ +@@ -389,3 +389,3 @@ CommandsRegistry.registerCommand({ 'type': 'boolean', - 'description': localize('workbench.extensions.installExtension.option.installOnlyNewlyAddedFromExtensionPackVSIX', "When enabled, VS Code installs only newly added extensions from the extension pack VSIX. This option is considered only while installing a VSIX."), + 'description': localize('workbench.extensions.installExtension.option.installOnlyNewlyAddedFromExtensionPackVSIX', "When enabled, !!APP_NAME!! installs only newly added extensions from the extension pack VSIX. This option is considered only while installing a VSIX."), default: false -@@ -395,3 +395,3 @@ CommandsRegistry.registerCommand({ +@@ -394,3 +394,3 @@ CommandsRegistry.registerCommand({ 'type': 'boolean', - 'description': localize('workbench.extensions.installExtension.option.installPreReleaseVersion', "When enabled, VS Code installs the pre-release version of the extension if available."), + 'description': localize('workbench.extensions.installExtension.option.installPreReleaseVersion', "When enabled, !!APP_NAME!! installs the pre-release version of the extension if available."), default: false -@@ -400,3 +400,3 @@ CommandsRegistry.registerCommand({ +@@ -399,3 +399,3 @@ CommandsRegistry.registerCommand({ 'type': 'boolean', - '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 -@@ -930,4 +930,4 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi +@@ -929,4 +929,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."), @@ -1076,7 +1076,7 @@ index c5729c39..dce19857 100644 + builtin printf '\x1b[0m\x1b[7m * \x1b[0;103m !!APP_NAME!! Python bash activation failed with exit code %d \x1b[0m' "$__vsc_activation_status" fi diff --git a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-rc.zsh b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-rc.zsh -index 4869a391..74e4cf65 100644 +index c43718e3..c20ee415 100644 --- a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-rc.zsh +++ b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-rc.zsh @@ -78,3 +78,3 @@ if [ -z "${VSCODE_PYTHON_AUTOACTIVATE_GUARD:-}" ]; then @@ -1108,7 +1108,7 @@ index 72a329b8..7992809f 100644 + Write-Host "`e[0m`e[7m * `e[0;103m !!APP_NAME!! Python powershell activation failed with exit code $($activationError.Exception.Message) `e[0m" } diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts -index fa6607bc..8dfffd3b 100644 +index 3d3e8f2d..c4f18742 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -322,3 +322,3 @@ const terminalConfiguration: IStringDictionary = { @@ -1148,10 +1148,10 @@ index fa6607bc..8dfffd3b 100644 type: 'object', @@ -484,3 +484,3 @@ const terminalConfiguration: IStringDictionary = { restricted: true, -- 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."), +- markdownDescription: localize('terminal.integrated.windowsUseConptyDll', "Whether to use the 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 conpty.dll (v1.25.260303002) shipped with !!APP_NAME!!, instead of the one bundled with Windows."), type: 'boolean', -@@ -614,3 +614,3 @@ const terminalConfiguration: IStringDictionary = { +@@ -610,3 +610,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#`'), @@ -1175,7 +1175,7 @@ index d595ccbd..f60becd3 100644 + 'description': `The problem matcher to use to parse task output for errors and warnings. Can be a predefined matcher like '$tsc' (TypeScript), '$eslint - stylish', '$gcc', etc., or a custom pattern defined in tasks.json. This helps !!APP_NAME!! display errors in the Problems panel and enables quick navigation to error locations.`, 'items': { diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/task/runTaskTool.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/task/runTaskTool.ts -index 67a6d43b..5b211e41 100644 +index 5fc22b73..099fb4a8 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/task/runTaskTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/task/runTaskTool.ts @@ -171,3 +171,3 @@ export const RunTaskToolData: IToolData = { @@ -1246,10 +1246,10 @@ index 297598ef..1fc5b45c 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 79eae7a1..7c0ff9dd 100644 +index 168a3d01..d64d47ba 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts -@@ -200,24 +200,2 @@ export const startEntries: GettingStartedStartEntryContent = [ +@@ -201,24 +201,2 @@ export const startEntries: GettingStartedStartEntryContent = [ }, - { - id: 'topLevelOpenTunnel', @@ -1274,7 +1274,7 @@ index 79eae7a1..7c0ff9dd 100644 - } - }, ]; -@@ -226,26 +204,2 @@ const Button = (title: string, href: string) => `[${title}](${href})`; +@@ -227,26 +205,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 ?? ''); @@ -1301,41 +1301,51 @@ index 79eae7a1..7c0ff9dd 100644 -} - export const walkthroughs: GettingStartedWalkthroughContent = [ -@@ -253,3 +207,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -254,3 +208,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 +212,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -259,3 +213,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 +217,2 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -264,16 +218,2 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ steps: [ - createCopilotSetupStep('CopilotSetupAnonymous', CopilotAnonymousButton, 'chatAnonymous && !chatSetupCompleted', true), -- createCopilotSetupStep('CopilotSetupSignedOut', CopilotSignedOutButton, 'chatEntitlementSignedOut && !chatAnonymous', false), +- createCopilotSetupStep('CopilotSetupSignedOut', CopilotSignedOutButton, 'chatEntitlementSignedOut && !chatAnonymous && !github.copilot.hasByokModels', false), - createCopilotSetupStep('CopilotSetupComplete', CopilotCompleteButton, 'chatSetupCompleted && !chatSetupDisabled && (chatAnonymous || chatPlanPro || chatPlanProPlus || chatPlanMax || chatPlanBusiness || chatPlanEnterprise || chatPlanFree)', false), - createCopilotSetupStep('CopilotSetupSignedIn', CopilotSignedInButton, '!chatEntitlementSignedOut && (!chatSetupCompleted || chatSetupDisabled || chatPlanCanSignUp)', false), +- { +- id: 'pickColorTheme', +- title: localize('gettingStarted.pickColor.title', "Choose your theme"), +- description: localize('gettingStarted.pickColor.description.interpolated', "The right theme helps you focus on your code, is easy on your eyes, and is simply more fun to use.\n{0}", Button(localize('titleID', "Browse Color Themes"), 'command:workbench.action.selectTheme')), +- completionEvents: [ +- 'onSettingChanged:workbench.colorTheme', +- 'onCommand:workbench.action.selectTheme' +- ], +- media: { type: 'markdown', path: 'theme_picker', } +- }, { -@@ -280,4 +230,4 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -281,4 +221,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 +239,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -290,3 +230,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 +245,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -296,3 +236,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 +271,6 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -322,6 +262,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')), @@ -1344,7 +1354,7 @@ index 79eae7a1..7c0ff9dd 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 +286,2 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -337,12 +277,2 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ }, - { - id: 'settingsSyncWeb', @@ -1357,42 +1367,42 @@ index 79eae7a1..7c0ff9dd 100644 - }, - }, { -@@ -349,3 +289,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -350,3 +280,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 +295,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -356,3 +286,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 +316,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -377,3 +307,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 +321,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -382,3 +312,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 +352,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -413,3 +343,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 +432,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -493,3 +423,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 +436,3 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -497,3 +427,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 +442,6 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ +@@ -503,6 +433,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')), diff --git a/patches/00-copilot-fix-action-condition.patch b/patches/00-copilot-fix-action-condition.patch index d4d56fbf1ad..71050b156ad 100644 --- a/patches/00-copilot-fix-action-condition.patch +++ b/patches/00-copilot-fix-action-condition.patch @@ -1,27 +1,27 @@ diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts -index 27d6be94..ddb504b4 100644 +index 55b877bd..9ba4d627 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts -@@ -1168,3 +1168,3 @@ export function registerChatActions() { +@@ -1169,3 +1169,3 @@ export function registerChatActions() { precondition: ContextKeyExpr.and( - ChatContextKeys.Setup.installed, + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.disabled.negate(), diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts -index d2fccfb3..cd0bb779 100644 +index 00c445ab..f1e87408 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts -@@ -318,3 +318,4 @@ class AttachSelectionToChatAction extends Action2 { +@@ -319,3 +319,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 883cd676..dc50ad73 100644 ---- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts -+++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts -@@ -1491,3 +1491,3 @@ configurationRegistry.registerConfiguration({ +diff --git a/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts +index 74a6b479..3c5ac6bc 100644 +--- a/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts ++++ b/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts +@@ -1615,3 +1615,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, @@ -35,10 +35,10 @@ index 8fb472b3..7b26c605 100644 + 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 6df31b5e..3cdad208 100644 +index 90d39a49..72455fa4 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupContributions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupContributions.ts -@@ -231,8 +231,9 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr +@@ -232,8 +232,9 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr f1: true, - precondition: ContextKeyExpr.or( - ChatContextKeys.Setup.hidden, @@ -54,27 +54,27 @@ index 6df31b5e..3cdad208 100644 + ChatContextKeys.Setup.completed, + ChatContextKeys.Entitlement.canSignUp.negate() ) -@@ -352,2 +353,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr +@@ -370,2 +371,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr when: ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.hidden.negate(), -@@ -384,2 +386,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr +@@ -403,2 +405,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr when: ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), IsWebContext.negate(), -@@ -414,2 +417,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr +@@ -452,2 +455,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr precondition: ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.hidden.negate(), -@@ -471,2 +475,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr +@@ -511,2 +515,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr precondition: ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.hidden.negate(), -@@ -563,2 +568,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr +@@ -609,2 +614,3 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr const internalGenerateCodeContext = ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.disableAIFeatures').negate(), ChatContextKeys.Setup.hidden.negate(), -@@ -806,3 +812,7 @@ export class ChatTeardownContribution extends Disposable implements IWorkbenchCo +@@ -837,3 +843,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( @@ -84,10 +84,10 @@ index 6df31b5e..3cdad208 100644 + ), menu: { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts -index 86516c54..2ac1702a 100644 +index 7e5d8f63..e949b389 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts -@@ -42,3 +42,4 @@ const inlineChatContextKey = ContextKeyExpr.and( +@@ -44,3 +44,4 @@ const inlineChatContextKey = ContextKeyExpr.and( EditorContextKeys.writable, - EditorContextKeys.editorSimpleInput.negate() + EditorContextKeys.editorSimpleInput.negate(), diff --git a/patches/20-keymap-use-custom-lib.patch b/patches/20-keymap-use-custom-lib.patch index ef9134f9265..0f567c6c272 100644 --- a/patches/20-keymap-use-custom-lib.patch +++ b/patches/20-keymap-use-custom-lib.patch @@ -24,7 +24,7 @@ index d1a8a227..b7209206 100644 +!@vscodium/native-keymap/build/Release/*.node diff --git a/eslint.config.js b/eslint.config.js -index dad155b9..91ed6530 100644 +index 9f0b5274..10eb5a99 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1518,3 +1518,3 @@ export default tseslint.config( @@ -33,7 +33,7 @@ index dad155b9..91ed6530 100644 + '@vscodium/native-keymap', 'net', diff --git a/package-lock.json b/package-lock.json -index 96150298..caa581eb 100644 +index 68f23433..d5c1dfa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,2 +41,3 @@ @@ -43,7 +43,7 @@ index 96150298..caa581eb 100644 @@ -59,3 +60,2 @@ "native-is-elevated": "0.9.0", - "native-keymap": "^3.3.5", - "node-pty": "^1.2.0-beta.12", + "node-pty": "^1.2.0-beta.13", @@ -4368,2 +4368,9 @@ }, + "node_modules/@vscodium/native-keymap": { @@ -90,14 +90,14 @@ index 96150298..caa581eb 100644 + "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", diff --git a/package.json b/package.json -index 79f619ca..7f89f523 100644 +index 5defb1ef..f827d888 100644 --- a/package.json +++ b/package.json @@ -136,3 +136,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.12", + "node-pty": "^1.2.0-beta.13", diff --git a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts index 50360a79..93df77af 100644 --- a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts diff --git a/patches/51-ext-copilot-remove-it.json b/patches/51-ext-copilot-remove-it.json index 9499fa26af0..a1cb8572ddc 100644 --- a/patches/51-ext-copilot-remove-it.json +++ b/patches/51-ext-copilot-remove-it.json @@ -3,13 +3,8 @@ "action": "remove", "paths": [ "extensions/copilot", - "src/vs/platform/agentHost/node/claude/anthropicErrors.ts", - "src/vs/platform/agentHost/node/claude/claudeAgent.ts", - "src/vs/platform/agentHost/node/claude/claudeAgentSdkService.ts", - "src/vs/platform/agentHost/node/claude/claudeAgentSession.ts", - "src/vs/platform/agentHost/node/claude/claudeMapSessionEvents.ts", - "src/vs/platform/agentHost/node/claude/claudePromptResolver.ts", - "src/vs/platform/agentHost/node/claude/claudeProxyService.ts", + "src/vs/platform/agentHost/node/claude", + "src/vs/platform/agentHost/node/otel", "src/vs/platform/agentHost/node/copilot/copilotAgent.ts", "src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts", "src/vs/platform/agentHost/node/copilot/copilotGitProject.ts", @@ -34,7 +29,23 @@ "src/vs/platform/agentHost/test/node/copilotToolDisplay.test.ts", "src/vs/platform/agentHost/test/node/historyRecordFixtures.test.ts", "src/vs/platform/agentHost/test/node/historyRecordFixtures.ts", - "src/vs/platform/agentHost/test/node/shared/copilotApiService.test.ts" + "src/vs/platform/agentHost/test/node/shared/copilotApiService.test.ts", + "src/vs/platform/agentHost/test/node/claudeFileEditObserver.test.ts", + "src/vs/platform/agentHost/test/node/claudePromptQueue.test.ts", + "src/vs/platform/agentHost/test/node/claudeReplayMapper.test.ts", + "src/vs/platform/agentHost/test/node/claudeSdkMessageRouter.test.ts", + "src/vs/platform/agentHost/test/node/claudeSdkPipeline.test.ts", + "src/vs/platform/agentHost/test/node/claudeSessionMetadataStore.test.ts", + "src/vs/platform/agentHost/test/node/claudeSubagentResolver.test.ts", + "src/vs/platform/agentHost/test/node/claudeSubagentSignals.test.ts", + "src/vs/platform/agentHost/test/node/anthropicBetas.test.ts", + "src/vs/platform/agentHost/test/node/claudeInteractiveTools.test.ts", + "src/vs/platform/agentHost/test/node/claudeMaterializer.test.ts", + "src/vs/platform/agentHost/test/node/claudeModelId.test.ts", + "src/vs/platform/agentHost/test/node/claudeProxyAuth.test.ts", + "src/vs/platform/agentHost/test/node/claudeSubagentRegistry.test.ts", + "src/vs/platform/agentHost/test/node/claudeToolDisplay.test.ts", + "src/vs/platform/agentHost/test/node/otel/agentHostOTelService.integrationTest.ts" ] } ] diff --git a/patches/52-ext-copilot-remove-it.patch b/patches/52-ext-copilot-remove-it.patch index ef9b79bb4d9..8180eb5ef2c 100644 --- a/patches/52-ext-copilot-remove-it.patch +++ b/patches/52-ext-copilot-remove-it.patch @@ -88,7 +88,7 @@ index 25aa6a57..7615c27a 100644 - compileCopilotExtensionBuildTask, compileExtensionMediaBuildTask, diff --git a/build/npm/dirs.ts b/build/npm/dirs.ts -index 468d2df8..64b1c19d 100644 +index 289a4697..8c6df6f7 100644 --- a/build/npm/dirs.ts +++ b/build/npm/dirs.ts @@ -17,3 +17,2 @@ export const dirs = [ @@ -134,7 +134,7 @@ index dc154d4b..834a4a78 100644 - } } diff --git a/package-lock.json b/package-lock.json -index 96150298..752530f6 100644 +index 68f23433..eea135bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,5 +12,2 @@ @@ -330,7 +330,7 @@ index 96150298..752530f6 100644 - }, "node_modules/vscode-oniguruma": { diff --git a/package.json b/package.json -index 79f619ca..5c2bcea8 100644 +index 5defb1ef..04990246 100644 --- a/package.json +++ b/package.json @@ -23,3 +23,3 @@ @@ -356,7 +356,7 @@ index 79f619ca..5c2bcea8 100644 - "@github/copilot-sdk": "^0.3.0", "@microsoft/1ds-core-js": "^3.2.13", diff --git a/remote/package.json b/remote/package.json -index 1544b4c9..b3c51d2f 100644 +index 92eed6c1..a7cebde8 100644 --- a/remote/package.json +++ b/remote/package.json @@ -5,4 +5,2 @@ @@ -365,7 +365,7 @@ index 1544b4c9..b3c51d2f 100644 - "@github/copilot-sdk": "^0.3.0", "@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 0232f81b..e6758b6b 100644 +index 2dc1125a..6a2107fc 100644 --- a/src/vs/platform/agentHost/node/agentHostMain.ts +++ b/src/vs/platform/agentHost/node/agentHostMain.ts @@ -17,3 +17,3 @@ import * as os from 'os'; @@ -373,15 +373,17 @@ index 0232f81b..e6758b6b 100644 -import { AgentHostClaudeSdkPathEnvVar, AgentHostIpcChannels, IAgentHostInspectInfo, IAgentHostSocketInfo, IConnectionTrackerService } from '../common/agentService.js'; +import { AgentHostIpcChannels, IAgentHostInspectInfo, IAgentHostSocketInfo, IConnectionTrackerService } from '../common/agentService.js'; import { AgentService } from './agentService.js'; -@@ -21,7 +21,2 @@ import { IAgentConfigurationService } from './agentConfigurationService.js'; +@@ -22,9 +22,2 @@ import { IAgentHostCompletions } from './agentHostCompletions.js'; import { IAgentHostTerminalManager } from './agentHostTerminalManager.js'; -import { CopilotAgent } from './copilot/copilotAgent.js'; -import { CopilotApiService, ICopilotApiService } from './shared/copilotApiService.js'; -import { ClaudeAgent } from './claude/claudeAgent.js'; -import { ClaudeAgentSdkService, IClaudeAgentSdkService } from './claude/claudeAgentSdkService.js'; -import { ClaudeProxyService, IClaudeProxyService } from './claude/claudeProxyService.js'; +-import { IAgentHostOTelService } from '../common/otel/agentHostOTelService.js'; +-import { AgentHostOTelService } from './otel/agentHostOTelService.js'; import { ProtocolServerHandler } from './protocolServerHandler.js'; -@@ -115,8 +110,2 @@ function startAgentHost(): void { +@@ -125,10 +118,2 @@ async function startAgentHost(): Promise { diServices.set(IAgentHostGitService, gitService); - const copilotApiService = instantiationService.createInstance(CopilotApiService, undefined); - diServices.set(ICopilotApiService, copilotApiService); @@ -389,9 +391,11 @@ index 0232f81b..e6758b6b 100644 - diServices.set(IClaudeProxyService, claudeProxyService); - const claudeAgentSdkService = instantiationService.createInstance(ClaudeAgentSdkService); - diServices.set(IClaudeAgentSdkService, claudeAgentSdkService); - agentService = new AgentService(logService, fileService, sessionDataService, productService, gitService, rootConfigResource); -@@ -129,11 +118,2 @@ function startAgentHost(): void { - diServices.set(IAgentConfigurationService, agentService.configurationService); +- const agentHostOTelService = disposables.add(instantiationService.createInstance(AgentHostOTelService)); +- diServices.set(IAgentHostOTelService, agentHostOTelService); + agentService = new AgentService(logService, fileService, sessionDataService, productService, gitService, rootConfigResource, telemetryService); +@@ -142,11 +127,2 @@ async function startAgentHost(): Promise { + diServices.set(IAgentHostCompletions, agentService.completionsService); - agentService.registerProvider(instantiationService.createInstance(CopilotAgent)); - // The Claude agent provider is opt-in. Gated on the - // `chat.agentHost.claudeAgent.path` workbench setting being non-empty, @@ -403,18 +407,20 @@ index 0232f81b..e6758b6b 100644 - } } catch (err) { diff --git a/src/vs/platform/agentHost/node/agentHostServerMain.ts b/src/vs/platform/agentHost/node/agentHostServerMain.ts -index 26cbf179..bd83ddf3 100644 +index 5f8f2c88..89b918a2 100644 --- a/src/vs/platform/agentHost/node/agentHostServerMain.ts +++ b/src/vs/platform/agentHost/node/agentHostServerMain.ts -@@ -33,7 +33,2 @@ import { InstantiationService } from '../../instantiation/common/instantiationSe +@@ -33,9 +33,2 @@ import { InstantiationService } from '../../instantiation/common/instantiationSe import { ServiceCollection } from '../../instantiation/common/serviceCollection.js'; -import { CopilotAgent } from './copilot/copilotAgent.js'; -import { CopilotApiService, ICopilotApiService } from './shared/copilotApiService.js'; -import { ClaudeAgent } from './claude/claudeAgent.js'; -import { ClaudeAgentSdkService, IClaudeAgentSdkService } from './claude/claudeAgentSdkService.js'; -import { ClaudeProxyService, IClaudeProxyService } from './claude/claudeProxyService.js'; +-import { IAgentHostOTelService } from '../common/otel/agentHostOTelService.js'; +-import { AgentHostOTelService } from './otel/agentHostOTelService.js'; import { AgentService } from './agentService.js'; -@@ -207,21 +202,2 @@ async function main(): Promise { +@@ -215,23 +208,2 @@ async function main(): Promise { diServices.set(IAgentHostGitService, gitService); - // Register `ICopilotApiService` BEFORE `IClaudeProxyService` — - // the proxy service constructor requires it. @@ -424,6 +430,8 @@ index 26cbf179..bd83ddf3 100644 - diServices.set(IClaudeProxyService, claudeProxyService); - const claudeAgentSdkService = instantiationService.createInstance(ClaudeAgentSdkService); - diServices.set(IClaudeAgentSdkService, claudeAgentSdkService); +- const agentHostOTelService = disposables.add(instantiationService.createInstance(AgentHostOTelService)); +- diServices.set(IAgentHostOTelService, agentHostOTelService); - const copilotAgent = disposables.add(instantiationService.createInstance(CopilotAgent)); - agentService.registerProvider(copilotAgent); - log('CopilotAgent registered'); @@ -437,7 +445,7 @@ index 26cbf179..bd83ddf3 100644 - } } diff --git a/src/vs/platform/agentHost/test/node/mockAgent.ts b/src/vs/platform/agentHost/test/node/mockAgent.ts -index 1c48abc3..71258ee8 100644 +index 249aa4bc..6c110b8e 100644 --- a/src/vs/platform/agentHost/test/node/mockAgent.ts +++ b/src/vs/platform/agentHost/test/node/mockAgent.ts @@ -12,3 +12,2 @@ import { type ISyncedCustomization } from '../../common/agentPluginManager.js'; @@ -446,8 +454,8 @@ index 1c48abc3..71258ee8 100644 import { ProtectedResourceMetadata, type MessageAttachment, type ModelSelection } from '../../common/state/protocol/state.js'; @@ -16,3 +15,3 @@ import type { ResolveSessionConfigResult, SessionConfigCompletionsResult } from import { ActionType } from '../../common/state/sessionActions.js'; --import { CustomizationStatus, ResponsePartKind, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, parseSubagentSessionUri, type CustomizationRef, type PendingMessage, type SessionCustomization, type StringOrMarkdown, type ToolCallResult, type Turn } from '../../common/state/sessionState.js'; -+import { CustomizationStatus, ResponsePartKind, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, type CustomizationRef, type PendingMessage, type SessionCustomization, type StringOrMarkdown, type ToolCallResult, type Turn } from '../../common/state/sessionState.js'; +-import { CustomizationStatus, ResponsePartKind, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, parseSubagentSessionUri, type CustomizationRef, type PendingMessage, type SessionCustomization, type StringOrMarkdown, type ToolCallResult, type Turn, type UsageInfo } from '../../common/state/sessionState.js'; ++import { CustomizationStatus, ResponsePartKind, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, type CustomizationRef, type PendingMessage, type SessionCustomization, type StringOrMarkdown, type ToolCallResult, type Turn, type UsageInfo } from '../../common/state/sessionState.js'; import { hasKey } from '../../../../base/common/types.js'; @@ -65,10 +64,2 @@ export class MockAgent implements IAgent { @@ -479,7 +487,7 @@ index 1c48abc3..71258ee8 100644 - ]; - // Track pending permission requests -@@ -684,9 +664,2 @@ export class ScriptedMockAgent implements IAgent { +@@ -685,9 +665,2 @@ export class ScriptedMockAgent implements IAgent { async getSessionMessages(session: URI): Promise { - const subagentInfo = parseSubagentSessionUri(session); - if (subagentInfo) { diff --git a/upstream/insider.json b/upstream/insider.json index 22716e1fc86..97e8082e0bb 100644 --- a/upstream/insider.json +++ b/upstream/insider.json @@ -1,4 +1,4 @@ { - "tag": "1.120.0", - "commit": "0958016b2af9f09bb4257e0df4a95e2f90590f9f" + "tag": "1.121.0", + "commit": "987c9597516278c9fcf10d963a0592ce1384ab93" } From 3122286bcc856584b79c7cd3a89d224088e2468c Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Thu, 21 May 2026 23:06:59 +0200 Subject: [PATCH 57/79] fix(ui): find widget (#2862) --- patches/00-ui-custom-font.patch | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/patches/00-ui-custom-font.patch b/patches/00-ui-custom-font.patch index 9defc27ebc6..516bf112f19 100644 --- a/patches/00-ui-custom-font.patch +++ b/patches/00-ui-custom-font.patch @@ -1069,17 +1069,6 @@ index 00000000..62a49d53 + assert.strictEqual(FONT.tabsSize35, 35); + }); +}); -diff --git a/src/vs/editor/browser/widget/codeEditor/editor.css b/src/vs/editor/browser/widget/codeEditor/editor.css -index 638055a0..713d8a6c 100644 ---- a/src/vs/editor/browser/widget/codeEditor/editor.css -+++ b/src/vs/editor/browser/widget/codeEditor/editor.css -@@ -17,2 +17,6 @@ - -+/* .monaco-editor .editor-widget { -+ font-size: var(--vscode-workbench-font-size, 13px); -+} */ -+ - /* -------------------- Editor -------------------- */ diff --git a/src/vs/editor/contrib/find/browser/findWidget.css b/src/vs/editor/contrib/find/browser/findWidget.css index 62c6056c..3d893baa 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.css @@ -2653,12 +2642,12 @@ index eeddba00..4a0ef488 100644 + }, 'workbench.settings.editor': { diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts -index 10e2c3ed..74e3bbe9 100644 +index 10e2c3ed..47f28faa 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 import { RunOnceScheduler, timeout } from '../../base/common/async.js'; -+import { FONT, getFontSize, updateDefaultSize } from '../../base/common/font.js'; ++import { getFontSize, updateDefaultSize } from '../../base/common/font.js'; import { isFirefox, isSafari, isChrome } from '../../base/browser/browser.js'; @@ -19,3 +20,3 @@ import { Position, Parts, IWorkbenchLayoutService, positionToString } from '../s import { IStorageService, WillSaveStateReason, StorageScope, StorageTarget } from '../../platform/storage/common/storage.js'; @@ -2692,7 +2681,7 @@ index 10e2c3ed..74e3bbe9 100644 - } - const aliasing = configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>('workbench.fontAliasing'); -@@ -296,2 +303,31 @@ export class Workbench extends Layout { +@@ -296,2 +303,33 @@ export class Workbench extends Layout { + private fontFamily: string | undefined; + private updateFontFamily(configurationService: IConfigurationService) { @@ -2711,20 +2700,22 @@ index 10e2c3ed..74e3bbe9 100644 + } + } + ++ private fontSize: number | undefined; + private updateFontSize(configurationService: IConfigurationService) { + const configuredSize = getFontSize(configurationService, 'workbench.experimental.fontSize', 13); + -+ if (FONT.defaultSize === configuredSize) { ++ if (this.fontSize === configuredSize) { + return; + } + + updateDefaultSize(configuredSize); + -+ this.mainContainer.style.setProperty('--vscode-workbench-font-size', `${FONT.defaultSize}px`); ++ this.fontSize = configuredSize; ++ this.mainContainer.style.setProperty('--vscode-workbench-font-size', `${configuredSize}px`); + } + private restoreFontInfo(storageService: IStorageService, configurationService: IConfigurationService): void { -@@ -339,3 +375,6 @@ export class Workbench extends Layout { +@@ -339,3 +377,6 @@ export class Workbench extends Layout { // Apply font aliasing - this.updateFontAliasing(undefined, configurationService); + this.updateFontAliasing(configurationService); From 8e9a907880e8fff40cc84f8a902fb9c09b92277c Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 22 May 2026 00:59:37 +0200 Subject: [PATCH 58/79] feat: minAge in hours and add it to extensions (#2868) --- patches/12-update-add-cooldown.patch | 47 +++++++++++++++++-- ...sions.patch => 19-system-extensions.patch} | 6 +-- 2 files changed, 45 insertions(+), 8 deletions(-) rename patches/insider/{system-extensions.patch => 19-system-extensions.patch} (90%) diff --git a/patches/12-update-add-cooldown.patch b/patches/12-update-add-cooldown.patch index 8c0a59b42b1..dabea659125 100644 --- a/patches/12-update-add-cooldown.patch +++ b/patches/12-update-add-cooldown.patch @@ -1,18 +1,18 @@ diff --git a/src/vs/platform/update/common/update.config.contribution.ts b/src/vs/platform/update/common/update.config.contribution.ts -index 2c63e9e6..556f2403 100644 +index 2c63e9e6..e98ed806 100644 --- a/src/vs/platform/update/common/update.config.contribution.ts +++ b/src/vs/platform/update/common/update.config.contribution.ts @@ -65,2 +65,8 @@ configurationRegistry.registerConfiguration({ }, + 'update.minReleaseAge': { + type: 'integer', -+ default: 7, ++ default: 120, + scope: ConfigurationScope.APPLICATION, -+ description: localize('update.cooldown', "Configure how old an update need to be before installing it (in days)."), ++ description: localize('update.cooldown', "Control how old an update need to be before installing it (in hours)."), + }, 'update.enableWindowsBackgroundUpdates': { diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts -index 49720f6a..7ce182cd 100644 +index d776f408..70b86dce 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -459,3 +459,26 @@ export abstract class AbstractUpdateService implements IUpdateService { @@ -32,7 +32,7 @@ index 49720f6a..7ce182cd 100644 + return Promise.resolve(undefined); + } + -+ const age = Math.round(Math.abs(Date.now() - releaseDate.getTime()) / (1000 * 60 * 60 * 24)); ++ const age = Math.round(Math.abs(Date.now() - releaseDate.getTime()) / (1000 * 60 * 60)); + + this.logService.info(`update#isLatestVersion() - releaseAge: ${age}, minReleaseAge: ${minReleaseAge}`); + @@ -43,3 +43,40 @@ index 49720f6a..7ce182cd 100644 + return Promise.resolve(undefined); + } }) +diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +index 464b3948..121bd09f 100644 +--- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts ++++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +@@ -168,2 +168,9 @@ Registry.as(ConfigurationExtensions.Configuration) + }, ++ 'extensions.minReleaseAge': { ++ type: 'integer', ++ default: 48, ++ scope: ConfigurationScope.APPLICATION, ++ description: localize('extensions.minReleaseAge', "Control how old an extension need to be before auto-updating it (in hours)."), ++ tags: ['usesOnlineServices'] ++ }, + 'extensions.showRecommendationsOnlyOnDemand': { +diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +index 49b3ec6a..26f44277 100644 +--- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts ++++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +@@ -112,3 +112,4 @@ export class Extension implements IExtension { + @IFileService private readonly fileService: IFileService, +- @IProductService private readonly productService: IProductService ++ @IProductService private readonly productService: IProductService, ++ @IConfigurationService protected configurationService: IConfigurationService + ) { +@@ -348,3 +349,11 @@ export class Extension implements IExtension { + if (semver.gt(this.latestVersion, this.version)) { +- return true; ++ const minReleaseAge = this.configurationService.getValue('extensions.minReleaseAge'); ++ ++ if(minReleaseAge === 0) { ++ return true; ++ } ++ ++ const age = Math.round(Math.abs(Date.now() - this.gallery.lastUpdated) / (1000 * 60 * 60)); ++ ++ return age >= minReleaseAge; + } diff --git a/patches/insider/system-extensions.patch b/patches/insider/19-system-extensions.patch similarity index 90% rename from patches/insider/system-extensions.patch rename to patches/insider/19-system-extensions.patch index ec3712d76cd..1fad325c36d 100644 --- a/patches/insider/system-extensions.patch +++ b/patches/insider/19-system-extensions.patch @@ -1,12 +1,12 @@ diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts -index 58422886..a1eac31e 100644 +index 26f44277..896ab329 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -112,2 +112,3 @@ export class Extension implements IExtension { @IFileService private readonly fileService: IFileService, + // @ts-ignore - @IProductService private readonly productService: IProductService -@@ -341,3 +342,3 @@ export class Extension implements IExtension { + @IProductService private readonly productService: IProductService, +@@ -342,3 +343,3 @@ export class Extension implements IExtension { // Do not allow updating system extensions in stable - 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())) { From 384ab3138fd1710659fe4b7490d9e8210787c615 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 22 May 2026 00:59:57 +0200 Subject: [PATCH 59/79] fix(msi): add msi install on second method... (#2867) --- patches/11-update-use-github-release.patch | 185 +++++---------------- 1 file changed, 43 insertions(+), 142 deletions(-) diff --git a/patches/11-update-use-github-release.patch b/patches/11-update-use-github-release.patch index ffbdb9cb9bb..ea812d8bdd6 100644 --- a/patches/11-update-use-github-release.patch +++ b/patches/11-update-use-github-release.patch @@ -273,7 +273,7 @@ index 2be53f61..53904dab 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 a7851933..dafc4447 100644 +index bbbc3bde..372de75a 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 @@ -304,16 +304,7 @@ index a7851933..dafc4447 100644 + _updateType = UpdateType.Archive; + } } -@@ -57,2 +60,8 @@ function getUpdateType(): UpdateType { - -+async function pollUntil(fn: () => boolean, millis = 1000): Promise { -+ while (!fn()) { -+ await timeout(millis); -+ } -+} -+ - export class Win32UpdateService extends AbstractUpdateService implements IRelaunchHandler { -@@ -144,5 +153,6 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -144,5 +147,6 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun try { + const updateType = getUpdateType(); const updatingVersion = (await readFile(updatingVersionPath, 'utf8')).trim(); @@ -321,12 +312,12 @@ index a7851933..dafc4447 100644 - const updatePackagePath = await this.getUpdatePackagePath(updatingVersion); + const updatePackagePath = await this.getUpdatePackagePath(updatingVersion, updateType); if (await pfs.Promises.exists(updatePackagePath)) { -@@ -157,3 +167,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -157,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 -@@ -215,12 +225,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -176,12 +180,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - protected buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined { - let platform = `win32-${process.arch}`; @@ -357,14 +348,14 @@ index a7851933..dafc4447 100644 - return createUpdateURL(this.productService.updateUrl!, platform, quality, commit, options); + return createUpdateURL(this.productService, quality, process.platform, process.arch, target); } -@@ -232,6 +252,2 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -193,6 +207,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 -@@ -241,9 +257,13 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -202,9 +212,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) @@ -383,7 +374,7 @@ index a7851933..dafc4447 100644 - if (!update || !update.url || !update.version || !update.productVersion) { + if(!result) { // If we were checking for an overwrite update and found nothing newer, -@@ -259,2 +279,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -220,2 +234,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + const { lastest, update } = result; + @@ -393,17 +384,17 @@ index a7851933..dafc4447 100644 + } + if (updateType === UpdateType.Archive) { -@@ -276,3 +303,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -237,3 +258,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun return this.cleanup(update.version).then(() => { - return this.getUpdatePackagePath(update.version).then(updatePackagePath => { + return this.getUpdatePackagePath(update.version, updateType).then(updatePackagePath => { return pfs.Promises.exists(updatePackagePath).then(exists => { -@@ -284,3 +311,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -245,3 +266,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 => { -@@ -329,8 +356,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -290,8 +311,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun }) - .then(undefined, err => { - this.telemetryService.publicLog2<{ messageHash: string }, UpdateErrorClassification>('update:error', { messageHash: String(hash(String(err))) }); @@ -415,7 +406,7 @@ index a7851933..dafc4447 100644 - const message: string | undefined = explicit ? (err.message || err) : undefined; + const message: string | undefined = explicit ? (error.message || error) : undefined; -@@ -354,5 +380,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -315,5 +335,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - private async getUpdatePackagePath(version: string): Promise { + private async getUpdatePackagePath(version: string, type: UpdateType): Promise { @@ -425,12 +416,12 @@ index a7851933..dafc4447 100644 + + return path.join(cachePath, `${this.productService.nameShort.replaceAll(/\s/g, '')}-${this.productService.quality}-${version}.${extension}`); } -@@ -360,3 +388,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -321,3 +343,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun private async cleanup(exceptVersion: string | null = null): Promise { - const filter = exceptVersion ? (one: string) => !(new RegExp(`${this.productService.quality}-${exceptVersion}\\.exe$`).test(one)) : () => true; + const filter = exceptVersion ? (one: string) => !(new RegExp(`${this.productService.quality}-${exceptVersion}\\.(exe|msi)$`).test(one)) : () => true; -@@ -394,77 +422,100 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -355,14 +377,10 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun await pfs.Promises.writeFile(this.availableUpdate.updateFilePath, 'flag'); - const child = spawn(this.availableUpdate.packagePath, - [ @@ -449,34 +440,15 @@ index a7851933..dafc4447 100644 + + const type = getUpdateType(); + if (type == UpdateType.WindowsInstaller) { ++ this.logService.info(`update#doApplyUpdate - msiexec.exe /i ${this.availableUpdate.packagePath}`); ++ + child = spawn('msiexec.exe', ['/i', this.availableUpdate.packagePath], { detached: true, - stdio: ['ignore', 'ignore', 'ignore'], -- windowsVerbatimArguments: true, -- env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } +@@ -371,4 +389,23 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } - } - ); + }); - -- // Track the process so we can cancel it if needed -- this.availableUpdate.updateProcess = child; -+ child.once('exit', () => { -+ this.availableUpdate = undefined; -+ this.setState(State.Idle(getUpdateType())); -+ }); - -- child.once('exit', () => { -- this.availableUpdate = undefined; -- this.setState(State.Idle(getUpdateType())); -- }); -+ const readyMutexName = `${this.productService.win32MutexName}-ready`; -+ const mutex = await import('@vscode/windows-mutex'); - -- const readyMutexName = `${this.productService.win32MutexName}-ready`; -- const mutex = await import('@vscode/windows-mutex'); -+ // poll for mutex-ready -+ pollUntil(() => mutex.isActive(readyMutexName)) -+ .then(() => this.setState(State.Ready(update, explicit, this._overwrite))); + } else { + child = spawn(this.availableUpdate.packagePath, + [ @@ -496,101 +468,30 @@ index a7851933..dafc4447 100644 + env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } + } + ); - -- this.updateCancellationTokenSource?.dispose(true); -- const cts = this.updateCancellationTokenSource = new CancellationTokenSource(); -- const token = cts.token; -+ // Track the process so we can cancel it if needed -+ this.availableUpdate.updateProcess = child; - -- const poll = async () => { -- while (this.state.type === StateType.Updating && !token.isCancellationRequested) { -- if (mutex.isActive(readyMutexName)) { -- this.setState(State.Ready(update, explicit, this._overwrite)); -- return; -- } -+ child.once('exit', () => { -+ this.availableUpdate = undefined; -+ this.setState(State.Idle(getUpdateType())); -+ }); - -- try { -- const progressContent = await readFile(progressFilePath, 'utf8'); -- if (!token.isCancellationRequested) { -- const [currentStr, maxStr] = progressContent.split(','); -- const currentProgress = parseInt(currentStr, 10); -- const maxProgress = parseInt(maxStr, 10); -- if (!isNaN(currentProgress) && !isNaN(maxProgress) && this.state.type === StateType.Updating) { -- if (this.state.currentProgress !== currentProgress || this.state.maxProgress !== maxProgress) { -- this.setState(State.Updating(update, explicit, currentProgress, maxProgress)); -+ const readyMutexName = `${this.productService.win32MutexName}-ready`; -+ const mutex = await import('@vscode/windows-mutex'); -+ -+ this.updateCancellationTokenSource?.dispose(true); -+ const cts = this.updateCancellationTokenSource = new CancellationTokenSource(); -+ const token = cts.token; -+ -+ const poll = async () => { -+ while (this.state.type === StateType.Updating && !token.isCancellationRequested) { -+ if (mutex.isActive(readyMutexName)) { -+ this.setState(State.Ready(update, explicit, this._overwrite)); -+ return; -+ } -+ -+ try { -+ const progressContent = await readFile(progressFilePath, 'utf8'); -+ if (!token.isCancellationRequested) { -+ const [currentStr, maxStr] = progressContent.split(','); -+ const currentProgress = parseInt(currentStr, 10); -+ const maxProgress = parseInt(maxStr, 10); -+ if (!isNaN(currentProgress) && !isNaN(maxProgress) && this.state.type === StateType.Updating) { -+ if (this.state.currentProgress !== currentProgress || this.state.maxProgress !== maxProgress) { -+ this.setState(State.Updating(update, explicit, currentProgress, maxProgress)); -+ } - } - } -+ } catch { -+ // Progress file may not exist yet or be locked, ignore - } -- } catch { -- // Progress file may not exist yet or be locked, ignore -- } -- -- await timeout(500); -- } -- }; - -- const cancelTimeout = new ProcessTimeRunOnceScheduler(() => { -- this.logService.warn('update#doApplyUpdate: polling timed out waiting for update to be ready'); -- this.setState(State.Idle(getUpdateType(), 'Update did not complete within expected time')); -- }, 60 * 60 * 1000); -- -- // Poll for progress and ready mutex for 1 hour. -- cancelTimeout.schedule(); -- poll().finally(() => { -- cancelTimeout.dispose(); -- if (this.updateCancellationTokenSource === cts) { -- this.updateCancellationTokenSource = undefined; -- } -- cts.dispose(); -- }); -+ await timeout(500); -+ } -+ }; -+ -+ const cancelTimeout = new ProcessTimeRunOnceScheduler(() => { -+ this.logService.warn('update#doApplyUpdate: polling timed out waiting for update to be ready'); -+ this.setState(State.Idle(getUpdateType(), 'Update did not complete within expected time')); -+ }, 60 * 60 * 1000); -+ -+ // Poll for progress and ready mutex for 1 hour. -+ cancelTimeout.schedule(); -+ poll().finally(() => { -+ cancelTimeout.dispose(); -+ if (this.updateCancellationTokenSource === cts) { -+ this.updateCancellationTokenSource = undefined; -+ } -+ cts.dispose(); -+ }); + } - } + +@@ -490,7 +527,19 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + } else { +- spawn(this.availableUpdate.packagePath, ['/silent', '/log', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], { +- detached: true, +- stdio: ['ignore', 'ignore', 'ignore'], +- env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } +- }); ++ const type = getUpdateType(); ++ if (type == UpdateType.WindowsInstaller) { ++ this.logService.info(`update#doQuitAndInstall - msiexec.exe /i ${this.availableUpdate.packagePath}`); ++ ++ spawn('msiexec.exe', ['/i', this.availableUpdate.packagePath], { ++ detached: true, ++ stdio: ['ignore', 'ignore', 'ignore'], ++ env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } ++ }); ++ } ++ else { ++ spawn(this.availableUpdate.packagePath, ['/silent', '/log', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], { ++ detached: true, ++ stdio: ['ignore', 'ignore', 'ignore'], ++ env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } ++ }); ++ } + } From f5857abcc0a8e869381137a049736aa300f33c8d Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 22 May 2026 17:12:34 +0200 Subject: [PATCH 60/79] build: prepare for stable build --- announcements-extra.json | 11 ++++++++--- upstream/stable.json | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/announcements-extra.json b/announcements-extra.json index 7ca48dc103d..42a46f8fa16 100644 --- a/announcements-extra.json +++ b/announcements-extra.json @@ -1,7 +1,12 @@ [ { - "id": "#2668", - "title": "[Windows] broken update on 1.107, need manual update", - "url": "https://github.com/VSCodium/vscodium/issues/2668" + "id": "#2836", + "title": "Securing VSCodium", + "url": "https://github.com/VSCodium/vscodium/discussions/2836" + }, + { + "id": "#2871", + "title": "Use minReleaseAge with auto-update", + "url": "https://github.com/VSCodium/vscodium/discussions/2871" } ] diff --git a/upstream/stable.json b/upstream/stable.json index 0909ccb60e9..97e8082e0bb 100644 --- a/upstream/stable.json +++ b/upstream/stable.json @@ -1,4 +1,4 @@ { - "tag": "1.116.0", - "commit": "560a9dba96f961efea7b1612916f89e5d5d4d679" + "tag": "1.121.0", + "commit": "987c9597516278c9fcf10d963a0592ce1384ab93" } From 393039df1b574a9293ec5f478d074954b25cd051 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 24 May 2026 11:03:05 +0200 Subject: [PATCH 61/79] docs: add section about use of AI --- CONTRIBUTING.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 81a95f6551b..2fc0756cfef 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,6 +12,16 @@ This project and everyone participating in it is governed by the [VSCodium Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. +## Use of AI + +We welcome use of AI tools to help draft discussions, issues, or code, but please follow these rules: + +- Use AI tools responsibly and disclose their use. +- Ensure all content passes a human review for authenticity and quality. +- Be concise. Do not write verbose discussions, issues or PR. + +Discussions, issues or PR that consist solely of unvetted AI outputs may be closed at the maintainer's discretion. + ## Reporting Bugs ### Before Submitting an Issue From eb5d6e23a9abe76460a22e41cccacf7a7d5fea96 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 24 May 2026 11:05:13 +0200 Subject: [PATCH 62/79] docs: remove build status --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 9dfae9e8715..e3246182688 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,6 @@ [![codium](https://snapcraft.io//codium/badge.svg)](https://snapcraft.io/codium) [![codium](https://snapcraft.io//codium/trending.svg?name=0)](https://snapcraft.io/codium) -[![build status (linux)](https://img.shields.io/github/actions/workflow/status/VSCodium/vscodium/stable-linux.yml?branch=master&label=build%28linux%29)](https://github.com/VSCodium/vscodium/actions/workflows/stable-linux.yml?query=branch%3Amaster) -[![build status (macos)](https://img.shields.io/github/actions/workflow/status/VSCodium/vscodium/stable-macos.yml?branch=master&label=build%28macOS%29)](https://github.com/VSCodium/vscodium/actions/workflows/stable-macos.yml?query=branch%3Amaster) -[![build status (windows)](https://img.shields.io/github/actions/workflow/status/VSCodium/vscodium/stable-windows.yml?branch=master&label=build%28windows%29)](https://github.com/VSCodium/vscodium/actions/workflows/stable-windows.yml?query=branch%3Amaster) - **This is not a fork. This is a repository of scripts to automatically build [Microsoft's `vscode` repository](https://github.com/microsoft/vscode) into freely-licensed binaries with a community-driven default configuration.** From c29e1f6524f3b675c8226c06aecd49914dcf1114 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Thu, 2 Jul 2026 12:16:02 +0200 Subject: [PATCH 63/79] feat(1.126): update patches (#2903) --- .github/workflows/ci-build-linux.yml | 7 - .github/workflows/publish-insider-linux.yml | 7 - .github/workflows/publish-stable-linux.yml | 7 - .nvmrc | 2 +- README.md | 8 +- build/linux/check_tags.sh | 52 - build/linux/deps.sh | 2 - build/linux/loong64/ripgrep.sh | 9 +- build/linux/package_bin.sh | 6 +- build/linux/package_reh.sh | 17 +- build/linux/riscv64/electron.sh | 2 +- build/linux/riscv64/electron.sha256sums | 22 +- build/linux/riscv64/ripgrep.sh | 9 +- build_cli.sh | 14 +- dev/patch.sh | 18 + dev/update_patches.sh | 11 + patches/00-brand-remove-branding.patch | 94 +- patches/00-build-disable-mangle.patch | 6 +- patches/00-build-fix-npm-preinstall.patch | 14 - patches/00-community-add-announcements.patch | 18 +- patches/00-remote-add-url.patch | 17 +- patches/00-ui-custom-font.patch | 206 +-- patches/00-vsce-use-custom-lib.patch | 60 +- patches/11-update-use-github-release.patch | 121 +- patches/20-keymap-use-custom-lib.patch | 36 +- patches/21-policy-use-custom-lib.patch | 113 +- patches/40-cli-use-reh-archive.patch | 73 +- patches/50-build-improve-gulp-tasks.patch | 20 +- patches/51-build-disable-non-ascii.patch | 23 + ...-it.json => 52-ext-copilot-remove-it.json} | 27 +- patches/52-ext-copilot-remove-it.patch | 499 ------ patches/53-ext-copilot-remove-it.patch | 1489 +++++++++++++++++ patches/81-ui-disable-onboarding.patch | 20 +- patches/linux/31-build-fix-dependencies.patch | 402 +++-- patches/linux/41-arch-add-support.patch | 42 +- patches/linux/42-arch-add-ppc64le.patch | 87 +- .../windows/00-build-replace-signature.patch | 185 ++ prepare_vscode.sh | 2 - release_notes.md | 30 - .../insider/snap/local/bin/electron-launch | 2 - .../stable/snap/local/bin/electron-launch | 2 - upstream/insider.json | 4 +- 42 files changed, 2451 insertions(+), 1334 deletions(-) delete mode 100644 patches/00-build-fix-npm-preinstall.patch create mode 100644 patches/51-build-disable-non-ascii.patch rename patches/{51-ext-copilot-remove-it.json => 52-ext-copilot-remove-it.json} (62%) delete mode 100644 patches/52-ext-copilot-remove-it.patch create mode 100644 patches/53-ext-copilot-remove-it.patch create mode 100644 patches/windows/00-build-replace-signature.patch diff --git a/.github/workflows/ci-build-linux.yml b/.github/workflows/ci-build-linux.yml index 22a256c8fab..a1cf4d46b07 100644 --- a/.github/workflows/ci-build-linux.yml +++ b/.github/workflows/ci-build-linux.yml @@ -115,10 +115,6 @@ jobs: vscode_arch: arm64 npm_arch: arm64 image: vscodium/vscodium-linux-build-agent:focal-arm64 - - slug: ARM32 - vscode_arch: armhf - npm_arch: arm - image: vscodium/vscodium-linux-build-agent:focal-armhf - slug: RISCV64 vscode_arch: riscv64 npm_arch: riscv64 @@ -214,9 +210,6 @@ jobs: - slug: ARM64 vscode_arch: arm64 npm_arch: arm64 - - slug: ARM32 - vscode_arch: armhf - npm_arch: arm - slug: PPC64 vscode_arch: ppc64le npm_arch: ppc64 diff --git a/.github/workflows/publish-insider-linux.yml b/.github/workflows/publish-insider-linux.yml index 4f09aa50dc4..55ad8bd31c9 100644 --- a/.github/workflows/publish-insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -135,10 +135,6 @@ jobs: vscode_arch: arm64 npm_arch: arm64 image: vscodium/vscodium-linux-build-agent:focal-arm64 - - slug: ARM32 - vscode_arch: armhf - npm_arch: arm - image: vscodium/vscodium-linux-build-agent:focal-armhf - slug: RISCV64 vscode_arch: riscv64 npm_arch: riscv64 @@ -247,9 +243,6 @@ jobs: - slug: ARM64 vscode_arch: arm64 npm_arch: arm64 - - slug: ARM32 - vscode_arch: armhf - npm_arch: arm - slug: PPC64 vscode_arch: ppc64le npm_arch: ppc64 diff --git a/.github/workflows/publish-stable-linux.yml b/.github/workflows/publish-stable-linux.yml index d2f76a630ab..d778cef8a08 100644 --- a/.github/workflows/publish-stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -136,10 +136,6 @@ jobs: vscode_arch: arm64 npm_arch: arm64 image: vscodium/vscodium-linux-build-agent:focal-arm64 - - slug: ARM32 - vscode_arch: armhf - npm_arch: arm - image: vscodium/vscodium-linux-build-agent:focal-armhf - slug: RISCV64 vscode_arch: riscv64 npm_arch: riscv64 @@ -248,9 +244,6 @@ jobs: - slug: ARM64 vscode_arch: arm64 npm_arch: arm64 - - slug: ARM32 - vscode_arch: armhf - npm_arch: arm - slug: PPC64 vscode_arch: ppc64le npm_arch: ppc64 diff --git a/.nvmrc b/.nvmrc index 32a2d7bd80d..5bf4400f229 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.22.1 +24.15.0 diff --git a/README.md b/README.md index e3246182688..f0b8143d472 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ - [Why Does This Exist](#why) - [More Info](#more-info) - [Supported Platforms](#supported-platforms) +- [Previously Supported Platforms](#previously-supported-platforms) ## Download/Install @@ -179,13 +180,18 @@ The minimal version is limited by the core component Electron, you may want to c - [x] macOS (`zip`, `dmg`) macOS 12 or newer arm64 - [x] GNU/Linux x64 (`deb`, `rpm`, `AppImage`, `snap`, `tar.gz`) - [x] GNU/Linux arm64 (`deb`, `rpm`, `snap`, `tar.gz`) -- [x] GNU/Linux armhf (`deb`, `rpm`, `tar.gz`) - [x] GNU/Linux riscv64 (`tar.gz`) - [x] GNU/Linux loong64 (`tar.gz`) - [x] GNU/Linux ppc64le (`tar.gz`) - [x] Windows 10 / Server 2012 R2 or newer x64 - [x] Windows 10 / Server 2012 R2 or newer arm64 +## Previously Supported Platforms + +- GNU/Linux armhf: + - Latest available: [v1.121.03429](https://github.com/VSCodium/vscodium/releases/tag/1.121.03429). + - Breaking point: `node-v24`. + ## Special thanks diff --git a/build/linux/check_tags.sh b/build/linux/check_tags.sh index 8962ea897fb..204a1f2a8fc 100644 --- a/build/linux/check_tags.sh +++ b/build/linux/check_tags.sh @@ -79,58 +79,6 @@ else fi fi - # linux-armhf - if [[ "${VSCODE_ARCH}" == "armhf" || "${CHECK_ALL}" == "yes" ]]; then - if [[ -z $( contains "armhf.deb" ) ]]; then - echo "Building on Linux arm because we have no DEB" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_DEB="no" - fi - - if [[ -z $( contains "armv7hl.rpm" ) ]]; then - echo "Building on Linux arm because we have no RPM" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_RPM="no" - fi - - if [[ -z $( contains "${APP_NAME}-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm because we have no TAR" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_TAR="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm because we have no REH archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH="no" - fi - - if [[ "${CHECK_REH}" != "no" && -z $( contains "${APP_NAME_LC}-reh-web-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm because we have no REH-web archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_REH_WEB="no" - fi - - export SHOULD_BUILD_APPIMAGE="no" - - if [[ -z $( contains "${APP_NAME_LC}-cli-linux-armhf-${RELEASE_VERSION}.tar.gz" ) ]]; then - echo "Building on Linux arm because we have no CLI archive" - export SHOULD_BUILD="yes" - else - export SHOULD_BUILD_CLI="no" - fi - - - if [[ "${SHOULD_BUILD}" != "yes" ]]; then - echo "Already have all the Linux arm builds" - fi - fi - # linux-ppc64le if [[ "${VSCODE_ARCH}" == "ppc64le" || "${CHECK_ALL}" == "yes" ]]; then export SHOULD_BUILD_APPIMAGE="no" diff --git a/build/linux/deps.sh b/build/linux/deps.sh index 8af94f5d06d..e6ca9b2c44c 100755 --- a/build/linux/deps.sh +++ b/build/linux/deps.sh @@ -8,6 +8,4 @@ sudo apt-get install -y libkrb5-dev if [[ "${VSCODE_ARCH}" == "arm64" ]]; then sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu crossbuild-essential-arm64 -elif [[ "${VSCODE_ARCH}" == "armhf" ]]; then - sudo apt-get install -y gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf crossbuild-essential-armhf fi diff --git a/build/linux/loong64/ripgrep.sh b/build/linux/loong64/ripgrep.sh index a9bb44338aa..d455ec6d6ef 100755 --- a/build/linux/loong64/ripgrep.sh +++ b/build/linux/loong64/ripgrep.sh @@ -8,11 +8,16 @@ if [ "$#" -ne 1 ]; then exit 1 fi -RG_PATH="$1/@vscode/ripgrep/bin/rg" +RG_PATH="$1/@vscode/ripgrep-universal/bin/linux-loong64/rg" RG_VERSION="14.1.1" echo "Replacing ripgrep binary with loong64 one" -rm "${RG_PATH}" +if [ -f "${RG_PATH}" ]; then + rm "${RG_PATH}" +else + mkdir -p "$(dirname "${RG_PATH}")" +fi + curl --silent --fail -L https://github.com/darkyzhou/ripgrep-loongarch64-musl/releases/download/${RG_VERSION}/rg -o "${RG_PATH}" chmod +x "${RG_PATH}" diff --git a/build/linux/package_bin.sh b/build/linux/package_bin.sh index 8db1c93e9b9..e31c4a55a27 100755 --- a/build/linux/package_bin.sh +++ b/build/linux/package_bin.sh @@ -23,7 +23,7 @@ export VSCODE_PLATFORM='linux' export VSCODE_SKIP_NODE_VERSION_CHECK=1 export VSCODE_SYSROOT_PREFIX='-glibc-2.28-gcc-10.5.0' -if [[ "${VSCODE_ARCH}" == "arm64" || "${VSCODE_ARCH}" == "armhf" ]]; then +if [[ "${VSCODE_ARCH}" == "arm64" ]]; then export VSCODE_SKIP_SYSROOT=1 # export USE_GNUPP2A=1 elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then @@ -71,6 +71,10 @@ if [[ -f "../build/linux/${VSCODE_ARCH}/electron.sh" ]]; then if [[ "${ELECTRON_VERSION}" != "${TARGET}" ]]; then # Force version replace "s|target=\"${TARGET}\"|target=\"${ELECTRON_VERSION}\"|" .npmrc + + cat .npmrc + + export VSCODE_ELECTRON_VERSION="${ELECTRON_VERSION}" fi fi diff --git a/build/linux/package_reh.sh b/build/linux/package_reh.sh index e7d23d99000..731584f5006 100755 --- a/build/linux/package_reh.sh +++ b/build/linux/package_reh.sh @@ -18,7 +18,7 @@ cd vscode || { echo "'vscode' dir not found"; exit 1; } GLIBC_VERSION="2.28" GLIBCXX_VERSION="3.4.26" -NODE_VERSION="22.21.1" +NODE_VERSION="24.15.0" export VSCODE_NODEJS_URLROOT='/download/release' export VSCODE_NODEJS_URLSUFFIX='' @@ -32,13 +32,6 @@ elif [[ "${VSCODE_ARCH}" == "arm64" ]]; then VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME="vscodium/vscodium-linux-build-agent:focal-devtoolset-arm64" - export VSCODE_SKIP_SYSROOT=1 - export USE_GNUPP2A=1 -elif [[ "${VSCODE_ARCH}" == "armhf" ]]; then - EXPECTED_GLIBC_VERSION="2.30" - - VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME="vscodium/vscodium-linux-build-agent:focal-devtoolset-armhf" - export VSCODE_SKIP_SYSROOT=1 export USE_GNUPP2A=1 elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then @@ -50,13 +43,11 @@ elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then export VSCODE_SYSROOT_REPOSITORY='VSCodium/vscode-linux-build-agent' export VSCODE_SYSROOT_VERSION='20240129-253798' elif [[ "${VSCODE_ARCH}" == "riscv64" ]]; then - NODE_VERSION="22.21.1" VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME="vscodium/vscodium-linux-build-agent:focal-devtoolset-riscv64" export VSCODE_SKIP_SETUPENV=1 export VSCODE_NODEJS_SITE='https://unofficial-builds.nodejs.org' elif [[ "${VSCODE_ARCH}" == "loong64" ]]; then - NODE_VERSION="22.21.1" VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME="vscodium/vscodium-linux-build-agent:beige-devtoolset-loong64" export VSCODE_SKIP_SETUPENV=1 @@ -160,6 +151,12 @@ node build/npm/preinstall.ts mv .npmrc .npmrc.bak cp ../npmrc .npmrc +echo "+ /usr/lib/node_modules/npm/node_modules/node-gyp/addon.gypi" +cat /usr/lib/node_modules/npm/node_modules/node-gyp/addon.gypi || true + +echo "+ ${HOME}/.gyp/include.gypi" +cat "${HOME}/.gyp/include.gypi" || true + for i in {1..5}; do # try 5 times npm ci && break if [[ $i == 5 ]]; then diff --git a/build/linux/riscv64/electron.sh b/build/linux/riscv64/electron.sh index 277200b1442..f456341deb2 100755 --- a/build/linux/riscv64/electron.sh +++ b/build/linux/riscv64/electron.sh @@ -2,5 +2,5 @@ set -ex -export ELECTRON_VERSION="39.2.7" +export ELECTRON_VERSION="42.3.3" export VSCODE_ELECTRON_TAG="v${ELECTRON_VERSION}.riscv1" diff --git a/build/linux/riscv64/electron.sha256sums b/build/linux/riscv64/electron.sha256sums index 5c501df38b4..64beebca56c 100644 --- a/build/linux/riscv64/electron.sha256sums +++ b/build/linux/riscv64/electron.sha256sums @@ -1,11 +1,11 @@ -6759ef2bd69a2e31a3f0e17c4e4c0bf239b54f08525572201ae7760851168487 *chromedriver-v39.2.7-linux-riscv64.zip -89562e30982d8ac71fbc1e0f549a4a6e19abd5cb98ea904d5f3cfceb7c61e582 *electron-v39.2.7-linux-riscv64-debug.tar.zst -71c6f265a2ef065f478ef910a06466a21009c02783dcb7053767549a6dbeb80d *electron-v39.2.7-linux-riscv64-symbols.tar.zst -136804dbd04f1c6b9a6047c4e7bb648876214ff453b62fb3bdc81505b6f5aab2 *electron-v39.2.7-linux-riscv64.zip -1059d6cb97b87464b3bd415bb5f96fceaf91d6e3af1c9733724ab9f2e14e2a08 *ffmpeg-v39.2.7-linux-riscv64.zip -224a84d4aaceb5ed8be3c4f65f8404d492dc86ded8ab336c2562dfdd21752068 *hunspell-dictionaries.zip -b6cb4f8902aad5de811efd106ddbdbca79e43cb7c8fa67f7eeddaedf2efd82d5 *libcxx-headers.zip -0664e200ec1eab1ce1957bc6e17ad89f6c0d4d904a9492d64fcf4175cd81e537 *libcxx-objects-v39.2.7-linux-riscv64.zip -96f9b66be7ff11e79ec2e781a0025938eb5ef97cbab429c05e9b45d24d421abf *libcxxabi-headers.zip -be0774857454f81b9407f6b941200f8843a0b3ecb86e2bb072209ef9f9bfe74a *mksnapshot-v39.2.7-linux-riscv64.zip -a8fca541e8f9a18de73c78f6862cbf439723c80c5eb60fe50405dfb751d2ee13 *node-v39.2.7-headers.tar.gz +2b0b61329886cf355a86126b4cac7869104340b530034c67c2572dc6ea41d6f1 *chromedriver-v42.3.3-linux-riscv64.zip +b27d12271f03df5591a3cc36e2242976bccc6ff8e8a5ec6ba19b3c64fca593c2 *electron-v42.3.3-linux-riscv64-debug.tar.zst +c171fa2342d1c5ca2ac827214ed9d9f075195a6f84122c69e2096820c89ea435 *electron-v42.3.3-linux-riscv64-symbols.tar.zst +f0d07addb8094aa216c9e1f75abce4eef53b6274c544e16abb1454ee613fdab8 *electron-v42.3.3-linux-riscv64.zip +a5f12bf3e256b1cee3288847d0ddfde2736dbff57a7b9cdce3e13944735cdbfc *ffmpeg-v42.3.3-linux-riscv64.zip +bad30deb9d83f85af01bde7745165827e8849366ec4f763929025f461f185372 *hunspell-dictionaries.zip +be9ebc3052cb8069321364839f06a793d8e27e04495ba3fef9531f90aa6a577d *libcxx-headers.zip +c7c7cc05068261600503eb0a4897b985fe3a0b3ad59853f2400134e6715e3872 *libcxx-objects-v42.3.3-linux-riscv64.zip +f17024e72c182f76e75cdfabf524c0044aebccc018d162729c940d4c2657e14a *libcxxabi-headers.zip +53974b8ca250c32a032cc0e2603ae179b86564d4f0f23c6d4a2b6eeac64da3e4 *mksnapshot-v42.3.3-linux-riscv64.zip +46545ab9ca90deb710e8811dee9cf4022c811d520d1a702cf36f6958d91aa119 *node-v42.3.3-headers.tar.gz diff --git a/build/linux/riscv64/ripgrep.sh b/build/linux/riscv64/ripgrep.sh index 67e0d005302..e7f6385a8e2 100755 --- a/build/linux/riscv64/ripgrep.sh +++ b/build/linux/riscv64/ripgrep.sh @@ -8,11 +8,16 @@ if [ "$#" -ne 1 ]; then exit 1 fi -RG_PATH="$1/@vscode/ripgrep/bin/rg" +RG_PATH="$1/@vscode/ripgrep-universal/bin/linux-riscv64/rg" RG_VERSION="14.1.1-4" echo "Replacing ripgrep binary with riscv64 one" -rm "${RG_PATH}" +if [ -f "${RG_PATH}" ]; then + rm "${RG_PATH}" +else + mkdir -p "$(dirname "${RG_PATH}")" +fi + curl --silent --fail -L https://github.com/riscv-forks/ripgrep-riscv64-prebuilt/releases/download/${RG_VERSION}/rg -o "${RG_PATH}" chmod +x "${RG_PATH}" diff --git a/build_cli.sh b/build_cli.sh index 21d3b46a65b..84c87813ede 100755 --- a/build_cli.sh +++ b/build_cli.sh @@ -62,25 +62,13 @@ else if [[ "${VSCODE_ARCH}" == "arm64" ]]; then VSCODE_CLI_TARGET="aarch64-unknown-linux-gnu" - + if [[ "${CI_BUILD}" != "no" ]]; then export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ export PKG_CONFIG_ALLOW_CROSS=1 fi - elif [[ "${VSCODE_ARCH}" == "armhf" ]]; then - VSCODE_CLI_TARGET="armv7-unknown-linux-gnueabihf" - - export OPENSSL_LIB_DIR="$( pwd )/openssl/out/arm-linux/lib" - export OPENSSL_INCLUDE_DIR="$( pwd )/openssl/out/arm-linux/include" - - if [[ "${CI_BUILD}" != "no" ]]; then - export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc - export CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc - export CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ - export PKG_CONFIG_ALLOW_CROSS=1 - fi elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then VSCODE_CLI_TARGET="powerpc64le-unknown-linux-gnu" diff --git a/dev/patch.sh b/dev/patch.sh index da264c97abb..4eb708b1eb4 100755 --- a/dev/patch.sh +++ b/dev/patch.sh @@ -52,6 +52,9 @@ if [[ "${FILE}" != "../patches/helper/settings.patch" ]]; then BASENAME=$(basename "${FILE}") DIRNAME=$(dirname "${FILE}") + echo $FILE + echo $BASENAME + echo $DIRNAME if [[ "${BASENAME}" =~ ^([0-9])([1-9])(-.*)\.patch$ ]]; then GROUP_ID="${BASH_REMATCH[1]}" @@ -91,8 +94,23 @@ if [[ "${FILE}" != "../patches/helper/settings.patch" ]]; then normalize_file "${CANDIDATE}" git apply --reject "${FILE}" + + NOT_FOUND=0 fi done + + if (( $NOT_FOUND )); then + for CANDIDATE in "${DIRNAME}/../../${GROUP_ID}${I}-"*.patch; do + if [[ -f "$CANDIDATE" ]]; then + echo "Candidate: ${CANDIDATE}" + normalize_file "${CANDIDATE}" + + git apply --reject "${FILE}" + + NOT_FOUND=0 + fi + done + fi fi done fi diff --git a/dev/update_patches.sh b/dev/update_patches.sh index c667cdac37c..5a559ed4013 100755 --- a/dev/update_patches.sh +++ b/dev/update_patches.sh @@ -158,9 +158,20 @@ for FILE in ../patches/*.patch; do ENDNAME="${BASH_REMATCH[3]}" for ((I = 0; I < INDEX; I++)); do + NOT_FOUND=1 + for CANDIDATE in "${DIRNAME}/${GROUP_ID}${I}-"*.patch; do if [[ -f "$CANDIDATE" ]]; then ADDITIONAL_FILES+=("$CANDIDATE") + NOT_FOUND=0 + fi + + if (( $NOT_FOUND )); then + for CANDIDATE in "${DIRNAME}/${GROUP_ID}${I}-"*.json; do + if [[ -f "$CANDIDATE" ]]; then + apply_actions "${CANDIDATE}" + fi + done fi done done diff --git a/patches/00-brand-remove-branding.patch b/patches/00-brand-remove-branding.patch index 7755070a3d0..98fa2c0fead 100644 --- a/patches/00-brand-remove-branding.patch +++ b/patches/00-brand-remove-branding.patch @@ -100,14 +100,14 @@ index 1be4d0e1..647b1474 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 6249500e..6d89804e 100644 +index a6ab287a..6cd79323 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 const relativeBadgeUrlRequiresHttpsRepository = l10n.t("Relative badge URLs require a repository with HTTPS protocol to be specified in this package.json."); -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 VS Code team."); +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."); - + const apiProposalVersionNotSupported = l10n.t("API proposal versions are no longer supported. Remove the '@' suffix."); diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 147a75f9..565109ec 100644 --- a/extensions/git/package.nls.json @@ -370,7 +370,7 @@ index 2ee1169f..0489215c 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 fad11bc9..64e48ce4 100644 +index 032dd35d..e38bc488 100644 --- a/extensions/notebook-renderers/package.json +++ b/extensions/notebook-renderers/package.json @@ -22,3 +22,3 @@ @@ -379,7 +379,7 @@ index fad11bc9..64e48ce4 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 1235a551..b647563b 100644 +index 5c77708d..6f092001 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -2,3 +2,3 @@ @@ -444,7 +444,7 @@ index a697f8c0..2ba1dab8 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 8d99637d..a8d99869 100644 +index 1154a29c..08545028 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 { @@ -490,7 +490,7 @@ index ae18dff7..9d158d2b 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 7403fed1..17c069a8 100644 +index d56cbf59..47bdd32c 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -2,3 +2,3 @@ @@ -499,7 +499,7 @@ index 7403fed1..17c069a8 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 1abff3d9..5d874614 100644 +index f3253b70..13d4ee3e 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -2,3 +2,3 @@ @@ -525,7 +525,7 @@ index 313fe87c..24ba85c7 100644 + "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 02607acc..414b7dc3 100644 +index 9b0de50d..b0318720 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -2,3 +2,3 @@ @@ -568,24 +568,24 @@ index a351aa77..c32e3efd 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 373c8809..24568091 100644 +index 5c9b4785..d35897b2 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts -@@ -211,3 +211,3 @@ export function activate(context: vscode.ExtensionContext) { +@@ -215,3 +215,3 @@ export function activate(context: vscode.ExtensionContext) { const serverBin = path.join(remoteDataDir, 'bin'); - progress.report({ message: 'Installing VSCode Server' }); + 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 42f599c9..1f90b59e 100644 +index f70cf87e..c6ad520f 100644 --- a/src/main.ts +++ b/src/main.ts -@@ -413,3 +413,3 @@ function createDefaultArgvConfigSync(argvConfigPath: string): void { +@@ -421,3 +421,3 @@ function createDefaultArgvConfigSync(argvConfigPath: string): void { const defaultArgvConfigContent = [ - '// This configuration file allows you to pass permanent command line arguments to VS Code.', + '// This configuration file allows you to pass permanent command line arguments to !!APP_NAME!!.', '// Only a subset of arguments is currently supported to reduce the likelihood of breaking', -@@ -419,6 +419,6 @@ function createDefaultArgvConfigSync(argvConfigPath: string): void { +@@ -427,6 +427,6 @@ function createDefaultArgvConfigSync(argvConfigPath: string): void { '//', - '// NOTE: Changing this file requires a restart of VS Code.', + '// NOTE: Changing this file requires a restart of !!APP_NAME!!.', @@ -595,10 +595,10 @@ index 42f599c9..1f90b59e 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 d886313c..a76267f5 100644 +index 811673d0..f49ee713 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 { +@@ -610,3 +610,3 @@ export class CodeApplication extends Disposable { async startup(): Promise { - this.logService.debug('Starting VS Code'); + this.logService.debug('Starting !!APP_NAME!!'); @@ -640,18 +640,6 @@ index 6eefd716..e6cafc5d 100644 - throw new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", this.manifest.displayName || this.manifest.name)); + throw new Error(nls.localize('restartCode', "Please restart !!APP_NAME!! before reinstalling {0}.", this.manifest.displayName || this.manifest.name)); } -diff --git a/src/vs/platform/extensions/common/extensionValidator.ts b/src/vs/platform/extensions/common/extensionValidator.ts -index 6346b9d4..77ff0f91 100644 ---- a/src/vs/platform/extensions/common/extensionValidator.ts -+++ b/src/vs/platform/extensions/common/extensionValidator.ts -@@ -380,5 +380,5 @@ export function areApiProposalsCompatible(apiProposals: string[], arg1?: string[ - if (incompatibleProposals.length === 1) { -- notices.push(nls.localize('apiProposalMismatch1', "This extension is using the API proposal '{0}' that is not compatible with the current version of VS Code.", incompatibleProposals[0])); -+ notices.push(nls.localize('apiProposalMismatch1', "This extension is using the API proposal '{0}' that is not compatible with the current version of !!APP_NAME!!.", incompatibleProposals[0])); - } else { -- notices.push(nls.localize('apiProposalMismatch2', "This extension is using the API proposals {0} and '{1}' that are not compatible with the current version of VS Code.", -+ notices.push(nls.localize('apiProposalMismatch2', "This extension is using the API proposals {0} and '{1}' that are not compatible with the current version of !!APP_NAME!!.", - incompatibleProposals.slice(0, incompatibleProposals.length - 1).map(p => `'${p}'`).join(', '), diff --git a/src/vs/platform/externalTerminal/node/externalTerminalService.ts b/src/vs/platform/externalTerminal/node/externalTerminalService.ts index e7cf3f54..4424cc7f 100644 --- a/src/vs/platform/externalTerminal/node/externalTerminalService.ts @@ -703,10 +691,10 @@ index 58ff362b..54815f3c 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 2028933f..01e243be 100644 +index 710009f8..01173665 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 +@@ -568,3 +568,3 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu owner: 'TylerLeonhardt'; - comment: 'Used to see which extensions are using the VSCode client id override'; + comment: 'Used to see which extensions are using the !!APP_NAME!! client id override'; @@ -762,7 +750,7 @@ index 0d713847..ae8d169b 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 c0e1300b..fc0a34f9 100644 +index ee85640c..4b1bb35f 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -690,3 +690,3 @@ class PolicyDiagnosticsAction extends Action2 { @@ -789,16 +777,16 @@ index e342f838..7c314e6b 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 eeddba00..313f8b1f 100644 +index f2a3b35e..b52ae385 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts -@@ -829,3 +829,3 @@ const registry = Registry.as(ConfigurationExtensions.Con +@@ -835,3 +835,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 c650e128..611a4282 100644 +index 51a4a3f2..e83d47da 100644 --- a/src/vs/workbench/common/contextkeys.ts +++ b/src/vs/workbench/common/contextkeys.ts @@ -40,3 +40,3 @@ export const EmbedderIdentifierContext = new RawContextKey(' @@ -807,15 +795,15 @@ index c650e128..611a4282 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.shared.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts -index 74a6b479..198de338 100644 +index 883dab9e..825ebc1f 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts -@@ -725,3 +725,3 @@ configurationRegistry.registerConfiguration({ +@@ -804,3 +804,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.") -@@ -752,3 +752,3 @@ configurationRegistry.registerConfiguration({ +@@ -831,3 +831,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."), @@ -873,30 +861,30 @@ index 0b9ced32..731d9526 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 464b3948..efd9d24b 100644 +index 0c0965d3..9b9876ee 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts -@@ -382,3 +382,3 @@ CommandsRegistry.registerCommand({ +@@ -419,3 +419,3 @@ CommandsRegistry.registerCommand({ description: '(optional) Options for installing the extension. Object with the following properties: ' + - '`installOnlyNewlyAddedFromExtensionPackVSIX`: When enabled, VS Code installs only newly added extensions from the extension pack VSIX. This option is considered only when installing VSIX. ', + '`installOnlyNewlyAddedFromExtensionPackVSIX`: When enabled, !!APP_NAME!! installs only newly added extensions from the extension pack VSIX. This option is considered only when installing VSIX. ', isOptional: true, -@@ -389,3 +389,3 @@ CommandsRegistry.registerCommand({ +@@ -426,3 +426,3 @@ CommandsRegistry.registerCommand({ 'type': 'boolean', - 'description': localize('workbench.extensions.installExtension.option.installOnlyNewlyAddedFromExtensionPackVSIX', "When enabled, VS Code installs only newly added extensions from the extension pack VSIX. This option is considered only while installing a VSIX."), + 'description': localize('workbench.extensions.installExtension.option.installOnlyNewlyAddedFromExtensionPackVSIX', "When enabled, !!APP_NAME!! installs only newly added extensions from the extension pack VSIX. This option is considered only while installing a VSIX."), default: false -@@ -394,3 +394,3 @@ CommandsRegistry.registerCommand({ +@@ -431,3 +431,3 @@ CommandsRegistry.registerCommand({ 'type': 'boolean', - 'description': localize('workbench.extensions.installExtension.option.installPreReleaseVersion', "When enabled, VS Code installs the pre-release version of the extension if available."), + 'description': localize('workbench.extensions.installExtension.option.installPreReleaseVersion', "When enabled, !!APP_NAME!! installs the pre-release version of the extension if available."), default: false -@@ -399,3 +399,3 @@ CommandsRegistry.registerCommand({ +@@ -436,3 +436,3 @@ CommandsRegistry.registerCommand({ 'type': 'boolean', - '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 -@@ -929,4 +929,4 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi +@@ -961,4 +961,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."), @@ -904,7 +892,7 @@ index 464b3948..efd9d24b 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 bdf319de..4fa098b5 100644 +index 452db88d..cd67d238 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 { @@ -922,21 +910,21 @@ index bdf319de..4fa098b5 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) { -@@ -2810,3 +2810,3 @@ export class ExtensionStatusAction extends ExtensionAction { +@@ -2825,3 +2825,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 49b3ec6a..5177602a 100644 +index a58d77d1..7c1aae76 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 { +@@ -479,3 +479,3 @@ export class Extension implements IExtension { return Promise.resolve(`# ${this.displayName || this.name} -**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. +**Notice:** This extension is bundled with !!APP_NAME!!. It can be disabled but not uninstalled. ## Features -@@ -516,3 +516,3 @@ ${this.description} +@@ -517,3 +517,3 @@ ${this.description} if (this.type === ExtensionType.System) { - return Promise.resolve(`Please check the [VS Code Release Notes](command:${ShowCurrentReleaseNotesActionId}) for changes to the built-in extensions.`); + return Promise.resolve(`Please check the [!!APP_NAME!! Release Notes](command:${ShowCurrentReleaseNotesActionId}) for changes to the built-in extensions.`); @@ -1085,7 +1073,7 @@ index c43718e3..c20ee415 100644 + builtin printf '\x1b[0m\x1b[7m * \x1b[0;103m !!APP_NAME!! Python zsh activation failed with exit code %d \x1b[0m' "$__vsc_activation_status" fi diff --git a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.fish b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.fish -index 6cff7487..98e3d6a3 100644 +index f87fc762..a7089d63 100644 --- a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.fish +++ b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.fish @@ -94,3 +94,3 @@ if not set -q VSCODE_PYTHON_AUTOACTIVATE_GUARD @@ -1436,7 +1424,7 @@ index bdd30bf2..317d11c8 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 64b8586a..dc331c17 100644 +index 6cccbced..877669a3 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -801,3 +801,3 @@ Registry.as(ConfigurationExtensions.Configuration) @@ -1450,15 +1438,15 @@ index 64b8586a..dc331c17 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 885cbf9b..0cc63956 100644 +index 9db62d8f..074209e5 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts -@@ -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.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.") }, -@@ -456,3 +456,3 @@ import product from '../../platform/product/common/product.js'; +@@ -457,3 +457,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.") @@ -1531,7 +1519,7 @@ index 09dd590e..c4877b48 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 2b6104a7..9d2dffd4 100644 +index 4340604b..3c5f770a 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 diff --git a/patches/00-build-disable-mangle.patch b/patches/00-build-disable-mangle.patch index 41904451c00..096b3215aa7 100644 --- a/patches/00-build-disable-mangle.patch +++ b/patches/00-build-disable-mangle.patch @@ -1,8 +1,8 @@ diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts -index 948c6b4..66ecdd3 100644 +index 32e3e25e..6e9396d2 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts -@@ -131,27 +131,3 @@ export function compileTask(src: string, out: string, build: boolean, options: { +@@ -139,27 +139,3 @@ export function compileTask(src: string, out: string, build: boolean, options: { - // mangle: TypeScript to TypeScript - let mangleStream = es.through(); @@ -27,6 +27,6 @@ index 948c6b4..66ecdd3 100644 - }); - } - - return srcPipe + const emit = util.streamToPromise(srcPipe - .pipe(mangleStream) .pipe(generator.stream) diff --git a/patches/00-build-fix-npm-preinstall.patch b/patches/00-build-fix-npm-preinstall.patch deleted file mode 100644 index 994243ee076..00000000000 --- a/patches/00-build-fix-npm-preinstall.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/build/npm/preinstall.ts b/build/npm/preinstall.ts -index 3476fca..e23329f 100644 ---- a/build/npm/preinstall.ts -+++ b/build/npm/preinstall.ts -@@ -129,3 +129,3 @@ function installHeaders() { - // Refs https://chromium-review.googlesource.com/c/v8/v8/+/6879784 -- if (process.platform === 'linux') { -+ if (process.platform === 'linux' && local) { - const homedir = os.homedir(); -@@ -133,3 +133,3 @@ function installHeaders() { - const nodeGypCache = path.join(cachePath, 'node-gyp'); -- const localHeaderPath = path.join(nodeGypCache, local!.target, 'include', 'node'); -+ const localHeaderPath = path.join(nodeGypCache, local.target, 'include', 'node'); - if (fs.existsSync(localHeaderPath)) { diff --git a/patches/00-community-add-announcements.patch b/patches/00-community-add-announcements.patch index 6710cc9be4c..e7d346e0108 100644 --- a/patches/00-community-add-announcements.patch +++ b/patches/00-community-add-announcements.patch @@ -1,8 +1,8 @@ diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts -index e025130..f42db8d 100644 +index 067580a4..2ad8759f 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts -@@ -343,2 +343,9 @@ configurationRegistry.registerConfiguration({ +@@ -316,2 +316,9 @@ configurationRegistry.registerConfiguration({ }, + 'workbench.welcomePage.extraAnnouncements': { + scope: ConfigurationScope.MACHINE, @@ -13,10 +13,10 @@ index e025130..f42db8d 100644 + }, 'workbench.startupEditor': { diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts -index 01778b2..615828d 100644 +index 0cb22b9c..1dfea59f 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts -@@ -117,4 +117,8 @@ type GettingStartedActionEvent = { +@@ -119,4 +119,8 @@ type GettingStartedActionEvent = { type RecentEntry = (IRecentFolder | IRecentWorkspace) & { id: string }; +type AnnouncementEntry = { id: string, title: string, url: string }; @@ -26,25 +26,25 @@ index 01778b2..615828d 100644 + export class GettingStartedPage extends EditorPane { @@ -154,2 +158,4 @@ export class GettingStartedPage extends EditorPane { - private gettingStartedList?: GettingStartedIndexList; + private readonly gettingStartedList = this._register(new MutableDisposable>()); + private announcementList?: GettingStartedIndexList; + private announcementData?: AnnouncementEntry[]; -@@ -880,2 +886,3 @@ export class GettingStartedPage extends EditorPane { +@@ -936,2 +942,3 @@ export class GettingStartedPage extends EditorPane { const gettingStartedList = this.buildGettingStartedWalkthroughsList(); + const announcementList = await this.buildAnnouncementList(); -@@ -890,3 +897,3 @@ export class GettingStartedPage extends EditorPane { +@@ -960,3 +967,3 @@ export class GettingStartedPage extends EditorPane { this.container.classList.remove('noWalkthroughs'); - reset(rightColumn, gettingStartedList.getDomElement()); + reset(rightColumn, gettingStartedList.getDomElement(), announcementList.getDomElement()); } -@@ -894,3 +901,3 @@ export class GettingStartedPage extends EditorPane { +@@ -964,3 +971,3 @@ export class GettingStartedPage extends EditorPane { this.container.classList.add('noWalkthroughs'); - reset(rightColumn); + reset(rightColumn, announcementList.getDomElement()); } -@@ -1047,2 +1054,55 @@ export class GettingStartedPage extends EditorPane { +@@ -1151,2 +1158,55 @@ export class GettingStartedPage extends EditorPane { + private async buildAnnouncementList(): Promise> { + const renderAnnouncement = (announcement: AnnouncementEntry) => { diff --git a/patches/00-remote-add-url.patch b/patches/00-remote-add-url.patch index bd7b4aab86f..88bf312dd4a 100644 --- a/patches/00-remote-add-url.patch +++ b/patches/00-remote-add-url.patch @@ -1,17 +1,16 @@ diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts -index 3c0e3a16..cb47f50b 100644 +index 62c30da5..c1163a05 100644 --- a/build/gulpfile.reh.ts +++ b/build/gulpfile.reh.ts -@@ -329,3 +329,3 @@ function packageTask(type: string, platform: string, arch: string, sourceFolderN - const productJsonStream = gulp.src(['product.json'], { base: '.' }) -- .pipe(jsonEditor({ commit, date: readISODate(sourceFolderName), version })) -+ .pipe(jsonEditor({ commit, date: readISODate(sourceFolderName), version, serverDownloadUrlTemplate: 'https://github.com/!!ASSETS_REPOSITORY!!/releases/download/!!RELEASE_VERSION!!/!!APP_NAME_LC!!-reh-${os}-${arch}-!!RELEASE_VERSION!!.tar.gz' })) - .pipe(es.through(function (file) { +@@ -369,2 +369,3 @@ function packageTask(type: string, platform: string, arch: string, sourceFolderN + json.version = version; ++ json.serverDownloadUrlTemplate = 'https://github.com/!!ASSETS_REPOSITORY!!/releases/download/!!RELEASE_VERSION!!/!!APP_NAME_LC!!-reh-${os}-${arch}-!!RELEASE_VERSION!!.tar.gz'; + // Stamp agentSdks from the per-platform results file produced diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index a636b48c..0d85c1e4 100644 +index 53987f88..661928c2 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -309,2 +309,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d +@@ -307,2 +307,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d json.version = version; + json.serverDownloadUrlTemplate = 'https://github.com/!!ASSETS_REPOSITORY!!/releases/download/!!RELEASE_VERSION!!/!!APP_NAME_LC!!-reh-${os}-${arch}-!!RELEASE_VERSION!!.tar.gz'; - return json; + // Stamp agentSdks from the per-platform results file produced diff --git a/patches/00-ui-custom-font.patch b/patches/00-ui-custom-font.patch index 516bf112f19..3d441a7372e 100644 --- a/patches/00-ui-custom-font.patch +++ b/patches/00-ui-custom-font.patch @@ -77,10 +77,10 @@ index e9e55ad9..0ce9147c 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 0ec4cbb4..704ed8a8 100644 +index f8f02ab6..a7c88ff8 100644 --- a/src/vs/base/browser/ui/button/button.css +++ b/src/vs/base/browser/ui/button/button.css -@@ -184 +184,46 @@ +@@ -194 +194,46 @@ } + + @@ -245,10 +245,10 @@ index b7cbca15..16f531cb 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 7bb4282a..92c027a2 100644 +index ab4d9495..fba44ec9 100644 --- a/src/vs/base/browser/ui/splitview/paneview.css +++ b/src/vs/base/browser/ui/splitview/paneview.css -@@ -153 +153,38 @@ +@@ -154 +154,38 @@ } + + @@ -289,33 +289,32 @@ index 7bb4282a..92c027a2 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 fb2e1f40..3b1e23f2 100644 +index 024b2147..19790a0b 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'; import { applyDragImage } from '../dnd/dnd.js'; +import { FONT } from '../../../common/font.js'; -@@ -50,4 +51,2 @@ export abstract class Pane extends Disposable implements IView { +@@ -50,8 +51,2 @@ export abstract class Pane extends Disposable implements IView { +- /** +- * Fallback header size (in px) used when the `--pane-header-size` CSS variable +- * is not resolvable (e.g. before the element is attached to the document). +- */ - private static readonly HEADER_SIZE = 22; - readonly element: HTMLElement; -@@ -121,3 +120,3 @@ export abstract class Pane extends Disposable implements IView { - private get headerSize(): number { -- return this.headerVisible ? Pane.HEADER_SIZE : 0; -+ return this.headerVisible ? FONT.sidebarSize22 : 0; - } -@@ -300,3 +299,3 @@ export abstract class Pane extends Disposable implements IView { - layout(size: number): void { -- const headerSize = this.headerVisible ? Pane.HEADER_SIZE : 0; -+ const headerSize = this.headerSize; - +@@ -151,3 +146,3 @@ export abstract class Pane extends Disposable implements IView { + const size = parseInt(getWindow(this.element).getComputedStyle(this.element).getPropertyValue('--pane-header-size'), 10); +- this._headerSize = isNaN(size) ? Pane.HEADER_SIZE : size; ++ this._headerSize = isNaN(size) ? FONT.sidebarSize22 : size; + } diff --git a/src/vs/base/browser/ui/toggle/toggle.css b/src/vs/base/browser/ui/toggle/toggle.css -index e2d206d3..e8dfa5a9 100644 +index 14516fc5..40a4410d 100644 --- a/src/vs/base/browser/ui/toggle/toggle.css +++ b/src/vs/base/browser/ui/toggle/toggle.css -@@ -69 +69,52 @@ +@@ -75 +75,52 @@ } + + @@ -370,7 +369,7 @@ index e2d206d3..e8dfa5a9 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 0b2fcbbb..6370e6f7 100644 +index 2a5e4a99..63a3f8d0 100644 --- a/src/vs/base/browser/ui/toggle/toggle.ts +++ b/src/vs/base/browser/ui/toggle/toggle.ts @@ -5,2 +5,3 @@ @@ -1306,7 +1305,7 @@ index 328285f0..0735dfa2 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css -index 1f6e583f..79401713 100644 +index b04b3c70..8c7a7a5f 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -11,20 +11,20 @@ @@ -1351,7 +1350,7 @@ index 1f6e583f..79401713 100644 + font-family: var(--vscode-workbench-font-family, var(--monaco-font)); + font-size: var(--vscode-workbench-font-size, 13px); -@@ -356 +358,41 @@ body { +@@ -355 +357,41 @@ body { } + + @@ -1395,7 +1394,7 @@ index 1f6e583f..79401713 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 86c8b184..bfd30e64 100644 +index ee371523..fea72aba 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -41,2 +41,3 @@ import { IViewsService } from '../../../services/views/common/viewsService.js'; @@ -1414,14 +1413,12 @@ index 86c8b184..bfd30e64 100644 static readonly COMPACT_ICON_SIZE = 16; + static readonly COMPACT_ICON_SIZE_RATIO = 16/24; -@@ -59,4 +63,4 @@ export class ActivitybarPart extends Part { +@@ -75,3 +79,3 @@ export class ActivitybarPart extends Part { + /** The intrinsic activity bar width (excludes any floating gutter). */ +- private get baseWidth(): number { return this._isCompact ? ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH : ActivitybarPart.ACTIVITYBAR_WIDTH; } ++ private get baseWidth(): number { return this._isCompact ? FONT.activityBarSize48 * ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH_RATIO : FONT.activityBarSize48; } -- 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; -@@ -91,2 +95,11 @@ export class ActivitybarPart extends Part { +@@ -111,2 +115,11 @@ export class ActivitybarPart extends Part { })); + + this._register(configurationService.onDidChangeConfiguration(e => { @@ -1433,21 +1430,21 @@ index 86c8b184..bfd30e64 100644 + } + })); } -@@ -97,4 +110,4 @@ export class ActivitybarPart extends Part { - this.element.style.setProperty('--activity-bar-width', `${this.minimumWidth}px`); +@@ -117,4 +130,4 @@ export class ActivitybarPart extends Part { + this.element.style.setProperty('--activity-bar-width', `${this.baseWidth}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`); } -@@ -154,2 +167,6 @@ export class ActivitybarPart extends Part { +@@ -174,2 +187,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(); -@@ -163,2 +180,34 @@ export class ActivitybarPart extends Part { +@@ -183,2 +200,34 @@ export class ActivitybarPart extends Part { + private applyActivityBarFontFamily(container?: HTMLElement): void { + const target = container ?? this.getContainer(); @@ -1568,7 +1565,7 @@ index 568a7212..b3d7e506 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 c89c0e4e..88c43a91 100644 +index 058edb02..87da0854 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 @@ -1576,7 +1573,7 @@ index c89c0e4e..88c43a91 100644 +import { FONT, getFontSize, updateSidebarSize } from '../../../../base/common/font.js'; +import { SidebarPart } from '../sidebar/sidebarPart.js'; -@@ -146,2 +148,8 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { +@@ -147,2 +149,8 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { } + if (e.affectsConfiguration(SidebarPart.fontSizeSettingsKey)) { + this.applyAuxiliaryBarFontSize(); @@ -1585,7 +1582,7 @@ index c89c0e4e..88c43a91 100644 + this.applyAuxiliaryBarFontFamily(); + } })); -@@ -197,2 +205,37 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { +@@ -198,2 +206,37 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : '0px'; + + this.applyAuxiliaryBarFontSize(container); @@ -2149,14 +2146,14 @@ index e1c147d8..63d0e109 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 235bad74..cdd90d95 100644 +index 10376ab9..fba2f37f 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 import { Extensions } from '../../panecomposite.js'; +import { FONT, getFontSize, updatePanelSize as updateBottomPaneSize } from '../../../../base/common/font.js'; -@@ -116,2 +117,8 @@ export class PanelPart extends AbstractPaneCompositePart { +@@ -117,2 +118,8 @@ export class PanelPart extends AbstractPaneCompositePart { } + if (e.affectsConfiguration('workbench.bottomPane.experimental.fontFamily')) { + this.applyPanelFontFamily(); @@ -2165,7 +2162,7 @@ index 235bad74..cdd90d95 100644 + this.applyPanelFontSize(); + } })); -@@ -132,2 +139,37 @@ export class PanelPart extends AbstractPaneCompositePart { +@@ -133,2 +140,37 @@ export class PanelPart extends AbstractPaneCompositePart { } + + this.applyPanelFontFamily(container); @@ -2261,7 +2258,7 @@ index decb51ab..d0db4363 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 f269e758..f87d5781 100644 +index eecc3b8d..de93d458 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 @@ -2272,7 +2269,7 @@ index f269e758..f87d5781 100644 static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid'; + static readonly fontSizeSettingsKey = 'workbench.sideBar.experimental.fontSize'; -@@ -125,2 +127,8 @@ export class SidebarPart extends AbstractPaneCompositePart { +@@ -126,2 +128,8 @@ export class SidebarPart extends AbstractPaneCompositePart { } + if (e.affectsConfiguration(SidebarPart.fontSizeSettingsKey)) { + this.applySidebarFontSize(); @@ -2281,13 +2278,13 @@ index f269e758..f87d5781 100644 + this.applySidebarFontFamily(); + } })); -@@ -177,2 +185,5 @@ export class SidebarPart extends AbstractPaneCompositePart { +@@ -178,2 +186,5 @@ export class SidebarPart extends AbstractPaneCompositePart { container.style.outlineColor = this.getColor(SIDE_BAR_DRAG_AND_DROP_BACKGROUND) ?? ''; + + this.applySidebarFontSize(container); + this.applySidebarFontFamily(container); } -@@ -319,2 +330,34 @@ export class SidebarPart extends AbstractPaneCompositePart { +@@ -320,2 +331,34 @@ export class SidebarPart extends AbstractPaneCompositePart { + private applySidebarFontFamily(container?: HTMLElement): void { + const target = container ?? this.getContainer(); @@ -2403,31 +2400,19 @@ index 1f3b102e..42ad22c0 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 18340a8a..0a33ce06 100644 +index 05bec26f..dcc8cf4a 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'; +@@ -39,2 +39,3 @@ import { IView } from '../../../../base/browser/ui/grid/grid.js'; import { isManagedHoverTooltipHTMLElement, isManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; -+import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { FONT, getFontSize, updateStatusBarSize } from '../../../../base/common/font.js'; -@@ -120,3 +122,3 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { +@@ -121,3 +122,3 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { - static readonly HEIGHT = 22; + static get HEIGHT() { return FONT.statusBarSize22; } -@@ -126,4 +128,4 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { - readonly maximumWidth: number = Number.POSITIVE_INFINITY; -- readonly minimumHeight: number = StatusbarPart.HEIGHT; -- readonly maximumHeight: number = StatusbarPart.HEIGHT; -+ get minimumHeight(): number { return FONT.statusBarSize22; } -+ get maximumHeight(): number { return FONT.statusBarSize22; } - -@@ -162,2 +164,3 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { - @IContextKeyService private readonly contextKeyService: IContextKeyService, -+ @IConfigurationService private readonly configurationService: IConfigurationService, - ) { -@@ -165,2 +168,11 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { +@@ -178,2 +179,11 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { + this._register(configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('workbench.statusBar.experimental.fontFamily')) { @@ -2439,13 +2424,13 @@ index 18340a8a..0a33ce06 100644 + })); + this.viewModel = this._register(new StatusbarViewModel(storageService)); -@@ -429,2 +441,5 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { +@@ -451,2 +461,5 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { + this.applyStatusBarFontFamily(this.element); + this.applyStatusBarFontSize(this.element); + return this.element; -@@ -432,2 +447,32 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { +@@ -454,2 +467,32 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { + private applyStatusBarFontFamily(container?: HTMLElement): void { + const target = container ?? this.getContainer(); @@ -2478,26 +2463,11 @@ index 18340a8a..0a33ce06 100644 + } + private createInitialStatusbarEntries(): void { -@@ -726,4 +771,5 @@ export class MainStatusbarPart extends StatusbarPart { - @IContextKeyService contextKeyService: IContextKeyService, -+ @IConfigurationService configurationService: IConfigurationService, - ) { -- super(Parts.STATUSBAR_PART, instantiationService, themeService, contextService, storageService, layoutService, contextMenuService, contextKeyService); -+ super(Parts.STATUSBAR_PART, instantiationService, themeService, contextService, storageService, layoutService, contextMenuService, contextKeyService, configurationService); - } -@@ -740,3 +786,3 @@ export class AuxiliaryStatusbarPart extends StatusbarPart implements IAuxiliaryS +@@ -763,3 +806,3 @@ export class AuxiliaryStatusbarPart extends StatusbarPart implements IAuxiliaryS - readonly height = StatusbarPart.HEIGHT; + get height() { return StatusbarPart.HEIGHT; } -@@ -751,5 +797,6 @@ export class AuxiliaryStatusbarPart extends StatusbarPart implements IAuxiliaryS - @IContextKeyService contextKeyService: IContextKeyService, -+ @IConfigurationService configurationService: IConfigurationService, - ) { - const id = AuxiliaryStatusbarPart.COUNTER++; -- super(`workbench.parts.auxiliaryStatus.${id}`, instantiationService, themeService, contextService, storageService, layoutService, contextMenuService, contextKeyService); -+ 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 aca98deb..5bf9bf71 100644 --- a/src/vs/workbench/browser/parts/views/media/paneviewlet.css @@ -2535,24 +2505,24 @@ index aca98deb..5bf9bf71 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 487cf079..438f1cc7 100644 +index c2ae0b4a..ff138204 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 import { Command } from '../../../../editor/common/languages.js'; +import { FONT } from '../../../../base/common/font.js'; -@@ -1166,3 +1167,3 @@ class TreeViewDelegate implements IListVirtualDelegate { +@@ -1169,3 +1170,3 @@ class TreeViewDelegate implements IListVirtualDelegate { getHeight(element: ITreeItem): number { - return TreeRenderer.ITEM_HEIGHT; + return FONT.sidebarSize22; } -@@ -1242,3 +1243,2 @@ interface ITreeExplorerTemplateData { +@@ -1245,3 +1246,2 @@ interface ITreeExplorerTemplateData { class TreeRenderer extends Disposable implements ITreeRenderer { - 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 eeddba00..4a0ef488 100644 +index f2a3b35e..9eb97639 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -716,2 +716,85 @@ const registry = Registry.as(ConfigurationExtensions.Con @@ -2737,51 +2707,51 @@ index 6f588651..3ab0ee4e 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 01aa7ab5..ca43c1e0 100644 +index d20ac963..d266aa13 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts -@@ -55,2 +55,3 @@ import { CancellationTokenSource } from '../../../../../base/common/cancellation - import { IChatSessionsService } from '../../common/chatSessionsService.js'; +@@ -57,2 +57,3 @@ import { createPixelSpinner } from '../../../../../base/browser/ui/pixelSpinner/ + import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; +import { FONT } from '../../../../../base/common/font.js'; -@@ -853,5 +854,2 @@ export class AgentSessionsListDelegate implements IListVirtualDelegate { +@@ -80,6 +81,8 @@ export class SearchDelegate implements IListVirtualDelegate { - public static ITEM_HEIGHT = 22; + static getHeight(): number { @@ -4054,23 +4024,23 @@ index 62d5db99..f86dba1f 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts -index d2778271..86b8b01e 100644 +index 2c60e2cb..d3c7426c 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts -@@ -87,2 +87,3 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet +@@ -88,2 +88,3 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet import { forcedExpandRecursively } from './searchActionsTopBar.js'; +import { FONT } from '../../../../base/common/font.js'; -@@ -126,2 +127,3 @@ const SEARCH_CANCELLED_MESSAGE = nls.localize('searchCanceled', "Search was canc +@@ -127,2 +128,3 @@ const SEARCH_CANCELLED_MESSAGE = nls.localize('searchCanceled', "Search was canc const DEBOUNCE_DELAY = 75; + export class SearchView extends ViewPane { -@@ -963,3 +965,3 @@ export class SearchView extends ViewPane { +@@ -996,3 +998,3 @@ export class SearchView extends ViewPane { overrideStyles: this.getLocationBasedColors().listOverrideStyles, - paddingBottom: SearchDelegate.ITEM_HEIGHT, + paddingBottom: SearchDelegate.getHeight(), collapseByDefault: (e: RenderableMatch) => { -@@ -1341,6 +1343,6 @@ export class SearchView extends ViewPane { +@@ -1374,6 +1376,6 @@ export class SearchView extends ViewPane { - this.searchWidget.setWidth(this.size.width - 28 /* container margin */); + this.searchWidget.setWidth(this.size.width - FONT.sidebarSize28); @@ -4080,7 +4050,7 @@ index d2778271..86b8b01e 100644 + this.inputPatternExcludes.setWidth(this.size.width - FONT.sidebarSize28); + this.inputPatternIncludes.setWidth(this.size.width - FONT.sidebarSize28); -@@ -1348,3 +1350,3 @@ export class SearchView extends ViewPane { +@@ -1381,3 +1383,3 @@ export class SearchView extends ViewPane { const messagesHeight = dom.getTotalHeight(this.messagesElement); - this.tree.layout(this.size.height - widgetHeight - messagesHeight, this.size.width - 28); + this.tree.layout(this.size.height - widgetHeight - messagesHeight, this.size.width - FONT.sidebarSize28); @@ -4156,7 +4126,7 @@ index 02fdfa31..3bed1bbb 100644 + return FONT.sidebarSize22; } diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts -index 631bd2a2..902bb810 100644 +index 8ab6a0c4..20950f27 100644 --- a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts @@ -34,3 +34,2 @@ import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from '../../../../common/theme. diff --git a/patches/00-vsce-use-custom-lib.patch b/patches/00-vsce-use-custom-lib.patch index 8a7fce4c7e3..e78ec0f9fae 100644 --- a/patches/00-vsce-use-custom-lib.patch +++ b/patches/00-vsce-use-custom-lib.patch @@ -1,5 +1,5 @@ diff --git a/build/.moduleignore b/build/.moduleignore -index f83624f8..00779f06 100644 +index 7af8defd..8eb8da15 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -82,7 +82,7 @@ native-is-elevated/deps/** @@ -16,38 +16,38 @@ index f83624f8..00779f06 100644 +!@vscodium/vsce-sign/bin/** diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index 080d97f3..180177c5 100644 +index 53987f88..8951078a 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -460,3 +460,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d +@@ -359,3 +359,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 a25395c9..d87ff9aa 100644 +index e487ff5c..88e5ca1d 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts -@@ -126,3 +126,3 @@ export function typeCheckExtensionStream(extensionPath: string, forWeb: boolean) +@@ -128,3 +128,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(); -@@ -148,3 +148,3 @@ function fromLocalNormal(extensionPath: string): Stream { +@@ -150,3 +150,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 92f3b6a4..420464b0 100644 +index 12162d60..a26843e8 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -51,3 +51,3 @@ - "@vscode/ripgrep": "^1.17.1", + "@vscode/ripgrep-universal": "^1.18.0", - "@vscode/vsce": "3.6.1", + "@vscodium/vsce": "3.6.1-258428", "ansi-colors": "^3.2.3", -@@ -1941,6 +1941,45 @@ +@@ -1928,6 +1928,45 @@ }, - "node_modules/@vscode/vsce": { - "version": "3.6.1", @@ -64,9 +64,9 @@ index 92f3b6a4..420464b0 100644 + } + }, + "node_modules/@vscode/vsce/node_modules/brace-expansion": { -+ "version": "5.0.5", -+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", -+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", ++ "version": "5.0.6", ++ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", ++ "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "extraneous": true, + "license": "MIT", + "dependencies": { @@ -97,11 +97,11 @@ index 92f3b6a4..420464b0 100644 + "resolved": "https://registry.npmjs.org/@vscodium/vsce/-/vsce-3.6.1-258428.tgz", + "integrity": "sha512-B2uys5YU5MGNmzQPHsMsRXxzlQvZanhxE8Sfkd4Sr/LBN9BaV5yhMCppV7jyLUOQajj9K3YZThLqUYm4CCBy3A==", "dev": true, -@@ -1953,3 +1992,2 @@ +@@ -1940,3 +1979,2 @@ "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", - "@vscode/vsce-sign": "^2.0.0", "azure-devops-node-api": "^12.5.0", -@@ -1988,148 +2026,3 @@ +@@ -1975,148 +2013,3 @@ }, - "node_modules/@vscode/vsce-sign": { - "version": "2.0.7", @@ -251,52 +251,52 @@ index 92f3b6a4..420464b0 100644 - "node_modules/@vscode/vsce/node_modules/ansi-styles": { + "node_modules/@vscodium/vsce/node_modules/ansi-styles": { "version": "4.3.0", -@@ -2149,3 +2042,3 @@ +@@ -2136,3 +2029,3 @@ }, - "node_modules/@vscode/vsce/node_modules/balanced-match": { + "node_modules/@vscodium/vsce/node_modules/balanced-match": { "version": "4.0.4", -@@ -2159,3 +2052,3 @@ +@@ -2146,3 +2039,3 @@ }, - "node_modules/@vscode/vsce/node_modules/brace-expansion": { + "node_modules/@vscodium/vsce/node_modules/brace-expansion": { - "version": "5.0.5", -@@ -2172,3 +2065,3 @@ + "version": "5.0.6", +@@ -2159,3 +2052,3 @@ }, - "node_modules/@vscode/vsce/node_modules/chalk": { + "node_modules/@vscodium/vsce/node_modules/chalk": { "version": "4.1.2", -@@ -2189,3 +2082,3 @@ +@@ -2176,3 +2069,3 @@ }, - "node_modules/@vscode/vsce/node_modules/color-convert": { + "node_modules/@vscodium/vsce/node_modules/color-convert": { "version": "2.0.1", -@@ -2202,3 +2095,3 @@ +@@ -2189,3 +2082,3 @@ }, - "node_modules/@vscode/vsce/node_modules/color-name": { + "node_modules/@vscodium/vsce/node_modules/color-name": { "version": "1.1.4", -@@ -2209,3 +2102,3 @@ +@@ -2196,3 +2089,3 @@ }, - "node_modules/@vscode/vsce/node_modules/glob": { + "node_modules/@vscodium/vsce/node_modules/glob": { "version": "11.1.0", -@@ -2233,3 +2126,3 @@ +@@ -2220,3 +2113,3 @@ }, - "node_modules/@vscode/vsce/node_modules/glob/node_modules/minimatch": { + "node_modules/@vscodium/vsce/node_modules/glob/node_modules/minimatch": { "version": "10.2.4", -@@ -2249,3 +2142,3 @@ +@@ -2236,3 +2129,3 @@ }, - "node_modules/@vscode/vsce/node_modules/has-flag": { + "node_modules/@vscodium/vsce/node_modules/has-flag": { "version": "4.0.0", -@@ -2259,3 +2152,3 @@ +@@ -2246,3 +2139,3 @@ }, - "node_modules/@vscode/vsce/node_modules/jsonc-parser": { + "node_modules/@vscodium/vsce/node_modules/jsonc-parser": { "version": "3.3.1", -@@ -2263,9 +2156,11 @@ +@@ -2250,9 +2143,11 @@ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true + "dev": true, @@ -313,12 +313,12 @@ index 92f3b6a4..420464b0 100644 "dev": true, + "license": "ISC", "bin": { -@@ -2277,3 +2172,3 @@ +@@ -2264,3 +2159,3 @@ }, - "node_modules/@vscode/vsce/node_modules/supports-color": { + "node_modules/@vscodium/vsce/node_modules/supports-color": { "version": "7.2.0", -@@ -2290,12 +2185,2 @@ +@@ -2277,12 +2172,2 @@ }, - "node_modules/@vscode/vsce/node_modules/yazl": { - "version": "2.5.1", @@ -331,7 +331,7 @@ index 92f3b6a4..420464b0 100644 - } - }, "node_modules/@xmldom/xmldom": { -@@ -7022,2 +6907,12 @@ +@@ -7099,2 +6984,12 @@ }, + "node_modules/yazl": { + "version": "2.5.1", @@ -345,11 +345,11 @@ index 92f3b6a4..420464b0 100644 + }, "node_modules/yocto-queue": { diff --git a/build/package.json b/build/package.json -index 4746ea2f..49867df9 100644 +index 27176291..1ae8fa80 100644 --- a/build/package.json +++ b/build/package.json @@ -45,3 +45,3 @@ - "@vscode/ripgrep": "^1.17.1", + "@vscode/ripgrep-universal": "^1.18.0", - "@vscode/vsce": "3.6.1", + "@vscodium/vsce": "3.6.1-258428", "ansi-colors": "^3.2.3", diff --git a/patches/11-update-use-github-release.patch b/patches/11-update-use-github-release.patch index ea812d8bdd6..6fd09192550 100644 --- a/patches/11-update-use-github-release.patch +++ b/patches/11-update-use-github-release.patch @@ -273,7 +273,7 @@ index 2be53f61..53904dab 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 bbbc3bde..372de75a 100644 +index 1911ac0a..ee1cae7e 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 @@ -304,7 +304,7 @@ index bbbc3bde..372de75a 100644 + _updateType = UpdateType.Archive; + } } -@@ -144,5 +147,6 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -157,5 +160,6 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun try { + const updateType = getUpdateType(); const updatingVersion = (await readFile(updatingVersionPath, 'utf8')).trim(); @@ -312,12 +312,12 @@ index bbbc3bde..372de75a 100644 - const updatePackagePath = await this.getUpdatePackagePath(updatingVersion); + const updatePackagePath = await this.getUpdatePackagePath(updatingVersion, updateType); if (await pfs.Promises.exists(updatePackagePath)) { -@@ -157,3 +161,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -170,3 +174,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 -@@ -176,12 +180,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -189,12 +193,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - protected buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined { - let platform = `win32-${process.arch}`; @@ -348,14 +348,14 @@ index bbbc3bde..372de75a 100644 - return createUpdateURL(this.productService.updateUrl!, platform, quality, commit, options); + return createUpdateURL(this.productService, quality, process.platform, process.arch, target); } -@@ -193,6 +207,2 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -206,6 +220,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 -@@ -202,9 +212,13 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -215,9 +225,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) @@ -374,7 +374,7 @@ index bbbc3bde..372de75a 100644 - if (!update || !update.url || !update.version || !update.productVersion) { + if(!result) { // If we were checking for an overwrite update and found nothing newer, -@@ -220,2 +234,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -233,2 +247,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + const { lastest, update } = result; + @@ -384,17 +384,17 @@ index bbbc3bde..372de75a 100644 + } + if (updateType === UpdateType.Archive) { -@@ -237,3 +258,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -250,3 +271,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun return this.cleanup(update.version).then(() => { - return this.getUpdatePackagePath(update.version).then(updatePackagePath => { + return this.getUpdatePackagePath(update.version, updateType).then(updatePackagePath => { return pfs.Promises.exists(updatePackagePath).then(exists => { -@@ -245,3 +266,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -258,3 +279,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 => { -@@ -290,8 +311,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -303,8 +324,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun }) - .then(undefined, err => { - this.telemetryService.publicLog2<{ messageHash: string }, UpdateErrorClassification>('update:error', { messageHash: String(hash(String(err))) }); @@ -406,7 +406,7 @@ index bbbc3bde..372de75a 100644 - const message: string | undefined = explicit ? (err.message || err) : undefined; + const message: string | undefined = explicit ? (error.message || error) : undefined; -@@ -315,5 +335,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -328,5 +348,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - private async getUpdatePackagePath(version: string): Promise { + private async getUpdatePackagePath(version: string, type: UpdateType): Promise { @@ -416,61 +416,60 @@ index bbbc3bde..372de75a 100644 + + return path.join(cachePath, `${this.productService.nameShort.replaceAll(/\s/g, '')}-${this.productService.quality}-${version}.${extension}`); } -@@ -321,3 +343,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -334,3 +356,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun private async cleanup(exceptVersion: string | null = null): Promise { - const filter = exceptVersion ? (one: string) => !(new RegExp(`${this.productService.quality}-${exceptVersion}\\.exe$`).test(one)) : () => true; + const filter = exceptVersion ? (one: string) => !(new RegExp(`${this.productService.quality}-${exceptVersion}\\.(exe|msi)$`).test(one)) : () => true; -@@ -355,14 +377,10 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - await pfs.Promises.writeFile(this.availableUpdate.updateFilePath, 'flag'); -- const child = spawn(this.availableUpdate.packagePath, -- [ -- '/verysilent', -- '/log', -- `/update="${this.availableUpdate.updateFilePath}"`, -- `/progress="${progressFilePath}"`, -- `/sessionend="${sessionEndFlagPath}"`, -- `/cancel="${cancelFilePath}"`, -- '/nocloseapplications', -- '/mergetasks=runcode,!desktopicon,!quicklaunchicon' -- ], -- { -+ -+ let child: ChildProcess -+ -+ const type = getUpdateType(); -+ if (type == UpdateType.WindowsInstaller) { -+ this.logService.info(`update#doApplyUpdate - msiexec.exe /i ${this.availableUpdate.packagePath}`); -+ -+ child = spawn('msiexec.exe', ['/i', this.availableUpdate.packagePath], { - detached: true, -@@ -371,4 +389,23 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } -- } -- ); -+ }); -+ } else { -+ child = spawn(this.availableUpdate.packagePath, -+ [ -+ '/verysilent', -+ '/log', -+ `/update="${this.availableUpdate.updateFilePath}"`, -+ `/progress="${progressFilePath}"`, -+ `/sessionend="${sessionEndFlagPath}"`, -+ `/cancel="${cancelFilePath}"`, -+ '/nocloseapplications', -+ '/mergetasks=runcode,!desktopicon,!quicklaunchicon' -+ ], -+ { -+ detached: true, -+ stdio: ['ignore', 'ignore', 'ignore'], -+ windowsVerbatimArguments: true, -+ env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } -+ } -+ ); -+ } +@@ -375,14 +397,9 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + +- const child = spawn(this.availableUpdate.packagePath, +- [ +- '/verysilent', +- '/log', +- `/update="${this.availableUpdate.updateFilePath}"`, +- `/progress="${progressFilePath}"`, +- `/sessionend="${sessionEndFlagPath}"`, +- `/cancel="${cancelFilePath}"`, +- '/nocloseapplications', +- '/mergetasks=runcode,!desktopicon,!quicklaunchicon' +- ], +- { ++ let child: ChildProcess ++ ++ const type = getUpdateType(); ++ if (type == UpdateType.WindowsInstaller) { ++ this.logService.info(`update#doApplyUpdate - msiexec.exe /i ${this.availableUpdate.packagePath}`); ++ ++ child = spawn('msiexec.exe', ['/i', this.availableUpdate.packagePath], { + detached: true, +@@ -391,4 +408,23 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun + env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } +- } +- ); ++ }); ++ } else { ++ child = spawn(this.availableUpdate.packagePath, ++ [ ++ '/verysilent', ++ '/log', ++ `/update="${this.availableUpdate.updateFilePath}"`, ++ `/progress="${progressFilePath}"`, ++ `/sessionend="${sessionEndFlagPath}"`, ++ `/cancel="${cancelFilePath}"`, ++ '/nocloseapplications', ++ '/mergetasks=runcode,!desktopicon,!quicklaunchicon' ++ ], ++ { ++ detached: true, ++ stdio: ['ignore', 'ignore', 'ignore'], ++ windowsVerbatimArguments: true, ++ env: { ...process.env, __COMPAT_LAYER: 'RunAsInvoker' } ++ } ++ ); ++ } -@@ -490,7 +527,19 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -529,7 +565,19 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun } else { - spawn(this.availableUpdate.packagePath, ['/silent', '/log', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], { - detached: true, diff --git a/patches/20-keymap-use-custom-lib.patch b/patches/20-keymap-use-custom-lib.patch index 0f567c6c272..ecead4cf5d4 100644 --- a/patches/20-keymap-use-custom-lib.patch +++ b/patches/20-keymap-use-custom-lib.patch @@ -1,5 +1,5 @@ diff --git a/.npmrc b/.npmrc -index 025e4b04..5e409d87 100644 +index 8c21e58e..25a93f40 100644 --- a/.npmrc +++ b/.npmrc @@ -6,2 +6,3 @@ ignore-scripts=false @@ -7,7 +7,7 @@ index 025e4b04..5e409d87 100644 +build_from_source_native_keymap="no" legacy-peer-deps="true" diff --git a/build/.moduleignore b/build/.moduleignore -index d1a8a227..b7209206 100644 +index 7af8defd..b21564c3 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -65,7 +65,7 @@ fsevents/test/** @@ -24,27 +24,27 @@ index d1a8a227..b7209206 100644 +!@vscodium/native-keymap/build/Release/*.node diff --git a/eslint.config.js b/eslint.config.js -index 9f0b5274..10eb5a99 100644 +index 51b085ee..938419e2 100644 --- a/eslint.config.js +++ b/eslint.config.js -@@ -1518,3 +1518,3 @@ export default tseslint.config( +@@ -1563,3 +1563,3 @@ export default defineConfig( 'node:module', - 'native-keymap', + '@vscodium/native-keymap', 'net', diff --git a/package-lock.json b/package-lock.json -index 68f23433..d5c1dfa7 100644 +index 81d315f0..1d99e154 100644 --- a/package-lock.json +++ b/package-lock.json -@@ -41,2 +41,3 @@ +@@ -43,2 +43,3 @@ "@vscode/windows-registry": "^1.2.0", + "@vscodium/native-keymap": "3.3.7-258424", - "@xterm/addon-clipboard": "^0.3.0-beta.213", -@@ -59,3 +60,2 @@ + "@xterm/addon-clipboard": "^0.3.0-beta.285", +@@ -62,3 +63,2 @@ "native-is-elevated": "0.9.0", - "native-keymap": "^3.3.5", - "node-pty": "^1.2.0-beta.13", -@@ -4368,2 +4368,9 @@ + "node-addon-api": "^6.0.0", +@@ -4498,2 +4498,9 @@ }, + "node_modules/@vscodium/native-keymap": { + "version": "3.3.7-258424", @@ -54,7 +54,7 @@ index 68f23433..d5c1dfa7 100644 + "license": "MIT" + }, "node_modules/@webgpu/types": { -@@ -14103,5 +14110,6 @@ +@@ -14159,5 +14166,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 68f23433..d5c1dfa7 100644 + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" }, -@@ -14114,9 +14122,2 @@ +@@ -14170,9 +14178,2 @@ }, - "node_modules/native-keymap": { - "version": "3.3.9", @@ -74,7 +74,7 @@ index 68f23433..d5c1dfa7 100644 - "license": "MIT" - }, "node_modules/natural-compare": { -@@ -15517,5 +15518,6 @@ +@@ -15582,5 +15583,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 68f23433..d5c1dfa7 100644 + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", "dependencies": { -@@ -15526,3 +15528,3 @@ +@@ -15591,3 +15593,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 5defb1ef..f827d888 100644 +index 36485330..2a5830cd 100644 --- a/package.json +++ b/package.json -@@ -136,3 +136,3 @@ +@@ -146,3 +146,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.13", + "node-addon-api": "^6.0.0", diff --git a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts -index 50360a79..93df77af 100644 +index 9b11a712..975c3127 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)', () => { diff --git a/patches/21-policy-use-custom-lib.patch b/patches/21-policy-use-custom-lib.patch index 01c2a5b8df7..dd43b32228b 100644 --- a/patches/21-policy-use-custom-lib.patch +++ b/patches/21-policy-use-custom-lib.patch @@ -1,5 +1,5 @@ diff --git a/build/.moduleignore b/build/.moduleignore -index b7209206..f6f1f761 100644 +index b21564c3..38aaef45 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -153,9 +153,11 @@ vsda/** @@ -31,66 +31,66 @@ index 7f650ba7..db927cb4 100644 + ``, ` `, diff --git a/build/lib/policies/render.ts b/build/lib/policies/render.ts -index 47b485d1..8437fd47 100644 +index bd5958cb..3f62d83f 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 +@@ -56,3 +56,3 @@ export function renderADMX(regKey: string, versions: string[], categories: Categ - + -@@ -167,3 +167,3 @@ export function renderProfileManifest(appName: string, bundleIdentifier: string, +@@ -174,3 +174,3 @@ export function renderProfileManifest(appName: string, bundleIdentifier: string, pfm_default - Microsoft + !!ORG_NAME!! pfm_name -@@ -185,3 +185,3 @@ export function renderProfileManifest(appName: string, bundleIdentifier: string, +@@ -192,3 +192,3 @@ export function renderProfileManifest(appName: string, bundleIdentifier: string, pfm_app_url - https://code.visualstudio.com/ + https://github.com/VSCodium/vscodium pfm_description -@@ -189,3 +189,3 @@ export function renderProfileManifest(appName: string, bundleIdentifier: string, +@@ -196,3 +196,3 @@ export function renderProfileManifest(appName: string, bundleIdentifier: string, pfm_documentation_url - https://code.visualstudio.com/docs/setup/enterprise + https://github.com/VSCodium/vscodium pfm_domain -@@ -255,3 +255,3 @@ ${policyEntries} +@@ -262,3 +262,3 @@ ${policyEntries} PayloadDescription - This profile manages ${appName}. For more information see https://code.visualstudio.com/docs/setup/enterprise + This profile manages ${appName}. For more information see https://github.com/VSCodium/vscodium PayloadDisplayName -@@ -261,3 +261,3 @@ ${policyEntries} +@@ -268,3 +268,3 @@ ${policyEntries} PayloadOrganization - Microsoft + !!ORG_NAME!! PayloadType diff --git a/eslint.config.js b/eslint.config.js -index 91ed6530..ef68a90d 100644 +index 938419e2..f3a103ca 100644 --- a/eslint.config.js +++ b/eslint.config.js -@@ -1500,3 +1500,3 @@ export default tseslint.config( +@@ -1544,3 +1544,3 @@ export default defineConfig( '@vscode/native-watchdog', - '@vscode/policy-watcher', + '@vscodium/policy-watcher', '@vscode/proxy-agent', diff --git a/package-lock.json b/package-lock.json -index caa581eb..8244642a 100644 +index 1d99e154..a99dcb95 100644 --- a/package-lock.json +++ b/package-lock.json -@@ -29,3 +29,2 @@ +@@ -31,3 +31,2 @@ "@vscode/native-watchdog": "^1.4.6", -- "@vscode/policy-watcher": "^1.3.2", - "@vscode/proxy-agent": "^0.41.0", -@@ -42,2 +41,3 @@ +- "@vscode/policy-watcher": "^1.4.0", + "@vscode/proxy-agent": "^0.42.0", +@@ -44,2 +43,3 @@ "@vscodium/native-keymap": "3.3.7-258424", -+ "@vscodium/policy-watcher": "^1.3.2-252465", - "@xterm/addon-clipboard": "^0.3.0-beta.213", -@@ -4017,22 +4017,2 @@ ++ "@vscodium/policy-watcher": "1.3.2-252465", + "@xterm/addon-clipboard": "^0.3.0-beta.285", +@@ -4122,22 +4122,2 @@ }, - "node_modules/@vscode/policy-watcher": { -- "version": "1.3.7", -- "resolved": "https://registry.npmjs.org/@vscode/policy-watcher/-/policy-watcher-1.3.7.tgz", -- "integrity": "sha512-OvIczTbtGLZs7YU0ResbjM0KEB2ORBnlJ4ICxaB9fKHNVBwNVp4i2qIkDQGp3UBGtu7P8/+eg4/ZKk2oJGFcug==", +- "version": "1.4.0", +- "resolved": "https://registry.npmjs.org/@vscode/policy-watcher/-/policy-watcher-1.4.0.tgz", +- "integrity": "sha512-QKTLV/UtV0HH5AJELfN5D3Jcxj2hB9CYT9GtG334I8bU5TdusaSGYSpDjumZBGWO2YxwVYpBNytA7mq/I3ZtzA==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { @@ -108,12 +108,12 @@ index caa581eb..8244642a 100644 - } - }, "node_modules/@vscode/proxy-agent": { -@@ -4375,2 +4355,22 @@ +@@ -4505,2 +4485,22 @@ }, + "node_modules/@vscodium/policy-watcher": { -+ "version": "1.3.2-255408", -+ "resolved": "https://registry.npmjs.org/@vscodium/policy-watcher/-/policy-watcher-1.3.2-255408.tgz", -+ "integrity": "sha512-0KERmB+VkSz9hvFWEDGalCpxQ9+qjLaUazXMBkzWQ9SjKPaD6zU9u6wA4/OUu816JnvCFEeJYEe9WcDZPnKQ1w==", ++ "version": "1.3.2-252465", ++ "resolved": "https://registry.npmjs.org/@vscodium/policy-watcher/-/policy-watcher-1.3.2-252465.tgz", ++ "integrity": "sha512-kpnb656HMteBIm8d9LhBpQ5gL2A/4rJrsaLCF0D8IWyrZAQ0UR9EzXM6tZ6p5H+KWot3QUjm0Gry6vMV1yye5Q==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { @@ -132,14 +132,14 @@ index caa581eb..8244642a 100644 + }, "node_modules/@webgpu/types": { diff --git a/package.json b/package.json -index 7f89f523..8c411b1e 100644 +index 2a5830cd..44b0c9fa 100644 --- a/package.json +++ b/package.json -@@ -106,3 +106,3 @@ +@@ -115,3 +115,3 @@ "@vscode/native-watchdog": "^1.4.6", -- "@vscode/policy-watcher": "^1.3.2", -+ "@vscodium/policy-watcher": "^1.3.2-252465", - "@vscode/proxy-agent": "^0.41.0", +- "@vscode/policy-watcher": "^1.4.0", ++ "@vscodium/policy-watcher": "1.3.2-252465", + "@vscode/proxy-agent": "^0.42.0", diff --git a/src/vs/base/test/node/uri.perf.data.txt b/src/vs/base/test/node/uri.perf.data.txt index ee0a24b5..881ce36a 100644 --- a/src/vs/base/test/node/uri.perf.data.txt @@ -240,7 +240,7 @@ index ee0a24b5..881ce36a 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 93df77af..69989991 100644 +index 975c3127..7ff873da 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)', () => { @@ -252,8 +252,31 @@ index 93df77af..69989991 100644 + const watcher = await import('@vscodium/policy-watcher'); + assert.ok(typeof watcher.createWatcher === 'function', testErrorMessage('@vscodium/policy-watcher')); }); +diff --git a/src/vs/platform/policy/node/copilotManagedSettingsService.ts b/src/vs/platform/policy/node/copilotManagedSettingsService.ts +index 5b76979d..2af19dbd 100644 +--- a/src/vs/platform/policy/node/copilotManagedSettingsService.ts ++++ b/src/vs/platform/policy/node/copilotManagedSettingsService.ts +@@ -13,3 +13,3 @@ import { collectManagedSettingsDefinitions, ICopilotManagedSettingsService } fro + import { PolicyDefinition, PolicyValue } from '../common/policy.js'; +-import type { Watcher } from '@vscode/policy-watcher'; ++import type { Watcher } from '@vscodium/policy-watcher'; + +@@ -20,2 +20,3 @@ export interface ICopilotPolicyWatcherOptions { + export type CopilotPolicyWatcherFactory = ( ++ vendorName: string, + productName: string, +@@ -91,3 +92,3 @@ export class CopilotManagedSettingsService extends Disposable implements ICopilo + +- const { createWatcher } = this.watcherFactory ? { createWatcher: this.watcherFactory } : (await import('@vscode/policy-watcher') as { createWatcher: CopilotPolicyWatcherFactory }); ++ const { createWatcher } = this.watcherFactory ? { createWatcher: this.watcherFactory } : (await import('@vscodium/policy-watcher') as { createWatcher: CopilotPolicyWatcherFactory }); + await this.throttler.queue(() => new Promise((c, e) => { +@@ -95,3 +96,3 @@ export class CopilotManagedSettingsService extends Disposable implements ICopilo + this.logService.trace(`Creating Copilot managed-settings watcher for productName ${this.productName}`); +- this.watcher.value = createWatcher(this.productName, managedSettingDefinitions, update => { ++ this.watcher.value = createWatcher('!!ORG_NAME!!', this.productName, managedSettingDefinitions, update => { + this._onDidManagedSettingsChange(update as Record); diff --git a/src/vs/platform/policy/node/nativePolicyService.ts b/src/vs/platform/policy/node/nativePolicyService.ts -index 6039dbb7..f2ea09cf 100644 +index 6039dbb7..978be1b4 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'; @@ -269,5 +292,29 @@ index 6039dbb7..f2ea09cf 100644 @@ -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.watcher.value = createWatcher('!!ORG_NAME!!', this.productName, policyDefinitions, update => { this._onDidPolicyChange(update); +diff --git a/src/vs/platform/policy/test/node/copilotManagedSettingsService.test.ts b/src/vs/platform/policy/test/node/copilotManagedSettingsService.test.ts +index 9735a461..1c868690 100644 +--- a/src/vs/platform/policy/test/node/copilotManagedSettingsService.test.ts ++++ b/src/vs/platform/policy/test/node/copilotManagedSettingsService.test.ts +@@ -24,3 +24,3 @@ suite('CopilotManagedSettingsService', () => { + let onDidChange: ((update: Record) => void) | undefined; +- const watcherFactory: CopilotPolicyWatcherFactory = (_productName, policies, callback) => { ++ const watcherFactory: CopilotPolicyWatcherFactory = (_vendorName, _productName, policies, callback) => { + assert.deepStrictEqual(policies, { [COPILOT_DISABLE_BYPASS_PERMISSIONS_MODE_KEY]: { type: 'string' } }); +@@ -64,3 +64,3 @@ suite('CopilotManagedSettingsService', () => { + let disposeCount = 0; +- const watcherFactory: CopilotPolicyWatcherFactory = (_productName, _policies, callback) => { ++ const watcherFactory: CopilotPolicyWatcherFactory = (_vendorName, _productName, _policies, callback) => { + onDidChange = callback; +@@ -89,3 +89,3 @@ suite('CopilotManagedSettingsService', () => { + let watcherCreateCount = 0; +- const watcherFactory: CopilotPolicyWatcherFactory = (_productName, _policies, callback) => { ++ const watcherFactory: CopilotPolicyWatcherFactory = (_vendorName, _productName, _policies, callback) => { + watcherCreateCount++; +@@ -128,3 +128,3 @@ suite('CopilotManagedSettingsService', () => { + const otherManagedSettingKey = 'permissions.otherManagedSetting'; +- const watcherFactory: CopilotPolicyWatcherFactory = (_productName, _policies, callback) => { ++ const watcherFactory: CopilotPolicyWatcherFactory = (_vendorName, _productName, _policies, callback) => { + onDidChange = callback; diff --git a/patches/40-cli-use-reh-archive.patch b/patches/40-cli-use-reh-archive.patch index afcc6af2150..de31d13ce54 100644 --- a/patches/40-cli-use-reh-archive.patch +++ b/patches/40-cli-use-reh-archive.patch @@ -1,19 +1,19 @@ diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs -index 74f2abdb..4d5d6bce 100644 +index 12178ef2..48b6c135 100644 --- a/cli/src/commands/serve_web.rs +++ b/cli/src/commands/serve_web.rs -@@ -792,3 +792,3 @@ impl ConnectionManager { +@@ -794,3 +794,3 @@ impl ConnectionManager { let dir_fut = cache.create(&args.release.commit, |target_dir| async move { - info!(log_for_fut, "Downloading server {}", release_for_fut.commit); + info!(log_for_fut, "Downloading server {}/{}", release_for_fut.commit, release_for_fut.name); let tmpdir = tempfile::tempdir().unwrap(); -@@ -820,3 +820,3 @@ impl ConnectionManager { +@@ -822,3 +822,3 @@ impl ConnectionManager { .join("bin") - .join(args.release.quality.server_entrypoint()); + .join(args.release.quality.server_entrypoint().unwrap()); diff --git a/cli/src/constants.rs b/cli/src/constants.rs -index f95cc77a..9a817c0c 100644 +index 938f4343..7277908a 100644 --- a/cli/src/constants.rs +++ b/cli/src/constants.rs @@ -37,3 +37,6 @@ pub const DOCUMENTATION_URL: Option<&'static str> = option_env!("VSCODE_CLI_DOCU @@ -58,19 +58,19 @@ index 7d152c0e..c0f2fb2e 100644 + Ok(server_name) } diff --git a/cli/src/tunnels/agent_host.rs b/cli/src/tunnels/agent_host.rs -index 2d50c926..11a349cb 100644 +index 93651587..77d19e6f 100644 --- a/cli/src/tunnels/agent_host.rs +++ b/cli/src/tunnels/agent_host.rs -@@ -166,3 +166,3 @@ impl AgentHostManager { +@@ -233,3 +233,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 3e4bb9eb..b4440f18 100644 +index df37b863..a3df1bea 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs -@@ -460,3 +460,3 @@ impl<'a> ServerBuilder<'a> { +@@ -499,3 +499,3 @@ impl<'a> ServerBuilder<'a> { .join("bin") - .join(self.server_params.release.quality.server_entrypoint()), + .join(self.server_params.release.quality.server_entrypoint().unwrap()), @@ -85,7 +85,7 @@ index 3d7d718a..98529bc6 100644 + .join(self.quality.server_entrypoint().unwrap()) }, diff --git a/cli/src/update_service.rs b/cli/src/update_service.rs -index 1070dac0..db064277 100644 +index cd94139f..db064277 100644 --- a/cli/src/update_service.rs +++ b/cli/src/update_service.rs @@ -10,3 +10,3 @@ use serde::{Deserialize, Serialize}; @@ -214,19 +214,20 @@ index 1070dac0..db064277 100644 -impl TargetKind { - fn download_segment(&self, platform: Platform) -> Option { - match *self { -- TargetKind::Server => Some(platform.headless()), +- TargetKind::Server => platform.headless(), - TargetKind::Archive => platform.archive(), -- TargetKind::Web => Some(platform.web()), +- TargetKind::Web => platform.web(), - TargetKind::Cli => Some(platform.cli()), - } - } -} - #[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] -@@ -224,30 +187,17 @@ pub enum Platform { +@@ -224,50 +187,36 @@ pub enum Platform { impl Platform { - pub fn archive(&self) -> Option { -- match self { ++ pub fn arch(&self) -> String { + match self { - Platform::LinuxX64 => Some("linux-x64".to_owned()), - Platform::LinuxARM64 => Some("linux-arm64".to_owned()), - Platform::LinuxARM32 => Some("linux-armhf".to_owned()), @@ -236,24 +237,6 @@ index 1070dac0..db064277 100644 - Platform::WindowsX86 => Some("win32-archive".to_owned()), - Platform::WindowsARM64 => Some("win32-arm64-archive".to_owned()), - _ => None, -- } -- } -- pub fn headless(&self) -> String { -+ pub fn arch(&self) -> String { - match self { -- Platform::LinuxAlpineARM64 => "server-alpine-arm64", -- Platform::LinuxAlpineX64 => "server-linux-alpine", -- Platform::LinuxX64 => "server-linux-x64", -- Platform::LinuxX64Legacy => "server-linux-legacy-x64", -- Platform::LinuxARM64 => "server-linux-arm64", -- Platform::LinuxARM64Legacy => "server-linux-legacy-arm64", -- Platform::LinuxARM32 => "server-linux-armhf", -- Platform::LinuxARM32Legacy => "server-linux-legacy-armhf", -- Platform::DarwinX64 => "server-darwin", -- Platform::DarwinARM64 => "server-darwin-arm64", -- Platform::WindowsX64 => "server-win32-x64", -- Platform::WindowsX86 => "server-win32", -- Platform::WindowsARM64 => "server-win32-arm64", + Platform::LinuxAlpineARM64 => "arm64", + Platform::LinuxAlpineX64 => "x64", + Platform::LinuxX64 => "x64", @@ -268,7 +251,27 @@ index 1070dac0..db064277 100644 + Platform::WindowsX86 => "ia42", + Platform::WindowsARM64 => "arm64", } -@@ -256,17 +206,17 @@ impl Platform { +- } +- pub fn headless(&self) -> Option { +- let name = match self { +- Platform::LinuxAlpineARM64 => "server-alpine-arm64", +- Platform::LinuxAlpineX64 => "server-linux-alpine", +- Platform::LinuxX64 => "server-linux-x64", +- Platform::LinuxX64Legacy => "server-linux-legacy-x64", +- Platform::LinuxARM64 => "server-linux-arm64", +- Platform::LinuxARM64Legacy => "server-linux-legacy-arm64", +- // No remote server is built for arm32 since Node.js dropped +- // 32-bit Linux on armv7 in v24. +- Platform::LinuxARM32 | Platform::LinuxARM32Legacy => return None, +- Platform::DarwinX64 => "server-darwin", +- Platform::DarwinARM64 => "server-darwin-arm64", +- Platform::WindowsX64 => "server-win32-x64", +- Platform::WindowsX86 => "server-win32", +- Platform::WindowsARM64 => "server-win32-arm64", +- }; +- Some(name.to_owned()) ++ .to_owned() + } - pub fn cli(&self) -> String { + pub fn os(&self) -> String { @@ -300,10 +303,10 @@ index 1070dac0..db064277 100644 + Platform::WindowsX86 => "win32", + Platform::WindowsARM64 => "win32", } -@@ -275,6 +225,2 @@ impl Platform { +@@ -276,6 +225,2 @@ impl Platform { -- pub fn web(&self) -> String { -- format!("{}-web", self.headless()) +- pub fn web(&self) -> Option { +- self.headless().map(|h| format!("{h}-web")) - } - pub fn env_default() -> Option { diff --git a/patches/50-build-improve-gulp-tasks.patch b/patches/50-build-improve-gulp-tasks.patch index b133513ea68..8127685d55d 100644 --- a/patches/50-build-improve-gulp-tasks.patch +++ b/patches/50-build-improve-gulp-tasks.patch @@ -1,29 +1,29 @@ diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index a636b48c..25aa6a57 100644 +index 53987f88..b897c6c6 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -30,3 +30,3 @@ import { createAsar } from './lib/asar.ts'; +@@ -27,3 +27,3 @@ import { createAsar } from './lib/asar.ts'; import minimist from 'minimist'; -import { compileBuildWithoutManglingTask, compileBuildWithManglingTask } from './gulpfile.compile.ts'; +import { compileBuildWithoutManglingTask } from './gulpfile.compile.ts'; import { compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileAllExtensionsBuildTask, compileExtensionMediaBuildTask, cleanExtensionsBuildTask, compileCopilotExtensionBuildTask } from './gulpfile.extensions.ts'; -@@ -623,3 +623,4 @@ BUILD_TARGETS.forEach(buildTarget => { +@@ -667,3 +667,4 @@ BUILD_TARGETS.forEach(buildTarget => { ); - vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( + + const prepackTask = task.define(`vscode${dashed(minified)}-prepack`, task.series( copyCodiconsTask, -@@ -629,2 +630,6 @@ BUILD_TARGETS.forEach(buildTarget => { +@@ -673,2 +674,6 @@ BUILD_TARGETS.forEach(buildTarget => { compileExtensionMediaBuildTask, + )); -+ gulp.task(prepackTask); ++ task.task(prepackTask); + + const packingTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}-packing`, task.series( writeISODate('out-build'), -@@ -633,5 +638,11 @@ BUILD_TARGETS.forEach(buildTarget => { +@@ -677,5 +682,11 @@ BUILD_TARGETS.forEach(buildTarget => { )); - } else { -+ gulp.task(packingTask); ++ task.task(packingTask); + vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( - minified ? compileBuildWithManglingTask : compileBuildWithoutManglingTask, @@ -34,15 +34,15 @@ index a636b48c..25aa6a57 100644 + const prepackTask = task.define(`vscode${dashed(minified)}-prepack`, task.series( + compileBuildWithoutManglingTask, cleanExtensionsBuildTask, -@@ -641,4 +652,14 @@ BUILD_TARGETS.forEach(buildTarget => { +@@ -685,4 +696,14 @@ BUILD_TARGETS.forEach(buildTarget => { minified ? minifyVSCodeTask : bundleVSCodeTask, + )); -+ gulp.task(prepackTask); ++ task.task(prepackTask); + + const packingTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}-packing`, task.series( vscodeTaskCI )); -+ gulp.task(packingTask); ++ task.task(packingTask); + + vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( + prepackTask, diff --git a/patches/51-build-disable-non-ascii.patch b/patches/51-build-disable-non-ascii.patch new file mode 100644 index 00000000000..2cef7a54f5c --- /dev/null +++ b/patches/51-build-disable-non-ascii.patch @@ -0,0 +1,23 @@ +diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts +index 21a17ee1..660620f8 100644 +--- a/build/lib/optimize.ts ++++ b/build/lib/optimize.ts +@@ -251,13 +251,8 @@ export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) => + const sourceMapFile = res.outputFiles.find(f => /\.(js|css)\.map$/.test(f.path))!; +- + const contents = Buffer.from(jsOrCSSFile.contents); +- const unicodeMatch = contents.toString().match(/[^\x00-\xFF]+/g); +- if (unicodeMatch) { +- cb(new Error(`Found non-ascii character ${unicodeMatch[0]} in the minified output of ${f.path}. Non-ASCII characters in the output can cause performance problems when loading. Please review if you have introduced a regular expression that esbuild is not automatically converting and convert it to using unicode escape sequences.`)); +- } else { +- f.contents = contents; +- f.sourceMap = JSON.parse(sourceMapFile.text); +- +- cb(undefined, f); +- } ++ ++ f.contents = contents; ++ f.sourceMap = JSON.parse(sourceMapFile.text); ++ ++ cb(undefined, f); + }, cb); diff --git a/patches/51-ext-copilot-remove-it.json b/patches/52-ext-copilot-remove-it.json similarity index 62% rename from patches/51-ext-copilot-remove-it.json rename to patches/52-ext-copilot-remove-it.json index a1cb8572ddc..58ddf28a98f 100644 --- a/patches/51-ext-copilot-remove-it.json +++ b/patches/52-ext-copilot-remove-it.json @@ -4,7 +4,12 @@ "paths": [ "extensions/copilot", "src/vs/platform/agentHost/node/claude", + "src/vs/platform/agentHost/node/codex", "src/vs/platform/agentHost/node/otel", + "src/vs/platform/agentHost/test/node/clientTools", + "src/vs/platform/agentHost/test/node/codex", + "src/vs/platform/agentHost/test/node/customizations", + "src/vs/platform/agentHost/test/node/otel", "src/vs/platform/agentHost/node/copilot/copilotAgent.ts", "src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts", "src/vs/platform/agentHost/node/copilot/copilotGitProject.ts", @@ -40,12 +45,30 @@ "src/vs/platform/agentHost/test/node/claudeSubagentSignals.test.ts", "src/vs/platform/agentHost/test/node/anthropicBetas.test.ts", "src/vs/platform/agentHost/test/node/claudeInteractiveTools.test.ts", - "src/vs/platform/agentHost/test/node/claudeMaterializer.test.ts", "src/vs/platform/agentHost/test/node/claudeModelId.test.ts", "src/vs/platform/agentHost/test/node/claudeProxyAuth.test.ts", "src/vs/platform/agentHost/test/node/claudeSubagentRegistry.test.ts", "src/vs/platform/agentHost/test/node/claudeToolDisplay.test.ts", - "src/vs/platform/agentHost/test/node/otel/agentHostOTelService.integrationTest.ts" + "src/vs/platform/agentHost/test/node/shared/copilotApiService.integrationTest.ts", + "src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts", + "src/vs/platform/agentHost/test/node/forwardedChatError.test.ts", + "src/vs/platform/agentHost/test/node/copilotTestEvents.ts", + "src/vs/platform/agentHost/test/node/claudeToolCallRegistry.test.ts", + "src/vs/platform/agentHost/test/node/claudeServerToolMcpServer.test.ts", + "src/vs/platform/agentHost/test/node/claudeSdkOptions.test.ts", + "src/vs/platform/agentHost/test/node/agentSdkDownloader.test.ts", + "src/vs/platform/agentHost/test/node/agentHostSessionTitleController.test.ts", + "src/vs/platform/agentHost/test/node/agentHostCommitOperationHandler.test.ts", + "src/vs/platform/agentHost/node/copilot/prompts/systemMessage.ts", + "src/vs/platform/agentHost/test/node/agentHostPromptRegistry.test.ts", + "src/vs/platform/agentHost/node/copilot/prompts/anthropicPrompt.ts", + "src/vs/platform/agentHost/node/copilot/prompts/allPrompts.ts", + "src/vs/platform/agentHost/node/copilot/copilotSystemNotification.ts", + "src/vs/platform/agentHost/node/copilot/copilotSessionLauncher.ts", + "src/vs/platform/agentHost/node/copilot/copilotBranchNameGenerator.ts", + "src/vs/platform/agentHost/node/agentHostCommitOperationHandler.ts", + "src/vs/platform/agentHost/node/agentHostCommitOperationProvider.ts", + "src/vs/platform/agentHost/test/node/agentHostCommitOperationProvider.test.ts" ] } ] diff --git a/patches/52-ext-copilot-remove-it.patch b/patches/52-ext-copilot-remove-it.patch deleted file mode 100644 index 8180eb5ef2c..00000000000 --- a/patches/52-ext-copilot-remove-it.patch +++ /dev/null @@ -1,499 +0,0 @@ -diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts -index 3c0e3a16..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, prepareBuiltInCopilotRipgrepShim } 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,12 +463,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { - --function prepareCopilotRipgrepShimTaskREH(platform: string, arch: string, destinationFolderName: string) { -- return async () => { -- const outputDir = path.join(BUILD_ROOT, destinationFolderName); -- const nodeModulesDir = path.join(outputDir, 'node_modules'); -- -- const builtInCopilotExtensionDir = path.join(outputDir, 'extensions', 'copilot'); -- prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, nodeModulesDir); -- }; --} -- - /** -@@ -524,3 +512,2 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { - packageTask(type, platform, arch, sourceFolderName, destinationFolderName), -- prepareCopilotRipgrepShimTaskREH(platform, arch, destinationFolderName) - ]; -@@ -538,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 25aa6a57..7615c27a 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, prepareBuiltInCopilotRipgrepShim } from './lib/copilot.ts'; - import { useEsbuildTranspile } from './buildConfig.ts'; -@@ -337,3 +336,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) -@@ -344,3 +342,2 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d - '**/@vscode/ripgrep/bin/*', -- '**/@github/copilot-*/**', - '**/node-pty/build/Release/*', -@@ -559,19 +556,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { - --function prepareCopilotRipgrepShimTask(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'); -- -- const builtInCopilotExtensionDir = path.join(appBase, 'extensions', 'copilot'); -- prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, appNodeModulesDir); -- }; --} -- - const buildRoot = path.dirname(root); -@@ -601,3 +581,2 @@ BUILD_TARGETS.forEach(buildTarget => { - packageTask(platform, arch, sourceFolderName, destinationFolderName, opts), -- prepareCopilotRipgrepShimTask(platform, arch, destinationFolderName) - ]; -@@ -628,3 +607,2 @@ BUILD_TARGETS.forEach(buildTarget => { - compileNonNativeExtensionsBuildTask, -- compileCopilotExtensionBuildTask, - compileExtensionMediaBuildTask, -@@ -649,3 +627,2 @@ BUILD_TARGETS.forEach(buildTarget => { - compileNonNativeExtensionsBuildTask, -- compileCopilotExtensionBuildTask, - compileExtensionMediaBuildTask, -diff --git a/build/npm/dirs.ts b/build/npm/dirs.ts -index 289a4697..8c6df6f7 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 dc154d4b..834a4a78 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 68f23433..eea135bb 100644 ---- a/package-lock.json -+++ b/package-lock.json -@@ -12,5 +12,2 @@ - "dependencies": { -- "@anthropic-ai/sdk": "^0.82.0", -- "@github/copilot": "1.0.39", -- "@github/copilot-sdk": "^0.3.0", - "@microsoft/1ds-core-js": "^3.2.13", -@@ -340,22 +337,2 @@ - }, -- "node_modules/@anthropic-ai/sdk": { -- "version": "0.82.0", -- "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.82.0.tgz", -- "integrity": "sha512-xdHTjL1GlUlDugHq/I47qdOKp/ROPvuHl7ROJCgUQigbvPu7asf9KcAcU1EqdrP2LuVhEKaTs7Z+ShpZDRzHdQ==", -- "license": "MIT", -- "dependencies": { -- "json-schema-to-ts": "^3.1.1" -- }, -- "bin": { -- "anthropic-ai-sdk": "bin/cli" -- }, -- "peerDependencies": { -- "zod": "^3.25.0 || ^4.0.0" -- }, -- "peerDependenciesMeta": { -- "zod": { -- "optional": true -- } -- } -- }, - "node_modules/@azure-rest/ai-translation-text": { -@@ -880,2 +857,3 @@ - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", -+ "dev": true, - "license": "MIT", -@@ -1152,138 +1130,2 @@ - }, -- "node_modules/@github/copilot": { -- "version": "1.0.39", -- "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.39.tgz", -- "integrity": "sha512-AY0VPYf6QQm88wUcOav2B36iedWKBUaMegKRxxY2uIHESiU6HueEuQR/n7D3U2UdD0zLox3jFRjYbZAsr2CgkQ==", -- "license": "SEE LICENSE IN LICENSE.md", -- "bin": { -- "copilot": "npm-loader.js" -- }, -- "optionalDependencies": { -- "@github/copilot-darwin-arm64": "1.0.39", -- "@github/copilot-darwin-x64": "1.0.39", -- "@github/copilot-linux-arm64": "1.0.39", -- "@github/copilot-linux-x64": "1.0.39", -- "@github/copilot-win32-arm64": "1.0.39", -- "@github/copilot-win32-x64": "1.0.39" -- } -- }, -- "node_modules/@github/copilot-darwin-arm64": { -- "version": "1.0.39", -- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.39.tgz", -- "integrity": "sha512-E8WfNL43NMzMTDDpCiYikaEmYCMAr6mz8LHrJtkaFuVXVkBr/q2NI3hAtwHFy8M11Fac/MeIe3/VEymWwwh3kw==", -- "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.39", -- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.39.tgz", -- "integrity": "sha512-0zbC4lDVX7l8Wvq+JSCMjO0xTN69nWLejTBCl3Ev5bP6P+/7wPURcUvZKoHEaXxOULQ3AGj0DwZNAsvvQkA/6Q==", -- "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.39", -- "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.39.tgz", -- "integrity": "sha512-x88FuByweJlHlAmUZXjq4JlmtqgoM57Fe7nXzQkGr2Y5wnc2EDydBzFYEOlYDSWozQreimaJIm0KEMAA5T8/Fg==", -- "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.39", -- "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.39.tgz", -- "integrity": "sha512-ssahg8r7a0VCsHVXPRmFFXx70xNAxaTM2SZfG7qPRfFB2OM8gHrW26F2oikTklDF6D+A2MfSAMpzJLBUZbPnhw==", -- "cpu": [ -- "x64" -- ], -- "license": "SEE LICENSE IN LICENSE.md", -- "optional": true, -- "os": [ -- "linux" -- ], -- "bin": { -- "copilot-linux-x64": "copilot" -- } -- }, -- "node_modules/@github/copilot-sdk": { -- "version": "0.3.0", -- "resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-0.3.0.tgz", -- "integrity": "sha512-SUo35k56pzzgYgwmDPHcu7kZxPrzXbH66IWXaEf6pmb94DlA709F82HrrDeja087TL4djJ9OuvRFWWOKCosAsg==", -- "license": "MIT", -- "dependencies": { -- "@github/copilot": "^1.0.36-0", -- "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.39", -- "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.39.tgz", -- "integrity": "sha512-hhBWGZQIywbp6MBxlqMX2GSmHqtUAOGwpo9b0igscecL4i0kz89QNasC+mKiN+zFEHP6I8gggOu87XPI17Io8Q==", -- "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.39", -- "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.39.tgz", -- "integrity": "sha512-0ehlMtBiwKjmfEY3hVZggdn7qrmPMC8ueBQv/b+6UY3SMRS/M/1Y7xkOCwG84NvJsktdSsk3SlQnE2LbkTVpSA==", -- "cpu": [ -- "x64" -- ], -- "license": "SEE LICENSE IN LICENSE.md", -- "optional": true, -- "os": [ -- "win32" -- ], -- "bin": { -- "copilot-win32-x64": "copilot.exe" -- } -- }, - "node_modules/@gulp-sourcemaps/identity-map": { -@@ -12671,2 +12513,3 @@ - "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", -+ "dev": true, - "license": "MIT", -@@ -18557,2 +18400,3 @@ - "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", -+ "dev": true, - "license": "MIT" -@@ -19596,11 +19440,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": { -diff --git a/package.json b/package.json -index 5defb1ef..04990246 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:chat-leak": "node scripts/chat-simulation/test-chat-mem-leaks.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/sdk": "^0.82.0", -- "@github/copilot": "1.0.39", -- "@github/copilot-sdk": "^0.3.0", - "@microsoft/1ds-core-js": "^3.2.13", -diff --git a/remote/package.json b/remote/package.json -index 92eed6c1..a7cebde8 100644 ---- a/remote/package.json -+++ b/remote/package.json -@@ -5,4 +5,2 @@ - "dependencies": { -- "@github/copilot": "1.0.39", -- "@github/copilot-sdk": "^0.3.0", - "@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 2dc1125a..6a2107fc 100644 ---- a/src/vs/platform/agentHost/node/agentHostMain.ts -+++ b/src/vs/platform/agentHost/node/agentHostMain.ts -@@ -17,3 +17,3 @@ import * as os from 'os'; - import * as inspector from 'inspector'; --import { AgentHostClaudeSdkPathEnvVar, AgentHostIpcChannels, IAgentHostInspectInfo, IAgentHostSocketInfo, IConnectionTrackerService } from '../common/agentService.js'; -+import { AgentHostIpcChannels, IAgentHostInspectInfo, IAgentHostSocketInfo, IConnectionTrackerService } from '../common/agentService.js'; - import { AgentService } from './agentService.js'; -@@ -22,9 +22,2 @@ import { IAgentHostCompletions } from './agentHostCompletions.js'; - import { IAgentHostTerminalManager } from './agentHostTerminalManager.js'; --import { CopilotAgent } from './copilot/copilotAgent.js'; --import { CopilotApiService, ICopilotApiService } from './shared/copilotApiService.js'; --import { ClaudeAgent } from './claude/claudeAgent.js'; --import { ClaudeAgentSdkService, IClaudeAgentSdkService } from './claude/claudeAgentSdkService.js'; --import { ClaudeProxyService, IClaudeProxyService } from './claude/claudeProxyService.js'; --import { IAgentHostOTelService } from '../common/otel/agentHostOTelService.js'; --import { AgentHostOTelService } from './otel/agentHostOTelService.js'; - import { ProtocolServerHandler } from './protocolServerHandler.js'; -@@ -125,10 +118,2 @@ async function startAgentHost(): Promise { - diServices.set(IAgentHostGitService, gitService); -- const copilotApiService = instantiationService.createInstance(CopilotApiService, undefined); -- diServices.set(ICopilotApiService, copilotApiService); -- const claudeProxyService = disposables.add(instantiationService.createInstance(ClaudeProxyService)); -- diServices.set(IClaudeProxyService, claudeProxyService); -- const claudeAgentSdkService = instantiationService.createInstance(ClaudeAgentSdkService); -- diServices.set(IClaudeAgentSdkService, claudeAgentSdkService); -- const agentHostOTelService = disposables.add(instantiationService.createInstance(AgentHostOTelService)); -- diServices.set(IAgentHostOTelService, agentHostOTelService); - agentService = new AgentService(logService, fileService, sessionDataService, productService, gitService, rootConfigResource, telemetryService); -@@ -142,11 +127,2 @@ async function startAgentHost(): Promise { - diServices.set(IAgentHostCompletions, agentService.completionsService); -- agentService.registerProvider(instantiationService.createInstance(CopilotAgent)); -- // The Claude agent provider is opt-in. Gated on the -- // `chat.agentHost.claudeAgent.path` workbench setting being non-empty, -- // forwarded by the agent host starters as `VSCODE_AGENT_HOST_CLAUDE_SDK_PATH`. -- // The SDK is intentionally not bundled with VS Code; the env var holds the -- // absolute path to a locally-installed `@anthropic-ai/claude-agent-sdk` package. -- if (process.env[AgentHostClaudeSdkPathEnvVar]) { -- agentService.registerProvider(instantiationService.createInstance(ClaudeAgent)); -- } - } catch (err) { -diff --git a/src/vs/platform/agentHost/node/agentHostServerMain.ts b/src/vs/platform/agentHost/node/agentHostServerMain.ts -index 5f8f2c88..89b918a2 100644 ---- a/src/vs/platform/agentHost/node/agentHostServerMain.ts -+++ b/src/vs/platform/agentHost/node/agentHostServerMain.ts -@@ -33,9 +33,2 @@ import { InstantiationService } from '../../instantiation/common/instantiationSe - import { ServiceCollection } from '../../instantiation/common/serviceCollection.js'; --import { CopilotAgent } from './copilot/copilotAgent.js'; --import { CopilotApiService, ICopilotApiService } from './shared/copilotApiService.js'; --import { ClaudeAgent } from './claude/claudeAgent.js'; --import { ClaudeAgentSdkService, IClaudeAgentSdkService } from './claude/claudeAgentSdkService.js'; --import { ClaudeProxyService, IClaudeProxyService } from './claude/claudeProxyService.js'; --import { IAgentHostOTelService } from '../common/otel/agentHostOTelService.js'; --import { AgentHostOTelService } from './otel/agentHostOTelService.js'; - import { AgentService } from './agentService.js'; -@@ -215,23 +208,2 @@ async function main(): Promise { - diServices.set(IAgentHostGitService, gitService); -- // Register `ICopilotApiService` BEFORE `IClaudeProxyService` — -- // the proxy service constructor requires it. -- const copilotApiService = instantiationService.createInstance(CopilotApiService, undefined); -- diServices.set(ICopilotApiService, copilotApiService); -- const claudeProxyService = disposables.add(instantiationService.createInstance(ClaudeProxyService)); -- diServices.set(IClaudeProxyService, claudeProxyService); -- const claudeAgentSdkService = instantiationService.createInstance(ClaudeAgentSdkService); -- diServices.set(IClaudeAgentSdkService, claudeAgentSdkService); -- const agentHostOTelService = disposables.add(instantiationService.createInstance(AgentHostOTelService)); -- diServices.set(IAgentHostOTelService, agentHostOTelService); -- const copilotAgent = disposables.add(instantiationService.createInstance(CopilotAgent)); -- agentService.registerProvider(copilotAgent); -- log('CopilotAgent registered'); -- if (options.claudeSdkPath) { -- // `ClaudeAgentSdkService` reads `AgentHostClaudeSdkPathEnvVar` directly, -- // so make sure it is set even if the path was provided via CLI flag. -- process.env[AgentHostClaudeSdkPathEnvVar] = options.claudeSdkPath; -- const claudeAgent = disposables.add(instantiationService.createInstance(ClaudeAgent)); -- agentService.registerProvider(claudeAgent); -- log('ClaudeAgent registered'); -- } - } -diff --git a/src/vs/platform/agentHost/test/node/mockAgent.ts b/src/vs/platform/agentHost/test/node/mockAgent.ts -index 249aa4bc..6c110b8e 100644 ---- a/src/vs/platform/agentHost/test/node/mockAgent.ts -+++ b/src/vs/platform/agentHost/test/node/mockAgent.ts -@@ -12,3 +12,2 @@ import { type ISyncedCustomization } from '../../common/agentPluginManager.js'; - import { AgentSession, type AgentProvider, type AgentSignal, type IAgent, type IAgentActionSignal, type IAgentCreateSessionConfig, type IAgentCreateSessionResult, type IAgentDescriptor, type IAgentModelInfo, type IAgentResolveSessionConfigParams, type IAgentSessionConfigCompletionsParams, type IAgentSessionMetadata, type IAgentToolPendingConfirmationSignal } from '../../common/agentService.js'; --import { buildSubagentTurnsFromHistory, buildTurnsFromHistory, type IHistoryRecord } from './historyRecordFixtures.js'; - import { ProtectedResourceMetadata, type MessageAttachment, type ModelSelection } from '../../common/state/protocol/state.js'; -@@ -16,3 +15,3 @@ import type { ResolveSessionConfigResult, SessionConfigCompletionsResult } from - import { ActionType } from '../../common/state/sessionActions.js'; --import { CustomizationStatus, ResponsePartKind, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, parseSubagentSessionUri, type CustomizationRef, type PendingMessage, type SessionCustomization, type StringOrMarkdown, type ToolCallResult, type Turn, type UsageInfo } from '../../common/state/sessionState.js'; -+import { CustomizationStatus, ResponsePartKind, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, type CustomizationRef, type PendingMessage, type SessionCustomization, type StringOrMarkdown, type ToolCallResult, type Turn, type UsageInfo } from '../../common/state/sessionState.js'; - import { hasKey } from '../../../../base/common/types.js'; -@@ -65,10 +64,2 @@ export class MockAgent implements IAgent { - -- /** -- * Configurable session history. Tests construct {@link IHistoryRecord} -- * entries (the agent-internal intermediate shape) and the mock converts -- * them to {@link Turn}s on demand. Subagent URIs are routed to filtered -- * subagent turns via {@link buildSubagentTurnsFromHistory}. -- */ -- sessionMessages: IHistoryRecord[] = []; -- - /** Optional overrides applied to session metadata from listSessions. */ -@@ -134,7 +125,3 @@ export class MockAgent implements IAgent { - async getSessionMessages(session: URI): Promise { -- const subagentInfo = parseSubagentSessionUri(session); -- if (subagentInfo) { -- return buildSubagentTurnsFromHistory(this.sessionMessages, subagentInfo.toolCallId, session.toString()); -- } -- return buildTurnsFromHistory(this.sessionMessages); -+ return [] - } -@@ -245,9 +232,2 @@ export class ScriptedMockAgent implements IAgent { - */ -- private readonly _preExistingMessages: IHistoryRecord[] = [ -- { type: 'message', role: 'user', session: PRE_EXISTING_SESSION_URI, messageId: 'h-msg-1', content: 'What files are here?' }, -- { type: 'tool_start', session: PRE_EXISTING_SESSION_URI, toolCallId: 'h-tc-1', toolName: 'list_files', displayName: 'List Files', invocationMessage: 'Listing files...' }, -- { type: 'tool_complete', session: PRE_EXISTING_SESSION_URI, toolCallId: 'h-tc-1', result: { pastTenseMessage: 'Listed files', content: [{ type: ToolResultContentType.Text, text: 'file1.ts\nfile2.ts' }], success: true } satisfies ToolCallResult }, -- { type: 'message', role: 'assistant', session: PRE_EXISTING_SESSION_URI, messageId: 'h-msg-2', content: 'Here are the files: file1.ts and file2.ts' }, -- ]; -- - // Track pending permission requests -@@ -685,9 +665,2 @@ export class ScriptedMockAgent implements IAgent { - async getSessionMessages(session: URI): Promise { -- const subagentInfo = parseSubagentSessionUri(session); -- if (subagentInfo) { -- return buildSubagentTurnsFromHistory(this._preExistingMessages, subagentInfo.toolCallId, session.toString()); -- } -- if (session.toString() === PRE_EXISTING_SESSION_URI.toString()) { -- return buildTurnsFromHistory(this._preExistingMessages); -- } - return []; diff --git a/patches/53-ext-copilot-remove-it.patch b/patches/53-ext-copilot-remove-it.patch new file mode 100644 index 00000000000..e1d1fb14c4d --- /dev/null +++ b/patches/53-ext-copilot-remove-it.patch @@ -0,0 +1,1489 @@ +diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts +index 62c30da5..8354a843 100644 +--- a/build/gulpfile.reh.ts ++++ b/build/gulpfile.reh.ts +@@ -25,3 +25,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'; +@@ -31,3 +31,3 @@ import buildfile from './buildfile.ts'; + import { fetchUrls, fetchGithub } from './lib/fetch.ts'; +-import { getCopilotExcludeFilter, getCopilotRuntimePrebuildFiles, getCopilotTgrepExcludeFilter, getRipgrepExcludeFilter, prepareBuiltInCopilotRipgrepShim } from './lib/copilot.ts'; ++import { getRipgrepExcludeFilter } from './lib/copilot.ts'; + import { readAgentSdkResults } from './agent-sdk/common.ts'; +@@ -397,6 +397,3 @@ function packageTask(type: string, platform: string, arch: string, sourceFolderN + .pipe(util.cleanNodeModules(path.join(import.meta.dirname, `.moduleignore.${process.platform}`))); +- const copilotRuntimePrebuilds = gulp.src(getCopilotRuntimePrebuildFiles(platform, arch, 'remote/node_modules'), { base: 'remote', dot: true, allowEmpty: true }); +- const deps = es.merge(cleanedDeps, copilotRuntimePrebuilds) +- .pipe(filter(getCopilotExcludeFilter(platform, arch))) +- .pipe(filter(getCopilotTgrepExcludeFilter(platform, arch))) ++ const deps = cleanedDeps + .pipe(filter(getRipgrepExcludeFilter(platform, arch))) +@@ -555,12 +552,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { + +-function prepareCopilotRipgrepShimTaskREH(platform: string, arch: string, destinationFolderName: string) { +- return async () => { +- const outputDir = path.join(BUILD_ROOT, destinationFolderName); +- const nodeModulesDir = path.join(outputDir, 'node_modules'); +- +- const builtInCopilotExtensionDir = path.join(outputDir, 'extensions', 'copilot'); +- prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, nodeModulesDir); +- }; +-} +- + /** +@@ -614,3 +601,2 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { + packageTask(type, platform, arch, sourceFolderName, destinationFolderName), +- prepareCopilotRipgrepShimTaskREH(platform, arch, destinationFolderName) + ]; +@@ -628,3 +614,2 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { + compileNonNativeExtensionsBuildTask, +- compileCopilotExtensionBuildTask, + compileExtensionMediaBuildTask, +diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts +index b897c6c6..97e316a3 100644 +--- a/build/gulpfile.vscode.ts ++++ b/build/gulpfile.vscode.ts +@@ -28,5 +28,5 @@ 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, getCopilotRuntimePrebuildFiles, getCopilotTgrepExcludeFilter, getRipgrepExcludeFilter, prepareBuiltInCopilotRipgrepShim } from './lib/copilot.ts'; ++import { getRipgrepExcludeFilter } from './lib/copilot.ts'; + import { readAgentSdkResults } from './agent-sdk/common.ts'; +@@ -342,6 +342,3 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d + .pipe(util.cleanNodeModules(path.join(import.meta.dirname, `.moduleignore.${process.platform}`))); +- const copilotRuntimePrebuilds = gulp.src(getCopilotRuntimePrebuildFiles(platform, arch), { base: '.', dot: true, allowEmpty: true }); +- const deps = es.merge(cleanedDeps, copilotRuntimePrebuilds) +- .pipe(filter(getCopilotExcludeFilter(platform, arch))) +- .pipe(filter(getCopilotTgrepExcludeFilter(platform, arch))) ++ const deps = cleanedDeps + .pipe(filter(getRipgrepExcludeFilter(platform, arch))) +@@ -603,19 +600,2 @@ function patchWin32DependenciesTask(destinationFolderName: string) { + +-function prepareCopilotRipgrepShimTask(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'); +- +- const builtInCopilotExtensionDir = path.join(appBase, 'extensions', 'copilot'); +- prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, appNodeModulesDir); +- }; +-} +- + const buildRoot = path.dirname(root); +@@ -645,3 +625,2 @@ BUILD_TARGETS.forEach(buildTarget => { + packageTask(platform, arch, sourceFolderName, destinationFolderName, opts), +- prepareCopilotRipgrepShimTask(platform, arch, destinationFolderName) + ]; +@@ -656,2 +635,3 @@ BUILD_TARGETS.forEach(buildTarget => { + let vscodeTask: task.Task; ++ + if (useEsbuildTranspile) { +@@ -693,3 +673,2 @@ BUILD_TARGETS.forEach(buildTarget => { + compileNonNativeExtensionsBuildTask, +- compileCopilotExtensionBuildTask, + compileExtensionMediaBuildTask, +@@ -709,2 +688,3 @@ BUILD_TARGETS.forEach(buildTarget => { + } ++ + task.task(vscodeTask); +diff --git a/build/npm/dirs.ts b/build/npm/dirs.ts +index 289a4697..8c6df6f7 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 0d00ac39..23e23c05 100644 +--- a/build/npm/postinstall.ts ++++ b/build/npm/postinstall.ts +@@ -320,33 +320,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 81d315f0..81314f3a 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -12,5 +12,2 @@ + "dependencies": { +- "@anthropic-ai/sdk": "^0.82.0", +- "@github/copilot": "^1.0.64-0", +- "@github/copilot-sdk": "^1.0.2", + "@microsoft/1ds-core-js": "^3.2.13", +@@ -323,22 +320,2 @@ + }, +- "node_modules/@anthropic-ai/sdk": { +- "version": "0.82.0", +- "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.82.0.tgz", +- "integrity": "sha512-xdHTjL1GlUlDugHq/I47qdOKp/ROPvuHl7ROJCgUQigbvPu7asf9KcAcU1EqdrP2LuVhEKaTs7Z+ShpZDRzHdQ==", +- "license": "MIT", +- "dependencies": { +- "json-schema-to-ts": "^3.1.1" +- }, +- "bin": { +- "anthropic-ai-sdk": "bin/cli" +- }, +- "peerDependencies": { +- "zod": "^3.25.0 || ^4.0.0" +- }, +- "peerDependenciesMeta": { +- "zod": { +- "optional": true +- } +- } +- }, + "node_modules/@azure-rest/ai-translation-text": { +@@ -850,11 +827,2 @@ + }, +- "node_modules/@babel/runtime": { +- "version": "7.29.2", +- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", +- "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", +- "license": "MIT", +- "engines": { +- "node": ">=6.9.0" +- } +- }, + "node_modules/@babel/template": { +@@ -1086,188 +1054,2 @@ + }, +- "node_modules/@github/copilot": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.64-0.tgz", +- "integrity": "sha512-PlH7ByBHjmPLqLXS4CE2q8hN6CFEfkCMV6ScBEzW/u73+KYQB4fGNouo8Lr8okL6D5CW5rzPJbsXyISyJqVOZg==", +- "license": "SEE LICENSE IN LICENSE.md", +- "dependencies": { +- "detect-libc": "^2.1.2", +- "os-theme": "^0.0.8" +- }, +- "bin": { +- "copilot": "npm-loader.js" +- }, +- "optionalDependencies": { +- "@github/copilot-darwin-arm64": "1.0.64-0", +- "@github/copilot-darwin-x64": "1.0.64-0", +- "@github/copilot-linux-arm64": "1.0.64-0", +- "@github/copilot-linux-x64": "1.0.64-0", +- "@github/copilot-linuxmusl-arm64": "1.0.64-0", +- "@github/copilot-linuxmusl-x64": "1.0.64-0", +- "@github/copilot-win32-arm64": "1.0.64-0", +- "@github/copilot-win32-x64": "1.0.64-0" +- } +- }, +- "node_modules/@github/copilot-darwin-arm64": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.64-0.tgz", +- "integrity": "sha512-97DUGiuYrkCYOlSSLWMmr+K0uGzAxz1JOL/GyO/7mNl6V/1xgs6Van1Jj+Dpj4ly96iHE8lUIW8cQNCG66644g==", +- "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.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.64-0.tgz", +- "integrity": "sha512-2PXY4mSFtIjFdRaAt8PakegRgGtf6Sz9z6U/dIgVygNfctVNzaL5FH65PNPm8Y80jaDvEcz1/XY5MiQtxnlzZQ==", +- "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.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.64-0.tgz", +- "integrity": "sha512-PLP+vR508fOTlCr9CSZiXi9geicHKXuX9jLGdwNqK2TMZO5TqCLz8wP7dBEmkdkeXcFKovMb8nQVB1Toc6xutw==", +- "cpu": [ +- "arm64" +- ], +- "libc": [ +- "glibc" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linux-arm64": "copilot" +- } +- }, +- "node_modules/@github/copilot-linux-x64": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.64-0.tgz", +- "integrity": "sha512-NvVjQ69zr390ijzo2f75+v0DHm6xnvPbi67ugnKDk7ZPbx8P3vSxVdAnrzrrL4T3T8ng3pJANcC4p+eGbx+UDw==", +- "cpu": [ +- "x64" +- ], +- "libc": [ +- "glibc" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linux-x64": "copilot" +- } +- }, +- "node_modules/@github/copilot-linuxmusl-arm64": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.64-0.tgz", +- "integrity": "sha512-qCnVF5vIcTO74CukAENZo8e5nqXm4QUshuKN69aiZb5GOhVvyyIKsf5Jo7ikZt54jJBHycAMUKlTA8L3/nK+KA==", +- "cpu": [ +- "arm64" +- ], +- "libc": [ +- "musl" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linuxmusl-arm64": "copilot" +- } +- }, +- "node_modules/@github/copilot-linuxmusl-x64": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.64-0.tgz", +- "integrity": "sha512-WDBEmkBk1RulTfdLK5IuttNBadjLOBpvQyonGQ/aLeaetRNNdapoygrSjFU7q1QBSenmCyanXH6D+TS7tP3Qsw==", +- "cpu": [ +- "x64" +- ], +- "libc": [ +- "musl" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linuxmusl-x64": "copilot" +- } +- }, +- "node_modules/@github/copilot-sdk": { +- "version": "1.0.2", +- "resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-1.0.2.tgz", +- "integrity": "sha512-JJDsGM/bA1LGy1Ro/8iC8RLpKsLmuiFdQ67oFAVfi0Hfxyx289teHwmM70ehK76DXBkrPqqcxcliJi56k1ggFA==", +- "license": "MIT", +- "dependencies": { +- "@github/copilot": "^1.0.64-0", +- "vscode-jsonrpc": "^8.2.1", +- "zod": "^4.3.6" +- }, +- "engines": { +- "node": "^20.19.0 || >=22.12.0" +- } +- }, +- "node_modules/@github/copilot-sdk/node_modules/zod": { +- "version": "4.4.3", +- "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", +- "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", +- "license": "MIT", +- "funding": { +- "url": "https://github.com/sponsors/colinhacks" +- } +- }, +- "node_modules/@github/copilot-win32-arm64": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.64-0.tgz", +- "integrity": "sha512-PC7yuUKcVbhli4bpzWFVT3juxj+v/iONazetNe3tMpHWza3W7MeFRifzAseSErKQCt2fHJth3m8bQAwFN2jfrA==", +- "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.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.64-0.tgz", +- "integrity": "sha512-d2fnUTIlqNxCqS2PuV+FD99ZOYBaX72OLtAmphbKyz36KyZ6D4ssiu8M4vHVTKWWdyc3TWiLsnIB+ryWdv1gGw==", +- "cpu": [ +- "x64" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "win32" +- ], +- "bin": { +- "copilot-win32-x64": "copilot.exe" +- } +- }, + "node_modules/@gulp-sourcemaps/identity-map": { +@@ -2343,41 +2125,2 @@ + }, +- "node_modules/@os-theme/darwin-arm64": { +- "version": "0.0.8", +- "resolved": "https://registry.npmjs.org/@os-theme/darwin-arm64/-/darwin-arm64-0.0.8.tgz", +- "integrity": "sha512-gMsOs+8Ju396a5yyMWigkbA0dMTxD78U3HzG3mlpiAyn6hfd5dbyI4VGP+sfTB82KGgWLzIhWWTFX5UYY6iX0A==", +- "cpu": [ +- "arm64" +- ], +- "license": "MIT", +- "optional": true, +- "os": [ +- "darwin" +- ] +- }, +- "node_modules/@os-theme/linux-x64": { +- "version": "0.0.8", +- "resolved": "https://registry.npmjs.org/@os-theme/linux-x64/-/linux-x64-0.0.8.tgz", +- "integrity": "sha512-zvjmBUiSQPjM1RbhpsfCDYMJxW4eLlGmkFPnpteC/03X2lz6CjiX2hfbN2EWLxXjNnIje3Jqaen8IsqEnWrRBg==", +- "cpu": [ +- "x64" +- ], +- "license": "MIT", +- "optional": true, +- "os": [ +- "linux" +- ] +- }, +- "node_modules/@os-theme/win32-x64": { +- "version": "0.0.8", +- "resolved": "https://registry.npmjs.org/@os-theme/win32-x64/-/win32-x64-0.0.8.tgz", +- "integrity": "sha512-N3yxKNbVl2IBa/ncDuq55QhwqwUjnYLJxDKMEmYeJbLIV950qZNojPw3scXA6PbfxPZfIiRa8iz1pzNg9XxP8w==", +- "cpu": [ +- "x64" +- ], +- "license": "MIT", +- "optional": true, +- "os": [ +- "win32" +- ] +- }, + "node_modules/@parcel/watcher": { +@@ -12711,15 +12454,2 @@ + }, +- "node_modules/json-schema-to-ts": { +- "version": "3.1.1", +- "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", +- "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", +- "license": "MIT", +- "dependencies": { +- "@babel/runtime": "^7.18.3", +- "ts-algebra": "^2.0.0" +- }, +- "engines": { +- "node": ">=16" +- } +- }, + "node_modules/json-schema-traverse": { +@@ -15030,16 +14760,2 @@ + }, +- "node_modules/os-theme": { +- "version": "0.0.8", +- "resolved": "https://registry.npmjs.org/os-theme/-/os-theme-0.0.8.tgz", +- "integrity": "sha512-u1q3bLSv5uMHNIiPItkfDrHXu6ZFs2juwqxWREFM/uVBa+7Kkhy2v49LmJev2JcinGwqiEccElB/XsH9gwasuA==", +- "license": "MIT", +- "optionalDependencies": { +- "@os-theme/darwin-arm64": "0.0.8", +- "@os-theme/linux-x64": "0.0.8", +- "@os-theme/win32-x64": "0.0.8" +- }, +- "peerDependencies": { +- "typescript": "^5" +- } +- }, + "node_modules/os-tmpdir": { +@@ -18557,8 +18273,2 @@ + }, +- "node_modules/ts-algebra": { +- "version": "2.0.0", +- "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", +- "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", +- "license": "MIT" +- }, + "node_modules/ts-api-utils": { +@@ -19581,11 +19291,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": { +diff --git a/package.json b/package.json +index 36485330..e719f70f 100644 +--- a/package.json ++++ b/package.json +@@ -28,4 +28,4 @@ + "codex:gen-protocol": "node build/codex/generate-protocol.mjs", +- "watch": "npm-run-all2 -lp watch-client-transpile watch-client watch-extensions watch-copilot", +- "watch-transpile": "npm-run-all2 -lp watch-client-transpile watch-extensions watch-copilot", ++ "watch": "npm-run-all2 -lp watch-client-transpile watch-client watch-extensions", ++ "watch-transpile": "npm-run-all2 -lp watch-client-transpile watch-extensions", + "watchd": "deemon npm run watch", +@@ -46,5 +46,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", +@@ -88,4 +85,2 @@ + "perf:chat-leak": "node scripts/chat-simulation/test-chat-mem-leaks.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)", +@@ -96,5 +91,2 @@ + "dependencies": { +- "@anthropic-ai/sdk": "^0.82.0", +- "@github/copilot": "^1.0.64-0", +- "@github/copilot-sdk": "^1.0.2", + "@microsoft/1ds-core-js": "^3.2.13", +diff --git a/remote/package-lock.json b/remote/package-lock.json +index a829c6d5..791019b4 100644 +--- a/remote/package-lock.json ++++ b/remote/package-lock.json +@@ -10,4 +10,2 @@ + "dependencies": { +- "@github/copilot": "^1.0.64-0", +- "@github/copilot-sdk": "^1.0.2", + "@microsoft/1ds-core-js": "^3.2.13", +@@ -61,188 +59,2 @@ + }, +- "node_modules/@github/copilot": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.64-0.tgz", +- "integrity": "sha512-PlH7ByBHjmPLqLXS4CE2q8hN6CFEfkCMV6ScBEzW/u73+KYQB4fGNouo8Lr8okL6D5CW5rzPJbsXyISyJqVOZg==", +- "license": "SEE LICENSE IN LICENSE.md", +- "dependencies": { +- "detect-libc": "^2.1.2", +- "os-theme": "^0.0.8" +- }, +- "bin": { +- "copilot": "npm-loader.js" +- }, +- "optionalDependencies": { +- "@github/copilot-darwin-arm64": "1.0.64-0", +- "@github/copilot-darwin-x64": "1.0.64-0", +- "@github/copilot-linux-arm64": "1.0.64-0", +- "@github/copilot-linux-x64": "1.0.64-0", +- "@github/copilot-linuxmusl-arm64": "1.0.64-0", +- "@github/copilot-linuxmusl-x64": "1.0.64-0", +- "@github/copilot-win32-arm64": "1.0.64-0", +- "@github/copilot-win32-x64": "1.0.64-0" +- } +- }, +- "node_modules/@github/copilot-darwin-arm64": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.64-0.tgz", +- "integrity": "sha512-97DUGiuYrkCYOlSSLWMmr+K0uGzAxz1JOL/GyO/7mNl6V/1xgs6Van1Jj+Dpj4ly96iHE8lUIW8cQNCG66644g==", +- "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.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.64-0.tgz", +- "integrity": "sha512-2PXY4mSFtIjFdRaAt8PakegRgGtf6Sz9z6U/dIgVygNfctVNzaL5FH65PNPm8Y80jaDvEcz1/XY5MiQtxnlzZQ==", +- "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.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.64-0.tgz", +- "integrity": "sha512-PLP+vR508fOTlCr9CSZiXi9geicHKXuX9jLGdwNqK2TMZO5TqCLz8wP7dBEmkdkeXcFKovMb8nQVB1Toc6xutw==", +- "cpu": [ +- "arm64" +- ], +- "libc": [ +- "glibc" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linux-arm64": "copilot" +- } +- }, +- "node_modules/@github/copilot-linux-x64": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.64-0.tgz", +- "integrity": "sha512-NvVjQ69zr390ijzo2f75+v0DHm6xnvPbi67ugnKDk7ZPbx8P3vSxVdAnrzrrL4T3T8ng3pJANcC4p+eGbx+UDw==", +- "cpu": [ +- "x64" +- ], +- "libc": [ +- "glibc" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linux-x64": "copilot" +- } +- }, +- "node_modules/@github/copilot-linuxmusl-arm64": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.64-0.tgz", +- "integrity": "sha512-qCnVF5vIcTO74CukAENZo8e5nqXm4QUshuKN69aiZb5GOhVvyyIKsf5Jo7ikZt54jJBHycAMUKlTA8L3/nK+KA==", +- "cpu": [ +- "arm64" +- ], +- "libc": [ +- "musl" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linuxmusl-arm64": "copilot" +- } +- }, +- "node_modules/@github/copilot-linuxmusl-x64": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.64-0.tgz", +- "integrity": "sha512-WDBEmkBk1RulTfdLK5IuttNBadjLOBpvQyonGQ/aLeaetRNNdapoygrSjFU7q1QBSenmCyanXH6D+TS7tP3Qsw==", +- "cpu": [ +- "x64" +- ], +- "libc": [ +- "musl" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "linux" +- ], +- "bin": { +- "copilot-linuxmusl-x64": "copilot" +- } +- }, +- "node_modules/@github/copilot-sdk": { +- "version": "1.0.2", +- "resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-1.0.2.tgz", +- "integrity": "sha512-JJDsGM/bA1LGy1Ro/8iC8RLpKsLmuiFdQ67oFAVfi0Hfxyx289teHwmM70ehK76DXBkrPqqcxcliJi56k1ggFA==", +- "license": "MIT", +- "dependencies": { +- "@github/copilot": "^1.0.64-0", +- "vscode-jsonrpc": "^8.2.1", +- "zod": "^4.3.6" +- }, +- "engines": { +- "node": "^20.19.0 || >=22.12.0" +- } +- }, +- "node_modules/@github/copilot-sdk/node_modules/zod": { +- "version": "4.4.3", +- "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", +- "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", +- "license": "MIT", +- "funding": { +- "url": "https://github.com/sponsors/colinhacks" +- } +- }, +- "node_modules/@github/copilot-win32-arm64": { +- "version": "1.0.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.64-0.tgz", +- "integrity": "sha512-PC7yuUKcVbhli4bpzWFVT3juxj+v/iONazetNe3tMpHWza3W7MeFRifzAseSErKQCt2fHJth3m8bQAwFN2jfrA==", +- "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.64-0", +- "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.64-0.tgz", +- "integrity": "sha512-d2fnUTIlqNxCqS2PuV+FD99ZOYBaX72OLtAmphbKyz36KyZ6D4ssiu8M4vHVTKWWdyc3TWiLsnIB+ryWdv1gGw==", +- "cpu": [ +- "x64" +- ], +- "license": "SEE LICENSE IN LICENSE.md", +- "optional": true, +- "os": [ +- "win32" +- ], +- "bin": { +- "copilot-win32-x64": "copilot.exe" +- } +- }, + "node_modules/@isaacs/fs-minipass": { +@@ -314,41 +126,2 @@ + }, +- "node_modules/@os-theme/darwin-arm64": { +- "version": "0.0.8", +- "resolved": "https://registry.npmjs.org/@os-theme/darwin-arm64/-/darwin-arm64-0.0.8.tgz", +- "integrity": "sha512-gMsOs+8Ju396a5yyMWigkbA0dMTxD78U3HzG3mlpiAyn6hfd5dbyI4VGP+sfTB82KGgWLzIhWWTFX5UYY6iX0A==", +- "cpu": [ +- "arm64" +- ], +- "license": "MIT", +- "optional": true, +- "os": [ +- "darwin" +- ] +- }, +- "node_modules/@os-theme/linux-x64": { +- "version": "0.0.8", +- "resolved": "https://registry.npmjs.org/@os-theme/linux-x64/-/linux-x64-0.0.8.tgz", +- "integrity": "sha512-zvjmBUiSQPjM1RbhpsfCDYMJxW4eLlGmkFPnpteC/03X2lz6CjiX2hfbN2EWLxXjNnIje3Jqaen8IsqEnWrRBg==", +- "cpu": [ +- "x64" +- ], +- "license": "MIT", +- "optional": true, +- "os": [ +- "linux" +- ] +- }, +- "node_modules/@os-theme/win32-x64": { +- "version": "0.0.8", +- "resolved": "https://registry.npmjs.org/@os-theme/win32-x64/-/win32-x64-0.0.8.tgz", +- "integrity": "sha512-N3yxKNbVl2IBa/ncDuq55QhwqwUjnYLJxDKMEmYeJbLIV950qZNojPw3scXA6PbfxPZfIiRa8iz1pzNg9XxP8w==", +- "cpu": [ +- "x64" +- ], +- "license": "MIT", +- "optional": true, +- "os": [ +- "win32" +- ] +- }, + "node_modules/@parcel/watcher": { +@@ -1445,16 +1218,2 @@ + }, +- "node_modules/os-theme": { +- "version": "0.0.8", +- "resolved": "https://registry.npmjs.org/os-theme/-/os-theme-0.0.8.tgz", +- "integrity": "sha512-u1q3bLSv5uMHNIiPItkfDrHXu6ZFs2juwqxWREFM/uVBa+7Kkhy2v49LmJev2JcinGwqiEccElB/XsH9gwasuA==", +- "license": "MIT", +- "optionalDependencies": { +- "@os-theme/darwin-arm64": "0.0.8", +- "@os-theme/linux-x64": "0.0.8", +- "@os-theme/win32-x64": "0.0.8" +- }, +- "peerDependencies": { +- "typescript": "^5" +- } +- }, + "node_modules/pend": { +@@ -1823,11 +1582,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": { +diff --git a/remote/package.json b/remote/package.json +index 879eb7d6..d77adbea 100644 +--- a/remote/package.json ++++ b/remote/package.json +@@ -5,4 +5,2 @@ + "dependencies": { +- "@github/copilot": "^1.0.64-0", +- "@github/copilot-sdk": "^1.0.2", + "@microsoft/1ds-core-js": "^3.2.13", +diff --git a/src/vs/platform/agentHost/common/otel/agentHostOTelService.ts b/src/vs/platform/agentHost/common/otel/agentHostOTelService.ts +index 667e3bc8..be71e31a 100644 +--- a/src/vs/platform/agentHost/common/otel/agentHostOTelService.ts ++++ b/src/vs/platform/agentHost/common/otel/agentHostOTelService.ts +@@ -5,3 +5,2 @@ + +-import type { TelemetryConfig } from '@github/copilot-sdk'; + import type { URI } from '../../../../base/common/uri.js'; +@@ -26,9 +25,2 @@ export interface IAgentHostOTelService { + +- /** +- * Returns the telemetry config to hand to `new CopilotClient({ telemetry })`, +- * starting the loopback receiver + store on first call when in DB mode. +- * Resolves to `undefined` when telemetry is disabled. +- */ +- getSdkTelemetryConfig(): Promise; +- + /** +diff --git a/src/vs/platform/agentHost/node/agentHostChangesetOperationService.ts b/src/vs/platform/agentHost/node/agentHostChangesetOperationService.ts +index 1cba4ca9..27e94912 100644 +--- a/src/vs/platform/agentHost/node/agentHostChangesetOperationService.ts ++++ b/src/vs/platform/agentHost/node/agentHostChangesetOperationService.ts +@@ -19,3 +19,2 @@ import { IInstantiationService } from '../../instantiation/common/instantiation. + import { AgentHostPullRequestOperationContribution } from './agentHostPullRequestOperationProvider.js'; +-import { AgentHostCommitOperationContribution } from './agentHostCommitOperationProvider.js'; + import { AgentHostDiscardChangesOperationContribution } from './agentHostDiscardChangesOperationProvider.js'; +@@ -44,3 +43,2 @@ export class AgentHostChangesetOperationService extends Disposable implements IA + this._register(this.registerContribution(instantiationService.createInstance(AgentHostPullRequestOperationContribution, this._stateManager))); +- this._register(this.registerContribution(instantiationService.createInstance(AgentHostCommitOperationContribution, this._stateManager))); + this._register(this.registerContribution(instantiationService.createInstance(AgentHostDiscardChangesOperationContribution, this._stateManager))); +diff --git a/src/vs/platform/agentHost/node/agentHostMain.ts b/src/vs/platform/agentHost/node/agentHostMain.ts +index 0dd36914..8c6e8585 100644 +--- a/src/vs/platform/agentHost/node/agentHostMain.ts ++++ b/src/vs/platform/agentHost/node/agentHostMain.ts +@@ -17,3 +17,3 @@ import * as os from 'os'; + import * as inspector from 'inspector'; +-import { AgentHostClaudeAgentEnabledEnvVar, AgentHostCodexAgentEnabledEnvVar, AgentHostIpcChannels, IAgentHostInspectInfo, IAgentHostSocketInfo, IAgentService, IConnectionTrackerService, isAgentEnabled } from '../common/agentService.js'; ++import { AgentHostIpcChannels, IAgentHostInspectInfo, IAgentHostSocketInfo, IAgentService, IConnectionTrackerService } from '../common/agentService.js'; + import { AgentService } from './agentService.js'; +@@ -22,13 +22,3 @@ import { IAgentHostCompletions } from './agentHostCompletions.js'; + import { IAgentHostTerminalManager } from './agentHostTerminalManager.js'; +-import { CopilotAgent } from './copilot/copilotAgent.js'; +-import { CopilotBranchNameGenerator, ICopilotBranchNameGenerator } from './copilot/copilotBranchNameGenerator.js'; +-import { CopilotApiService, ICopilotApiService } from './shared/copilotApiService.js'; +-import { ClaudeAgent } from './claude/claudeAgent.js'; +-import { ClaudeAgentSdkService, ClaudeSdkPackage, IClaudeAgentSdkService } from './claude/claudeAgentSdkService.js'; +-import { ClaudeProxyService, IClaudeProxyService } from './claude/claudeProxyService.js'; +-import { CodexAgent, CodexSdkPackage } from './codex/codexAgent.js'; +-import { CodexProxyService, ICodexProxyService } from './codex/codexProxyService.js'; + import { AgentSdkDownloader, IAgentSdkDownloader } from './agentSdkDownloader.js'; +-import { IAgentHostOTelService } from '../common/otel/agentHostOTelService.js'; +-import { AgentHostOTelService } from './otel/agentHostOTelService.js'; + import { ProtocolServerHandler } from './protocolServerHandler.js'; +@@ -164,13 +154,2 @@ async function startAgentHost(): Promise { + diServices.set(IAgentSdkDownloader, agentSdkDownloader); +- const copilotApiService = instantiationService.createInstance(CopilotApiService, undefined); +- diServices.set(ICopilotApiService, copilotApiService); +- diServices.set(ICopilotBranchNameGenerator, instantiationService.createInstance(CopilotBranchNameGenerator)); +- const claudeProxyService = disposables.add(instantiationService.createInstance(ClaudeProxyService)); +- diServices.set(IClaudeProxyService, claudeProxyService); +- const claudeAgentSdkService = instantiationService.createInstance(ClaudeAgentSdkService); +- diServices.set(IClaudeAgentSdkService, claudeAgentSdkService); +- const codexProxyService = disposables.add(instantiationService.createInstance(CodexProxyService)); +- diServices.set(ICodexProxyService, codexProxyService); +- const agentHostOTelService = disposables.add(instantiationService.createInstance(AgentHostOTelService)); +- diServices.set(IAgentHostOTelService, agentHostOTelService); + agentService = new AgentService(logService, fileService, sessionDataService, productService, gitService, checkpointService, rootConfigResource, telemetryService, fileMonitorService); +@@ -186,21 +165,2 @@ async function startAgentHost(): Promise { + diServices.set(IAgentHostCompletions, agentService.completionsService); +- agentService.registerProvider(instantiationService.createInstance(CopilotAgent)); +- // Claude and Codex providers are gated on two things: +- // 1. The user-facing enable toggle (`chat.agentHost.Agent.enabled`, +- // forwarded as an env var by the starters). Claude defaults to on, +- // Codex defaults to off. +- // 2. The SDK being reachable. Claude is a devDependency of this repo +- // so the bare-import path in `ClaudeAgentSdkService._loadSdk` +- // always succeeds in dev; in built products the SDK ships via +- // `product.agentSdks.claude` and the downloader handles it. Codex +- // has no equivalent dev path yet, so it still requires either the +- // env-var override or a `product.agentSdks.codex` entry. +- // If either gate fails, the provider is not registered and never appears +- // in the agent picker (matches the pre-CDN UX exactly). +- if (isAgentEnabled(process.env[AgentHostClaudeAgentEnabledEnvVar], true) && (!environmentService.isBuilt || agentSdkDownloader.isAvailable(ClaudeSdkPackage))) { +- agentService.registerProvider(instantiationService.createInstance(ClaudeAgent)); +- } +- if (isAgentEnabled(process.env[AgentHostCodexAgentEnabledEnvVar], false) && agentSdkDownloader.isAvailable(CodexSdkPackage)) { +- agentService.registerProvider(instantiationService.createInstance(CodexAgent)); +- } + } catch (err) { +diff --git a/src/vs/platform/agentHost/node/agentHostServerMain.ts b/src/vs/platform/agentHost/node/agentHostServerMain.ts +index 5c428d75..95d12fe4 100644 +--- a/src/vs/platform/agentHost/node/agentHostServerMain.ts ++++ b/src/vs/platform/agentHost/node/agentHostServerMain.ts +@@ -35,15 +35,4 @@ import { ServiceCollection } from '../../instantiation/common/serviceCollection. + import { registerAgentHostNetworkServices } from './agentHostBootstrap.js'; +-import { CopilotAgent } from './copilot/copilotAgent.js'; +-import { CopilotBranchNameGenerator, ICopilotBranchNameGenerator } from './copilot/copilotBranchNameGenerator.js'; +-import { CopilotApiService, ICopilotApiService } from './shared/copilotApiService.js'; +-import { ClaudeAgent } from './claude/claudeAgent.js'; +-import { ClaudeAgentSdkService, ClaudeSdkPackage, IClaudeAgentSdkService } from './claude/claudeAgentSdkService.js'; +-import { ClaudeProxyService, IClaudeProxyService } from './claude/claudeProxyService.js'; +-import { CodexAgent, CodexSdkPackage } from './codex/codexAgent.js'; +-import { CodexProxyService, ICodexProxyService } from './codex/codexProxyService.js'; +-import { AgentSdkDownloader, IAgentSdkDownloader } from './agentSdkDownloader.js'; +-import { IAgentHostOTelService } from '../common/otel/agentHostOTelService.js'; +-import { AgentHostOTelService } from './otel/agentHostOTelService.js'; + import { AgentService } from './agentService.js'; +-import { AgentHostClaudeAgentEnabledEnvVar, AgentHostClaudeSdkRootEnvVar, AgentHostCodexAgentEnabledEnvVar, IAgentService, AgentHostCodexAgentSdkRootEnvVar, isAgentEnabled } from '../common/agentService.js'; ++import { AgentHostClaudeSdkRootEnvVar, IAgentService, AgentHostCodexAgentSdkRootEnvVar } from '../common/agentService.js'; + import { IAgentConfigurationService } from './agentConfigurationService.js'; +@@ -260,50 +249,2 @@ async function main(): Promise { + diServices.set(IAgentHostGitService, gitService); +- // Register `ICopilotApiService` BEFORE `IClaudeProxyService` — +- // the proxy service constructor requires it. +- const copilotApiService = instantiationService.createInstance(CopilotApiService, undefined); +- diServices.set(ICopilotApiService, copilotApiService); +- diServices.set(ICopilotBranchNameGenerator, instantiationService.createInstance(CopilotBranchNameGenerator)); +- // CLI flags become env vars BEFORE the downloader is constructed so +- // `isAvailable()` and `loadSdkRoot()` see them as dev overrides. +- if (options.claudeSdkRoot) { +- process.env[AgentHostClaudeSdkRootEnvVar] = options.claudeSdkRoot; +- } +- if (options.codexSdkRoot) { +- process.env[AgentHostCodexAgentSdkRootEnvVar] = options.codexSdkRoot; +- } +- // Register the agent SDK downloader BEFORE any service that injects it. +- const agentSdkDownloader = instantiationService.createInstance(AgentSdkDownloader); +- diServices.set(IAgentSdkDownloader, agentSdkDownloader); +- const claudeProxyService = disposables.add(instantiationService.createInstance(ClaudeProxyService)); +- diServices.set(IClaudeProxyService, claudeProxyService); +- const claudeAgentSdkService = instantiationService.createInstance(ClaudeAgentSdkService); +- diServices.set(IClaudeAgentSdkService, claudeAgentSdkService); +- const codexProxyService = disposables.add(instantiationService.createInstance(CodexProxyService)); +- diServices.set(ICodexProxyService, codexProxyService); +- const agentHostOTelService = disposables.add(instantiationService.createInstance(AgentHostOTelService)); +- diServices.set(IAgentHostOTelService, agentHostOTelService); +- const copilotAgent = disposables.add(instantiationService.createInstance(CopilotAgent)); +- agentService.registerProvider(copilotAgent); +- log('CopilotAgent registered'); +- // Claude and Codex providers are gated on two things: +- // 1. The user-facing enable toggle (`chat.agentHost.Agent.enabled`, +- // forwarded as an env var by the renderer-side starters; the remote +- // server reads the env directly). Claude defaults to on, Codex +- // defaults to off. +- // 2. The SDK being reachable. Claude is a devDependency of this repo +- // so the bare-import path in `ClaudeAgentSdkService._loadSdk` +- // always succeeds in dev; in built/shipped server installs the +- // SDK comes from the CLI flag / env var dev override or a +- // `product.agentSdks.claude` entry. Codex still requires the +- // env-var override or product config. +- if (isAgentEnabled(process.env[AgentHostClaudeAgentEnabledEnvVar], true) && (!environmentService.isBuilt || agentSdkDownloader.isAvailable(ClaudeSdkPackage))) { +- const claudeAgent = disposables.add(instantiationService.createInstance(ClaudeAgent)); +- agentService.registerProvider(claudeAgent); +- log('ClaudeAgent registered'); +- } +- if (isAgentEnabled(process.env[AgentHostCodexAgentEnabledEnvVar], false) && agentSdkDownloader.isAvailable(CodexSdkPackage)) { +- const codexAgent = disposables.add(instantiationService.createInstance(CodexAgent)); +- agentService.registerProvider(codexAgent); +- log('CodexAgent registered'); +- } + } +diff --git a/src/vs/platform/agentHost/node/agentHostSessionTitleController.ts b/src/vs/platform/agentHost/node/agentHostSessionTitleController.ts +index 5db01700..52b0130b 100644 +--- a/src/vs/platform/agentHost/node/agentHostSessionTitleController.ts ++++ b/src/vs/platform/agentHost/node/agentHostSessionTitleController.ts +@@ -13,3 +13,2 @@ import { isAhpChatChannel, isDefaultChatUri, ResponsePartKind, type ResponsePart + import { AgentHostStateManager } from './agentHostStateManager.js'; +-import { ICopilotApiService, type ICopilotUtilityChatMessage } from './shared/copilotApiService.js'; + +@@ -27,3 +26,2 @@ export interface IAgentHostSessionTitleControllerOptions { + readonly getGitHubCopilotToken?: () => string | undefined; +- readonly copilotApiService?: ICopilotApiService; + } +@@ -226,71 +224,3 @@ export class AgentHostSessionTitleController extends Disposable { + private async _generateTitleFromPrompt(promptContent: string, isConversation: boolean, token: CancellationToken): Promise { +- if (token.isCancellationRequested) { +- return undefined; +- } +- +- const githubToken = this._options.getGitHubCopilotToken?.(); +- const copilotApiService = this._options.copilotApiService; +- if (!githubToken || !copilotApiService) { +- return undefined; +- } +- +- const abortController = new AbortController(); +- const cancellationListener = token.onCancellationRequested(() => abortController.abort()); +- try { +- const rawTitle = await copilotApiService.utilityChatCompletion(githubToken, { +- messages: this._buildTitlePrompt(promptContent, isConversation), +- }, { +- signal: abortController.signal, +- }); +- return this._cleanTitle(rawTitle); +- } catch (err) { +- if (token.isCancellationRequested) { +- return undefined; +- } +- this._logService.warn('[AgentHostSessionTitleController] Failed to generate session title', err); +- return undefined; +- } finally { +- cancellationListener.dispose(); +- } +- } +- +- private _buildTitlePrompt(promptContent: string, isConversation: boolean): ICopilotUtilityChatMessage[] { +- const userInstruction = isConversation +- ? `Please write a brief title for the following conversation:\n\n${promptContent}` +- : `Please write a brief title for the following request:\n\n${promptContent}`; +- return [ +- { +- role: 'system', +- content: [ +- 'You are an expert in crafting ultra-compact titles for chatbot conversations.', +- 'You are presented with a chat request or conversation, and you reply with only a brief title that captures the main topic.', +- 'Write the title in sentence case, not title case.', +- 'Preserve product names, abbreviations, code symbols, and proper nouns.', +- 'Aim for 3-6 words. Prefer the shortest accurate title.', +- 'Drop articles like "a", "an", and "the" unless needed for clarity.', +- 'Drop filler and generic framing like "help with", "question about", "request for", or "issue with".', +- 'Prefer short, concrete synonyms and omit unnecessary words.', +- 'Do not wrap the title in quotes or add trailing punctuation.', +- ].join(' '), +- }, +- { +- role: 'user', +- content: userInstruction, +- }, +- ]; +- } +- +- private _cleanTitle(rawTitle: string): string | undefined { +- let title = rawTitle.trim(); +- const firstLine = title.split(/\r?\n/).map(line => line.trim()).find(line => line.length > 0); +- title = firstLine ?? ''; +- if (title.startsWith('"') && title.endsWith('"') && title.length > 1) { +- title = title.slice(1, -1).trim(); +- } +- title = title.replace(/[.!?]+$/, '').trim(); +- +- if (!title || title.includes('can\'t assist with that')) { +- return undefined; +- } +- return title.slice(0, MAX_TITLE_LENGTH); ++ return undefined; + } +diff --git a/src/vs/platform/agentHost/node/agentService.ts b/src/vs/platform/agentHost/node/agentService.ts +index 8a11ef40..67d80bb3 100644 +--- a/src/vs/platform/agentHost/node/agentService.ts ++++ b/src/vs/platform/agentHost/node/agentService.ts +@@ -51,3 +51,2 @@ import { AgentHostSkillCompletionProvider } from './agentHostSkillCompletionProv + import { AgentHostWorkspaceFiles } from './agentHostWorkspaceFiles.js'; +-import { CopilotApiService, ICopilotApiService } from './shared/copilotApiService.js'; + import { parseMcpChannelUri } from './shared/mcpCustomizationController.js'; +@@ -213,3 +212,2 @@ export class AgentService extends Disposable implements IAgentService { + _fileMonitorService?: IAgentHostFileMonitorService, +- copilotApiService?: ICopilotApiService, + ) { +@@ -254,4 +252,2 @@ export class AgentService extends Disposable implements IAgentService { + services.set(IAgentHostOctoKitService, agentHostOctoKitService); +- const effectiveCopilotApiService = copilotApiService ?? instantiationService.createInstance(CopilotApiService, undefined); +- services.set(ICopilotApiService, effectiveCopilotApiService); + +@@ -303,3 +299,2 @@ export class AgentService extends Disposable implements IAgentService { + agents: this._agents, +- copilotApiService: effectiveCopilotApiService, + getGitHubCopilotToken: () => { +diff --git a/src/vs/platform/agentHost/node/agentSideEffects.ts b/src/vs/platform/agentHost/node/agentSideEffects.ts +index 0cb3f7fa..afe06218 100644 +--- a/src/vs/platform/agentHost/node/agentSideEffects.ts ++++ b/src/vs/platform/agentHost/node/agentSideEffects.ts +@@ -51,3 +51,2 @@ import { AgentHostTurnTracker } from './agentHostTurnTracker.js'; + import { AgentHostSessionTitleController } from './agentHostSessionTitleController.js'; +-import type { ICopilotApiService } from './shared/copilotApiService.js'; + +@@ -65,4 +64,2 @@ export interface IAgentSideEffectsOptions { + readonly getGitHubCopilotToken?: () => string | undefined; +- /** CAPI service used for Copilot utility title generation. */ +- readonly copilotApiService?: ICopilotApiService; + /** +@@ -137,3 +134,2 @@ export class AgentSideEffects extends Disposable { + getGitHubCopilotToken: this._options.getGitHubCopilotToken, +- copilotApiService: this._options.copilotApiService, + })); +diff --git a/src/vs/platform/agentHost/node/copilot/prompts/promptRegistry.ts b/src/vs/platform/agentHost/node/copilot/prompts/promptRegistry.ts +index 793cf5e1..d55c6a5b 100644 +--- a/src/vs/platform/agentHost/node/copilot/prompts/promptRegistry.ts ++++ b/src/vs/platform/agentHost/node/copilot/prompts/promptRegistry.ts +@@ -5,3 +5,2 @@ + +-import type { SectionOverride, SystemMessageConfig, SystemMessageSection } from '@github/copilot-sdk'; + import { agentHostCustomizationConfigSchema } from '../../../common/agentHostCustomizationConfig.js'; +@@ -9,3 +8,2 @@ import type { SchemaValue } from '../../../common/agentHostSchema.js'; + import type { ModelSelection } from '../../../common/state/protocol/state.js'; +-import { COPILOT_AGENT_HOST_SYSTEM_MESSAGE, fullSystemPrompt, sectionOverrides } from './systemMessage.js'; + +@@ -48,8 +46,2 @@ export interface IAgentHostPrompt { + resolveFullSystemPrompt?(model: ModelSelection, context: IAgentHostPromptContext): string | undefined; +- +- /** +- * Section-level overrides. Resolved into `{ mode: 'customize' }`, keeping the +- * SDK foundation prompt and guardrails intact. +- */ +- resolveSectionOverrides?(model: ModelSelection, context: IAgentHostPromptContext): Partial> | undefined; + } +@@ -95,48 +87,2 @@ export class AgentHostPromptRegistry { + } +- +- private _getContributor(model: ModelSelection): IAgentHostPromptCtor | undefined { +- for (const ctor of this._promptsWithMatcher) { +- if (ctor.matchesModel(model)) { +- return ctor; +- } +- } +- for (const { prefix, ctor } of this._familyPrefixList) { +- if (model.id.startsWith(prefix)) { +- return ctor; +- } +- } +- return undefined; +- } +- +- /** +- * Resolves the {@link SystemMessageConfig} for a session's model. +- * +- * Falls back to {@link COPILOT_AGENT_HOST_SYSTEM_MESSAGE} when the model is +- * unknown (e.g. server-side "Auto" selection where no model is chosen at +- * create time), when no contributor matches, or when the matching +- * contributor opts out for the current {@link context} (e.g. a setting that +- * gates it is disabled). +- */ +- resolveSystemMessageConfig(model: ModelSelection | undefined, context: IAgentHostPromptContext): SystemMessageConfig { +- if (!model) { +- return COPILOT_AGENT_HOST_SYSTEM_MESSAGE; +- } +- const ctor = this._getContributor(model); +- if (!ctor) { +- return COPILOT_AGENT_HOST_SYSTEM_MESSAGE; +- } +- const contributor = new ctor(); +- const fullPrompt = contributor.resolveFullSystemPrompt?.(model, context); +- if (fullPrompt !== undefined) { +- return fullSystemPrompt(fullPrompt); +- } +- const sections = contributor.resolveSectionOverrides?.(model, context); +- // An empty overrides object is treated as "no override" so we keep the +- // default identity customization rather than emitting a +- // `{ mode: 'customize', sections: {} }` that drops it. +- if (sections && Object.keys(sections).length > 0) { +- return sectionOverrides(sections); +- } +- return COPILOT_AGENT_HOST_SYSTEM_MESSAGE; +- } + } +diff --git a/src/vs/platform/agentHost/node/copilot/sessionCustomizationDiscovery.ts b/src/vs/platform/agentHost/node/copilot/sessionCustomizationDiscovery.ts +index 9331c71b..1f924384 100644 +--- a/src/vs/platform/agentHost/node/copilot/sessionCustomizationDiscovery.ts ++++ b/src/vs/platform/agentHost/node/copilot/sessionCustomizationDiscovery.ts +@@ -5,3 +5,2 @@ + +-import type { CopilotClient } from '@github/copilot-sdk'; + import { CancellationToken } from '../../../../base/common/cancellation.js'; +@@ -11,12 +10,7 @@ import { Disposable, type IDisposable } from '../../../../base/common/lifecycle. + import { ResourceMap, ResourceSet } from '../../../../base/common/map.js'; +-import { joinPath, dirname as uriDirname } from '../../../../base/common/resources.js'; ++import { joinPath } from '../../../../base/common/resources.js'; + import { compare as compareStrings } from '../../../../base/common/strings.js'; + import { URI } from '../../../../base/common/uri.js'; +-import { basename, isAbsolute } from '../../../../base/common/path.js'; + import { IFileService, IFileStatWithMetadata } from '../../../files/common/files.js'; + import { ILogService } from '../../../log/common/log.js'; +-import type { AgentsDiscoverRequest } from './copilotRCP.js'; +-import { AgentCustomization, ChildCustomization, CustomizationLoadStatus, CustomizationType, DirectoryCustomization, RuleCustomization, SkillCustomization, customizationId } from '../../common/state/sessionState.js'; +-import { ChildCustomizationType } from '../../common/state/protocol/state.js'; +-import { toAgentCustomizationMeta } from '../../common/meta/agentCustomizationMeta.js'; + +@@ -91,10 +85,2 @@ function compareDiscoveredFile(a: IDiscoveredFile, b: IDiscoveredFile): number { + +-function compareDirectoryCustomization(a: DirectoryCustomization, b: DirectoryCustomization): number { +- const byUri = compareStrings(a.uri, b.uri); +- if (byUri !== 0) { +- return byUri; +- } +- return compareStrings(a.contents, b.contents); +-} +- + /** +@@ -240,85 +226,2 @@ export class SessionCustomizationDiscovery extends Disposable { + +- public async discover(client: CopilotClient, token: CancellationToken): Promise { +- throwIfCancelled(token); +- +- const p: AgentsDiscoverRequest = { projectPaths: [this._workingDirectory.fsPath] }; +- +- try { +- const agents: AgentCustomization[] = []; +- +- const agentDiscovery = await client.rpc.agents.discover(p); +- for (const agent of agentDiscovery.agents) { +- if (agent.path) { +- const uri = URI.file(agent.path); +- agents.push({ type: CustomizationType.Agent, uri: uri.toString(), id: agent.id, name: agent.name, description: agent.description, _meta: toAgentCustomizationMeta({ userInvocable: agent.userInvocable }) }); +- } +- } +- +- const rules: RuleCustomization[] = []; +- +- const instructionDiscovery = await client.rpc.instructions.discover(p); +- for (const instruction of instructionDiscovery.sources) { +- let uri: URI; +- if (isAbsolute(instruction.sourcePath)) { +- uri = URI.file(instruction.sourcePath); +- } else { +- uri = joinPath(this._workingDirectory, instruction.sourcePath); +- } +- rules.push({ type: CustomizationType.Rule, uri: uri.toString(), id: instruction.id, name: instruction.label, description: instruction.description, globs: instruction.applyTo, alwaysApply: false }); +- } +- +- const skills: SkillCustomization[] = []; +- +- const skillDiscovery = await client.rpc.skills.discover(p); +- for (const skill of skillDiscovery.skills) { +- if (skill.path) { +- const uri = URI.file(skill.path); +- skills.push({ type: CustomizationType.Skill, uri: uri.toString(), id: skill.path, name: skill.name, description: skill.description }); +- } +- } +- +- const result: DirectoryCustomization[] = []; +- this.toDirectoryCustomizations(CustomizationType.Agent, agents, result); +- this.toDirectoryCustomizations(CustomizationType.Rule, rules, result); +- this.toDirectoryCustomizations(CustomizationType.Skill, skills, result); +- return result.sort(compareDirectoryCustomization); +- } catch (err) { +- this._logService.error(`[SessionCustomizationDiscovery] Error during discovery: ${err instanceof Error ? err.message : String(err)}`); +- return []; +- } +- } +- +- private toDirectoryCustomizations(type: ChildCustomizationType, customizations: readonly ChildCustomization[], result: DirectoryCustomization[]): void { +- const byParent = new ResourceMap<{ readonly uri: URI; readonly children: ChildCustomization[] }>(); +- for (const customization of customizations) { +- if (customization.type !== type) { +- continue; +- } +- const childUri = URI.parse(customization.uri); +- const parentUri = uriDirname(childUri); +- let entry = byParent.get(parentUri); +- if (!entry) { +- entry = { uri: parentUri, children: [] }; +- byParent.set(parentUri, entry); +- } +- entry.children.push(customization); +- } +- +- for (const { uri, children } of byParent.values()) { +- children.sort((a, b) => compareStrings(a.uri, b.uri)); +- result.push({ +- type: CustomizationType.Directory, +- id: customizationId(uri.toString()), +- uri: uri.toString(), +- name: basename(uri.path), +- enabled: true, +- contents: type, +- writable: true, +- load: { kind: CustomizationLoadStatus.Loaded }, +- children, +- }); +- } +- } +- +- + /** +diff --git a/src/vs/platform/agentHost/node/shared/forwardedChatError.ts b/src/vs/platform/agentHost/node/shared/forwardedChatError.ts +index 0de32af7..93e95eb6 100644 +--- a/src/vs/platform/agentHost/node/shared/forwardedChatError.ts ++++ b/src/vs/platform/agentHost/node/shared/forwardedChatError.ts +@@ -5,4 +5,2 @@ + +-import { CopilotApiError, COPILOT_API_ERROR_STATUS_STREAMING } from './copilotApiService.js'; +- + /** +@@ -88,57 +86,2 @@ function statusToFetchType(status: number): string { + +-/** +- * Builds a {@link IForwardedChatError} from a {@link CopilotApiError}. The +- * error's Anthropic envelope carries the upstream message and type, which are +- * surfaced as `reason`/`capiError` so the core formatter can render the right +- * message (rate limit, quota, filtered, etc.). +- */ +-export function buildForwardedChatError(err: CopilotApiError): IForwardedChatError { +- const status = err.status === COPILOT_API_ERROR_STATUS_STREAMING ? 502 : err.status; +- const requestId = typeof err.envelope.request_id === 'string' ? err.envelope.request_id : ''; +- // CAPI rate-limit/quota errors carry their fine-grained code in the +- // response body (e.g. `{ "error": { "code": "quota_exceeded", ... } }`). +- // The proxy synthesizes a non-conforming body into the Anthropic envelope +- // as `error.type: 'api_error'` with the raw body as the message, so prefer +- // a CAPI code/message parsed out of the message when present. +- const capiError = extractCapiError(err.envelope.error.message) ?? { code: err.envelope.error.type, message: err.envelope.error.message }; +- return { +- fetchError: { +- type: statusToFetchType(status), +- reason: capiError.message ?? err.envelope.error.message, +- requestId, +- capiError, +- }, +- }; +-} +- +-/** +- * Attempts to parse a CAPI-style error body (`{ "error": { "code", "message" } }`) +- * out of an envelope message string. Returns `undefined` when the message is +- * not such a JSON payload. +- */ +-function extractCapiError(message: string): { code?: string; message?: string } | undefined { +- let parsed: unknown; +- try { +- parsed = JSON.parse(message); +- } catch { +- return undefined; +- } +- if (!parsed || typeof parsed !== 'object') { +- return undefined; +- } +- const error = (parsed as { error?: unknown }).error; +- if (!error || typeof error !== 'object') { +- return undefined; +- } +- const code = (error as { code?: unknown }).code; +- const msg = (error as { message?: unknown }).message; +- if (typeof code !== 'string' && typeof msg !== 'string') { +- return undefined; +- } +- return { +- code: typeof code === 'string' ? code : undefined, +- message: typeof msg === 'string' ? msg : undefined, +- }; +-} +- + /** +diff --git a/src/vs/platform/agentHost/test/node/mockAgent.ts b/src/vs/platform/agentHost/test/node/mockAgent.ts +index 5daeb348..65fcacd5 100644 +--- a/src/vs/platform/agentHost/test/node/mockAgent.ts ++++ b/src/vs/platform/agentHost/test/node/mockAgent.ts +@@ -12,3 +12,2 @@ import { type ISyncedCustomization } from '../../common/agentPluginManager.js'; + import { AgentSession, type AgentProvider, type AgentSignal, type IAgent, type IAgentActionSignal, type IAgentCreateSessionConfig, type IAgentCreateSessionResult, type IAgentDescriptor, type IAgentModelInfo, type IAgentResolveSessionConfigParams, type IAgentSessionConfigCompletionsParams, type IAgentSessionMetadata, type IAgentToolPendingConfirmationSignal } from '../../common/agentService.js'; +-import { buildSubagentTurnsFromHistory, buildTurnsFromHistory, type IHistoryRecord } from './historyRecordFixtures.js'; + import { ProtectedResourceMetadata, ToolCallContributorKind, type AgentSelection, type MessageAttachment, type ModelSelection } from '../../common/state/protocol/state.js'; +@@ -16,3 +15,3 @@ import type { ResolveSessionConfigResult, SessionConfigCompletionsResult } from + import { ActionType } from '../../common/state/sessionActions.js'; +-import { ResponsePartKind, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, CustomizationLoadStatus, parseSubagentSessionUri, type ClientPluginCustomization, type Customization, type PendingMessage, type StringOrMarkdown, type ToolCallResult, type Turn, type UsageInfo } from '../../common/state/sessionState.js'; ++import { ResponsePartKind, ToolCallConfirmationReason, ToolCallStatus, ToolResultContentType, CustomizationLoadStatus, type ClientPluginCustomization, type Customization, type PendingMessage, type StringOrMarkdown, type ToolCallResult, type Turn, type UsageInfo } from '../../common/state/sessionState.js'; + import { hasKey } from '../../../../base/common/types.js'; +@@ -66,10 +65,2 @@ export class MockAgent implements IAgent { + +- /** +- * Configurable session history. Tests construct {@link IHistoryRecord} +- * entries (the agent-internal intermediate shape) and the mock converts +- * them to {@link Turn}s on demand. Subagent URIs are routed to filtered +- * subagent turns via {@link buildSubagentTurnsFromHistory}. +- */ +- sessionMessages: IHistoryRecord[] = []; +- + /** Optional overrides applied to session metadata from listSessions. */ +@@ -137,7 +128,3 @@ export class MockAgent implements IAgent { + async getSessionMessages(session: URI): Promise { +- const subagentInfo = parseSubagentSessionUri(session); +- if (subagentInfo) { +- return buildSubagentTurnsFromHistory(this.sessionMessages, subagentInfo.toolCallId, session.toString()); +- } +- return buildTurnsFromHistory(this.sessionMessages); ++ return [] + } +@@ -258,9 +245,2 @@ export class ScriptedMockAgent implements IAgent { + */ +- private readonly _preExistingMessages: IHistoryRecord[] = [ +- { type: 'message', role: 'user', session: PRE_EXISTING_SESSION_URI, messageId: 'h-msg-1', content: 'What files are here?' }, +- { type: 'tool_start', session: PRE_EXISTING_SESSION_URI, toolCallId: 'h-tc-1', toolName: 'list_files', displayName: 'List Files', invocationMessage: 'Listing files...' }, +- { type: 'tool_complete', session: PRE_EXISTING_SESSION_URI, toolCallId: 'h-tc-1', result: { pastTenseMessage: 'Listed files', content: [{ type: ToolResultContentType.Text, text: 'file1.ts\nfile2.ts' }], success: true } satisfies ToolCallResult }, +- { type: 'message', role: 'assistant', session: PRE_EXISTING_SESSION_URI, messageId: 'h-msg-2', content: 'Here are the files: file1.ts and file2.ts' }, +- ]; +- + // Track pending permission requests +@@ -744,9 +724,2 @@ export class ScriptedMockAgent implements IAgent { + async getSessionMessages(session: URI): Promise { +- const subagentInfo = parseSubagentSessionUri(session); +- if (subagentInfo) { +- return buildSubagentTurnsFromHistory(this._preExistingMessages, subagentInfo.toolCallId, session.toString()); +- } +- if (session.toString() === PRE_EXISTING_SESSION_URI.toString()) { +- return buildTurnsFromHistory(this._preExistingMessages); +- } + return []; +diff --git a/src/vs/platform/agentHost/test/node/sessionCustomizationDiscovery.test.ts b/src/vs/platform/agentHost/test/node/sessionCustomizationDiscovery.test.ts +index 8697c546..5dcae487 100644 +--- a/src/vs/platform/agentHost/test/node/sessionCustomizationDiscovery.test.ts ++++ b/src/vs/platform/agentHost/test/node/sessionCustomizationDiscovery.test.ts +@@ -6,3 +6,2 @@ + import assert from 'assert'; +-import type { CopilotClient } from '@github/copilot-sdk'; + import { DeferredPromise, raceTimeout, timeout } from '../../../../base/common/async.js'; +@@ -22,3 +21,2 @@ import { DiscoveredType, SessionCustomizationDiscovery } from '../../node/copilo + import { SessionPluginBundler } from '../../node/shared/sessionPluginBundler.js'; +-import { mapToParsedPlugin, toDiscoveredDirectoryCustomizations } from '../../node/copilot/copilotAgent.js'; + +@@ -76,38 +74,2 @@ suite('SessionCustomizationDiscovery', () => { + +- test('groups discovered customizations by parent folder', async () => { +- const discovery = disposables.add(instantiationService.createInstance(SessionCustomizationDiscovery, workspace, userHome)); +- const client = { +- rpc: { +- agents: { +- discover: async () => ({ +- agents: [ +- { id: 'one', name: 'One', description: '', path: '/workspace/.github/agents/one.agent.md', userInvocable: false }, +- { id: 'two', name: 'Two', description: '', path: '/workspace/.github/agents/two.agent.md', userInvocable: true }, +- { id: 'three', name: 'Three', description: '', path: '/workspace/.github/other/three.agent.md', userInvocable: false }, +- ], +- }), +- }, +- instructions: { discover: async () => ({ sources: [] }) }, +- skills: { discover: async () => ({ skills: [] }) }, +- }, +- } as unknown as CopilotClient; +- +- const customizations = await discovery.discover(client, CancellationToken.None); +- const agentDirectories = customizations.filter(customization => customization.contents === 'agent'); +- +- const getPath = (uri: string) => URI.parse(uri).path; +- +- assert.strictEqual(agentDirectories.length, 2); +- assert.deepStrictEqual(agentDirectories.map(customization => getPath(customization.uri)).sort(), [ +- '/workspace/.github/agents', +- '/workspace/.github/other', +- ]); +- const agentsInAgentsDir = agentDirectories.find(customization => getPath(customization.uri) === '/workspace/.github/agents'); +- assert.ok(agentsInAgentsDir); +- assert.deepStrictEqual(agentsInAgentsDir.children?.map(child => getPath(child.uri)).sort(), [ +- '/workspace/.github/agents/one.agent.md', +- '/workspace/.github/agents/two.agent.md', +- ]); +- }); +- + test('returns directories sorted by type and URI', async () => { +@@ -518,63 +480,2 @@ suite('SessionCustomizationDiscovery', () => { + }); +- +- test('maps discovered files to parsed plugin preserving source URIs', async () => { +- const agent = await seed('/workspace/.github/agents/foo.agent.md', '---\nname: Workspace Agent\ndescription: Agent description\n---\nbody'); +- const skill = await seed('/workspace/.github/skills/bar/SKILL.md', '---\nname: Workspace Skill\ndescription: Skill description\n---\nbody'); +- const instruction = await seed('/workspace/.github/instructions/baz.instructions.md', '---\nname: Workspace Rule\ndescription: Rule description\nglobs:\n - src/**\n---\nbody'); +- +- const discovery = disposables.add(instantiationService.createInstance(SessionCustomizationDiscovery, workspace, userHome)); +- const customizations = await toDiscoveredDirectoryCustomizations(await discovery.scan(CancellationToken.None), fileService); +- +- const plugin = mapToParsedPlugin(customizations); +- +- assert.ok(plugin); +- assert.strictEqual(plugin.agents.length, 1); +- assert.strictEqual(plugin.skills.length, 1); +- assert.strictEqual(plugin.instructions.length, 1); +- assert.deepStrictEqual( +- { +- agentUri: plugin.agents[0].uri.toString(), +- agentDescription: plugin.agents[0].description, +- skillUri: plugin.skills[0].uri.toString(), +- skillDescription: plugin.skills[0].description, +- ruleUri: plugin.instructions[0].uri.toString(), +- ruleDescription: plugin.instructions[0].description, +- }, +- { +- agentUri: agent.toString(), +- agentDescription: 'Agent description', +- skillUri: skill.toString(), +- skillDescription: 'Skill description', +- ruleUri: instruction.toString(), +- ruleDescription: 'Rule description', +- } +- ); +- }); +- +- test('does not include parsed agent-instruction rules in mapToParsedPlugin output', async () => { +- await seed('/workspace/.github/copilot-instructions.md', 'workspace instructions'); +- await seed('/workspace/.agents/skills/bar/SKILL.md', '---\nname: bar\ndescription: Skill description\n---\nbody'); +- +- const discovery = disposables.add(instantiationService.createInstance(SessionCustomizationDiscovery, workspace, userHome)); +- const customizations = await toDiscoveredDirectoryCustomizations(await discovery.scan(CancellationToken.None), fileService); +- +- const plugin = mapToParsedPlugin(customizations); +- +- assert.ok(plugin); +- assert.strictEqual(plugin.skills.length, 1); +- assert.strictEqual(plugin.instructions.length, 0); +- }); +- +- test('returns undefined from mapToParsedPlugin when all customizations are agent-instruction files', async () => { +- // Only agent instruction files are discovered — these are excluded from the parsed plugin output. +- await seed('/workspace/.github/copilot-instructions.md', 'workspace instructions'); +- await seed('/home/.copilot/copilot-instructions.md', 'user instructions'); +- +- const discovery = disposables.add(instantiationService.createInstance(SessionCustomizationDiscovery, workspace, userHome)); +- const customizations = await toDiscoveredDirectoryCustomizations(await discovery.scan(CancellationToken.None), fileService); +- +- const plugin = mapToParsedPlugin(customizations); +- +- assert.strictEqual(plugin, undefined); +- }); + }); diff --git a/patches/81-ui-disable-onboarding.patch b/patches/81-ui-disable-onboarding.patch index caa83250b3a..878ff04cc33 100644 --- a/patches/81-ui-disable-onboarding.patch +++ b/patches/81-ui-disable-onboarding.patch @@ -1,22 +1,24 @@ diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts -index 6f785eaa..4172f07f 100644 +index 777f3ad8..4172f07f 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts -@@ -33,5 +33,2 @@ import { mainWindow } from '../../../../base/browser/window.js'; +@@ -33,6 +33,2 @@ import { mainWindow } from '../../../../base/browser/window.js'; import { getActiveElement } from '../../../../base/browser/dom.js'; -import { isWeb } from '../../../../base/common/platform.js'; -import { IOnboardingService } from '../../welcomeOnboarding/common/onboardingService.js'; -import { ONBOARDING_STORAGE_KEY } from '../../welcomeOnboarding/common/onboardingTypes.js'; +-import { IChatEntitlementService } from '../../../services/chat/common/chatEntitlementService.js'; -@@ -96,3 +93,2 @@ export class StartupPageRunnerContribution extends Disposable implements IWorkbe +@@ -97,4 +93,2 @@ export class StartupPageRunnerContribution extends Disposable implements IWorkbe @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IOnboardingService private readonly onboardingService: IOnboardingService, +- @IChatEntitlementService private readonly chatEntitlementService: IChatEntitlementService, ) { -@@ -100,3 +96,2 @@ export class StartupPageRunnerContribution extends Disposable implements IWorkbe +@@ -102,3 +96,2 @@ export class StartupPageRunnerContribution extends Disposable implements IWorkbe - this.tryShowOnboarding(); this.run().then(undefined, onUnexpectedError); -@@ -232,32 +227,2 @@ export class StartupPageRunnerContribution extends Disposable implements IWorkbe +@@ -234,36 +227,2 @@ export class StartupPageRunnerContribution extends Disposable implements IWorkbe } - - private tryShowOnboarding(): void { @@ -32,6 +34,10 @@ index 6f785eaa..4172f07f 100644 - return; // experimental onboarding is disabled - } - +- if (this.chatEntitlementService.sentiment.hidden) { +- return; // AI features are hidden, do not show AI-focused onboarding +- } +- - if (!this.storageService.isNew(StorageScope.APPLICATION)) { - return; // only show onboarding for new users who have never used the product before - } @@ -50,10 +56,10 @@ index 6f785eaa..4172f07f 100644 - } } diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts -index 2c3be688..0ab9b329 100644 +index b6c33f80..e3a8ad35 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts -@@ -379,5 +379,2 @@ import './contrib/welcomeViews/common/newFile.contribution.js'; +@@ -391,5 +391,2 @@ import './contrib/welcomeViews/common/newFile.contribution.js'; -// Welcome Onboarding -import './contrib/welcomeOnboarding/browser/welcomeOnboarding.contribution.js'; diff --git a/patches/linux/31-build-fix-dependencies.patch b/patches/linux/31-build-fix-dependencies.patch index 055e6527fe2..05722881271 100644 --- a/patches/linux/31-build-fix-dependencies.patch +++ b/patches/linux/31-build-fix-dependencies.patch @@ -1,5 +1,5 @@ diff --git a/build/linux/dependencies-generator.ts b/build/linux/dependencies-generator.ts -index 874c8026..04731cfd 100644 +index eb1d73d0..29ec956e 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,118 +18,34 @@ index 874c8026..04731cfd 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 6025771a..b0637f1f 100644 +index db113606..a2d58933 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -17,3 +17,2 @@ "@electron/get": "^2.0.0", - "@electron/osx-sign": "^2.0.0", "@types/ansi-colors": "^3.2.0", -@@ -106,5 +105,5 @@ - "node_modules/@azure/core-auth": { -- "version": "1.9.0", -- "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", -- "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", -+ "version": "1.10.1", -+ "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", -+ "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", - "dev": true, -@@ -112,4 +111,4 @@ - "dependencies": { -- "@azure/abort-controller": "^2.0.0", -- "@azure/core-util": "^1.11.0", -+ "@azure/abort-controller": "^2.1.2", -+ "@azure/core-util": "^1.13.0", - "tslib": "^2.6.2" -@@ -117,3 +116,3 @@ +@@ -121,3 +120,3 @@ "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } -@@ -236,5 +235,5 @@ - "node_modules/@azure/core-rest-pipeline": { -- "version": "1.18.0", -- "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.0.tgz", -- "integrity": "sha512-QSoGUp4Eq/gohEFNJaUOwTN7BCc2nHTjjbm75JT0aD7W65PWM1H/tItz0GsABn22uaKyGxiMhWQLt2r+FGU89Q==", -+ "version": "1.22.2", -+ "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz", -+ "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==", - "dev": true, -@@ -242,9 +241,8 @@ - "dependencies": { -- "@azure/abort-controller": "^2.0.0", -- "@azure/core-auth": "^1.8.0", -- "@azure/core-tracing": "^1.0.1", -- "@azure/core-util": "^1.11.0", -- "@azure/logger": "^1.0.0", -- "http-proxy-agent": "^7.0.0", -- "https-proxy-agent": "^7.0.0", -+ "@azure/abort-controller": "^2.1.2", -+ "@azure/core-auth": "^1.10.0", -+ "@azure/core-tracing": "^1.3.0", -+ "@azure/core-util": "^1.13.0", -+ "@azure/logger": "^1.3.0", -+ "@typespec/ts-http-runtime": "^0.3.0", - "tslib": "^2.6.2" -@@ -252,3 +250,3 @@ +@@ -188,3 +187,3 @@ "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } -@@ -269,5 +267,5 @@ - "node_modules/@azure/core-tracing": { -- "version": "1.2.0", -- "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", -- "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", -+ "version": "1.3.1", -+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", -+ "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", - "dev": true, -@@ -278,3 +276,3 @@ +@@ -201,3 +200,3 @@ "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } -@@ -282,5 +280,5 @@ - "node_modules/@azure/core-util": { -- "version": "1.11.0", -- "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", -- "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", -+ "version": "1.13.1", -+ "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", -+ "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", - "dev": true, -@@ -288,3 +286,4 @@ - "dependencies": { -- "@azure/abort-controller": "^2.0.0", -+ "@azure/abort-controller": "^2.1.2", -+ "@typespec/ts-http-runtime": "^0.3.0", - "tslib": "^2.6.2" -@@ -292,3 +291,3 @@ +@@ -352,3 +351,3 @@ "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } -@@ -371,11 +370,13 @@ - "node_modules/@azure/logger": { -- "version": "1.0.1", -- "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.1.tgz", -- "integrity": "sha512-QYQeaJ+A5x6aMNu8BG5qdsVBnYBop9UMwgUvGihSjf1PdZZXB+c/oMdM2ajKwzobLBh9e9QuMQkN9iL+IxLBLA==", -+ "version": "1.3.0", -+ "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", -+ "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", - "dev": true, -+ "license": "MIT", - "dependencies": { -- "tslib": "^2.0.0" -+ "@typespec/ts-http-runtime": "^0.3.0", -+ "tslib": "^2.6.2" - }, - "engines": { -- "node": ">=8.0.0" -+ "node": ">=20.0.0" - } -@@ -482,5 +483,5 @@ +@@ -447,5 +446,5 @@ "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", @@ -138,7 +54,7 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, -@@ -539,50 +540,2 @@ +@@ -504,50 +503,2 @@ }, - "node_modules/@electron/osx-sign": { - "version": "2.0.0", @@ -189,7 +105,7 @@ index 6025771a..b0637f1f 100644 - } - }, "node_modules/@esbuild/aix-ppc64": { -@@ -1357,5 +1310,5 @@ +@@ -1322,5 +1273,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 +114,7 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.4.1.tgz", + "integrity": "sha512-XifMpBMdo0E1Fuh85YdcYAgy+okNg9WKBzIPIO4JUDnSWUVFihnogrM4cjDapeHkgzSgulwR8oJVJ17eyxI1bA==", "dev": true, -@@ -1364,5 +1317,5 @@ +@@ -1329,5 +1280,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 +123,7 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.4.1.tgz", + "integrity": "sha512-kAV7Sup3vwvqxKvBbf9lx/JaPHkRybQp/LLvA73U1AorPZE6XyfBAFG24BbMiCs4OX1ax4g7kXRuFPgMLWRf+g==", "dev": true, -@@ -1372,8 +1325,8 @@ +@@ -1337,8 +1288,8 @@ "@azu/style-format": "^1.0.1", - "@textlint/module-interop": "15.2.2", - "@textlint/resolver": "15.2.2", @@ -221,7 +137,7 @@ index 6025771a..b0637f1f 100644 + "debug": "^4.4.3", + "js-yaml": "^4.1.0", "lodash": "^4.17.21", -@@ -1449,2 +1402,9 @@ +@@ -1414,2 +1365,9 @@ }, + "node_modules/@textlint/linter-formatter/node_modules/emoji-regex": { + "version": "8.0.0", @@ -231,7 +147,7 @@ index 6025771a..b0637f1f 100644 + "license": "MIT" + }, "node_modules/@textlint/linter-formatter/node_modules/has-flag": { -@@ -1466,2 +1426,17 @@ +@@ -1431,2 +1389,17 @@ }, + "node_modules/@textlint/linter-formatter/node_modules/string-width": { + "version": "4.2.3", @@ -249,7 +165,7 @@ index 6025771a..b0637f1f 100644 + } + }, "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { -@@ -1493,5 +1468,5 @@ +@@ -1458,5 +1431,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 +174,7 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.4.1.tgz", + "integrity": "sha512-jHtM2E5CR68P3z/+FGrEU5pml2fQVzEo2sez9FEjrVHSPCrHtqHcPaKfsYbQJjc9C48ObwaWrCzRNaL3KedNCQ==", "dev": true, -@@ -1500,5 +1475,5 @@ +@@ -1465,5 +1438,5 @@ "node_modules/@textlint/resolver": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.2.2.tgz", @@ -267,7 +183,7 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.4.1.tgz", + "integrity": "sha512-uVssyG3XXXKNY+O7NOajGvQZTyOuhPviwlq7Xek6ZT9K1eDQtA8074cPkAQoLMYhi/TUyOE5P5kpz42UF8Lmdw==", "dev": true, -@@ -1507,5 +1482,5 @@ +@@ -1472,5 +1445,5 @@ "node_modules/@textlint/types": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.2.2.tgz", @@ -276,12 +192,12 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.4.1.tgz", + "integrity": "sha512-WByVZ3zblbvuI+voWQplUP7seSTKXI9z6TMVXEB3dY3JFrZCIXWKNfLbETX5lZV7fYkCMaDtILO1l6s11wdbQA==", "dev": true, -@@ -1513,3 +1488,3 @@ +@@ -1478,3 +1451,3 @@ "dependencies": { - "@textlint/ast-node-types": "15.2.2" + "@textlint/ast-node-types": "15.4.1" } -@@ -1607,12 +1582,2 @@ +@@ -1572,12 +1545,2 @@ }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", @@ -294,25 +210,16 @@ index 6025771a..b0637f1f 100644 - } - }, "node_modules/@types/gulp": { -@@ -1934,2 +1899,17 @@ - }, -+ "node_modules/@typespec/ts-http-runtime": { -+ "version": "0.3.2", -+ "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.2.tgz", -+ "integrity": "sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==", -+ "dev": true, -+ "license": "MIT", -+ "dependencies": { -+ "http-proxy-agent": "^7.0.0", -+ "https-proxy-agent": "^7.0.0", -+ "tslib": "^2.6.2" -+ }, -+ "engines": { -+ "node": ">=20.0.0" -+ } -+ }, - "node_modules/@vscode/iconv-lite-umd": { -@@ -2361,5 +2341,5 @@ +@@ -1900,5 +1863,5 @@ + "node_modules/@typespec/ts-http-runtime": { +- "version": "0.3.5", +- "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.5.tgz", +- "integrity": "sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw==", ++ "version": "0.3.6", ++ "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.6.tgz", ++ "integrity": "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og==", + "dev": true, +@@ -2335,5 +2298,5 @@ "node_modules/ansi-escapes": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.1.tgz", @@ -321,11 +228,11 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", "dev": true, -@@ -2406,2 +2386,3 @@ +@@ -2380,2 +2343,3 @@ "dev": true, + "license": "BSD-2-Clause", "dependencies": { -@@ -2436,17 +2417,7 @@ +@@ -2410,17 +2374,7 @@ "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -347,25 +254,25 @@ index 6025771a..b0637f1f 100644 - "license": "BSD-3-Clause" + "license": "Python-2.0" }, -@@ -2494,2 +2465,3 @@ +@@ -2468,2 +2422,3 @@ "dev": true, + "license": "BSD-2-Clause", "dependencies": { -@@ -2578,2 +2550,3 @@ +@@ -2552,2 +2507,3 @@ "dev": true, + "license": "MIT", "optional": true, -@@ -2589,3 +2562,4 @@ +@@ -2563,3 +2519,4 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "dev": true, + "license": "ISC" }, -@@ -2691,2 +2665,3 @@ +@@ -2681,2 +2638,3 @@ "dev": true, + "license": "MIT", "dependencies": { -@@ -2750,6 +2725,7 @@ +@@ -2740,6 +2698,7 @@ "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", @@ -376,7 +283,7 @@ index 6025771a..b0637f1f 100644 "dev": true, + "license": "MIT", "dependencies": { -@@ -2758,9 +2734,13 @@ +@@ -2748,9 +2707,13 @@ "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", @@ -395,7 +302,7 @@ index 6025771a..b0637f1f 100644 - "node": ">= 6" + "node": ">=20.18.1" }, -@@ -2975,6 +2955,7 @@ +@@ -2965,6 +2928,7 @@ "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -406,13 +313,13 @@ index 6025771a..b0637f1f 100644 "dev": true, + "license": "BSD-2-Clause", "engines": { -@@ -3133,3 +3114,4 @@ +@@ -3157,3 +3121,4 @@ } - ] + ], + "license": "BSD-2-Clause" }, -@@ -3151,6 +3133,7 @@ +@@ -3175,6 +3140,7 @@ "node_modules/domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", @@ -423,12 +330,12 @@ index 6025771a..b0637f1f 100644 "dev": true, + "license": "BSD-2-Clause", "dependencies": { -@@ -3158,3 +3141,3 @@ +@@ -3182,3 +3148,3 @@ "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" + "domhandler": "^5.0.3" }, -@@ -3225,5 +3208,5 @@ +@@ -3249,5 +3215,5 @@ "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -437,7 +344,7 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, -@@ -3231,2 +3214,16 @@ +@@ -3255,2 +3221,16 @@ }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", @@ -454,7 +361,7 @@ index 6025771a..b0637f1f 100644 + } + }, "node_modules/end-of-stream": { -@@ -3241,6 +3238,7 @@ +@@ -3265,6 +3245,7 @@ "node_modules/entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -465,7 +372,7 @@ index 6025771a..b0637f1f 100644 "dev": true, + "license": "BSD-2-Clause", "engines": { -@@ -3381,16 +3379,2 @@ +@@ -3405,16 +3386,2 @@ }, - "node_modules/esprima": { - "version": "4.0.1", @@ -482,19 +389,19 @@ index 6025771a..b0637f1f 100644 - } - }, "node_modules/events": { -@@ -3432,2 +3416,3 @@ +@@ -3456,2 +3423,3 @@ "dev": true, + "license": "ISC", "dependencies": { -@@ -3650,2 +3635,3 @@ +@@ -3665,2 +3633,3 @@ "dev": true, + "license": "ISC", "optional": true -@@ -3957,2 +3943,3 @@ +@@ -3972,2 +3941,3 @@ "dev": true, + "license": "MIT", "engines": { -@@ -4016,5 +4003,5 @@ +@@ -4031,5 +4001,5 @@ "node_modules/htmlparser2": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", @@ -503,7 +410,7 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", "dev": true, -@@ -4027,7 +4014,21 @@ +@@ -4042,7 +4012,21 @@ ], + "license": "MIT", "dependencies": { @@ -528,7 +435,7 @@ index 6025771a..b0637f1f 100644 + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } -@@ -4081,2 +4082,15 @@ +@@ -4096,2 +4080,15 @@ }, + "node_modules/iconv-lite": { + "version": "0.6.3", @@ -544,7 +451,7 @@ index 6025771a..b0637f1f 100644 + } + }, "node_modules/ieee754": { -@@ -4113,5 +4127,5 @@ +@@ -4128,5 +4125,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", @@ -553,15 +460,11 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", "dev": true, -@@ -4147,2 +4161,3 @@ +@@ -4162,2 +4159,3 @@ "dev": true, + "license": "MIT", "optional": true -@@ -4166,2 +4181,3 @@ - "dev": true, -+ "license": "ISC", - "bin": { -@@ -4321,5 +4337,5 @@ +@@ -4360,5 +4358,5 @@ "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", @@ -570,25 +473,25 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, -@@ -4327,4 +4343,3 @@ +@@ -4366,4 +4364,3 @@ "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, -@@ -4393,2 +4408,3 @@ +@@ -4486,2 +4483,3 @@ + "hasInstallScript": true, ++ "license": "MIT", + "optional": true, +@@ -4497,2 +4495,3 @@ "dev": true, + "license": "BSD-2-Clause", "dependencies": { -@@ -4432,2 +4448,3 @@ +@@ -4603,2 +4602,3 @@ "dev": true, + "license": "MIT", "dependencies": { -@@ -4471,2 +4488,3 @@ - "hasInstallScript": true, -+ "license": "MIT", - "optional": true, -@@ -4564,9 +4582,2 @@ +@@ -4628,9 +4628,2 @@ }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", @@ -598,19 +501,23 @@ index 6025771a..b0637f1f 100644 - "license": "Python-2.0" - }, "node_modules/matcher": { -@@ -4576,2 +4587,3 @@ +@@ -4640,2 +4633,3 @@ "dev": true, + "license": "MIT", "optional": true, -@@ -4589,2 +4601,3 @@ +@@ -4653,2 +4647,3 @@ "dev": true, + "license": "(MIT OR WTFPL)", "optional": true, -@@ -4727,2 +4740,3 @@ +@@ -4713,2 +4708,3 @@ + "dev": true, ++ "license": "ISC", + "bin": { +@@ -4791,2 +4787,3 @@ "dev": true, + "license": "MIT", "optional": true -@@ -4743,6 +4757,7 @@ +@@ -4807,6 +4804,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 +528,11 @@ index 6025771a..b0637f1f 100644 "dev": true, + "license": "MIT", "optional": true -@@ -4789,2 +4804,3 @@ +@@ -4847,2 +4845,3 @@ "dev": true, + "license": "ISC", "optional": true -@@ -4804,5 +4820,5 @@ +@@ -4862,5 +4861,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 +541,12 @@ index 6025771a..b0637f1f 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, -@@ -4814,3 +4830,3 @@ +@@ -4872,3 +4871,3 @@ "engines": { - "node": ">=18" + "node": ">=20" } -@@ -4891,5 +4907,5 @@ +@@ -4949,5 +4948,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 +555,7 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, -@@ -5019,5 +5035,5 @@ +@@ -5079,5 +5078,5 @@ "node_modules/p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", @@ -657,7 +564,7 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, -@@ -5097,8 +5113,9 @@ +@@ -5157,8 +5156,9 @@ "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -671,7 +578,7 @@ index 6025771a..b0637f1f 100644 - "entities": "^4.4.0" + "entities": "^6.0.0" }, -@@ -5109,8 +5126,22 @@ +@@ -5169,8 +5169,22 @@ "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", @@ -698,7 +605,7 @@ index 6025771a..b0637f1f 100644 "dependencies": { - "domhandler": "^5.0.2", "parse5": "^7.0.0" -@@ -5121,2 +5152,15 @@ +@@ -5181,2 +5195,15 @@ }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", @@ -714,7 +621,7 @@ index 6025771a..b0637f1f 100644 + } + }, "node_modules/path-expression-matcher": { -@@ -5156,5 +5200,5 @@ +@@ -5216,5 +5243,5 @@ "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", @@ -723,20 +630,20 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "dev": true, -@@ -5287,2 +5331,3 @@ +@@ -5296,2 +5323,3 @@ + "dev": true, ++ "license": "ISC", + "dependencies": { +@@ -5348,2 +5376,3 @@ "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "optional": true, -@@ -5294,3 +5339,3 @@ +@@ -5355,3 +5384,3 @@ "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", + "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", -@@ -5342,2 +5387,3 @@ - "dev": true, -+ "license": "ISC", - "dependencies": { -@@ -5435,22 +5481,2 @@ +@@ -5490,22 +5519,2 @@ }, - "node_modules/rc-config-loader/node_modules/argparse": { - "version": "2.0.1", @@ -759,11 +666,11 @@ index 6025771a..b0637f1f 100644 - } - }, "node_modules/read": { -@@ -5598,2 +5624,3 @@ +@@ -5653,2 +5662,3 @@ "dev": true, + "license": "MIT", "optional": true, -@@ -5641,7 +5668,15 @@ +@@ -5709,7 +5719,15 @@ }, + "node_modules/safer-buffer": { + "version": "2.1.2", @@ -783,23 +690,23 @@ index 6025771a..b0637f1f 100644 + "dev": true, + "license": "ISC" }, -@@ -5692,2 +5727,3 @@ +@@ -5760,2 +5778,3 @@ "dev": true, + "license": "MIT", "optional": true -@@ -5839,2 +5875,3 @@ +@@ -5907,2 +5926,3 @@ ], + "license": "BSD-3-Clause", "optional": true -@@ -5860,2 +5897,3 @@ +@@ -5928,2 +5948,3 @@ ], + "license": "MIT", "optional": true, -@@ -5939,2 +5977,3 @@ +@@ -6007,2 +6028,3 @@ "dev": true, + "license": "MIT", "engines": { -@@ -6018,5 +6057,5 @@ +@@ -6076,5 +6098,5 @@ "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -808,7 +715,7 @@ index 6025771a..b0637f1f 100644 + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, -@@ -6040,31 +6079,11 @@ +@@ -6098,31 +6120,11 @@ "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", @@ -846,7 +753,7 @@ index 6025771a..b0637f1f 100644 + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } -@@ -6081,2 +6100,9 @@ +@@ -6139,2 +6141,9 @@ }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", @@ -856,18 +763,18 @@ index 6025771a..b0637f1f 100644 + "license": "MIT" + }, "node_modules/string-width/node_modules/strip-ansi": { -@@ -6174,4 +6200,5 @@ +@@ -6232,4 +6241,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, -@@ -6209,2 +6236,3 @@ +@@ -6267,2 +6277,3 @@ "dev": true, + "license": "MIT", "dependencies": { -@@ -6295,2 +6323,24 @@ +@@ -6353,2 +6364,24 @@ }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", @@ -892,23 +799,14 @@ index 6025771a..b0637f1f 100644 + } + }, "node_modules/table/node_modules/strip-ansi": { -@@ -6426,5 +6476,5 @@ - "node_modules/tmp": { -- "version": "0.2.4", -- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", -- "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", -+ "version": "0.2.5", -+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", -+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", - "dev": true, -@@ -6550,4 +6600,5 @@ +@@ -6608,4 +6641,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, -@@ -6599,2 +6650,12 @@ +@@ -6657,2 +6691,12 @@ }, + "node_modules/undici": { + "version": "7.16.0", @@ -921,13 +819,13 @@ index 6025771a..b0637f1f 100644 + } + }, "node_modules/undici-types": { -@@ -6638,3 +6699,4 @@ +@@ -6690,3 +6734,4 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true + "dev": true, + "license": "MIT" }, -@@ -6819,2 +6881,25 @@ +@@ -6862,2 +6907,25 @@ }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", @@ -953,7 +851,7 @@ index 6025771a..b0637f1f 100644 + } + }, "node_modules/which": { -@@ -6923,2 +7008,24 @@ +@@ -6966,2 +7034,24 @@ }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", @@ -978,7 +876,7 @@ index 6025771a..b0637f1f 100644 + } + }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { -@@ -6949,27 +7056,2 @@ +@@ -6992,27 +7082,2 @@ }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", @@ -1006,7 +904,7 @@ index 6025771a..b0637f1f 100644 - } - }, "node_modules/wrappy": { -@@ -7026,2 +7108,47 @@ +@@ -7085,2 +7150,47 @@ }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", @@ -1055,28 +953,106 @@ index 6025771a..b0637f1f 100644 + }, "node_modules/yauzl": { diff --git a/build/package.json b/build/package.json -index 3b3d1db3..99a77e5a 100644 +index e12cb930..47301b94 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 81d315f0..9bb1f3ef 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -5741,2 +5741,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": { +@@ -6547,2 +6556,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/cross-spawn": { +@@ -17541,5 +17564,2 @@ + }, +- "node_modules/ssh2/node_modules/cpu-features": { +- "optional": true +- }, + "node_modules/stable": { diff --git a/package.json b/package.json -index 79f619ca..948b9313 100644 +index 36485330..57661aee 100644 --- a/package.json +++ b/package.json -@@ -260,3 +260,3 @@ +@@ -273,3 +273,3 @@ "ssh2": { - "cpu-features": "0.0.0" + "cpu-features": "0.0.10" - } + }, +diff --git a/remote/package-lock.json b/remote/package-lock.json +index a829c6d5..7397ebd3 100644 +--- a/remote/package-lock.json ++++ b/remote/package-lock.json +@@ -1053,2 +1053,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/chownr": { +@@ -1076,2 +1085,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/debug": { +@@ -1682,5 +1705,2 @@ + }, +- "node_modules/ssh2/node_modules/cpu-features": { +- "optional": true +- }, + "node_modules/string_decoder": { diff --git a/remote/package.json b/remote/package.json -index 1544b4c9..bed3fa7f 100644 +index 879eb7d6..7332cf03 100644 --- a/remote/package.json +++ b/remote/package.json -@@ -56,3 +56,3 @@ +@@ -61,3 +61,3 @@ "ssh2": { - "cpu-features": "0.0.0" + "cpu-features": "0.0.10" - } + }, diff --git a/patches/linux/41-arch-add-support.patch b/patches/linux/41-arch-add-support.patch index 65e8f4794ab..e0759bc1304 100644 --- a/patches/linux/41-arch-add-support.patch +++ b/patches/linux/41-arch-add-support.patch @@ -1,5 +1,5 @@ diff --git a/build/azure-pipelines/linux/setup-env.sh b/build/azure-pipelines/linux/setup-env.sh -index f0d5fe6..fdb3707 100755 +index 2f275d15..d844eb03 100755 --- a/build/azure-pipelines/linux/setup-env.sh +++ b/build/azure-pipelines/linux/setup-env.sh @@ -2,3 +2,3 @@ @@ -13,71 +13,83 @@ index f0d5fe6..fdb3707 100755 + SYSROOT_ARCH="$SYSROOT_ARCH" VSCODE_SYSROOT_DIR="$VSCODE_REMOTE_SYSROOT_DIR" node -e 'import { getVSCodeSysroot } from "./build/linux/debian/install-sysroot.ts"; (async () => { await getVSCodeSysroot(process.env["SYSROOT_ARCH"]); })()' fi diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts -index 8e7f6bb..0874203 100644 +index 62c30da5..85817274 100644 --- a/build/gulpfile.reh.ts +++ b/build/gulpfile.reh.ts -@@ -239,9 +239,23 @@ function nodejs(platform: string, arch: string): NodeJS.ReadWriteStream | undefi - case 'linux': -- return (product.nodejsRepository !== 'https://nodejs.org' ? +@@ -275,9 +275,24 @@ function nodejs(platform: string, arch: string): NodeJS.ReadWriteStream | undefi + case 'linux': { +- const downloaded = (product.nodejsRepository !== 'https://nodejs.org' ? - fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName!, checksumSha256 }) : - fetchUrls(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org', checksumSha256 }) - ).pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) - .pipe(filter('**/node')) - .pipe(util.setExecutableBit('**')) - .pipe(rename('node')); ++ let downloaded: NodeJS.ReadWriteStream; + if (process.env.VSCODE_NODEJS_SITE && process.env.VSCODE_NODEJS_URLROOT) { -+ return fetchUrls(`${process.env.VSCODE_NODEJS_URLROOT}/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}${process.env.VSCODE_NODEJS_URLSUFFIX}.tar.gz`, { base: process.env.VSCODE_NODEJS_SITE, checksumSha256 }) ++ downloaded = fetchUrls(`${process.env.VSCODE_NODEJS_URLROOT}/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}${process.env.VSCODE_NODEJS_URLSUFFIX}.tar.gz`, { base: process.env.VSCODE_NODEJS_SITE, checksumSha256 }) + .pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) + .pipe(filter('**/node')) + .pipe(util.setExecutableBit('**')) + .pipe(rename('node')); + } -+ if (product.nodejsRepository !== 'https://nodejs.org') { -+ return fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName!, checksumSha256 }) ++ else if (product.nodejsRepository !== 'https://nodejs.org') { ++ downloaded = fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName!, checksumSha256 }) + .pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) + .pipe(filter('**/node')) + .pipe(util.setExecutableBit('**')) + .pipe(rename('node')); + } + else { -+ return fetchUrls(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org', checksumSha256 }) ++ downloaded = fetchUrls(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org', checksumSha256 }) + .pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) + .pipe(filter('**/node')) + .pipe(util.setExecutableBit('**')) + .pipe(rename('node')); + } - case 'alpine': + return platform === 'linux' && arch === 'x64' ? downloaded.pipe(patchElfLoadAlign()) : downloaded; diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index 25a3600..c9402f3 100644 +index 53987f88..50e75b95 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -516,4 +516,15 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d +@@ -425,4 +425,17 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d + const electronOverride: { repo?: string; tag?: string } = {}; + if (process.env.VSCODE_ELECTRON_REPOSITORY) { + // official electron doesn't support all arch, override the repo with `VSCODE_ELECTRON_REPOSITORY`. + electronOverride.repo = process.env.VSCODE_ELECTRON_REPOSITORY; ++ console.log('Using Electron Repository:', electronOverride.repo); + } + + if (process.env.VSCODE_ELECTRON_TAG) { + electronOverride.tag = process.env.VSCODE_ELECTRON_TAG; ++ console.log('Using Electron Tag:', electronOverride.tag); + } + const electronConfig = { ...config, + ...electronOverride, platform, +diff --git a/build/lib/electron.ts b/build/lib/electron.ts +index 016c25f7..a04f56bb 100644 +--- a/build/lib/electron.ts ++++ b/build/lib/electron.ts +@@ -103,3 +103,3 @@ function darwinBundleDocumentTypes(types: { [name: string]: string | string[] }, + const { msBuildId } = util.getElectronVersion(); +-const electronVersion = '42.2.0'; ++const electronVersion = process.env.VSCODE_ELECTRON_VERSION ?? '42.2.0'; + diff --git a/build/linux/debian/dep-lists.ts b/build/linux/debian/dep-lists.ts -index 46c257d..78bfb66 100644 +index 7690e25d..42692905 100644 --- a/build/linux/debian/dep-lists.ts +++ b/build/linux/debian/dep-lists.ts -@@ -141,3 +141,3 @@ export const referenceGeneratedDepsByArch = { +@@ -143,3 +143,3 @@ export const referenceGeneratedDepsByArch = { 'xdg-utils (>= 1.0.2)' - ] + ], }; diff --git a/build/linux/debian/install-sysroot.ts b/build/linux/debian/install-sysroot.ts -index 0dfc69a..7b6ac8b 100644 +index 0dfc69a0..7b6ac8bb 100644 --- a/build/linux/debian/install-sysroot.ts +++ b/build/linux/debian/install-sysroot.ts @@ -82,3 +82,5 @@ async function fetchUrl(options: IFetchOptions, retries = 10, retryDelay = 1000) diff --git a/patches/linux/42-arch-add-ppc64le.patch b/patches/linux/42-arch-add-ppc64le.patch index 3fa1f7c30a7..d97e30dfb7a 100644 --- a/patches/linux/42-arch-add-ppc64le.patch +++ b/patches/linux/42-arch-add-ppc64le.patch @@ -1,8 +1,8 @@ diff --git a/build/azure-pipelines/linux/setup-env.sh b/build/azure-pipelines/linux/setup-env.sh -index fdb3707..67bc741 100755 +index d844eb03..e18eb1c1 100755 --- a/build/azure-pipelines/linux/setup-env.sh +++ b/build/azure-pipelines/linux/setup-env.sh -@@ -76,2 +76,14 @@ elif [ "$npm_config_arch" == "arm" ]; then +@@ -90,2 +90,14 @@ elif [ "$npm_config_arch" == "arm" ]; then export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_REMOTE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot -L$VSCODE_REMOTE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/usr/lib/arm-linux-gnueabihf -L$VSCODE_REMOTE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/lib/arm-linux-gnueabihf" +elif [ "$npm_config_arch" == "ppc64" ]; then + # Set compiler toolchain for client native modules @@ -18,7 +18,7 @@ index fdb3707..67bc741 100755 + export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_REMOTE_SYSROOT_DIR/powerpc64le-linux-gnu/powerpc64le-linux-gnu/sysroot -L$VSCODE_REMOTE_SYSROOT_DIR/powerpc64le-linux-gnu/powerpc64le-linux-gnu/sysroot/usr/lib/powerpc64le-linux-gnu -L$VSCODE_REMOTE_SYSROOT_DIR/powerpc64le-linux-gnu/powerpc64le-linux-gnu/sysroot/lib/powerpc64le-linux-gnu" fi diff --git a/build/azure-pipelines/linux/verify-glibc-requirements.sh b/build/azure-pipelines/linux/verify-glibc-requirements.sh -index 5294177..1e33aeb 100755 +index 52941776..1e33aebb 100755 --- a/build/azure-pipelines/linux/verify-glibc-requirements.sh +++ b/build/azure-pipelines/linux/verify-glibc-requirements.sh @@ -9,2 +9,4 @@ elif [ "$VSCODE_ARCH" == "armhf" ]; then @@ -27,7 +27,7 @@ index 5294177..1e33aeb 100755 + TRIPLE="powerpc64le-linux-gnu" fi diff --git a/build/checksums/vscode-sysroot.txt b/build/checksums/vscode-sysroot.txt -index 847383e..04962e7 100644 +index 847383e7..04962e71 100644 --- a/build/checksums/vscode-sysroot.txt +++ b/build/checksums/vscode-sysroot.txt @@ -7 +7,3 @@ ac4b6b14b4cec027a22a51bbbb049b3504958a78106c8a8d5cec144206b767d1 x86_64-linux-g @@ -35,30 +35,30 @@ index 847383e..04962e7 100644 +fa8176d27be18bb0eeb7f55b0fa22255050b430ef68c29136599f02976eb0b1b powerpc64le-linux-gnu-glibc-2.28.tar.gz +bdab0a372cf5bfdc154226cc5ed72d417096c725085d193509d1f12e0debec66 powerpc64le-linux-gnu-glibc-2.28-gcc-10.5.0.tar.gz diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts -index 904130f..42888f0 100644 +index 8ecd10b2..469b73e8 100644 --- a/build/gulpfile.reh.ts +++ b/build/gulpfile.reh.ts -@@ -56,2 +56,3 @@ const BUILD_TARGETS = [ +@@ -51,2 +51,3 @@ const BUILD_TARGETS = [ { platform: 'linux', arch: 'arm64' }, + { platform: 'linux', arch: 'ppc64le' }, { platform: 'alpine', arch: 'arm64' }, diff --git a/build/gulpfile.scan.ts b/build/gulpfile.scan.ts -index 19e50c0..47b25cf 100644 +index 972b335a..6d3d8991 100644 --- a/build/gulpfile.scan.ts +++ b/build/gulpfile.scan.ts -@@ -24,2 +24,3 @@ const BUILD_TARGETS = [ +@@ -23,2 +23,3 @@ const BUILD_TARGETS = [ { platform: 'linux', arch: 'arm64' }, + { platform: 'linux', arch: 'ppc64le' }, ]; diff --git a/build/gulpfile.vscode.linux.ts b/build/gulpfile.vscode.linux.ts -index c5d2163..da4fc1f 100644 +index 45179160..18454fd9 100644 --- a/build/gulpfile.vscode.linux.ts +++ b/build/gulpfile.vscode.linux.ts -@@ -32,2 +32,3 @@ function getDebPackageArch(arch: string): string { +@@ -31,2 +31,3 @@ function getDebPackageArch(arch: string): string { case 'arm64': return 'arm64'; + case 'ppc64le': return 'ppc64el'; default: throw new Error(`Unknown arch: ${arch}`); -@@ -142,2 +143,3 @@ function getRpmPackageArch(arch: string): string { +@@ -141,2 +142,3 @@ function getRpmPackageArch(arch: string): string { case 'arm64': return 'aarch64'; + case 'ppc64le': return 'ppc64le'; default: throw new Error(`Unknown arch: ${arch}`); @@ -67,15 +67,15 @@ index c5d2163..da4fc1f 100644 + { arch: 'ppc64le' }, ]; diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts -index 5e373f6..4e1fcf6 100644 +index d192dd89..8bec6a04 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts -@@ -702,2 +702,3 @@ const BUILD_TARGETS = [ +@@ -641,2 +641,3 @@ const BUILD_TARGETS = [ { platform: 'linux', arch: 'arm64' }, + { platform: 'linux', arch: 'ppc64le' }, ]; diff --git a/build/linux/debian/calculate-deps.ts b/build/linux/debian/calculate-deps.ts -index 98a9630..6c6bbf5 100644 +index 98a96302..6c6bbf55 100644 --- a/build/linux/debian/calculate-deps.ts +++ b/build/linux/debian/calculate-deps.ts @@ -61,2 +61,9 @@ function calculatePackageDeps(binaryPath: string, arch: DebianArchString, chromi @@ -89,10 +89,10 @@ index 98a9630..6c6bbf5 100644 + break; } diff --git a/build/linux/debian/dep-lists.ts b/build/linux/debian/dep-lists.ts -index 78bfb66..5acd3a0 100644 +index 42692905..0601c349 100644 --- a/build/linux/debian/dep-lists.ts +++ b/build/linux/debian/dep-lists.ts -@@ -142,2 +142,41 @@ export const referenceGeneratedDepsByArch = { +@@ -144,2 +144,41 @@ export const referenceGeneratedDepsByArch = { ], + 'ppc64el': [ + 'ca-certificates', @@ -135,7 +135,7 @@ index 78bfb66..5acd3a0 100644 + ], }; diff --git a/build/linux/debian/install-sysroot.ts b/build/linux/debian/install-sysroot.ts -index 7b6ac8b..ef7dd2b 100644 +index 7b6ac8bb..ef7dd2ba 100644 --- a/build/linux/debian/install-sysroot.ts +++ b/build/linux/debian/install-sysroot.ts @@ -158,2 +158,7 @@ export async function getVSCodeSysroot(arch: DebianArchString, isMusl: boolean = @@ -147,7 +147,7 @@ index 7b6ac8b..ef7dd2b 100644 + break; } diff --git a/build/linux/debian/types.ts b/build/linux/debian/types.ts -index e97485e..c56d067 100644 +index e97485ef..c56d0678 100644 --- a/build/linux/debian/types.ts +++ b/build/linux/debian/types.ts @@ -5,6 +5,6 @@ @@ -160,10 +160,10 @@ index e97485e..c56d067 100644 + return ['amd64', 'armhf', 'arm64', 'ppc64el', 'ppc64le'].includes(s); } diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts -index 0424c8d..c7e0820 100644 +index e9f414e4..887e9275 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts -@@ -318,2 +318,102 @@ export const referenceGeneratedDepsByArch = { +@@ -335,2 +335,102 @@ export const referenceGeneratedDepsByArch = { 'xdg-utils' + ], + "ppc64le": [ @@ -267,7 +267,7 @@ index 0424c8d..c7e0820 100644 + 'xdg-utils' ] diff --git a/build/linux/rpm/types.ts b/build/linux/rpm/types.ts -index c6a01da..3f3c3f5 100644 +index c6a01da1..3f3c3f5f 100644 --- a/build/linux/rpm/types.ts +++ b/build/linux/rpm/types.ts @@ -5,6 +5,6 @@ @@ -279,60 +279,47 @@ index c6a01da..3f3c3f5 100644 - return ['x86_64', 'armv7hl', 'aarch64'].includes(s); + return ['x86_64', 'armv7hl', 'aarch64', 'ppc64le'].includes(s); } -diff --git a/build/npm/preinstall.ts b/build/npm/preinstall.ts -index dd53ff4..ca427d9 100644 ---- a/build/npm/preinstall.ts -+++ b/build/npm/preinstall.ts -@@ -149,3 +149,7 @@ function installHeaders() { - } catch (error) { -- throw new Error(`Error applying v8-source-location.patch: ${(error as Error).message}`); -+ if (process.env['VSCODE_ARCH'] === 'ppc64le') { -+ console.log('Ignoring patch error for ppc64le architecture'); -+ } else { -+ throw new Error(`Error applying v8-source-location.patch: ${(error as Error).message}`); -+ } - } diff --git a/cli/src/update_service.rs b/cli/src/update_service.rs -index 3b7ef5c..ec97760 100644 +index 82d2814c..69367048 100644 --- a/cli/src/update_service.rs +++ b/cli/src/update_service.rs -@@ -183,2 +183,3 @@ pub enum Platform { +@@ -179,2 +179,3 @@ pub enum Platform { LinuxARM32Legacy, + LinuxPPC64LE, DarwinX64, -@@ -201,2 +202,3 @@ impl Platform { +@@ -197,2 +198,3 @@ impl Platform { Platform::LinuxARM32Legacy => "armhf", + Platform::LinuxPPC64LE => "ppc64le", Platform::DarwinX64 => "x64", -@@ -220,2 +222,3 @@ impl Platform { +@@ -216,2 +218,3 @@ impl Platform { Platform::LinuxARM32Legacy => "linux", + Platform::LinuxPPC64LE => "linux", Platform::DarwinX64 => "darwin", -@@ -248,2 +251,4 @@ impl Platform { +@@ -244,2 +247,4 @@ impl Platform { Some(Platform::LinuxARM64) + } else if cfg!(all(target_os = "linux", target_arch = "powerpc64")) { + Some(Platform::LinuxPPC64LE) } else if cfg!(all(target_os = "macos", target_arch = "x86_64")) { -@@ -275,2 +280,3 @@ impl fmt::Display for Platform { +@@ -271,2 +276,3 @@ impl fmt::Display for Platform { Platform::LinuxARM32Legacy => "LinuxARM32Legacy", + Platform::LinuxPPC64LE => "LinuxPPC64LE", Platform::DarwinX64 => "DarwinX64", diff --git a/cli/src/util/prereqs.rs b/cli/src/util/prereqs.rs -index 44c8597..205c274 100644 +index 83dd024b..e499af60 100644 --- a/cli/src/util/prereqs.rs +++ b/cli/src/util/prereqs.rs -@@ -82,2 +82,4 @@ impl PreReqChecker { +@@ -83,2 +83,4 @@ impl PreReqChecker { Platform::LinuxARM32 + } else if cfg!(target_arch = "powerpc64") { + Platform::LinuxPPC64LE } else { -@@ -207,2 +209,4 @@ async fn check_glibcxx_version() -> Result { +@@ -208,2 +210,4 @@ async fn check_glibcxx_version() -> Result { const DEFAULT_LIB_PATH: &str = "/usr/lib64/libstdc++.so.6"; + #[cfg(target_arch = "powerpc64")] + const DEFAULT_LIB_PATH: &str = "/usr/lib64/libstdc++.so.6"; #[cfg(any(target_arch = "x86", target_arch = "arm"))] diff --git a/resources/server/bin/helpers/check-requirements-linux.sh b/resources/server/bin/helpers/check-requirements-linux.sh -index 8ea4c0b..94028e3 100644 +index 8ea4c0b5..94028e34 100644 --- a/resources/server/bin/helpers/check-requirements-linux.sh +++ b/resources/server/bin/helpers/check-requirements-linux.sh @@ -55,2 +55,3 @@ case $ARCH in @@ -340,28 +327,28 @@ index 8ea4c0b..94028e3 100644 + ppc64le) LDCONFIG_ARCH="64bit";; esac diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts -index ab11f6b..fd1ec31 100644 +index 7b0415e4..26197506 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts -@@ -48,2 +48,3 @@ export function TargetPlatformToString(targetPlatform: TargetPlatform) { +@@ -49,2 +49,3 @@ export function TargetPlatformToString(targetPlatform: TargetPlatform) { case TargetPlatform.LINUX_ARMHF: return 'Linux ARM'; + case TargetPlatform.LINUX_PPC64LE: return 'Linux PowerPC64'; -@@ -71,2 +72,3 @@ export function toTargetPlatform(targetPlatform: string): TargetPlatform { +@@ -72,2 +73,3 @@ export function toTargetPlatform(targetPlatform: string): TargetPlatform { case TargetPlatform.LINUX_ARMHF: return TargetPlatform.LINUX_ARMHF; + case TargetPlatform.LINUX_PPC64LE: return TargetPlatform.LINUX_PPC64LE; -@@ -106,2 +108,5 @@ export function getTargetPlatform(platform: Platform | 'alpine', arch: string | +@@ -107,2 +109,5 @@ export function getTargetPlatform(platform: Platform | 'alpine', arch: string | } + if (arch === 'ppc64le') { + return TargetPlatform.LINUX_PPC64LE; + } return TargetPlatform.UNKNOWN; diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts -index 3e5b309..0fbbb67 100644 +index e26d74e2..5fb399dd 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts -@@ -343,2 +343,3 @@ export const enum TargetPlatform { +@@ -345,2 +345,3 @@ export const enum TargetPlatform { LINUX_ARMHF = 'linux-armhf', + LINUX_PPC64LE = 'linux-ppc64le', diff --git a/patches/windows/00-build-replace-signature.patch b/patches/windows/00-build-replace-signature.patch new file mode 100644 index 00000000000..ea67bea3ea0 --- /dev/null +++ b/patches/windows/00-build-replace-signature.patch @@ -0,0 +1,185 @@ +diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts +index 62c30da5..46925452 100644 +--- a/build/gulpfile.reh.ts ++++ b/build/gulpfile.reh.ts +@@ -33,2 +33,3 @@ import { getCopilotExcludeFilter, getCopilotRuntimePrebuildFiles, getCopilotTgre + import { readAgentSdkResults } from './agent-sdk/common.ts'; ++import { stripAuthenticodeSignature } from './win32/signature.ts'; + +@@ -486,34 +487,2 @@ function packageTask(type: string, platform: string, arch: string, sourceFolderN + +-function hasAuthenticodeSignature(filePath: string): Promise { +- return new Promise((resolve, reject) => { +- const proc = cp.spawn('signtool.exe', ['verify', '/pa', filePath]); +- proc.on('error', reject); +- proc.on('exit', code => resolve(code === 0)); +- }); +-} +- +-async function stripAuthenticodeSignature(filePath: string): Promise { +- // ESRP's `signtool /as` (append) fails with 0x800700C1 on PEs whose existing +- // Authenticode signature was invalidated by rcedit. Strip cleanly first so +- // rcedit operates on an unsigned PE. +- if (!await hasAuthenticodeSignature(filePath)) { +- return; +- } +- await new Promise((resolve, reject) => { +- const proc = cp.spawn('signtool.exe', ['remove', '/s', filePath]); +- let out = ''; +- proc.stdout?.on('data', chunk => out += chunk.toString()); +- proc.stderr?.on('data', chunk => out += chunk.toString()); +- proc.on('error', reject); +- proc.on('exit', code => { +- if (code === 0) { +- resolve(); +- } else { +- process.stderr.write(out); +- reject(new Error(`signtool remove /s failed for ${filePath} (exit ${code})`)); +- } +- }); +- }); +-} +- + function patchWin32DependenciesTask(destinationFolderName: string) { +@@ -539,3 +508,3 @@ function patchWin32DependenciesTask(destinationFolderName: string) { + 'version-string': { +- 'CompanyName': 'Microsoft Corporation', ++ 'CompanyName': '!!ORG_NAME!!', + 'FileDescription': productContents.nameLong, +@@ -543,3 +512,3 @@ function patchWin32DependenciesTask(destinationFolderName: string) { + 'InternalName': basename, +- 'LegalCopyright': 'Copyright (C) 2026 Microsoft. All rights reserved', ++ 'LegalCopyright': 'Copyright (C) 2026 !!ORG_NAME!!. All rights reserved', + 'OriginalFilename': basename, +diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts +index 53987f88..bac039b8 100644 +--- a/build/gulpfile.vscode.ts ++++ b/build/gulpfile.vscode.ts +@@ -21,3 +21,2 @@ import product from '../product.json' with { type: 'json' }; + import * as crypto from 'crypto'; +-import * as cp from 'child_process'; + import * as i18n from './lib/i18n.ts'; +@@ -38,2 +37,3 @@ import { spawnTsgo } from './lib/tsgo.ts'; + import { runEsbuildTranspile, runEsbuildBundle } from './lib/esbuild.ts'; ++import { stripAuthenticodeSignature } from './win32/signature.ts'; + +@@ -532,34 +532,2 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d + +-function hasAuthenticodeSignature(filePath: string): Promise { +- return new Promise((resolve, reject) => { +- const proc = cp.spawn('signtool.exe', ['verify', '/pa', filePath]); +- proc.on('error', reject); +- proc.on('exit', code => resolve(code === 0)); +- }); +-} +- +-async function stripAuthenticodeSignature(filePath: string): Promise { +- // ESRP's `signtool /as` (append) fails with 0x800700C1 on PEs whose existing +- // Authenticode signature was invalidated by rcedit. Strip cleanly first so +- // rcedit operates on an unsigned PE. +- if (!await hasAuthenticodeSignature(filePath)) { +- return; +- } +- await new Promise((resolve, reject) => { +- const proc = cp.spawn('signtool.exe', ['remove', '/s', filePath]); +- let out = ''; +- proc.stdout?.on('data', chunk => out += chunk.toString()); +- proc.stderr?.on('data', chunk => out += chunk.toString()); +- proc.on('error', reject); +- proc.on('exit', code => { +- if (code === 0) { +- resolve(); +- } else { +- process.stderr.write(out); +- reject(new Error(`signtool remove /s failed for ${filePath} (exit ${code})`)); +- } +- }); +- }); +-} +- + function patchWin32DependenciesTask(destinationFolderName: string) { +@@ -587,3 +555,3 @@ function patchWin32DependenciesTask(destinationFolderName: string) { + 'version-string': { +- 'CompanyName': 'Microsoft Corporation', ++ 'CompanyName': '!!ORG_NAME!!', + 'FileDescription': product.nameLong, +@@ -591,3 +559,3 @@ function patchWin32DependenciesTask(destinationFolderName: string) { + 'InternalName': basename, +- 'LegalCopyright': 'Copyright (C) 2026 Microsoft. All rights reserved', ++ 'LegalCopyright': 'Copyright (C) 2026 !!ORG_NAME!!. All rights reserved', + 'OriginalFilename': basename, +diff --git a/build/win32/signature.ts b/build/win32/signature.ts +new file mode 100644 +index 00000000..ee722946 +--- /dev/null ++++ b/build/win32/signature.ts +@@ -0,0 +1,68 @@ ++import * as cp from 'child_process'; ++import { promises as fs } from 'node:fs'; ++ ++export async function stripAuthenticodeSignature(filePath: string): Promise { ++ const signPath = await getSigntoolPath() ++ // ESRP's `signtool /as` (append) fails with 0x800700C1 on PEs whose existing ++ // Authenticode signature was invalidated by rcedit. Strip cleanly first so ++ // rcedit operates on an unsigned PE. ++ if (!await hasAuthenticodeSignature(filePath, signPath)) { ++ return; ++ } ++ await new Promise((resolve, reject) => { ++ const proc = cp.spawn(signPath, ['remove', '/s', filePath]); ++ let out = ''; ++ proc.stdout?.on('data', chunk => out += chunk.toString()); ++ proc.stderr?.on('data', chunk => out += chunk.toString()); ++ proc.on('error', reject); ++ proc.on('exit', code => { ++ if (code === 0) { ++ resolve(); ++ } else { ++ process.stderr.write(out); ++ reject(new Error(`signtool remove /s failed for ${filePath} (exit ${code})`)); ++ } ++ }); ++ }); ++} ++ ++async function getSigntoolPath(): Promise { ++ const windowsKitsFolder = 'C:/Program Files (x86)/Windows Kits/10/bin/'; ++ const folders = await fs.readdir(windowsKitsFolder); ++ let fileName = ''; ++ let maxVersion = 0; ++ for (const folder of folders) { ++ if (!folder.endsWith('.0')) { ++ continue; ++ } ++ const folderVersion = parseInt(folder.replace(/\./g,'')); ++ if (folderVersion > maxVersion) { ++ const signtoolFilename = `${windowsKitsFolder}${folder}/x64/signtool.exe`; ++ try { ++ const stat = await fs.stat(signtoolFilename); ++ if (stat.isFile()) { ++ fileName = signtoolFilename; ++ maxVersion = folderVersion; ++ } ++ } ++ catch { ++ console.warn('Skipping %s due to error.', signtoolFilename); ++ } ++ } ++ } ++ if(fileName == '') { ++ throw new Error('Unable to find signtool.exe in ' + windowsKitsFolder); ++ } ++ ++ console.log(`Signtool location is ${fileName}.`); ++ ++ return fileName; ++} ++ ++function hasAuthenticodeSignature(filePath: string, signPath: string): Promise { ++ return new Promise((resolve, reject) => { ++ const proc = cp.spawn(signPath, ['verify', '/pa', filePath]); ++ proc.on('error', reject); ++ proc.on('exit', code => resolve(code === 0)); ++ }); ++} +\ No newline at end of file diff --git a/prepare_vscode.sh b/prepare_vscode.sh index 2f5febcf2b4..574f938207f 100755 --- a/prepare_vscode.sh +++ b/prepare_vscode.sh @@ -13,8 +13,6 @@ 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/release_notes.md b/release_notes.md index d0e88952e09..105ce77ae34 100644 --- a/release_notes.md +++ b/release_notes.md @@ -187,36 +187,6 @@ update vscode to [@@MS_TAG@@](@@MS_URL@@)
-## ARM 32bits - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Linux.deb@@BINARY_NAME@@_@@VERSION@@_armhf.deb
.rpm@@BINARY_NAME@@-@@VERSION@@-el8.armv7hl.rpm
.tar.gz@@APP_NAME@@-linux-armhf-@@VERSION@@@@QUALITY@@.tar.gz
Remote Host@@APP_NAME_LC@@-reh-linux-armhf-@@VERSION@@@@QUALITY@@.tar.gz
Web Host@@APP_NAME_LC@@-reh-web-linux-armhf-@@VERSION@@@@QUALITY@@.tar.gz
CLI@@APP_NAME_LC@@-cli-linux-armhf-@@VERSION@@@@QUALITY@@.tar.gz
- ## PPC 64bits diff --git a/stores/snapcraft/insider/snap/local/bin/electron-launch b/stores/snapcraft/insider/snap/local/bin/electron-launch index ba7f293f666..157939adbe4 100755 --- a/stores/snapcraft/insider/snap/local/bin/electron-launch +++ b/stores/snapcraft/insider/snap/local/bin/electron-launch @@ -77,8 +77,6 @@ chmod 700 "$SNAP_USER_DATA/.config" if [ "$SNAP_ARCH" == "amd64" ]; then ARCH="x86_64-linux-gnu" -elif [ "$SNAP_ARCH" == "armhf" ]; then - ARCH="arm-linux-gnueabihf" elif [ "$SNAP_ARCH" == "arm64" ]; then ARCH="aarch64-linux-gnu" else diff --git a/stores/snapcraft/stable/snap/local/bin/electron-launch b/stores/snapcraft/stable/snap/local/bin/electron-launch index ba7f293f666..157939adbe4 100755 --- a/stores/snapcraft/stable/snap/local/bin/electron-launch +++ b/stores/snapcraft/stable/snap/local/bin/electron-launch @@ -77,8 +77,6 @@ chmod 700 "$SNAP_USER_DATA/.config" if [ "$SNAP_ARCH" == "amd64" ]; then ARCH="x86_64-linux-gnu" -elif [ "$SNAP_ARCH" == "armhf" ]; then - ARCH="arm-linux-gnueabihf" elif [ "$SNAP_ARCH" == "arm64" ]; then ARCH="aarch64-linux-gnu" else diff --git a/upstream/insider.json b/upstream/insider.json index 97e8082e0bb..fe07fae52fe 100644 --- a/upstream/insider.json +++ b/upstream/insider.json @@ -1,4 +1,4 @@ { - "tag": "1.121.0", - "commit": "987c9597516278c9fcf10d963a0592ce1384ab93" + "tag": "1.126.0", + "commit": "7e7950df89d055b5a378379db9ee14290772148a" } From 920683a6882e65e5b115d0aaaa4d9bd25abf9e27 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Thu, 2 Jul 2026 14:12:07 +0200 Subject: [PATCH 64/79] ci: update gh actions (#2907) --- .github/workflows/ci-build-linux.yml | 12 ++++++------ .github/workflows/ci-build-macos.yml | 4 ++-- .github/workflows/ci-build-windows.yml | 8 ++++---- .github/workflows/lint-zizmor.yml | 4 ++-- .github/workflows/mod-lock-closed-threads.yml | 2 +- .github/workflows/mod-stale-issue-pr.yml | 2 +- .github/workflows/publish-insider-linux.yml | 18 +++++++++--------- .github/workflows/publish-insider-macos.yml | 4 ++-- .../workflows/publish-insider-spearhead.yml | 4 ++-- .github/workflows/publish-insider-windows.yml | 12 ++++++------ .github/workflows/publish-stable-linux.yml | 18 +++++++++--------- .github/workflows/publish-stable-macos.yml | 4 ++-- .github/workflows/publish-stable-spearhead.yml | 4 ++-- .github/workflows/publish-stable-windows.yml | 12 ++++++------ 14 files changed, 54 insertions(+), 54 deletions(-) diff --git a/.github/workflows/ci-build-linux.yml b/.github/workflows/ci-build-linux.yml index 22a256c8fab..67abc129bc5 100644 --- a/.github/workflows/ci-build-linux.yml +++ b/.github/workflows/ci-build-linux.yml @@ -46,7 +46,7 @@ jobs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -68,7 +68,7 @@ jobs: node-version-file: .nvmrc - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.11" @@ -144,7 +144,7 @@ jobs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -238,7 +238,7 @@ jobs: VSCODE_ARCH: ${{ matrix.vscode_arch }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -264,7 +264,7 @@ jobs: if: env.DISABLED != 'yes' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' if: env.DISABLED != 'yes' @@ -322,7 +322,7 @@ jobs: VSCODE_ARCH: ${{ matrix.vscode_arch }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false diff --git a/.github/workflows/ci-build-macos.yml b/.github/workflows/ci-build-macos.yml index 0663b367dc0..0290a57757d 100644 --- a/.github/workflows/ci-build-macos.yml +++ b/.github/workflows/ci-build-macos.yml @@ -47,7 +47,7 @@ jobs: vscode_arch: arm64 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -63,7 +63,7 @@ jobs: node-version-file: '.nvmrc' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' if: env.VSCODE_ARCH == 'x64' diff --git a/.github/workflows/ci-build-windows.yml b/.github/workflows/ci-build-windows.yml index e806897105c..24b63e80346 100644 --- a/.github/workflows/ci-build-windows.yml +++ b/.github/workflows/ci-build-windows.yml @@ -47,7 +47,7 @@ jobs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -63,7 +63,7 @@ jobs: node-version-file: '.nvmrc' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' @@ -116,7 +116,7 @@ jobs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -132,7 +132,7 @@ jobs: node-version-file: '.nvmrc' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' diff --git a/.github/workflows/lint-zizmor.yml b/.github/workflows/lint-zizmor.yml index a9c484a431c..e13d2e38ed6 100644 --- a/.github/workflows/lint-zizmor.yml +++ b/.github/workflows/lint-zizmor.yml @@ -22,9 +22,9 @@ jobs: security-events: write steps: - name: Checkout repo - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Run zizmor - uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 + uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7 diff --git a/.github/workflows/mod-lock-closed-threads.yml b/.github/workflows/mod-lock-closed-threads.yml index a9a4084c591..2d6c3bf51c6 100644 --- a/.github/workflows/mod-lock-closed-threads.yml +++ b/.github/workflows/mod-lock-closed-threads.yml @@ -13,7 +13,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0 + - uses: dessant/lock-threads@89ae32b08ed1a541efecbab17912962a5e38981c # v6.0.2 with: github-token: ${{ github.token }} issue-inactive-days: "90" diff --git a/.github/workflows/mod-stale-issue-pr.yml b/.github/workflows/mod-stale-issue-pr.yml index c8d8ac02abf..b82cb87b1a0 100644 --- a/.github/workflows/mod-stale-issue-pr.yml +++ b/.github/workflows/mod-stale-issue-pr.yml @@ -11,7 +11,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 with: days-before-stale: 180 days-before-close: 30 diff --git a/.github/workflows/publish-insider-linux.yml b/.github/workflows/publish-insider-linux.yml index 4f09aa50dc4..169551996cd 100644 --- a/.github/workflows/publish-insider-linux.yml +++ b/.github/workflows/publish-insider-linux.yml @@ -30,7 +30,7 @@ jobs: SHOULD_BUILD: ${{ env.SHOULD_BUILD }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -59,7 +59,7 @@ jobs: BUILD_SOURCEVERSION: ${{ env.BUILD_SOURCEVERSION }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -79,7 +79,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.11" if: env.SHOULD_BUILD == 'yes' @@ -166,7 +166,7 @@ jobs: SHOULD_BUILD: ${{ env.SHOULD_BUILD }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -273,7 +273,7 @@ jobs: if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -293,7 +293,7 @@ jobs: if: env.DISABLED != 'yes' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' if: env.DISABLED != 'yes' @@ -363,7 +363,7 @@ jobs: if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -461,12 +461,12 @@ jobs: if: needs.check.outputs.SHOULD_BUILD_SNAP != 'no' && vars.DISABLE_INSIDER_SNAP != 'yes' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false - - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 + - uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 - uses: diddlesnaps/snapcraft-multiarch-action@cfd7a246fad6bea65bb92f69a1c8d07898c231e5 # v1.9.0 with: diff --git a/.github/workflows/publish-insider-macos.yml b/.github/workflows/publish-insider-macos.yml index f3bd0cc2552..d58ed7f9e8a 100644 --- a/.github/workflows/publish-insider-macos.yml +++ b/.github/workflows/publish-insider-macos.yml @@ -35,7 +35,7 @@ jobs: vscode_arch: arm64 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -46,7 +46,7 @@ jobs: node-version-file: .nvmrc - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.11" if: env.VSCODE_ARCH == 'x64' diff --git a/.github/workflows/publish-insider-spearhead.yml b/.github/workflows/publish-insider-spearhead.yml index d5d76d232ef..fdab389391f 100644 --- a/.github/workflows/publish-insider-spearhead.yml +++ b/.github/workflows/publish-insider-spearhead.yml @@ -37,7 +37,7 @@ jobs: SHOULD_BUILD: ${{ env.SHOULD_BUILD }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: insider persist-credentials: false @@ -67,7 +67,7 @@ jobs: if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: insider persist-credentials: false diff --git a/.github/workflows/publish-insider-windows.yml b/.github/workflows/publish-insider-windows.yml index 3595dcd3406..a9461e37ecf 100644 --- a/.github/workflows/publish-insider-windows.yml +++ b/.github/workflows/publish-insider-windows.yml @@ -28,7 +28,7 @@ jobs: SHOULD_BUILD: ${{ env.SHOULD_BUILD }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -60,7 +60,7 @@ jobs: BUILD_SOURCEVERSION: ${{ env.BUILD_SOURCEVERSION }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -73,7 +73,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.11" if: env.SHOULD_BUILD == 'yes' @@ -133,7 +133,7 @@ jobs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -146,7 +146,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' if: env.SHOULD_BUILD == 'yes' @@ -234,7 +234,7 @@ jobs: ASSETS_REPOSITORY: vscodium-insiders steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false diff --git a/.github/workflows/publish-stable-linux.yml b/.github/workflows/publish-stable-linux.yml index d2f76a630ab..e9995826267 100644 --- a/.github/workflows/publish-stable-linux.yml +++ b/.github/workflows/publish-stable-linux.yml @@ -30,7 +30,7 @@ jobs: SHOULD_BUILD: ${{ env.SHOULD_BUILD }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -60,7 +60,7 @@ jobs: BUILD_SOURCEVERSION: ${{ env.BUILD_SOURCEVERSION }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -80,7 +80,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.11" if: env.SHOULD_BUILD == 'yes' @@ -167,7 +167,7 @@ jobs: SHOULD_BUILD: ${{ env.SHOULD_BUILD }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -274,7 +274,7 @@ jobs: if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -294,7 +294,7 @@ jobs: if: env.DISABLED != 'yes' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' if: env.DISABLED != 'yes' @@ -364,7 +364,7 @@ jobs: if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -457,7 +457,7 @@ jobs: if: needs.check.outputs.SHOULD_BUILD_SNAP != 'no' && vars.DISABLE_STABLE_SNAP != 'yes' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -470,7 +470,7 @@ jobs: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }} run: ./stores/snapcraft/check_version.sh - - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 + - uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 - uses: diddlesnaps/snapcraft-multiarch-action@cfd7a246fad6bea65bb92f69a1c8d07898c231e5 # v1.9.0 with: diff --git a/.github/workflows/publish-stable-macos.yml b/.github/workflows/publish-stable-macos.yml index 664660b3ad6..543f7483e6a 100644 --- a/.github/workflows/publish-stable-macos.yml +++ b/.github/workflows/publish-stable-macos.yml @@ -35,7 +35,7 @@ jobs: vscode_arch: arm64 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -46,7 +46,7 @@ jobs: node-version-file: .nvmrc - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.11" if: env.VSCODE_ARCH == 'x64' diff --git a/.github/workflows/publish-stable-spearhead.yml b/.github/workflows/publish-stable-spearhead.yml index 34ec21e004b..ad834df4553 100644 --- a/.github/workflows/publish-stable-spearhead.yml +++ b/.github/workflows/publish-stable-spearhead.yml @@ -37,7 +37,7 @@ jobs: SHOULD_BUILD: ${{ env.SHOULD_BUILD }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: master persist-credentials: false @@ -67,7 +67,7 @@ jobs: if: needs.check.outputs.SHOULD_BUILD == 'yes' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: master persist-credentials: false diff --git a/.github/workflows/publish-stable-windows.yml b/.github/workflows/publish-stable-windows.yml index 5f371c8a393..57f1d85e7a9 100644 --- a/.github/workflows/publish-stable-windows.yml +++ b/.github/workflows/publish-stable-windows.yml @@ -28,7 +28,7 @@ jobs: SHOULD_BUILD: ${{ env.SHOULD_BUILD }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -60,7 +60,7 @@ jobs: BUILD_SOURCEVERSION: ${{ env.BUILD_SOURCEVERSION }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -73,7 +73,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.11" if: env.SHOULD_BUILD == 'yes' @@ -133,7 +133,7 @@ jobs: RELEASE_VERSION: ${{ env.RELEASE_VERSION }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false @@ -146,7 +146,7 @@ jobs: if: env.SHOULD_BUILD == 'yes' - name: Setup Python 3 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' if: env.SHOULD_BUILD == 'yes' @@ -232,7 +232,7 @@ jobs: APP_IDENTIFIER: VSCodium.VSCodium steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ env.GITHUB_BRANCH }} persist-credentials: false From 0b19ebf7dfe75d9697f3701d66cd4e6f10401383 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:15:53 +0200 Subject: [PATCH 65/79] build(deps-dev): bump js-yaml from 4.1.1 to 4.3.0 in /font-size (#2901) --- font-size/package-lock.json | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/font-size/package-lock.json b/font-size/package-lock.json index 2cdbfbba6b7..244adcb9ac3 100644 --- a/font-size/package-lock.json +++ b/font-size/package-lock.json @@ -4859,10 +4859,20 @@ } }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" From c8daf63531ab4a6cce6039888fa5832637b14bd1 Mon Sep 17 00:00:00 2001 From: Alex Garcia <115115137+lex-ibm@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:28:14 -0500 Subject: [PATCH 66/79] fix(ppc64le): don't set VSCODE_SKIP_SETUPENV (#2908) --- build/linux/package_bin.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/build/linux/package_bin.sh b/build/linux/package_bin.sh index e31c4a55a27..382ab7a7fad 100755 --- a/build/linux/package_bin.sh +++ b/build/linux/package_bin.sh @@ -32,7 +32,6 @@ elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then export VSCODE_SYSROOT_PREFIX="-glibc-2.28" export ELECTRON_SKIP_BINARY_DOWNLOAD=1 export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 - export VSCODE_SKIP_SETUPENV=1 export VSCODE_ELECTRON_REPOSITORY='lex-ibm/electron-ppc64le-build-scripts' export IGNORE_ELECTRON_VERSION="yes" elif [[ "${VSCODE_ARCH}" == "riscv64" ]]; then From 0e406ade19822ca2961304f96c61cde96abf8494 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Thu, 2 Jul 2026 20:31:02 +0200 Subject: [PATCH 67/79] fix: overwrite update uses version number not commit id (#2909) --- patches/11-update-use-github-release.patch | 119 +++++++++++++++++---- 1 file changed, 96 insertions(+), 23 deletions(-) diff --git a/patches/11-update-use-github-release.patch b/patches/11-update-use-github-release.patch index 6fd09192550..5f6097f2629 100644 --- a/patches/11-update-use-github-release.patch +++ b/patches/11-update-use-github-release.patch @@ -49,7 +49,7 @@ index 92ae7b96..c3fe6214 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 8cf5dceb..d776f408 100644 +index 8cf5dceb..4c550923 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -17,3 +17,3 @@ import { ILogService } from '../../log/common/log.js'; @@ -81,14 +81,47 @@ index 8cf5dceb..d776f408 100644 - - return url.toString(); } +@@ -178,3 +175,3 @@ export abstract class AbstractUpdateService implements IUpdateService { + +- if (!this.buildUpdateFeedUrl(quality, this.productService.commit!)) { ++ if (!this.buildUpdateFeedUrl(quality)) { + this.setState(State.Disabled(DisablementReason.InvalidConfiguration)); +@@ -373,5 +370,5 @@ export abstract class AbstractUpdateService implements IUpdateService { + +- const pendingUpdateCommit = this._state.update.version; ++ const pendingUpdateVersion = this._state.update.productVersion; + +- if (!pendingUpdateCommit || pendingUpdateCommit === 'unknown') { ++ if (!pendingUpdateVersion) { + return false; +@@ -384,3 +381,3 @@ export abstract class AbstractUpdateService implements IUpdateService { + const timeoutPromise = timeout(2000).then(() => { cts.cancel(); return undefined; }); +- isLatest = await Promise.race([this.isLatestVersion(pendingUpdateCommit, cts.token), timeoutPromise]); ++ isLatest = await Promise.race([this.isLatestVersion(pendingUpdateVersion, cts.token), timeoutPromise]); + cts.dispose(); +@@ -405,3 +402,3 @@ export abstract class AbstractUpdateService implements IUpdateService { + this.setState(State.Overwriting(this._state.update, explicit)); +- this.doCheckForUpdates(explicit, pendingUpdateCommit); ++ this.doCheckForUpdates(explicit, pendingUpdateVersion); + return true; +@@ -412,3 +409,3 @@ export abstract class AbstractUpdateService implements IUpdateService { + +- async isLatestVersion(commit?: string, token: CancellationToken = CancellationToken.None): Promise { ++ async isLatestVersion(pendingVersion?: string, token: CancellationToken = CancellationToken.None): Promise { + if (!this.quality) { @@ -419,3 +416,3 @@ export abstract class AbstractUpdateService implements IUpdateService { - if (mode === 'none') { + if (mode === 'none' || mode === 'manual') { return undefined; -@@ -429,18 +426,37 @@ export abstract class AbstractUpdateService implements IUpdateService { +@@ -423,3 +420,3 @@ export abstract class AbstractUpdateService implements IUpdateService { + +- const url = this.buildUpdateFeedUrl(this.quality, commit ?? this.productService.commit!, { internalOrg: this.getInternalOrg() }); ++ const url = this.buildUpdateFeedUrl(this.quality, { internalOrg: this.getInternalOrg() }); -+ return this._isLatestVersion(url, false) +@@ -429,18 +426,47 @@ export abstract class AbstractUpdateService implements IUpdateService { + ++ return this._isLatestVersion(url, false, pendingVersion, token) + .then((result) => { + return Promise.resolve(result ? result.lastest : result); + }) @@ -100,7 +133,7 @@ index 8cf5dceb..d776f408 100644 + }); + } + -+ _isLatestVersion(url: string, explicit: boolean): Promise<{lastest: boolean, update: IUpdate} | undefined> { ++ _isLatestVersion(url: string, explicit: boolean, pendingVersion?: string, token: CancellationToken = CancellationToken.None): Promise<{lastest: boolean, update: IUpdate} | undefined> { const headers = getUpdateRequestHeaders(this.productService.version); - this.logService.trace('update#isLatestVersion() - checking update server', { url, headers }); @@ -121,24 +154,41 @@ index 8cf5dceb..d776f408 100644 + return this.requestService.request({ url, headers, callSite: NO_FETCH_TELEMETRY }, CancellationToken.None) + .then(asJson) + .then(update => { -+ if (!update || !update.url || !update.version || !update.productVersion) { ++ if (!update || !update.url || !update.version || !update.productVersion || token.isCancellationRequested) { + this.setState(State.Idle(UpdateType.Setup, undefined, explicit || undefined)); + + return Promise.resolve(undefined); + } + + const fetchedVersion = /\d+\.\d+\.\d+\.\d+/.test(update.productVersion) ? update.productVersion.replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') : update.productVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); -+ const currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); + -+ this.logService.info(`update#isLatestVersion() - found: ${fetchedVersion}, current: ${currentVersion}`); ++ let currentVersion: string; ++ ++ if(pendingVersion) { ++ currentVersion = pendingVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); ++ ++ this.logService.info(`update#isLatestVersion() - found: ${fetchedVersion}, pending: ${currentVersion}`); ++ } ++ else { ++ currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); ++ ++ this.logService.info(`update#isLatestVersion() - found: ${fetchedVersion}, current: ${currentVersion}`); ++ } + + const lastest = semver.compareBuild(currentVersion, fetchedVersion) >= 0; + + return Promise.resolve({ lastest, update }); + }) } +@@ -480,4 +506,4 @@ export abstract class AbstractUpdateService implements IUpdateService { + +- protected abstract buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined; +- protected abstract doCheckForUpdates(explicit: boolean, pendingCommit?: string): void; ++ protected abstract buildUpdateFeedUrl(quality: string, options?: IUpdateURLOptions): string | undefined; ++ protected abstract doCheckForUpdates(explicit: boolean, pendingVersion?: string): void; + } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts -index e92c54e2..c4354cd1 100644 +index e92c54e2..b8057a06 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'; @@ -146,7 +196,7 @@ index e92c54e2..c4354cd1 100644 -import { asJson, IRequestService } from '../../request/common/request.js'; +import { asJson, IRequestService, NO_FETCH_TELEMETRY } from '../../request/common/request.js'; import { IApplicationStorageMainService } from '../../storage/electron-main/storageMainService.js'; -@@ -93,15 +93,4 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau +@@ -93,18 +93,7 @@ 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,16 +211,25 @@ index e92c54e2..c4354cd1 100644 - return undefined; - } - return url; -+ protected buildUpdateFeedUrl(quality: string, _commit: string, _options?: IUpdateURLOptions): string | undefined { ++ protected buildUpdateFeedUrl(quality: string, _options?: IUpdateURLOptions): string | undefined { + return createUpdateURL(this.productService, quality, process.platform, process.arch); } + +- protected doCheckForUpdates(explicit: boolean, pendingCommit?: string): void { ++ protected doCheckForUpdates(explicit: boolean, pendingVersion?: string): void { + if (!this.quality) { +@@ -117,3 +106,3 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau + const background = !explicit && !internalOrg; +- const url = this.buildUpdateFeedUrl(this.quality, pendingCommit ?? this.productService.commit!, { background, internalOrg }); ++ const url = this.buildUpdateFeedUrl(this.quality, { background, internalOrg }); + @@ -131,4 +120,32 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau - this.logService.trace('update#doCheckForUpdates - using Electron autoUpdater', { url, explicit, background }); - electron.autoUpdater.checkForUpdates(); + this.logService.info('update#doCheckForUpdates', { url, explicit, background }); + -+ this._isLatestVersion(url, explicit) ++ this._isLatestVersion(url, explicit, pendingVersion) + .then((result) => { + if(!result) { + this.setState(State.Idle(UpdateType.Archive)); @@ -204,8 +263,13 @@ index e92c54e2..c4354cd1 100644 - 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; +@@ -198,3 +215,3 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau + // Rebuild feed URL and trigger download via Electron's auto-updater +- this.buildUpdateFeedUrl(this.quality!, state.update.version, { internalOrg: this.getInternalOrg() }); ++ this.buildUpdateFeedUrl(this.quality!, { internalOrg: this.getInternalOrg() }); + this.setState(State.CheckingForUpdates(true)); diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts -index 2be53f61..53904dab 100644 +index 2be53f61..36a747ab 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 @@ @@ -221,21 +285,26 @@ index 2be53f61..53904dab 100644 -import { AvailableForDownload, IUpdate, State, UpdateType } from '../common/update.js'; +import { AvailableForDownload, State, UpdateType } from '../common/update.js'; import { AbstractUpdateService, createUpdateURL, IUpdateURLOptions } from './abstractUpdateService.js'; -@@ -36,4 +35,4 @@ export class LinuxUpdateService extends AbstractUpdateService { +@@ -36,7 +35,7 @@ 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 { ++ protected buildUpdateFeedUrl(quality: string, _options?: IUpdateURLOptions): string { + return createUpdateURL(this.productService, quality, process.platform, process.arch); } -@@ -45,2 +44,4 @@ export class LinuxUpdateService extends AbstractUpdateService { + +- protected doCheckForUpdates(explicit: boolean, _pendingCommit?: string): void { ++ protected doCheckForUpdates(explicit: boolean, pendingVersion?: string): void { + if (!this.quality) { +@@ -45,20 +44,33 @@ export class LinuxUpdateService extends AbstractUpdateService { + this.setState(State.CheckingForUpdates(explicit)); + const internalOrg = this.getInternalOrg(); -@@ -48,17 +49,28 @@ export class LinuxUpdateService extends AbstractUpdateService { - const url = this.buildUpdateFeedUrl(this.quality, this.productService.commit!, { background, internalOrg }); + const background = !explicit && !internalOrg; +- const url = this.buildUpdateFeedUrl(this.quality, this.productService.commit!, { background, internalOrg }); - this.setState(State.CheckingForUpdates(explicit)); ++ const url = this.buildUpdateFeedUrl(this.quality, { background, internalOrg }); - this.requestService.request({ url, callSite: 'updateService.linux.checkForUpdates' }, CancellationToken.None) - .then(asJson) @@ -243,7 +312,7 @@ index 2be53f61..53904dab 100644 - if (!update || !update.url || !update.version || !update.productVersion) { + this.logService.info('update#doCheckForUpdates', { url, explicit, background }); + -+ this._isLatestVersion(url, explicit) ++ this._isLatestVersion(url, explicit, pendingVersion) + .then((result) => { + if(!result) { + this.setState(State.Idle(UpdateType.Archive)); @@ -273,7 +342,7 @@ index 2be53f61..53904dab 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 1911ac0a..ee1cae7e 100644 +index 1911ac0a..e231d59e 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 @@ -317,7 +386,7 @@ index 1911ac0a..ee1cae7e 100644 - 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 -@@ -189,12 +193,22 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +@@ -189,15 +193,25 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - protected buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined { - let platform = `win32-${process.arch}`; @@ -326,7 +395,7 @@ index 1911ac0a..ee1cae7e 100644 - platform += '-archive'; - } else if (this.productService.target === 'user') { - platform += '-user'; -+ protected buildUpdateFeedUrl(quality: string, _commit: string, _options?: IUpdateURLOptions): string { ++ protected buildUpdateFeedUrl(quality: string, _options?: IUpdateURLOptions): string { + let target: Target; + + switch (getUpdateType()) { @@ -348,6 +417,10 @@ index 1911ac0a..ee1cae7e 100644 - return createUpdateURL(this.productService.updateUrl!, platform, quality, commit, options); + return createUpdateURL(this.productService, quality, process.platform, process.arch, target); } + +- protected doCheckForUpdates(explicit: boolean, pendingCommit?: string): void { ++ protected doCheckForUpdates(explicit: boolean, pendingVersion?: string): void { + if (!this.quality) { @@ -206,6 +220,2 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun - const internalOrg = this.getInternalOrg(); @@ -363,11 +436,11 @@ index 1911ac0a..ee1cae7e 100644 - .then(update => { + const internalOrg = this.getInternalOrg(); + const background = !explicit && !internalOrg; -+ const url = this.buildUpdateFeedUrl(this.quality, pendingCommit ?? this.productService.commit!, { background, internalOrg }); ++ const url = this.buildUpdateFeedUrl(this.quality, { background, internalOrg }); + + this.logService.info('update#doCheckForUpdates', { url, explicit, background }); + -+ this._isLatestVersion(url, explicit) ++ this._isLatestVersion(url, explicit, pendingVersion) + .then((result) => { const updateType = getUpdateType(); From 702370af977d2f0fda080bbe0f8fcc7e90df5d15 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Fri, 3 Jul 2026 01:12:44 +0200 Subject: [PATCH 68/79] ci: use macos-14 runner (#2910) --- .github/workflows/publish-insider-macos.yml | 2 +- .github/workflows/publish-stable-macos.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-insider-macos.yml b/.github/workflows/publish-insider-macos.yml index d58ed7f9e8a..6d6cead1410 100644 --- a/.github/workflows/publish-insider-macos.yml +++ b/.github/workflows/publish-insider-macos.yml @@ -31,7 +31,7 @@ jobs: include: - runner: macos-15-intel vscode_arch: x64 - - runner: [self-hosted, macOS, ARM64] + - runner: macos-14 vscode_arch: arm64 steps: diff --git a/.github/workflows/publish-stable-macos.yml b/.github/workflows/publish-stable-macos.yml index 543f7483e6a..37fe1531281 100644 --- a/.github/workflows/publish-stable-macos.yml +++ b/.github/workflows/publish-stable-macos.yml @@ -31,7 +31,7 @@ jobs: include: - runner: macos-15-intel vscode_arch: x64 - - runner: [self-hosted, macOS, ARM64] + - runner: macos-14 vscode_arch: arm64 steps: From d5be0edd268387052b10df5a3047609dee15cf93 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 6 Jul 2026 11:04:06 +0200 Subject: [PATCH 69/79] fix(loong64): update electron (#2912) --- build/linux/loong64/electron.sh | 2 +- build/linux/loong64/electron.sha256sums | 20 +++++++++++--------- build/linux/package_bin.sh | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/build/linux/loong64/electron.sh b/build/linux/loong64/electron.sh index 63cb443fa42..792e70de554 100644 --- a/build/linux/loong64/electron.sh +++ b/build/linux/loong64/electron.sh @@ -2,5 +2,5 @@ set -ex -export ELECTRON_VERSION="39.2.3" +export ELECTRON_VERSION="42.3.0" export VSCODE_ELECTRON_TAG="v${ELECTRON_VERSION}" diff --git a/build/linux/loong64/electron.sha256sums b/build/linux/loong64/electron.sha256sums index a727d66d1e1..11b09d08423 100644 --- a/build/linux/loong64/electron.sha256sums +++ b/build/linux/loong64/electron.sha256sums @@ -1,9 +1,11 @@ -d26b2189e7466a08c73861d0225c9b28730fdfc30918f3ea70853b43a2581dc4 *chromedriver-v39.2.3-linux-loong64.zip -8cc36f7468f5b2d98cde3f73c10c535555754c54be43e45c320a382c35b8e466 *electron-v39.2.3-linux-loong64.zip -f6e7462d6fd795ae2b08344ee0fdca817eba148b62a62717b3f512c845d96a64 *ffmpeg-v39.2.3-linux-loong64.zip -b122599dc84b81526ba4eecbca4794f3a2d25300242ad3829d1445cb6948f470 *hunspell-dictionaries.zip -76ef17d2810df5e77c5071863e2a375df914cfb7a362ad0582ee0eedca2441b9 *libcxx-headers.zip -9b61ba9f0780a57ee2749f7963759395784eadcaccc54af313de1a540240298e *libcxx-objects-v39.2.3-linux-loong64.zip -9ae64aff9e391eae401142e55654b5b8cf54d0611b1ecb540f2f4e89a2b4f772 *libcxxabi-headers.zip -7d7e6e08c84aa38b74037f5910534918bc792ffbe2ca6d667067f587f27f5118 *mksnapshot-v39.2.3-linux-loong64.zip -db48f8a9d2271e8b3a1c3f26ea1ae9bd489deb1b464b6ae424a15d5df7529fdc *node-v39.2.3-headers.tar.gz +d79a0b6955a66c75b68e8204c6e619f1217397c57e6ac8041d0d644178f1d2d4 *chromedriver-v42.3.0-linux-loong64.zip +349944821a5b0b96280bd68dcfdfae623152ecaf074c79ac73deab03f9a5f581 *electron-v42.3.0-linux-loong64-debug.zip +6dc3af9e4396f0ee6863c299197018e89cb22b95d48ee55d4838280d27988178 *electron-v42.3.0-linux-loong64-symbols.zip +92b0ca0c9c18ed90166918a4ac1970266c4fa967aee9277031b3b250b905526e *electron-v42.3.0-linux-loong64.zip +1efa327bf004c805c4fbbe723cb98e324bc1bb7b957c196acddae5c7fe7bd67d *ffmpeg-v42.3.0-linux-loong64.zip +7050ac1a2fef9962bb12094cb08cb26c22d70ae8dc0fe6b23c05a7f1380a330f *hunspell-dictionaries.zip +a1f1483a02250e5add3d1957466a6936510c41ed9bc9011a5adc6cad8b8590e3 *libcxx-headers.zip +01176a11233dd00913929217a495958d1f0c54c682b2ce25609a76756602356f *libcxx-objects-v42.3.0-linux-loong64.zip +da3c0c2dc5522066522bb48bf33d3a1e691fa683e301951626256c7729cc903b *libcxxabi-headers.zip +c0b0a28c6c6e648f1850e7925e888e3e47d30024972673ca42ae87a21e55d64e *mksnapshot-v42.3.0-linux-loong64.zip +285692ab65685424574bdf8264c78898c51a5172588ad7c2f3285ac346d3dfcd *node-v42.3.0-headers.tar.gz diff --git a/build/linux/package_bin.sh b/build/linux/package_bin.sh index 382ab7a7fad..d24d47b71ca 100755 --- a/build/linux/package_bin.sh +++ b/build/linux/package_bin.sh @@ -57,7 +57,7 @@ if [[ -f "../build/linux/${VSCODE_ARCH}/electron.sh" ]]; then # shellcheck disable=SC1090 source "../build/linux/${VSCODE_ARCH}/electron.sh" - TARGET=$( npm config get target ) + TARGET=$( grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' build/lib/electron.ts ) # Only fails at different major versions if [[ "${ELECTRON_VERSION%%.*}" != "${TARGET%%.*}" ]] && [[ "${IGNORE_ELECTRON_VERSION}" != "yes" ]]; then From 8b0c7facefdf242864feff69a90b976c6de00ff0 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 6 Jul 2026 19:03:53 +0200 Subject: [PATCH 70/79] fix(update): use common function to normalize version (#2914) --- patches/11-update-use-github-release.patch | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/patches/11-update-use-github-release.patch b/patches/11-update-use-github-release.patch index 5f6097f2629..3d1d01ce4e3 100644 --- a/patches/11-update-use-github-release.patch +++ b/patches/11-update-use-github-release.patch @@ -49,7 +49,7 @@ index 92ae7b96..c3fe6214 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 8cf5dceb..4c550923 100644 +index 8cf5dceb..c45ccf85 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -17,3 +17,3 @@ import { ILogService } from '../../log/common/log.js'; @@ -160,17 +160,17 @@ index 8cf5dceb..4c550923 100644 + return Promise.resolve(undefined); + } + -+ const fetchedVersion = /\d+\.\d+\.\d+\.\d+/.test(update.productVersion) ? update.productVersion.replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') : update.productVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); ++ const fetchedVersion = normalizeVersion(update.productVersion); + + let currentVersion: string; + + if(pendingVersion) { -+ currentVersion = pendingVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); ++ currentVersion = normalizeVersion(pendingVersion); + + this.logService.info(`update#isLatestVersion() - found: ${fetchedVersion}, pending: ${currentVersion}`); + } + else { -+ currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); ++ currentVersion = normalizeVersion(this.productService.version); + + this.logService.info(`update#isLatestVersion() - found: ${fetchedVersion}, current: ${currentVersion}`); + } @@ -180,12 +180,20 @@ index 8cf5dceb..4c550923 100644 + return Promise.resolve({ lastest, update }); + }) } -@@ -480,4 +506,4 @@ export abstract class AbstractUpdateService implements IUpdateService { +@@ -480,4 +506,12 @@ export abstract class AbstractUpdateService implements IUpdateService { - protected abstract buildUpdateFeedUrl(quality: string, commit: string, options?: IUpdateURLOptions): string | undefined; - protected abstract doCheckForUpdates(explicit: boolean, pendingCommit?: string): void; + protected abstract buildUpdateFeedUrl(quality: string, options?: IUpdateURLOptions): string | undefined; + protected abstract doCheckForUpdates(explicit: boolean, pendingVersion?: string): void; ++} ++ ++function normalizeVersion(version: string): string { ++ const normalizedVersion = version ++ .replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') ++ .replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3'); ++ ++ return normalizedVersion; } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index e92c54e2..b8057a06 100644 From bbfbaedbc227df130f6166df672ad32838066602 Mon Sep 17 00:00:00 2001 From: Alex Garcia <115115137+lex-ibm@users.noreply.github.com> Date: Mon, 6 Jul 2026 18:29:00 -0500 Subject: [PATCH 71/79] feat(ppc64le): use GCC-10.5.0 for REH and BIN (#2915) --- build/linux/package_bin.sh | 3 +-- build/linux/package_reh.sh | 6 +----- patches/linux/42-arch-add-ppc64le.patch | 2 +- patches/linux/45-arch-add-s390x.patch | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/build/linux/package_bin.sh b/build/linux/package_bin.sh index d24d47b71ca..8cd724eb5e6 100755 --- a/build/linux/package_bin.sh +++ b/build/linux/package_bin.sh @@ -28,8 +28,7 @@ if [[ "${VSCODE_ARCH}" == "arm64" ]]; then # export USE_GNUPP2A=1 elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then export VSCODE_SYSROOT_REPOSITORY='VSCodium/vscode-linux-build-agent' - export VSCODE_SYSROOT_VERSION='20240129-253798' - export VSCODE_SYSROOT_PREFIX="-glibc-2.28" + export VSCODE_SYSROOT_VERSION='20260706' export ELECTRON_SKIP_BINARY_DOWNLOAD=1 export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 export VSCODE_ELECTRON_REPOSITORY='lex-ibm/electron-ppc64le-build-scripts' diff --git a/build/linux/package_reh.sh b/build/linux/package_reh.sh index 731584f5006..50a7bf27765 100755 --- a/build/linux/package_reh.sh +++ b/build/linux/package_reh.sh @@ -35,13 +35,9 @@ elif [[ "${VSCODE_ARCH}" == "arm64" ]]; then export VSCODE_SKIP_SYSROOT=1 export USE_GNUPP2A=1 elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then - GLIBC_VERSION="2.28" - VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME="vscodium/vscodium-linux-build-agent:focal-devtoolset-ppc64le" - VSCODE_SYSROOT_PREFIX="-glibc-${GLIBC_VERSION}" - export VSCODE_SYSROOT_REPOSITORY='VSCodium/vscode-linux-build-agent' - export VSCODE_SYSROOT_VERSION='20240129-253798' + export VSCODE_SYSROOT_VERSION='20260706' elif [[ "${VSCODE_ARCH}" == "riscv64" ]]; then VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME="vscodium/vscodium-linux-build-agent:focal-devtoolset-riscv64" diff --git a/patches/linux/42-arch-add-ppc64le.patch b/patches/linux/42-arch-add-ppc64le.patch index d97e30dfb7a..a8909135aca 100644 --- a/patches/linux/42-arch-add-ppc64le.patch +++ b/patches/linux/42-arch-add-ppc64le.patch @@ -33,7 +33,7 @@ index 847383e7..04962e71 100644 @@ -7 +7,3 @@ ac4b6b14b4cec027a22a51bbbb049b3504958a78106c8a8d5cec144206b767d1 x86_64-linux-g 1ebb6ef1fe2983269fd0855a88f9c9a37f9b515d16524a9146198e4cabdf34f7 x86_64-linux-gnu-glibc-2.28-gcc-8.5.0.tar.gz +fa8176d27be18bb0eeb7f55b0fa22255050b430ef68c29136599f02976eb0b1b powerpc64le-linux-gnu-glibc-2.28.tar.gz -+bdab0a372cf5bfdc154226cc5ed72d417096c725085d193509d1f12e0debec66 powerpc64le-linux-gnu-glibc-2.28-gcc-10.5.0.tar.gz ++2d1a21d3ed310a62e229f056839cccd52c96c7d7c63542b7ba7c34ff20a3f832 powerpc64le-linux-gnu-glibc-2.28-gcc-10.5.0.tar.gz diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts index 8ecd10b2..469b73e8 100644 --- a/build/gulpfile.reh.ts diff --git a/patches/linux/45-arch-add-s390x.patch b/patches/linux/45-arch-add-s390x.patch index 348584cecbf..efab08d79ae 100644 --- a/patches/linux/45-arch-add-s390x.patch +++ b/patches/linux/45-arch-add-s390x.patch @@ -22,7 +22,7 @@ index dd159f8..8e8cbe9 100644 --- a/build/checksums/vscode-sysroot.txt +++ b/build/checksums/vscode-sysroot.txt @@ -8 +8,2 @@ ac4b6b14b4cec027a22a51bbbb049b3504958a78106c8a8d5cec144206b767d1 x86_64-linux-g - bdab0a372cf5bfdc154226cc5ed72d417096c725085d193509d1f12e0debec66 powerpc64le-linux-gnu-glibc-2.28-gcc-10.5.0.tar.gz + 2d1a21d3ed310a62e229f056839cccd52c96c7d7c63542b7ba7c34ff20a3f832 powerpc64le-linux-gnu-glibc-2.28-gcc-10.5.0.tar.gz +7055f3d40e7195fb1e13f0fbaf5ffadf781bddaca5fd5e0d9972f4157a203fb5 s390x-linux-gnu-glibc-2.28.tar.gz diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts index e6ff02c..65f7efe 100644 From 4015f2d0191311733aa5dbb2abde8101dce63eef Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 7 Jul 2026 14:30:40 +0200 Subject: [PATCH 72/79] feat: use vscode 1.126.0 --- upstream/stable.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/upstream/stable.json b/upstream/stable.json index 97e8082e0bb..fe07fae52fe 100644 --- a/upstream/stable.json +++ b/upstream/stable.json @@ -1,4 +1,4 @@ { - "tag": "1.121.0", - "commit": "987c9597516278c9fcf10d963a0592ce1384ab93" + "tag": "1.126.0", + "commit": "7e7950df89d055b5a378379db9ee14290772148a" } From 12b08023848522f884b04173ac0894849dd9fa05 Mon Sep 17 00:00:00 2001 From: Levi Zim Date: Fri, 10 Jul 2026 22:57:21 +0800 Subject: [PATCH 73/79] fix(riscv64): fix REH builds (#2921) --- build/linux/package_reh.sh | 9 +++++++-- patches/linux/41-arch-add-support.patch | 14 ++++++++++++-- patches/linux/43-arch-add-riscv64.patch | 10 ++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/build/linux/package_reh.sh b/build/linux/package_reh.sh index 50a7bf27765..9d74ba68e69 100755 --- a/build/linux/package_reh.sh +++ b/build/linux/package_reh.sh @@ -39,10 +39,15 @@ elif [[ "${VSCODE_ARCH}" == "ppc64le" ]]; then export VSCODE_SYSROOT_REPOSITORY='VSCodium/vscode-linux-build-agent' export VSCODE_SYSROOT_VERSION='20260706' elif [[ "${VSCODE_ARCH}" == "riscv64" ]]; then - VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME="vscodium/vscodium-linux-build-agent:focal-devtoolset-riscv64" + VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME="vscodium/vscodium-linux-build-agent:jammy-devtoolset-riscv64" + NODE_VERSION="24.18.0" + NODEJS_RELEASE_TAG="v${NODE_VERSION}-riscv64.1" + NODEJS_ASSET_NAME="node-v${NODE_VERSION}-linux-${VSCODE_ARCH}-local1.tar.gz" export VSCODE_SKIP_SETUPENV=1 - export VSCODE_NODEJS_SITE='https://unofficial-builds.nodejs.org' + export VSCODE_NODEJS_REPOSITORY='riscv-forks/node-riscv' + export VSCODE_NODEJS_TAG="${NODEJS_RELEASE_TAG}" + export VSCODE_NODEJS_NAME="${NODEJS_ASSET_NAME}" elif [[ "${VSCODE_ARCH}" == "loong64" ]]; then VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME="vscodium/vscodium-linux-build-agent:beige-devtoolset-loong64" diff --git a/patches/linux/41-arch-add-support.patch b/patches/linux/41-arch-add-support.patch index e0759bc1304..4394112b736 100644 --- a/patches/linux/41-arch-add-support.patch +++ b/patches/linux/41-arch-add-support.patch @@ -16,7 +16,7 @@ diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts index 62c30da5..85817274 100644 --- a/build/gulpfile.reh.ts +++ b/build/gulpfile.reh.ts -@@ -275,9 +275,24 @@ function nodejs(platform: string, arch: string): NodeJS.ReadWriteStream | undefi +@@ -275,9 +276,34 @@ function nodejs(platform: string, arch: string): NodeJS.ReadWriteStream | undefi case 'linux': { - const downloaded = (product.nodejsRepository !== 'https://nodejs.org' ? - fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName!, checksumSha256 }) : @@ -26,7 +26,17 @@ index 62c30da5..85817274 100644 - .pipe(util.setExecutableBit('**')) - .pipe(rename('node')); + let downloaded: NodeJS.ReadWriteStream; -+ if (process.env.VSCODE_NODEJS_SITE && process.env.VSCODE_NODEJS_URLROOT) { ++ if (process.env.VSCODE_NODEJS_REPOSITORY) { ++ const nodejsVersion = (process.env.VSCODE_NODEJS_TAG ?? `${nodeVersion}-${internalNodeVersion}`).replace(/^v/, ''); ++ const nodejsName = process.env.VSCODE_NODEJS_NAME ?? expectedName!; ++ const nodejsChecksumSha256 = getNodeChecksum(nodejsName) ?? checksumSha256; ++ downloaded = fetchGithub(process.env.VSCODE_NODEJS_REPOSITORY, { version: nodejsVersion, name: nodejsName, checksumSha256: nodejsChecksumSha256 }) ++ .pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) ++ .pipe(filter('**/node')) ++ .pipe(util.setExecutableBit('**')) ++ .pipe(rename('node')); ++ } ++ else if (process.env.VSCODE_NODEJS_SITE && process.env.VSCODE_NODEJS_URLROOT) { + downloaded = fetchUrls(`${process.env.VSCODE_NODEJS_URLROOT}/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}${process.env.VSCODE_NODEJS_URLSUFFIX}.tar.gz`, { base: process.env.VSCODE_NODEJS_SITE, checksumSha256 }) + .pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) + .pipe(filter('**/node')) diff --git a/patches/linux/43-arch-add-riscv64.patch b/patches/linux/43-arch-add-riscv64.patch index 637d8085b3f..2fa0895f36a 100644 --- a/patches/linux/43-arch-add-riscv64.patch +++ b/patches/linux/43-arch-add-riscv64.patch @@ -6,6 +6,16 @@ index f986b4e..557990e 100644 { platform: 'linux', arch: 'ppc64le' }, + { platform: 'linux', arch: 'riscv64' }, { platform: 'alpine', arch: 'arm64' }, +diff --git a/build/checksums/nodejs.txt b/build/checksums/nodejs.txt +index 12a2a80d..8d429dc4 100644 +--- a/build/checksums/nodejs.txt ++++ b/build/checksums/nodejs.txt +@@ -2,4 +2,5 @@ + ffd5ee293467927f3ee731a553eb88fd1f48cf74eebc2d74a6babe4af228673b node-v24.15.0-darwin-x64.tar.gz + 73afc234d558c24919875f51c2d1ea002a2ada4ea6f83601a383869fefa64eed node-v24.15.0-linux-arm64.tar.gz + 44836872d9aec49f1e6b52a9a922872db9a2b02d235a616a5681b6a85fec8d89 node-v24.15.0-linux-x64.tar.gz ++65109e57e34279184efcca5888bc156cff3d24bf8eead73de16a28a313c18884 node-v24.18.0-linux-riscv64-local1.tar.gz + 49a54c103f4919ce64199a043ef5cd309507de491d718085edee089cd8e87543 win-arm64/node.exe diff --git a/build/gulpfile.scan.ts b/build/gulpfile.scan.ts index 47b25cf..dd5bb56 100644 --- a/build/gulpfile.scan.ts From 560d37a205457910391494bd49508eeaab318122 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 09:25:32 +0200 Subject: [PATCH 74/79] build(deps-dev): bump fast-uri from 3.1.2 to 3.1.4 in /font-size (#2938) --- font-size/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/font-size/package-lock.json b/font-size/package-lock.json index 244adcb9ac3..64a092904e0 100644 --- a/font-size/package-lock.json +++ b/font-size/package-lock.json @@ -3275,9 +3275,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "dev": true, "funding": [ { From a66f6b1e40fc543f0c18927a836e10d8101a2b83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 09:30:57 +0200 Subject: [PATCH 75/79] build(deps): bump postcss from 8.5.14 to 8.5.18 in /font-size (#2939) --- font-size/package-lock.json | 16 ++++++++-------- font-size/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/font-size/package-lock.json b/font-size/package-lock.json index 64a092904e0..2ce3584b375 100644 --- a/font-size/package-lock.json +++ b/font-size/package-lock.json @@ -12,7 +12,7 @@ "@zokugun/fs-extra-plus": "0.4.1", "@zokugun/xtry": "0.11.6", "fast-glob": "3.3.3", - "postcss": "8.5.14" + "postcss": "8.5.18" }, "devDependencies": { "@types/node": "22.19.18", @@ -5226,9 +5226,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "funding": [ { "type": "github", @@ -5974,9 +5974,9 @@ } }, "node_modules/postcss": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", - "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "version": "8.5.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.18.tgz", + "integrity": "sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==", "funding": [ { "type": "opencollective", @@ -5993,7 +5993,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, diff --git a/font-size/package.json b/font-size/package.json index 61c17a62828..1aefa160217 100644 --- a/font-size/package.json +++ b/font-size/package.json @@ -25,7 +25,7 @@ "@zokugun/fs-extra-plus": "0.4.1", "@zokugun/xtry": "0.11.6", "fast-glob": "3.3.3", - "postcss": "8.5.14" + "postcss": "8.5.18" }, "devDependencies": { "@types/node": "22.19.18", From c7ffd77bb58cf3423fb6d40258f22c1156dd6505 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 09:31:31 +0200 Subject: [PATCH 76/79] build(deps-dev): bump brace-expansion in /font-size (#2940) --- font-size/package-lock.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/font-size/package-lock.json b/font-size/package-lock.json index 2ce3584b375..5d9dfa1db76 100644 --- a/font-size/package-lock.json +++ b/font-size/package-lock.json @@ -576,9 +576,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -1217,9 +1217,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -3787,16 +3787,16 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/glob/node_modules/minimatch": { From 7926fae692dc2931bad650828be0f23c888a6edc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 09:12:13 +0200 Subject: [PATCH 77/79] build(deps-dev): bump fast-uri from 3.1.4 to 3.1.5 in /font-size (#2955) --- font-size/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/font-size/package-lock.json b/font-size/package-lock.json index 5d9dfa1db76..e0e87ec77aa 100644 --- a/font-size/package-lock.json +++ b/font-size/package-lock.json @@ -3275,9 +3275,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", - "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "dev": true, "funding": [ { From d14478d0b6b7961cc3a0c9c7214e374012611a37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 09:53:26 +0200 Subject: [PATCH 78/79] build(deps): bump postcss from 8.5.18 to 8.5.23 in /font-size (#2956) --- font-size/package-lock.json | 10 +++++----- font-size/package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/font-size/package-lock.json b/font-size/package-lock.json index e0e87ec77aa..dc97c80fd07 100644 --- a/font-size/package-lock.json +++ b/font-size/package-lock.json @@ -12,7 +12,7 @@ "@zokugun/fs-extra-plus": "0.4.1", "@zokugun/xtry": "0.11.6", "fast-glob": "3.3.3", - "postcss": "8.5.18" + "postcss": "8.5.23" }, "devDependencies": { "@types/node": "22.19.18", @@ -5974,9 +5974,9 @@ } }, "node_modules/postcss": { - "version": "8.5.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.18.tgz", - "integrity": "sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==", + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "funding": [ { "type": "opencollective", @@ -5993,7 +5993,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, diff --git a/font-size/package.json b/font-size/package.json index 1aefa160217..1a4aa8a4c36 100644 --- a/font-size/package.json +++ b/font-size/package.json @@ -25,7 +25,7 @@ "@zokugun/fs-extra-plus": "0.4.1", "@zokugun/xtry": "0.11.6", "fast-glob": "3.3.3", - "postcss": "8.5.18" + "postcss": "8.5.23" }, "devDependencies": { "@types/node": "22.19.18", From f8091bc9cd1adb017e549bf022d06d520e89bb65 Mon Sep 17 00:00:00 2001 From: Thad House Date: Mon, 31 Aug 2026 03:16:16 -0700 Subject: [PATCH 79/79] fix(build): skip CLI builds (#2982) --- build_cli.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build_cli.sh b/build_cli.sh index 84c87813ede..1d84559ba68 100755 --- a/build_cli.sh +++ b/build_cli.sh @@ -1,5 +1,10 @@ #!/usr/bin/env bash +if [[ "${SHOULD_BUILD_CLI}" == "no" ]]; then + echo "Skipping CLI build" + return 0 +fi + set -ex cd cli