From 59ddfcb36b2c1996131d7000c71692326ad80e08 Mon Sep 17 00:00:00 2001 From: Zane Staggs Date: Wed, 16 Jul 2025 14:10:01 -0700 Subject: [PATCH 01/29] chore(release): release version 1.1.0 --- Cargo.lock | 14 +++++++------- Cargo.toml | 2 +- ui/desktop/package-lock.json | 4 ++-- ui/desktop/package.json | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e84e17b8f8a6..d9644d92bebb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3434,7 +3434,7 @@ dependencies = [ [[package]] name = "goose" -version = "1.0.36" +version = "1.1.0" dependencies = [ "ahash", "anyhow", @@ -3499,7 +3499,7 @@ dependencies = [ [[package]] name = "goose-bench" -version = "1.0.36" +version = "1.1.0" dependencies = [ "anyhow", "async-trait", @@ -3523,7 +3523,7 @@ dependencies = [ [[package]] name = "goose-cli" -version = "1.0.36" +version = "1.1.0" dependencies = [ "anyhow", "async-trait", @@ -3575,7 +3575,7 @@ dependencies = [ [[package]] name = "goose-ffi" -version = "1.0.36" +version = "1.1.0" dependencies = [ "cbindgen", "futures", @@ -3589,7 +3589,7 @@ dependencies = [ [[package]] name = "goose-llm" -version = "1.0.36" +version = "1.1.0" dependencies = [ "anyhow", "async-trait", @@ -3619,7 +3619,7 @@ dependencies = [ [[package]] name = "goose-mcp" -version = "1.0.36" +version = "1.1.0" dependencies = [ "anyhow", "async-trait", @@ -3671,7 +3671,7 @@ dependencies = [ [[package]] name = "goose-server" -version = "1.0.36" +version = "1.1.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index a386d2647d34..ac2d0f5e4129 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ resolver = "2" [workspace.package] edition = "2021" -version = "1.0.36" +version = "1.1.0" authors = ["Block "] license = "Apache-2.0" repository = "https://github.com/block/goose" diff --git a/ui/desktop/package-lock.json b/ui/desktop/package-lock.json index 4ecc8c7faa8b..37b80b4b88fb 100644 --- a/ui/desktop/package-lock.json +++ b/ui/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "goose-app", - "version": "1.1.0-alpha.4", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "goose-app", - "version": "1.1.0-alpha.4", + "version": "1.1.0", "license": "Apache-2.0", "dependencies": { "@ai-sdk/openai": "^0.0.72", diff --git a/ui/desktop/package.json b/ui/desktop/package.json index aa1e70af2976..1586111425b1 100644 --- a/ui/desktop/package.json +++ b/ui/desktop/package.json @@ -1,7 +1,7 @@ { "name": "goose-app", "productName": "Goose", - "version": "1.1.0-alpha.4", + "version": "1.1.0", "description": "Goose App", "engines": { "node": "^22.9.0" From 18b9334ea2e81b105243f909048f2311ce6fff70 Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Wed, 16 Jul 2025 19:06:56 -0700 Subject: [PATCH 02/29] Fix a few ui edge cases - refresh occasionally crashing, chat loader over text and chat input height returning to auto (#3469) --- ui/desktop/openapi.json | 2 +- ui/desktop/src/components/BaseChat.tsx | 3 -- ui/desktop/src/components/ChatInput.tsx | 9 +++-- ui/desktop/src/components/pair.tsx | 2 +- .../components/sessions/SessionsInsights.tsx | 31 ++++++++++++++-- ui/desktop/src/main.ts | 36 +++++++++++++++++-- ui/desktop/src/preload.ts | 15 +++++++- 7 files changed, 85 insertions(+), 13 deletions(-) diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 329ce854f84f..98be026ba10a 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -10,7 +10,7 @@ "license": { "name": "Apache-2.0" }, - "version": "1.0.36" + "version": "1.1.0" }, "paths": { "/agent/tools": { diff --git a/ui/desktop/src/components/BaseChat.tsx b/ui/desktop/src/components/BaseChat.tsx index 50ad42f5164f..f26ab7c0746b 100644 --- a/ui/desktop/src/components/BaseChat.tsx +++ b/ui/desktop/src/components/BaseChat.tsx @@ -446,9 +446,6 @@ function BaseChatContent({ {/* Custom content after messages */} {renderAfterMessages && renderAfterMessages()} - - {/* Bottom padding to make space for the loading indicator */} -
{/* Fixed loading indicator at bottom left of chat container */} diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index a7f2aecc9a98..7467dfe3c176 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -427,7 +427,6 @@ export default function ChatInput({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [numTokens, toolCount, tokenLimit, isTokenLimitLoaded, addAlert, clearAlerts]); - const minHeight = '1rem'; const maxHeight = 10 * 24; // Debounced function to update actual value @@ -456,6 +455,13 @@ export default function ChatInput({ } }, [debouncedAutosize, displayValue]); + // Reset textarea height when displayValue is empty + useEffect(() => { + if (textAreaRef.current && displayValue === '') { + textAreaRef.current.style.height = 'auto'; + } + }, [displayValue]); + const handleChange = (evt: React.ChangeEvent) => { const val = evt.target.value; const cursorPosition = evt.target.selectionStart; @@ -910,7 +916,6 @@ export default function ChatInput({ ref={textAreaRef} rows={1} style={{ - minHeight: `${minHeight}px`, maxHeight: `${maxHeight}px`, overflowY: 'auto', opacity: isRecording ? 0 : 1, diff --git a/ui/desktop/src/components/pair.tsx b/ui/desktop/src/components/pair.tsx index e44d2d8cd0aa..b2597672ee27 100644 --- a/ui/desktop/src/components/pair.tsx +++ b/ui/desktop/src/components/pair.tsx @@ -199,7 +199,7 @@ export default function Pair({ onMessageStreamFinish={handleMessageStreamFinish} renderBeforeMessages={renderBeforeMessages} customChatInputProps={customChatInputProps} - contentClassName={cn('pr-1', (isMobile || sidebarState === 'collapsed') && 'pt-11')} // Use dynamic content class with mobile margin and sidebar state + contentClassName={cn('pr-1 pb-10', (isMobile || sidebarState === 'collapsed') && 'pt-11')} // Use dynamic content class with mobile margin and sidebar state showPopularTopics={!isTransitioningFromHub} // Don't show popular topics while transitioning from Hub suppressEmptyState={isTransitioningFromHub} // Suppress all empty state content while transitioning from Hub /> diff --git a/ui/desktop/src/components/sessions/SessionsInsights.tsx b/ui/desktop/src/components/sessions/SessionsInsights.tsx index a3d61b0c48b1..41b0a218d687 100644 --- a/ui/desktop/src/components/sessions/SessionsInsights.tsx +++ b/ui/desktop/src/components/sessions/SessionsInsights.tsx @@ -23,6 +23,7 @@ export function SessionInsights() { const [error, setError] = useState(null); const [recentSessions, setRecentSessions] = useState([]); const [isLoading, setIsLoading] = useState(true); + const [isLoadingSessions, setIsLoadingSessions] = useState(true); // const [recentProjects, setRecentProjects] = useState([]); const navigate = useNavigate(); @@ -67,7 +68,8 @@ export function SessionInsights() { setRecentSessions(sessions.slice(0, 3)); } catch (error) { console.error('Failed to load recent sessions:', error); - // Don't set loading to false here since insights is the primary data + } finally { + setIsLoadingSessions(false); } }; @@ -400,7 +402,32 @@ export function SessionInsights() {
- {recentSessions.length > 0 ? ( + {isLoadingSessions ? ( + // Show skeleton while sessions are loading + <> +
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+ + +
+ +
+ + ) : recentSessions.length > 0 ? ( recentSessions.map((session, index) => (
{ const configStr = JSON.stringify(windowConfig).replace(/'/g, "\\'"); - mainWindow.webContents.executeJavaScript(` - localStorage.setItem('gooseConfig', '${configStr}') - `); + // Add error handling and retry logic for localStorage access + mainWindow.webContents + .executeJavaScript( + ` + try { + if (typeof Storage !== 'undefined' && window.localStorage) { + localStorage.setItem('gooseConfig', '${configStr}'); + } else { + console.warn('localStorage not available, retrying in 100ms'); + setTimeout(() => { + try { + localStorage.setItem('gooseConfig', '${configStr}'); + } catch (e) { + console.error('Failed to set localStorage after retry:', e); + } + }, 100); + } + } catch (e) { + console.error('Failed to access localStorage:', e); + // Retry after a short delay + setTimeout(() => { + try { + localStorage.setItem('gooseConfig', '${configStr}'); + } catch (retryError) { + console.error('Failed to set localStorage after retry:', retryError); + } + }, 100); + } + ` + ) + .catch((error) => { + console.error('Failed to execute localStorage script:', error); + }); }); // Handle new window creation for links diff --git a/ui/desktop/src/preload.ts b/ui/desktop/src/preload.ts index b1b6218fdd80..091c2be99e93 100644 --- a/ui/desktop/src/preload.ts +++ b/ui/desktop/src/preload.ts @@ -116,7 +116,20 @@ type AppConfigAPI = { const electronAPI: ElectronAPI = { platform: process.platform, reactReady: () => ipcRenderer.send('react-ready'), - getConfig: () => config, + getConfig: () => { + // Add fallback to localStorage if config from preload is empty or missing + if (!config || Object.keys(config).length === 0) { + try { + const storedConfig = localStorage.getItem('gooseConfig'); + if (storedConfig) { + return JSON.parse(storedConfig); + } + } catch (e) { + console.warn('Failed to parse stored config from localStorage:', e); + } + } + return config; + }, hideWindow: () => ipcRenderer.send('hide-window'), directoryChooser: (replace?: boolean) => ipcRenderer.invoke('directory-chooser', replace), createChatWindow: ( From c2aef56902f0834b9bfa5374a2940a99ebaf77e8 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Wed, 16 Jul 2025 21:05:54 -0400 Subject: [PATCH 03/29] Don't default to main for build-cli (#3467) --- .github/workflows/build-cli.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index f91360a21f56..b18eb6c02c2a 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -17,7 +17,7 @@ on: ref: type: string required: false - default: 'refs/heads/main' + default: "" name: "Reusable workflow to build CLI" From 4c640f04ffe9c70b83b02538c25136cf5df27089 Mon Sep 17 00:00:00 2001 From: Zane Staggs Date: Wed, 16 Jul 2025 19:13:47 -0700 Subject: [PATCH 04/29] chore(release): release version 1.1.1 --- Cargo.lock | 14 +++++++------- Cargo.toml | 2 +- ui/desktop/package-lock.json | 4 ++-- ui/desktop/package.json | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d9644d92bebb..56a9ce9a65e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3434,7 +3434,7 @@ dependencies = [ [[package]] name = "goose" -version = "1.1.0" +version = "1.1.1" dependencies = [ "ahash", "anyhow", @@ -3499,7 +3499,7 @@ dependencies = [ [[package]] name = "goose-bench" -version = "1.1.0" +version = "1.1.1" dependencies = [ "anyhow", "async-trait", @@ -3523,7 +3523,7 @@ dependencies = [ [[package]] name = "goose-cli" -version = "1.1.0" +version = "1.1.1" dependencies = [ "anyhow", "async-trait", @@ -3575,7 +3575,7 @@ dependencies = [ [[package]] name = "goose-ffi" -version = "1.1.0" +version = "1.1.1" dependencies = [ "cbindgen", "futures", @@ -3589,7 +3589,7 @@ dependencies = [ [[package]] name = "goose-llm" -version = "1.1.0" +version = "1.1.1" dependencies = [ "anyhow", "async-trait", @@ -3619,7 +3619,7 @@ dependencies = [ [[package]] name = "goose-mcp" -version = "1.1.0" +version = "1.1.1" dependencies = [ "anyhow", "async-trait", @@ -3671,7 +3671,7 @@ dependencies = [ [[package]] name = "goose-server" -version = "1.1.0" +version = "1.1.1" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index ac2d0f5e4129..7f30a32c592c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ resolver = "2" [workspace.package] edition = "2021" -version = "1.1.0" +version = "1.1.1" authors = ["Block "] license = "Apache-2.0" repository = "https://github.com/block/goose" diff --git a/ui/desktop/package-lock.json b/ui/desktop/package-lock.json index 37b80b4b88fb..3e260e8463da 100644 --- a/ui/desktop/package-lock.json +++ b/ui/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "goose-app", - "version": "1.1.0", + "version": "1.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "goose-app", - "version": "1.1.0", + "version": "1.1.1", "license": "Apache-2.0", "dependencies": { "@ai-sdk/openai": "^0.0.72", diff --git a/ui/desktop/package.json b/ui/desktop/package.json index 1586111425b1..49bd3c46c71c 100644 --- a/ui/desktop/package.json +++ b/ui/desktop/package.json @@ -1,7 +1,7 @@ { "name": "goose-app", "productName": "Goose", - "version": "1.1.0", + "version": "1.1.1", "description": "Goose App", "engines": { "node": "^22.9.0" From 992340d52b80716c60e95019a05e9195728fe476 Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Thu, 17 Jul 2025 19:43:35 -0700 Subject: [PATCH 05/29] Fix tool call allow still showing initial state in chat after navigating back (#3498) --- ui/desktop/src/components/GooseMessage.tsx | 2 +- .../src/components/ToolCallConfirmation.tsx | 90 ++++++++++++++++--- 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/ui/desktop/src/components/GooseMessage.tsx b/ui/desktop/src/components/GooseMessage.tsx index 1fea8eee58f1..62bbe95321cf 100644 --- a/ui/desktop/src/components/GooseMessage.tsx +++ b/ui/desktop/src/components/GooseMessage.tsx @@ -220,7 +220,7 @@ export default function GooseMessage({ {hasToolConfirmation && ( diff --git a/ui/desktop/src/components/ToolCallConfirmation.tsx b/ui/desktop/src/components/ToolCallConfirmation.tsx index f2f9dfccf546..764a460402a9 100644 --- a/ui/desktop/src/components/ToolCallConfirmation.tsx +++ b/ui/desktop/src/components/ToolCallConfirmation.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { snakeToTitleCase } from '../utils'; import PermissionModal from './settings/permission/PermissionModal'; import { ChevronRight } from 'lucide-react'; @@ -9,6 +9,17 @@ const ALWAYS_ALLOW = 'always_allow'; const ALLOW_ONCE = 'allow_once'; const DENY = 'deny'; +// Global state to track tool confirmation decisions +// This persists across navigation within the same session +const toolConfirmationState = new Map< + string, + { + clicked: boolean; + status: string; + actionDisplay: string; + } +>(); + interface ToolConfirmationProps { isCancelledMessage: boolean; isClicked: boolean; @@ -22,30 +33,75 @@ export default function ToolConfirmation({ toolConfirmationId, toolName, }: ToolConfirmationProps) { - const [clicked, setClicked] = useState(isClicked); - const [status, setStatus] = useState('unknown'); - const [actionDisplay, setActionDisplay] = useState(''); + // Check if we have a stored state for this tool confirmation + const storedState = toolConfirmationState.get(toolConfirmationId); + + // Initialize state from stored state if available, otherwise use props/defaults + const [clicked, setClicked] = useState(storedState?.clicked ?? isClicked); + const [status, setStatus] = useState(storedState?.status ?? 'unknown'); + const [actionDisplay, setActionDisplay] = useState(storedState?.actionDisplay ?? ''); const [isModalOpen, setIsModalOpen] = useState(false); + // Sync internal state with stored state and props + useEffect(() => { + const currentStoredState = toolConfirmationState.get(toolConfirmationId); + + // If we have stored state, use it + if (currentStoredState) { + setClicked(currentStoredState.clicked); + setStatus(currentStoredState.status); + setActionDisplay(currentStoredState.actionDisplay); + } else if (isClicked && !clicked) { + // Fallback to prop-based logic for historical confirmations + setClicked(isClicked); + if (status === 'unknown') { + setStatus('confirmed'); + setActionDisplay('confirmed'); + + // Store this state for future renders + toolConfirmationState.set(toolConfirmationId, { + clicked: true, + status: 'confirmed', + actionDisplay: 'confirmed', + }); + } + } + }, [isClicked, clicked, status, toolName, toolConfirmationId]); + const handleButtonClick = async (action: string) => { - setClicked(true); - setStatus(action); + const newClicked = true; + const newStatus = action; + let newActionDisplay = ''; + if (action === ALWAYS_ALLOW) { - setActionDisplay('always allowed'); + newActionDisplay = 'always allowed'; } else if (action === ALLOW_ONCE) { - setActionDisplay('allowed once'); + newActionDisplay = 'allowed once'; } else { - setActionDisplay('denied'); + newActionDisplay = 'denied'; } + + // Update local state + setClicked(newClicked); + setStatus(newStatus); + setActionDisplay(newActionDisplay); + + // Store in global state for persistence across navigation + toolConfirmationState.set(toolConfirmationId, { + clicked: newClicked, + status: newStatus, + actionDisplay: newActionDisplay, + }); + try { const response = await confirmPermission({ body: { id: toolConfirmationId, action, principal_type: 'Tool' }, }); if (response.error) { - console.error('Failed to confirm permission: ', response.error); + console.error('Failed to confirm permission:', response.error); } } catch (err) { - console.error('Error fetching tools:', err); + console.error('Error confirming permission:', err); } }; @@ -106,6 +162,18 @@ export default function ToolConfirmation({ )} + {status === 'confirmed' && ( + + + + )} {isClicked ? 'Tool confirmation is not available' From e2dde6b439dac562d808fe5518a520ef5c6cc234 Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Thu, 17 Jul 2025 19:43:45 -0700 Subject: [PATCH 06/29] Fix launching session in new window (#3497) --- ui/desktop/src/App.tsx | 28 +++++++++++++++++++ ui/desktop/src/agent/index.ts | 8 ++++++ .../schedule/ScheduleDetailView.tsx | 16 ----------- .../sessions/SessionHistoryView.tsx | 9 +----- .../src/components/sessions/SessionsView.tsx | 12 -------- .../providers/ProviderSettingsPage.tsx | 22 +++++++++++---- ui/desktop/src/hooks/useMessageStream.ts | 8 ------ ui/desktop/src/utils/providerUtils.ts | 4 +-- 8 files changed, 54 insertions(+), 53 deletions(-) diff --git a/ui/desktop/src/App.tsx b/ui/desktop/src/App.tsx index 6f05bafbc468..ebd32050d0a3 100644 --- a/ui/desktop/src/App.tsx +++ b/ui/desktop/src/App.tsx @@ -763,6 +763,34 @@ export default function App() { // Check for session resume first - this takes priority over other navigation if (resumeSessionId) { console.log('Session resume detected, letting useChat hook handle navigation'); + + // Even when resuming a session, we need to initialize the system + const initializeForSessionResume = async () => { + try { + await initConfig(); + await readAllConfig({ throwOnError: true }); + + const config = window.electron.getConfig(); + const provider = (await read('GOOSE_PROVIDER', false)) ?? config.GOOSE_DEFAULT_PROVIDER; + const model = (await read('GOOSE_MODEL', false)) ?? config.GOOSE_DEFAULT_MODEL; + + if (provider && model) { + await initializeSystem(provider as string, model as string, { + getExtensions, + addExtension, + }); + } else { + throw new Error('No provider/model configured for session resume'); + } + } catch (error) { + console.error('Failed to initialize system for session resume:', error); + setFatalError( + `Failed to initialize system for session resume: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } + }; + + initializeForSessionResume(); return; } diff --git a/ui/desktop/src/agent/index.ts b/ui/desktop/src/agent/index.ts index 5e980570d378..13fdb383021f 100644 --- a/ui/desktop/src/agent/index.ts +++ b/ui/desktop/src/agent/index.ts @@ -17,5 +17,13 @@ export async function initializeAgent({ model, provider }: initializeAgentProps) model: model, }), }); + + if (!response.ok) { + const responseText = await response.text(); + throw new Error( + `Failed to initialize agent: ${response.status} ${response.statusText} - ${responseText}` + ); + } + return response; } diff --git a/ui/desktop/src/components/schedule/ScheduleDetailView.tsx b/ui/desktop/src/components/schedule/ScheduleDetailView.tsx index 171f0ed61899..e8bfd92e2596 100644 --- a/ui/desktop/src/components/schedule/ScheduleDetailView.tsx +++ b/ui/desktop/src/components/schedule/ScheduleDetailView.tsx @@ -449,21 +449,6 @@ const ScheduleDetailView: React.FC = ({ scheduleId, onN loadAndShowSessionDetails(sessionIdFromCard); }; - const handleResumeViewedSession = () => { - if (selectedSessionDetails) { - const { session_id, metadata } = selectedSessionDetails; - if (metadata.working_dir) { - console.log( - `Resuming session ID ${session_id} in new chat window. Dir: ${metadata.working_dir}` - ); - window.electron.createChatWindow(undefined, metadata.working_dir, undefined, session_id); - } else { - console.error('Cannot resume session: working directory is missing.'); - toastError({ title: 'Cannot Resume Session', msg: 'Working directory is missing.' }); - } - } - }; - if (selectedSessionDetails) { return ( = ({ scheduleId, onN setSelectedSessionDetails(null); setSessionDetailsError(null); }} - onResume={handleResumeViewedSession} onRetry={() => loadAndShowSessionDetails(selectedSessionDetails.session_id)} showActionButtons={true} /> diff --git a/ui/desktop/src/components/sessions/SessionHistoryView.tsx b/ui/desktop/src/components/sessions/SessionHistoryView.tsx index ff5a6b8b21de..58b40d218bd5 100644 --- a/ui/desktop/src/components/sessions/SessionHistoryView.tsx +++ b/ui/desktop/src/components/sessions/SessionHistoryView.tsx @@ -10,7 +10,6 @@ import { Target, LoaderCircle, AlertCircle, - ExternalLink, } from 'lucide-react'; import { type SessionDetails } from '../../sessions'; import { Button } from '../ui/button'; @@ -74,7 +73,6 @@ interface SessionHistoryViewProps { isLoading: boolean; error: string | null; onBack: () => void; - onResume: () => void; onRetry: () => void; showActionButtons?: boolean; } @@ -169,7 +167,6 @@ const SessionHistoryView: React.FC = ({ isLoading, error, onBack, - onResume, onRetry, showActionButtons = true, }) => { @@ -292,14 +289,10 @@ const SessionHistoryView: React.FC = ({ )} - - ) : null; diff --git a/ui/desktop/src/components/sessions/SessionsView.tsx b/ui/desktop/src/components/sessions/SessionsView.tsx index 1d41ab4bd21a..9fa3eb1848e6 100644 --- a/ui/desktop/src/components/sessions/SessionsView.tsx +++ b/ui/desktop/src/components/sessions/SessionsView.tsx @@ -63,17 +63,6 @@ const SessionsView: React.FC = ({ setView }) => { setError(null); }; - const handleResumeSession = () => { - if (selectedSession) { - console.log('Resuming session in current window:', selectedSession.session_id); - - // Navigate to pair view with the session data - setView('pair', { - resumedSession: selectedSession, - }); - } - }; - const handleRetryLoadSession = () => { if (selectedSession) { loadSessionDetails(selectedSession.session_id); @@ -99,7 +88,6 @@ const SessionsView: React.FC = ({ setView }) => { isLoading={isLoadingSession} error={error} onBack={handleBackToSessions} - onResume={handleResumeSession} onRetry={handleRetryLoadSession} /> ) : ( diff --git a/ui/desktop/src/components/settings/providers/ProviderSettingsPage.tsx b/ui/desktop/src/components/settings/providers/ProviderSettingsPage.tsx index 7b92b967dfa2..816cb4f7a62c 100644 --- a/ui/desktop/src/components/settings/providers/ProviderSettingsPage.tsx +++ b/ui/desktop/src/components/settings/providers/ProviderSettingsPage.tsx @@ -73,15 +73,25 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett getExtensions, addExtension, }); + + toastService.configure({ silent: false }); + toastService.success({ + title: 'Success!', + msg: `Started goose with ${model} by ${provider.metadata.display_name}. You can change the model via the lower right corner.`, + }); + + onClose(); } catch (error) { console.error(`Failed to initialize with provider ${provider_name}:`, error); + + // Show error toast + toastService.configure({ silent: false }); + toastService.error({ + title: 'Initialization Failed', + msg: `Failed to initialize with ${provider.metadata.display_name}: ${error instanceof Error ? error.message : String(error)}`, + traceback: error instanceof Error ? error.stack || '' : '', + }); } - toastService.configure({ silent: false }); - toastService.success({ - title: 'Success!', - msg: `Started goose with ${model} by ${provider.metadata.display_name}. You can change the model via the lower right corner.`, - }); - onClose(); }, [onClose, upsert, getExtensions, addExtension] ); diff --git a/ui/desktop/src/hooks/useMessageStream.ts b/ui/desktop/src/hooks/useMessageStream.ts index 4277b16ac814..c186e99ee5b9 100644 --- a/ui/desktop/src/hooks/useMessageStream.ts +++ b/ui/desktop/src/hooks/useMessageStream.ts @@ -405,12 +405,6 @@ export function useMessageStream({ // Filter out messages where sendToLLM is explicitly false const filteredMessages = requestMessages.filter((message) => message.sendToLLM !== false); - // Log request details for debugging - console.log('Request details:', { - messages: filteredMessages, - body: extraMetadataRef.current.body, - }); - // Send request to the server const response = await fetch(api, { method: 'POST', @@ -485,8 +479,6 @@ export function useMessageStream({ // If a string is passed, convert it to a Message object const messageToAppend = typeof message === 'string' ? createUserMessage(message) : message; - console.log('Appending message:', JSON.stringify(messageToAppend, null, 2)); - const currentMessages = [...messagesRef.current, messageToAppend]; mutate(currentMessages, false); await sendRequest(currentMessages); diff --git a/ui/desktop/src/utils/providerUtils.ts b/ui/desktop/src/utils/providerUtils.ts index e79435dff07c..4c01006a81cc 100644 --- a/ui/desktop/src/utils/providerUtils.ts +++ b/ui/desktop/src/utils/providerUtils.ts @@ -206,6 +206,7 @@ export const initializeSystem = async ( : desktopPrompt, }), }); + if (!response.ok) { console.warn(`Failed to extend system prompt: ${response.statusText}`); } else { @@ -229,8 +230,6 @@ export const initializeSystem = async ( }); if (!sessionConfigResponse.ok) { console.warn(`Failed to configure session: ${sessionConfigResponse.statusText}`); - } else { - console.log('Configured session with response schema'); } } @@ -244,7 +243,6 @@ export const initializeSystem = async ( const configVersion = localStorage.getItem('configVersion'); const shouldMigrateExtensions = !configVersion || parseInt(configVersion, 10) < 3; - console.log(`shouldMigrateExtensions is ${shouldMigrateExtensions}`); if (shouldMigrateExtensions) { await migrateExtensionsToSettingsV3(); } From a10521c9c1a9f98a37e100b7591b93c386cccf28 Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Thu, 17 Jul 2025 16:19:43 -0700 Subject: [PATCH 07/29] fix token alert indicator/popovers hiding and showing (#3492) --- .../bottom_menu/BottomMenuAlertPopover.tsx | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/ui/desktop/src/components/bottom_menu/BottomMenuAlertPopover.tsx b/ui/desktop/src/components/bottom_menu/BottomMenuAlertPopover.tsx index 360d8d179e72..3cf85f7b2cf2 100644 --- a/ui/desktop/src/components/bottom_menu/BottomMenuAlertPopover.tsx +++ b/ui/desktop/src/components/bottom_menu/BottomMenuAlertPopover.tsx @@ -16,6 +16,7 @@ export default function BottomMenuAlertPopover({ alerts }: AlertPopoverProps) { const [isHovered, setIsHovered] = useState(false); const [wasAutoShown, setWasAutoShown] = useState(false); const [popoverPosition, setPopoverPosition] = useState({ top: 0, left: 0 }); + const [shouldShowIndicator, setShouldShowIndicator] = useState(false); // Stable indicator state const previousAlertsRef = useRef([]); const hideTimerRef = useRef>(); const triggerRef = useRef(null); @@ -91,9 +92,18 @@ export default function BottomMenuAlertPopover({ alerts }: AlertPopoverProps) { }, duration); }, []); + // Manage stable indicator visibility - once we have alerts, keep showing until explicitly cleared + useEffect(() => { + if (alerts.length > 0) { + setShouldShowIndicator(true); + } + }, [alerts.length]); + // Handle initial show and new alerts useEffect(() => { - if (alerts.length === 0) return; + if (alerts.length === 0) { + return; + } // Find new or changed alerts const changedAlerts = alerts.filter((alert, index) => { @@ -144,14 +154,17 @@ export default function BottomMenuAlertPopover({ alerts }: AlertPopoverProps) { }; }, [isOpen]); - if (alerts.length === 0) return null; + // Use shouldShowIndicator instead of alerts.length for rendering decision + if (!shouldShowIndicator) { + return null; + } - // Determine the icon and styling based on the alerts + // Determine the icon and styling based on the alerts (use current alerts if available, or default to info) const hasError = alerts.some((alert) => alert.type === AlertType.Error); const hasInfo = alerts.some((alert) => alert.type === AlertType.Info); const triggerColor = hasError ? 'text-[#d7040e]' // Red color for error alerts - : hasInfo + : hasInfo || alerts.length === 0 // Default to green for context info when no alerts ? 'text-[#00b300]' // Green color for info alerts : 'text-[#cc4b03]'; // Orange color for warning alerts From 0f85c89bb88fef293a574f55c0e5a2e9ecbb324d Mon Sep 17 00:00:00 2001 From: Max Novich Date: Thu, 17 Jul 2025 15:26:53 -0700 Subject: [PATCH 08/29] fix the output not being visible issue (#3491) --- .../src/components/ProgressiveMessageList.tsx | 4 +++- .../sessions/SessionHistoryView.tsx | 23 +------------------ ui/desktop/src/hooks/useChatEngine.ts | 23 +------------------ 3 files changed, 5 insertions(+), 45 deletions(-) diff --git a/ui/desktop/src/components/ProgressiveMessageList.tsx b/ui/desktop/src/components/ProgressiveMessageList.tsx index 5c84cc657ed5..4386f38e6518 100644 --- a/ui/desktop/src/components/ProgressiveMessageList.tsx +++ b/ui/desktop/src/components/ProgressiveMessageList.tsx @@ -62,6 +62,8 @@ export default function ProgressiveMessageList({ const [isLoading, setIsLoading] = useState(() => messages.length > showLoadingThreshold); const timeoutRef = useRef(null); const mountedRef = useRef(true); + const hasOnlyToolResponses = (message: Message) => + message.content.every((c) => c.type === 'toolResponse'); // Try to use context manager, but don't require it for session history let hasContextHandlerContent: ((message: Message) => boolean) | undefined; @@ -199,7 +201,7 @@ export default function ProgressiveMessageList({ }} /> ) : ( - + !hasOnlyToolResponses(message) && )} ) : ( diff --git a/ui/desktop/src/components/sessions/SessionHistoryView.tsx b/ui/desktop/src/components/sessions/SessionHistoryView.tsx index 58b40d218bd5..e2bc544d7478 100644 --- a/ui/desktop/src/components/sessions/SessionHistoryView.tsx +++ b/ui/desktop/src/components/sessions/SessionHistoryView.tsx @@ -43,29 +43,8 @@ const isUserMessage = (message: Message): boolean => { return true; }; -// Filter messages for display (same logic as useChatEngine) const filterMessagesForDisplay = (messages: Message[]): Message[] => { - return messages.filter((message) => { - // Only filter out when display is explicitly false - if (message.display === false) return false; - - // Keep all assistant messages and user messages that aren't just tool responses - if (message.role === 'assistant') return true; - - // For user messages, check if they're only tool responses - if (message.role === 'user') { - const hasOnlyToolResponses = message.content.every((c) => c.type === 'toolResponse'); - const hasTextContent = message.content.some((c) => c.type === 'text'); - const hasToolConfirmation = message.content.every( - (c) => c.type === 'toolConfirmationRequest' - ); - - // Keep the message if it has text content or tool confirmation or is not just tool responses - return hasTextContent || !hasOnlyToolResponses || hasToolConfirmation; - } - - return true; - }); + return messages.filter((message) => message.display); }; interface SessionHistoryViewProps { diff --git a/ui/desktop/src/hooks/useChatEngine.ts b/ui/desktop/src/hooks/useChatEngine.ts index e5c4b928cfb5..857f99e68b9a 100644 --- a/ui/desktop/src/hooks/useChatEngine.ts +++ b/ui/desktop/src/hooks/useChatEngine.ts @@ -309,29 +309,8 @@ export const useChatEngine = ({ } }, [stop, messages, _setInput, setMessages]); - // Filter out standalone tool response messages for rendering const filteredMessages = useMemo(() => { - return [...ancestorMessages, ...messages].filter((message) => { - // Only filter out when display is explicitly false - if (message.display === false) return false; - - // Keep all assistant messages and user messages that aren't just tool responses - if (message.role === 'assistant') return true; - - // For user messages, check if they're only tool responses - if (message.role === 'user') { - const hasOnlyToolResponses = message.content.every((c) => c.type === 'toolResponse'); - const hasTextContent = message.content.some((c) => c.type === 'text'); - const hasToolConfirmation = message.content.every( - (c) => c.type === 'toolConfirmationRequest' - ); - - // Keep the message if it has text content or tool confirmation or is not just tool responses - return hasTextContent || !hasOnlyToolResponses || hasToolConfirmation; - } - - return true; - }); + return [...ancestorMessages, ...messages].filter((message) => message.display); }, [ancestorMessages, messages]); // Generate command history from filtered messages From 5228909900f182d5b5a0bdeb5ee87d88d6e9f7d5 Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Thu, 17 Jul 2025 15:33:44 -0700 Subject: [PATCH 09/29] Fix llm errors not propagating to the ui and auto summarize not starting (#3490) --- ui/desktop/src/components/BaseChat.tsx | 77 +++++++++++++++++------- ui/desktop/src/hooks/useChatEngine.ts | 17 ++++++ ui/desktop/src/hooks/useMessageStream.ts | 50 ++++++++++++++- 3 files changed, 118 insertions(+), 26 deletions(-) diff --git a/ui/desktop/src/components/BaseChat.tsx b/ui/desktop/src/components/BaseChat.tsx index f26ab7c0746b..247d8bee1116 100644 --- a/ui/desktop/src/components/BaseChat.tsx +++ b/ui/desktop/src/components/BaseChat.tsx @@ -237,7 +237,6 @@ function BaseChatContent({ }); useEffect(() => { - // Log all messages when the component first mounts window.electron.logInfo( 'Initial messages when resuming session: ' + JSON.stringify(chat.messages, null, 2) ); @@ -414,28 +413,60 @@ function BaseChatContent({ )} - {error && ( -
-
- {error.message || 'Honk! Goose experienced an error while responding'} -
-
{ - // Find the last user message - const lastUserMessage = messages.reduceRight( - (found, m) => found || (m.role === 'user' ? m : null), - null as Message | null - ); - if (lastUserMessage) { - append(lastUserMessage); - } - }} - > - Retry Last Message -
-
- )} + {error && + !(error as Error & { isTokenLimitError?: boolean }).isTokenLimitError && ( + <> +
+
+ {error.message || 'Honk! Goose experienced an error while responding'} +
+ + {/* Expandable Error Details */} +
+ + Error details + +
+
+ Error Type: {error.name || 'Unknown'} +
+
+ Message: {error.message || 'No message'} +
+ {error.stack && ( +
+ Stack Trace: +
+                                    {error.stack}
+                                  
+
+ )} +
+
+ + {/* Regular retry button for non-token-limit errors */} +
{ + // Find the last user message + const lastUserMessage = messages.reduceRight( + (found, m) => found || (m.role === 'user' ? m : null), + null as Message | null + ); + if (lastUserMessage) { + append(lastUserMessage); + } + }} + > + Retry Last Message +
+
+ + )} + + {/* Token limit errors should be handled by ContextHandler, not shown here */} + {error && + (error as Error & { isTokenLimitError?: boolean }).isTokenLimitError && <>}
) : showPopularTopics ? ( diff --git a/ui/desktop/src/hooks/useChatEngine.ts b/ui/desktop/src/hooks/useChatEngine.ts index 857f99e68b9a..c852b1699c7c 100644 --- a/ui/desktop/src/hooks/useChatEngine.ts +++ b/ui/desktop/src/hooks/useChatEngine.ts @@ -108,6 +108,23 @@ export const useChatEngine = ({ onMessageStreamFinish?.(); }, + onError: (error) => { + console.log( + 'CHAT ENGINE RECEIVED ERROR FROM MESSAGE STREAM:', + JSON.stringify( + { + errorMessage: error.message, + errorName: error.name, + isTokenLimitError: (error as Error & { isTokenLimitError?: boolean }).isTokenLimitError, + errorStack: error.stack, + timestamp: new Date().toISOString(), + chatId: chat.id, + }, + null, + 2 + ) + ); + }, }); // Wrap append to store messages in global history (if enabled) diff --git a/ui/desktop/src/hooks/useMessageStream.ts b/ui/desktop/src/hooks/useMessageStream.ts index c186e99ee5b9..e4db0ac4372e 100644 --- a/ui/desktop/src/hooks/useMessageStream.ts +++ b/ui/desktop/src/hooks/useMessageStream.ts @@ -323,8 +323,45 @@ export function useMessageStream({ break; } - case 'Error': - throw new Error(parsedEvent.error); + case 'Error': { + // Check if this is a token limit error (more specific detection) + const errorMessage = parsedEvent.error; + const isTokenLimitError = + errorMessage && + ((errorMessage.toLowerCase().includes('token') && + errorMessage.toLowerCase().includes('limit')) || + (errorMessage.toLowerCase().includes('context') && + errorMessage.toLowerCase().includes('length') && + errorMessage.toLowerCase().includes('exceeded'))); + + // If this is a token limit error, create a contextLengthExceeded message instead of throwing + if (isTokenLimitError) { + const contextMessage: Message = { + id: `context-${Date.now()}`, + role: 'assistant', + created: Math.floor(Date.now() / 1000), + content: [ + { + type: 'contextLengthExceeded', + msg: errorMessage, + }, + ], + display: true, + sendToLLM: false, + }; + + currentMessages = [...currentMessages, contextMessage]; + mutate(currentMessages, false); + + // Clear any existing error state since we handled this as a context message + setError(undefined); + break; // Don't throw error, just add the message + } + + // For non-token-limit errors, still throw the error + const error = new Error(parsedEvent.error); + throw error; + } case 'Finish': { // Call onFinish with the last message if available @@ -371,6 +408,11 @@ export function useMessageStream({ if (onError && e instanceof Error) { onError(e); } + // Don't re-throw here, let the error be handled by the outer catch + // Instead, set the error state directly + if (e instanceof Error) { + setError(e); + } } } } @@ -381,6 +423,8 @@ export function useMessageStream({ if (onError) { onError(e); } + // Re-throw the error so it gets caught by sendRequest and sets the error state + throw e; } } finally { reader.releaseLock(); @@ -388,7 +432,7 @@ export function useMessageStream({ return currentMessages; }, - [mutate, onFinish, onError, forceUpdate] + [mutate, onFinish, onError, forceUpdate, setError] ); // Send a request to the server From 687633c00f39db268a363c30900891a28b4c9229 Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Thu, 17 Jul 2025 15:33:34 -0700 Subject: [PATCH 10/29] fix issue with generating a share url and add missing url verification in settings (#3482) --- ui/desktop/index.html | 2 +- .../sessions/SessionSharingSection.tsx | 124 +++++++++++++++--- ui/desktop/src/main.ts | 36 ++++- 3 files changed, 144 insertions(+), 18 deletions(-) diff --git a/ui/desktop/index.html b/ui/desktop/index.html index 3f8c17512877..f606653e47eb 100644 --- a/ui/desktop/index.html +++ b/ui/desktop/index.html @@ -2,7 +2,7 @@ - + Goose