Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/goose/src/agents/extension_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,11 @@ impl ExtensionManager {
pub async fn add_extension(&self, config: ExtensionConfig) -> ExtensionResult<()> {
let config_name = config.key().to_string();
let sanitized_name = normalize(config_name.clone());

if self.extensions.lock().await.contains_key(&sanitized_name) {
return Ok(());
}

let mut temp_dir = None;

let client: Box<dyn McpClientTrait> = match &config {
Expand Down
48 changes: 6 additions & 42 deletions ui/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import PermissionSettingsView from './components/settings/permission/PermissionS
import ExtensionsView, { ExtensionsViewOptions } from './components/extensions/ExtensionsView';
import RecipesView from './components/recipes/RecipesView';
import { View, ViewOptions } from './utils/navigationUtils';
import { NoProviderOrModelError, useAgent } from './hooks/useAgent';

import { useNavigation } from './hooks/useNavigation';
import { errorMessage } from './utils/conversionUtils';
import { usePageViewTracking } from './hooks/useAnalytics';
Expand All @@ -51,10 +51,10 @@ function PageViewTracker() {
}

// Route Components
const HubRouteWrapper = ({ isExtensionsLoading }: { isExtensionsLoading: boolean }) => {
const HubRouteWrapper = () => {
const setView = useNavigation();

return <Hub setView={setView} isExtensionsLoading={isExtensionsLoading} />;
return <Hub setView={setView} />;
};

const PairRouteWrapper = ({
Expand Down Expand Up @@ -363,15 +363,12 @@ const ExtensionsRoute = () => {

export function AppInner() {
const [fatalError, setFatalError] = useState<string | null>(null);
const [agentWaitingMessage, setAgentWaitingMessage] = useState<string | null>(null);
const [isLoadingSharedSession, setIsLoadingSharedSession] = useState(false);
const [sharedSessionError, setSharedSessionError] = useState<string | null>(null);
const [isExtensionsLoading, setIsExtensionsLoading] = useState(false);
const [didSelectProvider, setDidSelectProvider] = useState<boolean>(false);

const navigate = useNavigate();
const setView = useNavigation();
const location = useLocation();

const [chat, setChat] = useState<ChatType>({
sessionId: '',
Expand All @@ -384,7 +381,6 @@ export function AppInner() {
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);

const { addExtension } = useConfig();
const { loadCurrentChat } = useAgent();

useEffect(() => {
console.log('Sending reactReady signal to Electron');
Expand All @@ -398,28 +394,6 @@ export function AppInner() {
}
}, []);

// Handle URL parameters and deeplinks on app startup
const loadingHub = location.pathname === '/';
useEffect(() => {
if (loadingHub) {
(async () => {
try {
const loadedChat = await loadCurrentChat({
setAgentWaitingMessage,
setIsExtensionsLoading,
});
setChat(loadedChat);
} catch (e) {
if (e instanceof NoProviderOrModelError) {
// the onboarding flow will trigger
} else {
throw e;
}
}
})();
}
}, [loadCurrentChat, setAgentWaitingMessage, navigate, loadingHub, setChat]);

useEffect(() => {
const handleOpenSharedSession = async (_event: IpcRendererEvent, ...args: unknown[]) => {
const link = args[0] as string;
Expand Down Expand Up @@ -616,18 +590,13 @@ export function AppInner() {
path="/"
element={
<ProviderGuard didSelectProvider={didSelectProvider}>
<ChatProvider
chat={chat}
setChat={setChat}
contextKey="hub"
agentWaitingMessage={agentWaitingMessage}
>
<ChatProvider chat={chat} setChat={setChat} contextKey="hub">
<AppLayout />
</ChatProvider>
</ProviderGuard>
}
>
<Route index element={<HubRouteWrapper isExtensionsLoading={isExtensionsLoading} />} />
<Route index element={<HubRouteWrapper />} />
<Route
path="pair"
element={
Expand All @@ -643,12 +612,7 @@ export function AppInner() {
<Route
path="extensions"
element={
<ChatProvider
chat={chat}
setChat={setChat}
contextKey="extensions"
agentWaitingMessage={agentWaitingMessage}
>
<ChatProvider chat={chat} setChat={setChat} contextKey="extensions">
<ExtensionsRoute />
</ChatProvider>
}
Expand Down
25 changes: 10 additions & 15 deletions ui/desktop/src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ interface ChatInputProps {
initialPrompt?: string;
toolCount: number;
append?: (message: Message) => void;
isExtensionsLoading?: boolean;
}

export default function ChatInput({
Expand All @@ -121,7 +120,6 @@ export default function ChatInput({
initialPrompt,
toolCount,
append: _append,
isExtensionsLoading = false,
}: ChatInputProps) {
const [_value, setValue] = useState(initialValue);
const [displayValue, setDisplayValue] = useState(initialValue); // For immediate visual feedback
Expand Down Expand Up @@ -1109,8 +1107,7 @@ export default function ChatInput({
isAnyImageLoading ||
isAnyDroppedFileLoading ||
isRecording ||
isTranscribing ||
isExtensionsLoading;
isTranscribing;

// Queue management functions - no storage persistence, only in-memory
const handleRemoveQueuedMessage = (messageId: string) => {
Expand Down Expand Up @@ -1353,17 +1350,15 @@ export default function ChatInput({
</TooltipTrigger>
<TooltipContent>
<p>
{isExtensionsLoading
? 'Loading extensions...'
: isAnyImageLoading
? 'Waiting for images to save...'
: isAnyDroppedFileLoading
? 'Processing dropped files...'
: isRecording
? 'Recording...'
: isTranscribing
? 'Transcribing...'
: 'Send'}
{isAnyImageLoading
? 'Waiting for images to save...'
: isAnyDroppedFileLoading
? 'Processing dropped files...'
: isRecording
? 'Recording...'
: isTranscribing
? 'Transcribing...'
: 'Send'}
</p>
</TooltipContent>
</Tooltip>
Expand Down
3 changes: 0 additions & 3 deletions ui/desktop/src/components/Hub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ import { startNewSession } from '../sessions';

export default function Hub({
setView,
isExtensionsLoading,
}: {
setView: (view: View, viewOptions?: ViewOptions) => void;
isExtensionsLoading: boolean;
}) {
const handleSubmit = async (e: React.FormEvent) => {
const customEvent = e as unknown as CustomEvent;
Expand Down Expand Up @@ -59,7 +57,6 @@ export default function Hub({
messages={[]}
disableAnimation={false}
sessionCosts={undefined}
isExtensionsLoading={isExtensionsLoading}
toolCount={0}
/>
</div>
Expand Down
4 changes: 0 additions & 4 deletions ui/desktop/src/contexts/ChatContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ interface ChatContextType {
clearRecipe: () => void;
// Context identification
contextKey: string; // 'hub' or 'pair-{sessionId}'
agentWaitingMessage: string | null;
}

const ChatContext = createContext<ChatContextType | undefined>(undefined);
Expand All @@ -24,14 +23,12 @@ interface ChatProviderProps {
chat: ChatType;
setChat: (chat: ChatType) => void;
contextKey?: string; // Optional context key, defaults to 'hub'
agentWaitingMessage: string | null;
}

export const ChatProvider: React.FC<ChatProviderProps> = ({
children,
chat,
setChat,
agentWaitingMessage,
contextKey = 'hub',
}) => {
const resetChat = () => {
Expand Down Expand Up @@ -69,7 +66,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
setRecipe,
clearRecipe,
contextKey,
agentWaitingMessage,
};

return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;
Expand Down
Loading
Loading