feat: add grouped extension loading notification (#5529)

Signed-off-by: jom-sq <148157197+jom-sq@users.noreply.github.com>
This commit is contained in:
jom-sq
2025-11-03 20:43:20 -05:00
committed by GitHub
parent 7511a533d6
commit 86c3e42e43
8 changed files with 422 additions and 51 deletions
@@ -0,0 +1,30 @@
/**
* Shared constants and utilities for extension error handling
*/
export const MAX_ERROR_MESSAGE_LENGTH = 70;
/**
* Creates recovery hints for the "Ask goose" feature when extension loading fails
*/
export function createExtensionRecoverHints(errorMsg: string): string {
return (
`Explain the following error: ${errorMsg}. ` +
'This happened while trying to install an extension. Look out for issues where the ' +
"extension attempted to execute something incorrectly, didn't exist, or there was trouble with " +
'the network configuration - VPNs like WARP often cause issues.'
);
}
/**
* Formats an error message for display, truncating long messages with a fallback
* @param errorMsg - The full error message
* @param fallback - The fallback message to show if the error is too long
* @returns The formatted error message
*/
export function formatExtensionErrorMessage(
errorMsg: string,
fallback: string = 'Failed to add extension'
): string {
return errorMsg.length < MAX_ERROR_MESSAGE_LENGTH ? errorMsg : fallback;
}
+51 -1
View File
@@ -5,6 +5,9 @@ import {
} from '../components/settings/extensions';
import type { ExtensionConfig, FixedExtensionEntry } from '../components/ConfigContext';
import { Recipe, updateAgentProvider, updateFromSession } from '../api';
import { toastService, ExtensionLoadingStatus } from '../toasts';
import { errorMessage } from './conversionUtils';
import { createExtensionRecoverHints } from './extensionErrorUtils';
// Helper function to substitute parameters in text
export const substituteParameters = (text: string, params: Record<string, string>): string => {
@@ -77,23 +80,70 @@ export const initializeSystem = async (
// Add enabled extensions to agent in parallel
const enabledExtensions = refreshedExtensions.filter((ext) => ext.enabled);
if (enabledExtensions.length === 0) {
return;
}
options?.setIsExtensionsLoading?.(true);
// Initialize extension status tracking
const extensionStatuses: Map<string, ExtensionLoadingStatus> = new Map(
enabledExtensions.map((ext) => [ext.name, { name: ext.name, status: 'loading' as const }])
);
// Show initial loading toast
const updateToast = (isComplete: boolean = false) => {
toastService.extensionLoading(
Array.from(extensionStatuses.values()),
enabledExtensions.length,
isComplete
);
};
updateToast();
// Load extensions in parallel and update status
const extensionLoadingPromises = enabledExtensions.map(async (extensionConfig) => {
const extensionName = extensionConfig.name;
try {
await addToAgentOnStartup({
extensionConfig,
toastOptions: { silent: false },
toastOptions: { silent: true }, // Silent since we're using grouped notification
sessionId,
});
// Update status to success
extensionStatuses.set(extensionName, {
name: extensionName,
status: 'success',
});
updateToast();
} catch (error) {
console.error(`Failed to load extension ${extensionName}:`, error);
// Extract error message using shared utility
const errMsg = errorMessage(error);
// Create recovery hints for "Ask goose" button
const recoverHints = createExtensionRecoverHints(errMsg);
// Update status to error
extensionStatuses.set(extensionName, {
name: extensionName,
status: 'error',
error: errMsg,
recoverHints,
});
updateToast();
}
});
await Promise.allSettled(extensionLoadingPromises);
// Show final completion toast
updateToast(true);
options?.setIsExtensionsLoading?.(false);
} catch (error) {
console.error('Failed to initialize agent:', error);