fix: suppress duplicate extension warnings during startup (#26208)

This commit is contained in:
Coco Sheng
2026-04-30 10:11:06 -04:00
committed by GitHub
parent a15568e013
commit d743c6fae6
3 changed files with 79 additions and 16 deletions
+24 -16
View File
@@ -37,6 +37,7 @@ import {
getAdminErrorMessage,
isHeadlessMode,
Config,
SimpleExtensionLoader,
resolveToRealPath,
applyAdminAllowlist,
applyRequiredServers,
@@ -558,6 +559,7 @@ export interface LoadCliConfigOptions {
disabled?: string[];
};
worktreeSettings?: WorktreeSettings;
skipExtensions?: boolean;
}
export async function loadCliConfig(
@@ -566,7 +568,7 @@ export async function loadCliConfig(
argv: CliArgs,
options: LoadCliConfigOptions = {},
): Promise<Config> {
const { cwd = process.cwd(), projectHooks } = options;
const { cwd = process.cwd(), projectHooks, skipExtensions = false } = options;
const debugMode = isDebugMode(argv);
const worktreeSettings =
@@ -641,21 +643,24 @@ export async function loadCliConfig(
includeDirectories.push(...ideFolders);
}
const extensionManager = new ExtensionManager({
settings,
requestConsent: requestConsentNonInteractive,
requestSetting: promptForSetting,
workspaceDir: cwd,
enabledExtensionOverrides: argv.extensions,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
eventEmitter: coreEvents as EventEmitter<ExtensionEvents>,
clientVersion: await getVersion(),
});
await extensionManager.loadExtensions();
let extensionManager: ExtensionManager | undefined;
if (!skipExtensions) {
extensionManager = new ExtensionManager({
settings,
requestConsent: requestConsentNonInteractive,
requestSetting: promptForSetting,
workspaceDir: cwd,
enabledExtensionOverrides: argv.extensions,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
eventEmitter: coreEvents as EventEmitter<ExtensionEvents>,
clientVersion: await getVersion(),
});
await extensionManager.loadExtensions();
}
const extensionPlanSettings = extensionManager
.getExtensions()
.find((ext) => ext.isActive && ext.plan?.directory)?.plan;
?.getExtensions()
?.find((ext) => ext.isActive && ext.plan?.directory)?.plan;
const experimentalJitContext = settings.experimental.jitContext ?? true;
@@ -673,6 +678,9 @@ export async function loadCliConfig(
let fileCount = 0;
let filePaths: string[] = [];
const finalExtensionLoader =
extensionManager ?? new SimpleExtensionLoader([]);
if (!experimentalJitContext) {
// Call the (now wrapper) loadHierarchicalGeminiMemory which calls the server's version
const result = await loadServerHierarchicalMemory(
@@ -681,7 +689,7 @@ export async function loadCliConfig(
? includeDirectories
: [],
fileService,
extensionManager,
finalExtensionLoader,
trustedFolder,
memoryImportFormat,
memoryFileFiltering,
@@ -1037,7 +1045,7 @@ export async function loadCliConfig(
listSessions: argv.listSessions || false,
deleteSession: argv.deleteSession,
enabledExtensions: argv.extensions,
extensionLoader: extensionManager,
extensionLoader: finalExtensionLoader,
extensionRegistryURI,
enableExtensionReloading: settings.experimental?.extensionReloading,
enableAgents: settings.experimental?.enableAgents,
@@ -0,0 +1,54 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { loadCliConfig, type CliArgs } from './config.js';
import { ExtensionManager } from './extension-manager.js';
import { createTestMergedSettings } from './settings.js';
vi.mock('./extension-manager.js', () => ({
ExtensionManager: vi.fn().mockImplementation(() => ({
loadExtensions: vi.fn().mockResolvedValue([]),
getExtensions: vi.fn().mockReturnValue([]),
})),
}));
describe('loadCliConfig skipExtensions', () => {
const settings = createTestMergedSettings();
const argv = {
query: undefined,
model: undefined,
sandbox: undefined,
debug: undefined,
prompt: undefined,
promptInteractive: undefined,
yolo: undefined,
approvalMode: undefined,
policy: undefined,
adminPolicy: undefined,
allowedMcpServerNames: undefined,
allowedTools: undefined,
extensions: undefined,
listExtensions: undefined,
resume: undefined,
sessionId: undefined,
listSessions: undefined,
} as unknown as CliArgs;
beforeEach(() => {
vi.clearAllMocks();
});
it('should load extensions by default', async () => {
await loadCliConfig(settings, 'session-id', argv);
expect(ExtensionManager).toHaveBeenCalled();
});
it('should skip extensions when skipExtensions is true', async () => {
await loadCliConfig(settings, 'session-id', argv, { skipExtensions: true });
expect(ExtensionManager).not.toHaveBeenCalled();
});
});
+1
View File
@@ -409,6 +409,7 @@ export async function main() {
const partialConfig = await loadCliConfig(settings.merged, sessionId, argv, {
projectHooks: settings.workspace.settings.hooks,
skipExtensions: true,
});
adminControlsListner.setConfig(partialConfig);