From bed01ea0b6c9028d91c8f1339d0fb73a962a4ade Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:07:40 +0000 Subject: [PATCH 1/5] fix: ensure summary generation triggers for correct session on tab close When closing an active listening tab, the summary was incorrectly being generated for the next tab that became active instead of the closed tab. This fix tracks the previous liveSessionId and only triggers enhancement if the current tab's sessionId matches the session that was actually listening before it stopped. Co-Authored-By: john@hyprnote.com --- apps/desktop/src/hooks/useAutoEnhance.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/hooks/useAutoEnhance.ts b/apps/desktop/src/hooks/useAutoEnhance.ts index a5f13720cb..2e7c81a0fa 100644 --- a/apps/desktop/src/hooks/useAutoEnhance.ts +++ b/apps/desktop/src/hooks/useAutoEnhance.ts @@ -22,6 +22,8 @@ export function useAutoEnhance(tab: Extract) { const listenerStatus = useListener((state) => state.live.status); const prevListenerStatus = usePrevious(listenerStatus); + const liveSessionId = useListener((state) => state.live.sessionId); + const prevLiveSessionId = usePrevious(liveSessionId); const indexes = main.UI.useIndexes(main.STORE_ID); @@ -169,11 +171,18 @@ export function useAutoEnhance(tab: Extract) { useEffect(() => { const listenerJustStopped = prevListenerStatus === "active" && listenerStatus !== "active"; + const wasThisSessionListening = prevLiveSessionId === sessionId; - if (listenerJustStopped) { + if (listenerJustStopped && wasThisSessionListening) { createAndStartEnhance(); } - }, [listenerStatus, prevListenerStatus, createAndStartEnhance]); + }, [ + listenerStatus, + prevListenerStatus, + prevLiveSessionId, + sessionId, + createAndStartEnhance, + ]); useEffect(() => { if (listenerStatus === "finalizing" && indexes) { From be928cdabca6f548c0afc16f1158f74a7b007279 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:56:54 +0000 Subject: [PATCH 2/5] feat: add confirmation popover for closing active listening tabs - Add pendingCloseConfirmationTab state to lifecycle store - Add confirmation popover UI in shared.tsx with Cmd+W keyboard support - Pass confirmation state through index.tsx to tab components - Handle close with stop in sessions/index.tsx using Zustand subscribe - Update useTabsShortcuts to show confirmation for active listening tabs - Remove global registerOnClose handler from _layout.tsx Co-Authored-By: john@hyprnote.com --- .../src/components/main/body/index.tsx | 39 +++++- .../components/main/body/sessions/index.tsx | 32 ++++- .../src/components/main/body/shared.tsx | 115 ++++++++++++++++-- apps/desktop/src/routes/app/main/_layout.tsx | 17 +-- .../src/store/zustand/tabs/lifecycle.ts | 5 + 5 files changed, 182 insertions(+), 26 deletions(-) diff --git a/apps/desktop/src/components/main/body/index.tsx b/apps/desktop/src/components/main/body/index.tsx index e94e3f9cdc..bcc64afd93 100644 --- a/apps/desktop/src/components/main/body/index.tsx +++ b/apps/desktop/src/components/main/body/index.tsx @@ -93,6 +93,8 @@ function Header({ tabs }: { tabs: Tab[] }) { closeAll, pin, unpin, + pendingCloseConfirmationTab, + setPendingCloseConfirmationTab, } = useTabs( useShallow((state) => ({ select: state.select, @@ -106,6 +108,8 @@ function Header({ tabs }: { tabs: Tab[] }) { closeAll: state.closeAll, pin: state.pin, unpin: state.unpin, + pendingCloseConfirmationTab: state.pendingCloseConfirmationTab, + setPendingCloseConfirmationTab: state.setPendingCloseConfirmationTab, })), ); @@ -195,6 +199,8 @@ function Header({ tabs }: { tabs: Tab[] }) { handlePin={pin} handleUnpin={unpin} tabIndex={1} + pendingCloseConfirmationTab={pendingCloseConfirmationTab} + setPendingCloseConfirmationTab={setPendingCloseConfirmationTab} /> )} @@ -249,6 +255,10 @@ function Header({ tabs }: { tabs: Tab[] }) { handlePin={pin} handleUnpin={unpin} tabIndex={shortcutIndex} + pendingCloseConfirmationTab={pendingCloseConfirmationTab} + setPendingCloseConfirmationTab={ + setPendingCloseConfirmationTab + } /> ); @@ -300,6 +310,8 @@ function TabItem({ handlePin, handleUnpin, tabIndex, + pendingCloseConfirmationTab, + setPendingCloseConfirmationTab, }: { tab: Tab; handleClose: (tab: Tab) => void; @@ -309,6 +321,8 @@ function TabItem({ handlePin: (tab: Tab) => void; handleUnpin: (tab: Tab) => void; tabIndex?: number; + pendingCloseConfirmationTab?: Tab | null; + setPendingCloseConfirmationTab?: (tab: Tab | null) => void; }) { const handleCloseOthers = () => handleCloseOthersCallback(tab); const handlePinThis = () => handlePin(tab); @@ -325,6 +339,8 @@ function TabItem({ handleCloseAll={handleCloseAll} handlePinThis={handlePinThis} handleUnpinThis={handleUnpinThis} + pendingCloseConfirmationTab={pendingCloseConfirmationTab} + setPendingCloseConfirmationTab={setPendingCloseConfirmationTab} /> ); } @@ -734,6 +750,7 @@ function useTabsShortcuts() { restoreLastClosedTab, openNew, unpin, + setPendingCloseConfirmationTab, } = useTabs( useShallow((state) => ({ tabs: state.tabs, @@ -745,8 +762,13 @@ function useTabsShortcuts() { restoreLastClosedTab: state.restoreLastClosedTab, openNew: state.openNew, unpin: state.unpin, + setPendingCloseConfirmationTab: state.setPendingCloseConfirmationTab, })), ); + const liveSessionId = useListener((state) => state.live.sessionId); + const liveStatus = useListener((state) => state.live.status); + const isListening = liveStatus === "active" || liveStatus === "finalizing"; + const newNote = useNewNote({ behavior: "new" }); const newNoteCurrent = useNewNote({ behavior: "current" }); const newEmptyTab = useNewEmptyTab(); @@ -783,7 +805,13 @@ function useTabsShortcuts() { "mod+w", async () => { if (currentTab) { - if (currentTab.pinned) { + const isCurrentTabListening = + isListening && + currentTab.type === "sessions" && + currentTab.id === liveSessionId; + if (isCurrentTabListening) { + setPendingCloseConfirmationTab(currentTab); + } else if (currentTab.pinned) { unpin(currentTab); } else { close(currentTab); @@ -795,7 +823,14 @@ function useTabsShortcuts() { enableOnFormTags: true, enableOnContentEditable: true, }, - [currentTab, close, unpin], + [ + currentTab, + close, + unpin, + isListening, + liveSessionId, + setPendingCloseConfirmationTab, + ], ); useHotkeys( diff --git a/apps/desktop/src/components/main/body/sessions/index.tsx b/apps/desktop/src/components/main/body/sessions/index.tsx index 503ec8d879..37bdba5f4c 100644 --- a/apps/desktop/src/components/main/body/sessions/index.tsx +++ b/apps/desktop/src/components/main/body/sessions/index.tsx @@ -11,6 +11,7 @@ import { cn } from "@hypr/utils"; import AudioPlayer from "../../../../contexts/audio-player"; import { useListener } from "../../../../contexts/listener"; import { useShell } from "../../../../contexts/shell"; +import { listenerStore } from "../../../../store/zustand/listener/instance"; import { useAutoEnhance } from "../../../../hooks/useAutoEnhance"; import { useIsSessionEnhancing } from "../../../../hooks/useEnhancedNotes"; import { useStartListening } from "../../../../hooks/useStartListening"; @@ -48,6 +49,8 @@ export const TabItemNote: TabItem> = ({ handleCloseAll, handlePinThis, handleUnpinThis, + pendingCloseConfirmationTab, + setPendingCloseConfirmationTab, }) => { const title = main.UI.useCell( "sessions", @@ -56,11 +59,36 @@ export const TabItemNote: TabItem> = ({ main.STORE_ID, ); const sessionMode = useListener((state) => state.getSessionMode(tab.id)); + const stop = useListener((state) => state.stop); const isEnhancing = useIsSessionEnhancing(tab.id); const isActive = sessionMode === "active" || sessionMode === "finalizing"; const isFinalizing = sessionMode === "finalizing"; const showSpinner = !tab.active && (isFinalizing || isEnhancing); + const showCloseConfirmation = + pendingCloseConfirmationTab?.type === "sessions" && + pendingCloseConfirmationTab?.id === tab.id; + + const handleCloseConfirmationChange = (show: boolean) => { + if (!show) { + setPendingCloseConfirmationTab?.(null); + } + }; + + const handleCloseWithStop = () => { + if (isActive) { + stop(); + const unsubscribe = listenerStore.subscribe((state) => { + if (state.live.status !== "active") { + unsubscribe(); + handleCloseThis(tab); + } + }); + } else { + handleCloseThis(tab); + } + }; + return ( } @@ -70,7 +98,9 @@ export const TabItemNote: TabItem> = ({ finalizing={showSpinner} pinned={tab.pinned} tabIndex={tabIndex} - handleCloseThis={() => handleCloseThis(tab)} + showCloseConfirmation={showCloseConfirmation} + onCloseConfirmationChange={handleCloseConfirmationChange} + handleCloseThis={handleCloseWithStop} handleSelectThis={() => handleSelectThis(tab)} handleCloseOthers={handleCloseOthers} handleCloseAll={handleCloseAll} diff --git a/apps/desktop/src/components/main/body/shared.tsx b/apps/desktop/src/components/main/body/shared.tsx index 027475006c..5632eed45b 100644 --- a/apps/desktop/src/components/main/body/shared.tsx +++ b/apps/desktop/src/components/main/body/shared.tsx @@ -1,7 +1,13 @@ import { Pin, X } from "lucide-react"; -import { useState } from "react"; +import { useCallback, useEffect, useState } from "react"; +import { Button } from "@hypr/ui/components/ui/button"; import { Kbd } from "@hypr/ui/components/ui/kbd"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@hypr/ui/components/ui/popover"; import { Spinner } from "@hypr/ui/components/ui/spinner"; import { cn } from "@hypr/utils"; @@ -16,6 +22,8 @@ type TabItemProps = { tab: T; tabIndex?: number } & { handleCloseAll: () => void; handlePinThis: (tab: T) => void; handleUnpinThis: (tab: T) => void; + pendingCloseConfirmationTab?: Tab | null; + setPendingCloseConfirmationTab?: (tab: Tab | null) => void; }; type TabItemBaseProps = { @@ -28,6 +36,8 @@ type TabItemBaseProps = { allowPin?: boolean; isEmptyTab?: boolean; tabIndex?: number; + showCloseConfirmation?: boolean; + onCloseConfirmationChange?: (show: boolean) => void; } & { handleCloseThis: () => void; handleSelectThis: () => void; @@ -51,6 +61,8 @@ export function TabItemBase({ allowPin = true, isEmptyTab = false, tabIndex, + showCloseConfirmation = false, + onCloseConfirmationChange, handleCloseThis, handleSelectThis, handleCloseOthers, @@ -60,6 +72,51 @@ export function TabItemBase({ }: TabItemBaseProps) { const isCmdPressed = useCmdKeyPressed(); const [isHovered, setIsHovered] = useState(false); + const [localShowConfirmation, setLocalShowConfirmation] = useState(false); + + const isConfirmationOpen = showCloseConfirmation || localShowConfirmation; + + useEffect(() => { + if (showCloseConfirmation) { + setLocalShowConfirmation(true); + } + }, [showCloseConfirmation]); + + const handleCloseConfirmationChange = (open: boolean) => { + setLocalShowConfirmation(open); + onCloseConfirmationChange?.(open); + }; + + const handleAttemptClose = () => { + if (active) { + handleCloseConfirmationChange(true); + } else { + handleCloseThis(); + } + }; + + const handleConfirmClose = useCallback(() => { + setLocalShowConfirmation(false); + onCloseConfirmationChange?.(false); + handleCloseThis(); + }, [handleCloseThis, onCloseConfirmationChange]); + + useEffect(() => { + if (!isConfirmationOpen) return; + + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key === "w") { + e.preventDefault(); + e.stopPropagation(); + handleConfirmClose(); + } + }; + + window.addEventListener("keydown", handleKeyDown, { capture: true }); + return () => { + window.removeEventListener("keydown", handleKeyDown, { capture: true }); + }; + }, [isConfirmationOpen, handleConfirmClose]); const handleMouseDown = (e: React.MouseEvent) => { if (e.button === 1 && !active) { @@ -72,7 +129,7 @@ export function TabItemBase({ const contextMenu = active || (selected && !isEmptyTab) ? [ - { id: "close-tab", text: "Close", action: handleCloseThis }, + { id: "close-tab", text: "Close", action: handleAttemptClose }, ...(allowPin ? [ { separator: true as const }, @@ -87,7 +144,7 @@ export function TabItemBase({ : []), ] : [ - { id: "close-tab", text: "Close", action: handleCloseThis }, + { id: "close-tab", text: "Close", action: handleAttemptClose }, { id: "close-others", text: "Close others", @@ -114,7 +171,7 @@ export function TabItemBase({
setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} - className="h-full" + className="h-full relative" > {finalizing ? ( @@ -176,13 +233,13 @@ export function TabItemBase({
)}
+ + +
+ + e.preventDefault()} + > +
+

+ Are you sure you want to close this tab? This will stop Hyprnote + from listening. +

+ +
+
+
); } diff --git a/apps/desktop/src/routes/app/main/_layout.tsx b/apps/desktop/src/routes/app/main/_layout.tsx index 75d3fb1dee..e70e725089 100644 --- a/apps/desktop/src/routes/app/main/_layout.tsx +++ b/apps/desktop/src/routes/app/main/_layout.tsx @@ -27,15 +27,12 @@ function Component() { const { persistedStore, aiTaskStore, toolRegistry } = useRouteContext({ from: "__root__", }); - const { registerOnEmpty, registerCanClose, registerOnClose, openNew, pin } = - useTabs(); + const { registerOnEmpty, registerCanClose, openNew, pin } = useTabs(); const tabs = useTabs((state) => state.tabs); const hasOpenedInitialTab = useRef(false); const liveSessionId = useListener((state) => state.live.sessionId); const liveStatus = useListener((state) => state.live.status); const prevLiveStatus = usePrevious(liveStatus); - const getSessionMode = useListener((state) => state.getSessionMode); - const stop = useListener((state) => state.stop); useDeeplinkHandler(); @@ -66,18 +63,6 @@ function Component() { } }, [liveStatus, prevLiveStatus, liveSessionId, pin]); - useEffect(() => { - registerOnClose((tab) => { - if (tab.type !== "sessions") { - return; - } - const mode = getSessionMode(tab.id); - if (mode === "active" || mode === "finalizing") { - stop(); - } - }); - }, [registerOnClose, getSessionMode, stop]); - useEffect(() => { registerCanClose(() => true); }, [registerCanClose]); diff --git a/apps/desktop/src/store/zustand/tabs/lifecycle.ts b/apps/desktop/src/store/zustand/tabs/lifecycle.ts index 40e78c1841..e19e682226 100644 --- a/apps/desktop/src/store/zustand/tabs/lifecycle.ts +++ b/apps/desktop/src/store/zustand/tabs/lifecycle.ts @@ -6,12 +6,14 @@ export type LifecycleState = { onClose: ((tab: Tab) => void) | null; onEmpty: (() => void) | null; canClose: ((tab: Tab) => boolean) | null; + pendingCloseConfirmationTab: Tab | null; }; export type LifecycleActions = { registerOnClose: (handler: (tab: Tab) => void) => void; registerOnEmpty: (handler: () => void) => void; registerCanClose: (handler: (tab: Tab) => boolean) => void; + setPendingCloseConfirmationTab: (tab: Tab | null) => void; }; export const createLifecycleSlice = ( @@ -21,9 +23,12 @@ export const createLifecycleSlice = ( onClose: null, onEmpty: null, canClose: null, + pendingCloseConfirmationTab: null, registerOnClose: (handler) => set({ onClose: handler } as Partial), registerOnEmpty: (handler) => set({ onEmpty: handler } as Partial), registerCanClose: (handler) => set({ canClose: handler } as Partial), + setPendingCloseConfirmationTab: (tab) => + set({ pendingCloseConfirmationTab: tab } as Partial), }); type LifecycleMiddleware = < From 9fec0ebe246663fec140f6204d125a0d8adb9848 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:03:45 +0000 Subject: [PATCH 3/5] fix: correct import order for dprint formatting Co-Authored-By: john@hyprnote.com --- apps/desktop/src/components/main/body/sessions/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/src/components/main/body/sessions/index.tsx b/apps/desktop/src/components/main/body/sessions/index.tsx index 37bdba5f4c..248efa7064 100644 --- a/apps/desktop/src/components/main/body/sessions/index.tsx +++ b/apps/desktop/src/components/main/body/sessions/index.tsx @@ -11,13 +11,13 @@ import { cn } from "@hypr/utils"; import AudioPlayer from "../../../../contexts/audio-player"; import { useListener } from "../../../../contexts/listener"; import { useShell } from "../../../../contexts/shell"; -import { listenerStore } from "../../../../store/zustand/listener/instance"; import { useAutoEnhance } from "../../../../hooks/useAutoEnhance"; import { useIsSessionEnhancing } from "../../../../hooks/useEnhancedNotes"; import { useStartListening } from "../../../../hooks/useStartListening"; import { useSTTConnection } from "../../../../hooks/useSTTConnection"; import { useTitleGeneration } from "../../../../hooks/useTitleGeneration"; import * as main from "../../../../store/tinybase/store/main"; +import { listenerStore } from "../../../../store/zustand/listener/instance"; import { rowIdfromTab, type Tab, From 4a47346705c3982891a06c588f65b124853a2ad7 Mon Sep 17 00:00:00 2001 From: ComputelessComputer Date: Fri, 30 Jan 2026 02:50:34 +0900 Subject: [PATCH 4/5] feat(sessions): add automatic note enhancement on session close --- .../components/main/body/sessions/index.tsx | 108 +++++++++++++++++- .../src/store/zustand/ai-task/tasks.ts | 15 +++ 2 files changed, 117 insertions(+), 6 deletions(-) diff --git a/apps/desktop/src/components/main/body/sessions/index.tsx b/apps/desktop/src/components/main/body/sessions/index.tsx index 248efa7064..1f6a1c21c0 100644 --- a/apps/desktop/src/components/main/body/sessions/index.tsx +++ b/apps/desktop/src/components/main/body/sessions/index.tsx @@ -2,21 +2,32 @@ import { useQuery } from "@tanstack/react-query"; import { convertFileSrc } from "@tauri-apps/api/core"; import { StickyNoteIcon } from "lucide-react"; import { AnimatePresence, motion } from "motion/react"; -import React, { useEffect, useRef, useState } from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; +import { commands as analyticsCommands } from "@hypr/plugin-analytics"; import { commands as fsSyncCommands } from "@hypr/plugin-fs-sync"; +import { md2json } from "@hypr/tiptap/shared"; import { cn } from "@hypr/utils"; +import { useAITask } from "../../../../contexts/ai-task"; import AudioPlayer from "../../../../contexts/audio-player"; import { useListener } from "../../../../contexts/listener"; import { useShell } from "../../../../contexts/shell"; import { useAutoEnhance } from "../../../../hooks/useAutoEnhance"; -import { useIsSessionEnhancing } from "../../../../hooks/useEnhancedNotes"; +import { + useCreateEnhancedNote, + useIsSessionEnhancing, +} from "../../../../hooks/useEnhancedNotes"; +import { + useLanguageModel, + useLLMConnection, +} from "../../../../hooks/useLLMConnection"; import { useStartListening } from "../../../../hooks/useStartListening"; import { useSTTConnection } from "../../../../hooks/useSTTConnection"; import { useTitleGeneration } from "../../../../hooks/useTitleGeneration"; import * as main from "../../../../store/tinybase/store/main"; +import { createTaskId } from "../../../../store/zustand/ai-task/task-configs"; import { listenerStore } from "../../../../store/zustand/listener/instance"; import { rowIdfromTab, @@ -65,6 +76,90 @@ export const TabItemNote: TabItem> = ({ const isFinalizing = sessionMode === "finalizing"; const showSpinner = !tab.active && (isFinalizing || isEnhancing); + const store = main.UI.useStore(main.STORE_ID) as main.Store | undefined; + const indexes = main.UI.useIndexes(main.STORE_ID); + const model = useLanguageModel(); + const { conn: llmConn } = useLLMConnection(); + const createEnhancedNote = useCreateEnhancedNote(); + const generate = useAITask((state) => state.generate); + + const triggerEnhancementOnClose = useCallback(() => { + if (!store || !indexes || !model) { + return; + } + + const sessionId = tab.id; + + const transcriptIds = indexes.getSliceRowIds( + main.INDEXES.transcriptBySession, + sessionId, + ); + if (!transcriptIds || transcriptIds.length === 0) { + return; + } + + const firstTranscriptId = transcriptIds[0]; + const wordsJson = store.getCell("transcripts", firstTranscriptId, "words"); + const words = wordsJson + ? (JSON.parse(wordsJson as string) as unknown[]) + : []; + if (words.length < 5) { + return; + } + + const enhancedNoteId = createEnhancedNote(sessionId); + if (!enhancedNoteId) { + return; + } + + void analyticsCommands.event({ + event: "note_enhanced", + is_auto: true, + llm_provider: llmConn?.providerId, + llm_model: llmConn?.modelId, + }); + + const taskId = createTaskId(enhancedNoteId, "enhance"); + void generate(taskId, { + model, + taskType: "enhance", + args: { sessionId, enhancedNoteId }, + onComplete: (text) => { + if (!text || !store) return; + try { + const jsonContent = md2json(text); + store.setPartialRow("enhanced_notes", enhancedNoteId, { + content: JSON.stringify(jsonContent), + }); + + const currentTitle = store.getCell("sessions", sessionId, "title"); + const trimmedTitle = + typeof currentTitle === "string" ? currentTitle.trim() : ""; + if (!trimmedTitle) { + const titleTaskId = createTaskId(sessionId, "title"); + void generate(titleTaskId, { + model, + taskType: "title", + args: { sessionId }, + onComplete: (titleText) => { + if (titleText && store) { + const trimmed = titleText.trim(); + if (trimmed && trimmed !== "") { + store.setPartialRow("sessions", sessionId, { + title: trimmed, + }); + } + } + }, + }); + } + } catch (error) { + console.error("Failed to convert markdown to JSON:", error); + } + }, + }); + }, [tab.id, store, indexes, model, llmConn, createEnhancedNote, generate]); + const showCloseConfirmation = pendingCloseConfirmationTab?.type === "sessions" && pendingCloseConfirmationTab?.id === tab.id; @@ -75,19 +170,20 @@ export const TabItemNote: TabItem> = ({ } }; - const handleCloseWithStop = () => { + const handleCloseWithStop = useCallback(() => { if (isActive) { + handleCloseThis(tab); stop(); const unsubscribe = listenerStore.subscribe((state) => { - if (state.live.status !== "active") { + if (state.live.status === "inactive") { unsubscribe(); - handleCloseThis(tab); + triggerEnhancementOnClose(); } }); } else { handleCloseThis(tab); } - }; + }, [isActive, triggerEnhancementOnClose, stop, tab, handleCloseThis]); return ( ( onComplete?: (text: string) => void; }, ) => { + console.log("[AI Task] generate called", { + taskId, + taskType: config.taskType, + args: config.args, + }); const abortController = new AbortController(); const taskConfig = TASK_CONFIGS[config.taskType]; try { + console.log("[AI Task] transformArgs starting"); const enrichedArgs = await taskConfig.transformArgs( config.args, deps.persistedStore, deps.settingsStore, ); + console.log("[AI Task] transformArgs completed"); set((state) => mutate(state, (draft) => { @@ -200,6 +207,10 @@ export const createTasksSlice = ( } } + console.log( + "[AI Task] stream completed, fullText length:", + fullText.length, + ); set((state) => mutate(state, (draft) => { draft.tasks[taskId] = { @@ -213,12 +224,15 @@ export const createTasksSlice = ( }), ); + console.log("[AI Task] calling onComplete"); config.onComplete?.(fullText); } catch (err) { + console.error("[AI Task] error caught:", err); if ( err instanceof Error && (err.name === "AbortError" || err.message === "Aborted") ) { + console.log("[AI Task] task was aborted"); set((state) => mutate(state, (draft) => { draft.tasks[taskId] = { @@ -233,6 +247,7 @@ export const createTasksSlice = ( ); } else { const error = extractUnderlyingError(err); + console.error("[AI Task] task failed with error:", error); set((state) => mutate(state, (draft) => { draft.tasks[taskId] = { From ae1b68a8265dca4b5cb367b968c22e1a4f612b57 Mon Sep 17 00:00:00 2001 From: ComputelessComputer Date: Fri, 30 Jan 2026 02:55:14 +0900 Subject: [PATCH 5/5] refactor(ai-task): remove debug logging in tasks store --- apps/desktop/src/store/zustand/ai-task/tasks.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/apps/desktop/src/store/zustand/ai-task/tasks.ts b/apps/desktop/src/store/zustand/ai-task/tasks.ts index 1acfd1aa57..1ac0d2bb6e 100644 --- a/apps/desktop/src/store/zustand/ai-task/tasks.ts +++ b/apps/desktop/src/store/zustand/ai-task/tasks.ts @@ -125,22 +125,15 @@ export const createTasksSlice = ( onComplete?: (text: string) => void; }, ) => { - console.log("[AI Task] generate called", { - taskId, - taskType: config.taskType, - args: config.args, - }); const abortController = new AbortController(); const taskConfig = TASK_CONFIGS[config.taskType]; try { - console.log("[AI Task] transformArgs starting"); const enrichedArgs = await taskConfig.transformArgs( config.args, deps.persistedStore, deps.settingsStore, ); - console.log("[AI Task] transformArgs completed"); set((state) => mutate(state, (draft) => { @@ -207,10 +200,6 @@ export const createTasksSlice = ( } } - console.log( - "[AI Task] stream completed, fullText length:", - fullText.length, - ); set((state) => mutate(state, (draft) => { draft.tasks[taskId] = { @@ -224,15 +213,12 @@ export const createTasksSlice = ( }), ); - console.log("[AI Task] calling onComplete"); config.onComplete?.(fullText); } catch (err) { - console.error("[AI Task] error caught:", err); if ( err instanceof Error && (err.name === "AbortError" || err.message === "Aborted") ) { - console.log("[AI Task] task was aborted"); set((state) => mutate(state, (draft) => { draft.tasks[taskId] = { @@ -247,7 +233,6 @@ export const createTasksSlice = ( ); } else { const error = extractUnderlyingError(err); - console.error("[AI Task] task failed with error:", error); set((state) => mutate(state, (draft) => { draft.tasks[taskId] = {