fix(coding-agent): avoid blocking footer branch refresh and isolate invalid extension provider registrations closes #2418 closes #2431

This commit is contained in:
Mario Zechner
2026-03-19 21:39:38 +01:00
parent dc1ceb4dd0
commit e0f85a3b5e
9 changed files with 284 additions and 56 deletions

View File

@@ -141,8 +141,8 @@ export function createExtensionRuntime(): ExtensionRuntime {
pendingProviderRegistrations: [],
// Pre-bind: queue registrations so bindCore() can flush them once the
// model registry is available. bindCore() replaces both with direct calls.
registerProvider: (name, config) => {
runtime.pendingProviderRegistrations.push({ name, config });
registerProvider: (name, config, extensionPath = "<unknown>") => {
runtime.pendingProviderRegistrations.push({ name, config, extensionPath });
},
unregisterProvider: (name) => {
runtime.pendingProviderRegistrations = runtime.pendingProviderRegistrations.filter((r) => r.name !== name);
@@ -271,11 +271,11 @@ function createExtensionAPI(
},
registerProvider(name: string, config: ProviderConfig) {
runtime.registerProvider(name, config);
runtime.registerProvider(name, config, extension.path);
},
unregisterProvider(name: string) {
runtime.unregisterProvider(name);
runtime.unregisterProvider(name, extension.path);
},
events: eventBus,

View File

@@ -271,11 +271,20 @@ export class ExtensionRunner {
this.getSystemPromptFn = contextActions.getSystemPrompt;
// Flush provider registrations queued during extension loading
for (const { name, config } of this.runtime.pendingProviderRegistrations) {
if (providerActions?.registerProvider) {
providerActions.registerProvider(name, config);
} else {
this.modelRegistry.registerProvider(name, config);
for (const { name, config, extensionPath } of this.runtime.pendingProviderRegistrations) {
try {
if (providerActions?.registerProvider) {
providerActions.registerProvider(name, config);
} else {
this.modelRegistry.registerProvider(name, config);
}
} catch (err) {
this.emitError({
extensionPath,
event: "register_provider",
error: err instanceof Error ? err.message : String(err),
stack: err instanceof Error ? err.stack : undefined,
});
}
}
this.runtime.pendingProviderRegistrations = [];

View File

@@ -1307,15 +1307,15 @@ export type SetLabelHandler = (entryId: string, label: string | undefined) => vo
export interface ExtensionRuntimeState {
flagValues: Map<string, boolean | string>;
/** Provider registrations queued during extension loading, processed when runner binds */
pendingProviderRegistrations: Array<{ name: string; config: ProviderConfig }>;
pendingProviderRegistrations: Array<{ name: string; config: ProviderConfig; extensionPath: string }>;
/**
* Register or unregister a provider.
*
* Before bindCore(): queues registrations / removes from queue.
* After bindCore(): calls ModelRegistry directly for immediate effect.
*/
registerProvider: (name: string, config: ProviderConfig) => void;
unregisterProvider: (name: string) => void;
registerProvider: (name: string, config: ProviderConfig, extensionPath?: string) => void;
unregisterProvider: (name: string, extensionPath?: string) => void;
}
/**