fix(coding-agent): cache extension imports for session switches

Closes #5905.
This commit is contained in:
Armin Ronacher
2026-06-20 21:05:55 +02:00
parent a1da88aed4
commit 5505316ea2
4 changed files with 226 additions and 10 deletions
@@ -9,7 +9,12 @@ export type { ResourceCollision, ResourceDiagnostic } from "./diagnostics.ts";
import { canonicalizePath, isLocalPath, resolvePath } from "../utils/paths.ts";
import { createEventBus, type EventBus } from "./event-bus.ts";
import { createExtensionRuntime, loadExtensionFromFactory, loadExtensions } from "./extensions/loader.ts";
import {
clearExtensionCache,
createExtensionRuntime,
loadExtensionFromFactory,
loadExtensionsCached,
} from "./extensions/loader.ts";
import type { Extension, ExtensionFactory, ExtensionRuntime, LoadExtensionsResult } from "./extensions/types.ts";
import { DefaultPackageManager, type PathMetadata, type ResolvedResource } from "./package-manager.ts";
import type { PromptTemplate } from "./prompt-templates.ts";
@@ -206,6 +211,7 @@ export class DefaultResourceLoader implements ResourceLoader {
private extensionThemeSourceInfos: Map<string, SourceInfo>;
private lastPromptPaths: string[];
private lastThemePaths: string[];
private loaded: boolean;
constructor(options: DefaultResourceLoaderOptions) {
this.cwd = resolvePath(options.cwd);
@@ -252,6 +258,7 @@ export class DefaultResourceLoader implements ResourceLoader {
this.extensionThemeSourceInfos = new Map();
this.lastPromptPaths = [];
this.lastThemePaths = [];
this.loaded = false;
}
getExtensions(): LoadExtensionsResult {
@@ -331,6 +338,10 @@ export class DefaultResourceLoader implements ResourceLoader {
}
async reload(options?: ResourceLoaderReloadOptions): Promise<void> {
if (this.loaded) {
clearExtensionCache();
}
let preTrustExtensions: LoadExtensionsResult | undefined;
if (options?.resolveProjectTrust) {
preTrustExtensions = await this.loadProjectTrustExtensions();
@@ -475,6 +486,7 @@ export class DefaultResourceLoader implements ResourceLoader {
this.appendSystemPrompt = this.appendSystemPromptOverride
? this.appendSystemPromptOverride(baseAppend)
: baseAppend;
this.loaded = true;
}
private async loadCurrentExtensionSet(options: { includeInlineFactories: boolean }): Promise<LoadExtensionsResult> {
@@ -487,7 +499,7 @@ export class DefaultResourceLoader implements ResourceLoader {
const extensionPaths = this.noExtensions
? cliEnabledExtensions
: this.mergePaths(cliEnabledExtensions, enabledExtensions);
const extensionsResult = await loadExtensions(extensionPaths, this.cwd, this.eventBus);
const extensionsResult = await loadExtensionsCached(extensionPaths, this.cwd, this.eventBus);
if (!options.includeInlineFactories) {
return extensionsResult;
}
@@ -507,7 +519,7 @@ export class DefaultResourceLoader implements ResourceLoader {
preTrustExtensions: LoadExtensionsResult | undefined,
): Promise<LoadExtensionsResult> {
if (!preTrustExtensions) {
const extensionsResult = await loadExtensions(extensionPaths, this.cwd, this.eventBus);
const extensionsResult = await loadExtensionsCached(extensionPaths, this.cwd, this.eventBus);
const inlineExtensions = await this.loadExtensionFactories(extensionsResult.runtime);
extensionsResult.extensions.push(...inlineExtensions.extensions);
extensionsResult.errors.push(...inlineExtensions.errors);
@@ -527,7 +539,7 @@ export class DefaultResourceLoader implements ResourceLoader {
const resolvedPath = this.resolveExtensionLoadPath(path);
return !preloadedByPath.has(resolvedPath) && !failedPreloadPaths.has(resolvedPath);
});
const remainingExtensions = await loadExtensions(
const remainingExtensions = await loadExtensionsCached(
remainingPaths,
this.cwd,
this.eventBus,