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
62 changes: 53 additions & 9 deletions ui/desktop/src/hooks/useChatEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const useChatEngine = ({
const [sessionOutputTokens, setSessionOutputTokens] = useState<number>(0);
const [localInputTokens, setLocalInputTokens] = useState<number>(0);
const [localOutputTokens, setLocalOutputTokens] = useState<number>(0);
const [powerSaveTimeoutId, setPowerSaveTimeoutId] = useState<number | null>(null);

// Store message in global history when it's added (if enabled)
const storeMessageInHistory = useCallback(
Expand All @@ -63,6 +64,20 @@ export const useChatEngine = ({
[enableLocalStorage]
);

const stopPowerSaveBlocker = useCallback(() => {
try {
window.electron.stopPowerSaveBlocker();
} catch (error) {
console.error('Failed to stop power save blocker:', error);
}

// Clear timeout if it exists
if (powerSaveTimeoutId) {
window.clearTimeout(powerSaveTimeoutId);
setPowerSaveTimeoutId(null);
}
}, [powerSaveTimeoutId]);

const {
messages,
append: originalAppend,
Expand All @@ -84,7 +99,7 @@ export const useChatEngine = ({
initialMessages: chat.messages,
body: { session_id: chat.id, session_working_dir: window.appConfig.get('GOOSE_WORKING_DIR') },
onFinish: async (_message, _reason) => {
window.electron.stopPowerSaveBlocker();
stopPowerSaveBlocker();

const timeSinceLastInteraction = Date.now() - lastInteractionTime;
window.electron.logInfo('last interaction:' + lastInteractionTime);
Expand All @@ -110,6 +125,8 @@ export const useChatEngine = ({
onMessageStreamFinish?.();
},
onError: (error) => {
stopPowerSaveBlocker();

console.log(
'CHAT ENGINE RECEIVED ERROR FROM MESSAGE STREAM:',
JSON.stringify(
Expand Down Expand Up @@ -206,40 +223,67 @@ export const useChatEngine = ({
}
}, [sessionMetadata]);

useEffect(() => {
return () => {
if (powerSaveTimeoutId) {
window.clearTimeout(powerSaveTimeoutId);
}
try {
window.electron.stopPowerSaveBlocker();
} catch (error) {
console.error('Failed to stop power save blocker during cleanup:', error);
}
};
}, [powerSaveTimeoutId]);

// Handle submit
const handleSubmit = useCallback(
(combinedTextFromInput: string, onSummaryReset?: () => void) => {
if (combinedTextFromInput.trim()) {
window.electron.startPowerSaveBlocker();
try {
window.electron.startPowerSaveBlocker();
} catch (error) {
console.error('Failed to start power save blocker:', error);
}

setLastInteractionTime(Date.now());

// Set a timeout to automatically stop the power save blocker after 15 minutes
const timeoutId = window.setTimeout(
() => {
console.warn('Power save blocker timeout - stopping automatically after 15 minutes');
stopPowerSaveBlocker();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the intention of the power save blocker? Do we have it so that people can walk away from their computers for long periods of time and have goose continue to work? In which case we may not want to turn it off after 15 mins of inactivity?

cc @michaelneale

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes was exactly that - as sleep comes soon for some setups but you want it to run just a bit longer. I don't know how widely it is used and I would be ok if it made sense to retire it (this predates schedules and recipes and headless and all these other things) but then again, may still be relevant.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have put more details in the original description. The reason I added this is PCI requirements state that the screen should auto lock after 15 mins of user inactivity and the power save blocker was stopping the screen from locking. Also added logic to make sure its actually tracked/stopped in other windows.

},
15 * 60 * 1000
);

setPowerSaveTimeoutId(timeoutId);

const userMessage = createUserMessage(combinedTextFromInput.trim());

if (onSummaryReset) {
onSummaryReset();
setTimeout(() => {
window.setTimeout(() => {
append(userMessage);
// Call onMessageSent after the message is sent
onMessageSent?.();
}, 150);
} else {
append(userMessage);
// Call onMessageSent after the message is sent
onMessageSent?.();
}
} else {
// If nothing was actually submitted (e.g. empty input and no images pasted)
window.electron.stopPowerSaveBlocker();
stopPowerSaveBlocker();
}
},
[append, onMessageSent]
[append, onMessageSent, stopPowerSaveBlocker]
);

// Handle stopping the message stream
const onStopGoose = useCallback(() => {
stop();
setLastInteractionTime(Date.now());
window.electron.stopPowerSaveBlocker();
stopPowerSaveBlocker();

// Handle stopping the message stream
const lastMessage = messages[messages.length - 1];
Expand Down Expand Up @@ -330,7 +374,7 @@ export const useChatEngine = ({
setMessages([...messages, responseMessage]);
}
}
}, [stop, messages, _setInput, setMessages, enableLocalStorage]);
}, [stop, messages, _setInput, setMessages, stopPowerSaveBlocker, enableLocalStorage]);

const filteredMessages = useMemo(() => {
return [...ancestorMessages, ...messages].filter((message) => message.display ?? true);
Expand Down
87 changes: 72 additions & 15 deletions ui/desktop/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,8 @@ let appConfig = {
let windowCounter = 0;
const windowMap = new Map<number, BrowserWindow>();

// Track power save blocker ID globally
let powerSaveBlockerId: number | null = null;
// Track power save blockers per window
const windowPowerSaveBlockers = new Map<number, number>(); // windowId -> blockerId

const createChat = async (
app: App,
Expand Down Expand Up @@ -838,6 +838,23 @@ const createChat = async (
// Handle window closure
mainWindow.on('closed', () => {
windowMap.delete(windowId);

if (windowPowerSaveBlockers.has(windowId)) {
const blockerId = windowPowerSaveBlockers.get(windowId)!;
try {
powerSaveBlocker.stop(blockerId);
console.log(
`[Main] Stopped power save blocker ${blockerId} for closing window ${windowId}`
);
} catch (error) {
console.error(
`[Main] Failed to stop power save blocker ${blockerId} for window ${windowId}:`,
error
);
}
windowPowerSaveBlockers.delete(windowId);
}

if (goosedProcess && typeof goosedProcess === 'object' && 'kill' in goosedProcess) {
goosedProcess.kill();
}
Expand Down Expand Up @@ -1220,10 +1237,22 @@ ipcMain.handle('set-wakelock', async (_event, enable: boolean) => {
settings.enableWakelock = enable;
saveSettings(settings);

// Stop any existing power save blocker when disabling the setting
if (!enable && powerSaveBlockerId !== null) {
powerSaveBlocker.stop(powerSaveBlockerId);
powerSaveBlockerId = null;
// Stop all existing power save blockers when disabling the setting
if (!enable) {
for (const [windowId, blockerId] of windowPowerSaveBlockers.entries()) {
try {
powerSaveBlocker.stop(blockerId);
console.log(
`[Main] Stopped power save blocker ${blockerId} for window ${windowId} due to wakelock setting disabled`
);
} catch (error) {
console.error(
`[Main] Failed to stop power save blocker ${blockerId} for window ${windowId}:`,
error
);
}
}
windowPowerSaveBlockers.clear();
}

return true;
Expand Down Expand Up @@ -2026,21 +2055,36 @@ app.whenReady().then(async () => {
}
});

ipcMain.handle('start-power-save-blocker', () => {
if (powerSaveBlockerId === null) {
powerSaveBlockerId = powerSaveBlocker.start('prevent-app-suspension');
ipcMain.handle('start-power-save-blocker', (event) => {
const window = BrowserWindow.fromWebContents(event.sender);
const windowId = window?.id;

if (windowId && !windowPowerSaveBlockers.has(windowId)) {
const blockerId = powerSaveBlocker.start('prevent-app-suspension');
windowPowerSaveBlockers.set(windowId, blockerId);
console.log(`[Main] Started power save blocker ${blockerId} for window ${windowId}`);
return true;
}

if (windowId && windowPowerSaveBlockers.has(windowId)) {
console.log(`[Main] Power save blocker already active for window ${windowId}`);
}

return false;
});

ipcMain.handle('stop-power-save-blocker', () => {
if (powerSaveBlockerId !== null) {
powerSaveBlocker.stop(powerSaveBlockerId);
powerSaveBlockerId = null;
ipcMain.handle('stop-power-save-blocker', (event) => {
const window = BrowserWindow.fromWebContents(event.sender);
const windowId = window?.id;

if (windowId && windowPowerSaveBlockers.has(windowId)) {
const blockerId = windowPowerSaveBlockers.get(windowId)!;
powerSaveBlocker.stop(blockerId);
windowPowerSaveBlockers.delete(windowId);
console.log(`[Main] Stopped power save blocker ${blockerId} for window ${windowId}`);
return true;
}

return false;
});

Expand Down Expand Up @@ -2153,11 +2197,24 @@ async function getAllowList(): Promise<string[]> {
}

app.on('will-quit', async () => {
for (const [windowId, blockerId] of windowPowerSaveBlockers.entries()) {
try {
powerSaveBlocker.stop(blockerId);
console.log(
`[Main] Stopped power save blocker ${blockerId} for window ${windowId} during app quit`
);
} catch (error) {
console.error(
`[Main] Failed to stop power save blocker ${blockerId} for window ${windowId}:`,
error
);
}
}
windowPowerSaveBlockers.clear();

// Unregister all shortcuts when quitting
globalShortcut.unregisterAll();

// Clean up the temp directory on app quit
console.log('[Main] App "will-quit". Cleaning up temporary image directory...');
try {
await fs.access(gooseTempDir); // Check if directory exists to avoid error on fs.rm if it doesn't

Expand Down
Loading