void;
startTour?: () => void;
setFuzzyFileSearchOpen?: (open: boolean) => void;
+ onEditAgent?: (session: Session) => void;
}
export function QuickActionsModal(props: QuickActionsModalProps) {
@@ -82,7 +83,7 @@ export function QuickActionsModal(props: QuickActionsModalProps) {
setShortcutsHelpOpen, setAboutModalOpen, setLogViewerOpen, setProcessMonitorOpen,
setAgentSessionsOpen, setActiveAgentSessionId, setGitDiffPreview, setGitLogOpen,
onRenameTab, onToggleReadOnlyMode, onOpenTabSwitcher, tabShortcuts, isAiMode, setPlaygroundOpen, onRefreshGitFileState,
- onDebugReleaseQueuedItem, markdownEditMode, onToggleMarkdownEditMode, setUpdateCheckModalOpen, openWizard, wizardGoToStep, setDebugWizardModalOpen, startTour, setFuzzyFileSearchOpen
+ onDebugReleaseQueuedItem, markdownEditMode, onToggleMarkdownEditMode, setUpdateCheckModalOpen, openWizard, wizardGoToStep, setDebugWizardModalOpen, startTour, setFuzzyFileSearchOpen, onEditAgent
} = props;
const [search, setSearch] = useState('');
@@ -204,6 +205,16 @@ export function QuickActionsModal(props: QuickActionsModalProps) {
setRenameInstanceModalOpen(true);
setQuickActionOpen(false);
} }] : []),
+ ...(activeSession && onEditAgent ? [{ id: 'editAgent', label: `Edit Agent: ${activeSession.name}`, action: () => {
+ onEditAgent(activeSession);
+ setQuickActionOpen(false);
+ } }] : []),
+ ...(activeSession ? [{ id: 'toggleBookmark', label: activeSession.bookmarked ? `Unbookmark: ${activeSession.name}` : `Bookmark: ${activeSession.name}`, action: () => {
+ setSessions(prev => prev.map(s =>
+ s.id === activeSessionId ? { ...s, bookmarked: !s.bookmarked } : s
+ ));
+ setQuickActionOpen(false);
+ } }] : []),
...(activeSession?.groupId ? [{
id: 'renameGroup',
label: 'Rename Group',
diff --git a/src/renderer/components/SettingsModal.tsx b/src/renderer/components/SettingsModal.tsx
index e6bf78cd0..75f78096a 100644
--- a/src/renderer/components/SettingsModal.tsx
+++ b/src/renderer/components/SettingsModal.tsx
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef, memo } from 'react';
-import { X, Key, Moon, Sun, Keyboard, Check, Terminal, Bell, Cpu, Settings, Palette, Sparkles, History, Download } from 'lucide-react';
-import type { AgentConfig, Theme, ThemeColors, ThemeId, Shortcut, ShellInfo, CustomAICommand } from '../types';
+import { X, Key, Moon, Sun, Keyboard, Check, Terminal, Bell, Cpu, Settings, Palette, Sparkles, History, Download, Bug } from 'lucide-react';
+import type { Theme, ThemeColors, ThemeId, Shortcut, ShellInfo, CustomAICommand } from '../types';
import { CustomThemeBuilder } from './CustomThemeBuilder';
import { useLayerStack } from '../contexts/LayerStackContext';
import { MODAL_PRIORITIES } from '../constants/modalPriorities';
@@ -8,7 +8,6 @@ import { AICommandsPanel } from './AICommandsPanel';
import { formatShortcutKeys } from '../utils/shortcutFormatter';
import { ToggleButtonGroup, ToggleButtonOption } from './ToggleButtonGroup';
import { SettingCheckbox } from './SettingCheckbox';
-import { AgentSelectionPanel } from './AgentSelectionPanel';
import { FontConfigurationPanel } from './FontConfigurationPanel';
import { NotificationsPanel } from './NotificationsPanel';
@@ -36,8 +35,6 @@ interface SettingsModalProps {
setApiKey: (key: string) => void;
shortcuts: Record
;
setShortcuts: (shortcuts: Record) => void;
- defaultAgent: string;
- setDefaultAgent: (agentId: string) => void;
fontFamily: string;
setFontFamily: (font: string) => void;
fontSize: number;
@@ -70,6 +67,8 @@ interface SettingsModalProps {
setToastDuration: (value: number) => void;
checkForUpdatesOnStartup: boolean;
setCheckForUpdatesOnStartup: (value: boolean) => void;
+ crashReportingEnabled: boolean;
+ setCrashReportingEnabled: (value: boolean) => void;
customAICommands: CustomAICommand[];
setCustomAICommands: (commands: CustomAICommand[]) => void;
initialTab?: 'general' | 'llm' | 'shortcuts' | 'theme' | 'notifications' | 'aicommands';
@@ -85,9 +84,6 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
const [customFonts, setCustomFonts] = useState([]);
const [fontLoading, setFontLoading] = useState(false);
const [fontsLoaded, setFontsLoaded] = useState(false);
- const [agents, setAgents] = useState([]);
- const [loading, setLoading] = useState(true);
- const [agentConfigs, setAgentConfigs] = useState>>({});
const [recordingId, setRecordingId] = useState(null);
const [shortcutsFilter, setShortcutsFilter] = useState('');
const [testingLLM, setTestingLLM] = useState(false);
@@ -95,7 +91,6 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
const [shells, setShells] = useState([]);
const [shellsLoading, setShellsLoading] = useState(false);
const [shellsLoaded, setShellsLoaded] = useState(false);
- const [customAgentPaths, setCustomAgentPaths] = useState>({});
// Layer stack integration
const { registerLayer, unregisterLayer, updateLayerHandler } = useLayerStack();
@@ -105,7 +100,6 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
useEffect(() => {
if (isOpen) {
- loadAgents();
// Don't load fonts immediately - only when user interacts with font selector
// Set initial tab if provided, otherwise default to 'general'
setActiveTab(initialTab || 'general');
@@ -201,30 +195,6 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
}
}, [isOpen, activeTab]);
- const loadAgents = async () => {
- setLoading(true);
- try {
- const detectedAgents = await window.maestro.agents.detect();
- setAgents(detectedAgents);
-
- // Load configurations for all agents
- const configs: Record> = {};
- for (const agent of detectedAgents) {
- const config = await window.maestro.agents.getConfig(agent.id);
- configs[agent.id] = config;
- }
- setAgentConfigs(configs);
-
- // Load custom paths for agents
- const paths = await window.maestro.agents.getAllCustomPaths();
- setCustomAgentPaths(paths);
- } catch (error) {
- console.error('Failed to load agents:', error);
- } finally {
- setLoading(false);
- }
- };
-
const loadFonts = async () => {
if (fontsLoaded) return; // Don't reload if already loaded
@@ -578,19 +548,6 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
{activeTab === 'general' && (
-
-
{/* Font Family */}
+
+ {/* Crash Reporting */}
+
)}
diff --git a/src/renderer/components/TabSwitcherModal.tsx b/src/renderer/components/TabSwitcherModal.tsx
index ba94a261d..bfdde7532 100644
--- a/src/renderer/components/TabSwitcherModal.tsx
+++ b/src/renderer/components/TabSwitcherModal.tsx
@@ -208,7 +208,7 @@ export function TabSwitcherModal({
)
);
// Then load all named sessions (including the ones we just synced)
- const sessions = await window.maestro.claude.getAllNamedSessions();
+ const sessions = await window.maestro.agentSessions.getAllNamedSessions();
setNamedSessions(sessions);
setNamedSessionsLoaded(true);
};
diff --git a/src/renderer/global.d.ts b/src/renderer/global.d.ts
index 6012cd5ea..eb1aa2583 100644
--- a/src/renderer/global.d.ts
+++ b/src/renderer/global.d.ts
@@ -201,6 +201,7 @@ interface MaestroAPI {
fs: {
readDir: (dirPath: string) => Promise
;
readFile: (filePath: string) => Promise;
+ writeFile: (filePath: string, content: string) => Promise<{ success: boolean }>;
};
webserver: {
getUrl: () => Promise;
@@ -328,6 +329,15 @@ interface MaestroAPI {
hasCostData: boolean;
}>;
}) => void) => () => void;
+ getAllNamedSessions: () => Promise>;
+ registerSessionOrigin: (projectPath: string, agentSessionId: string, origin: 'user' | 'auto', sessionName?: string) => Promise;
+ updateSessionName: (projectPath: string, agentSessionId: string, sessionName: string) => Promise;
};
dialog: {
selectFolder: () => Promise;
diff --git a/src/renderer/hooks/useAgentExecution.ts b/src/renderer/hooks/useAgentExecution.ts
index 63b23b86f..939da18a2 100644
--- a/src/renderer/hooks/useAgentExecution.ts
+++ b/src/renderer/hooks/useAgentExecution.ts
@@ -299,17 +299,16 @@ export function useAgentExecution(
// Spawn the agent for batch processing
// Use effectiveCwd which may be a worktree path for parallel execution
const commandToUse = agent.path || agent.command;
- // Only add Claude-specific permission-mode flag for Claude Code
- const spawnArgs = session.toolType === 'claude-code'
- ? [...(agent.args || []), '--permission-mode', 'plan']
- : [...(agent.args || [])];
+ // Batch processing runs in read-only mode (plan mode) to prevent unintended writes
+ // The main process uses agent-specific readOnlyArgs builders for correct CLI args
window.maestro.process.spawn({
sessionId: targetSessionId,
toolType: session.toolType,
cwd: effectiveCwd,
command: commandToUse,
- args: spawnArgs,
- prompt
+ args: agent.args || [],
+ prompt,
+ readOnlyMode: true, // Batch operations run in read-only/plan mode
}).catch(() => {
cleanup();
resolve({ success: false });
diff --git a/src/renderer/hooks/useAgentSessionManagement.ts b/src/renderer/hooks/useAgentSessionManagement.ts
index 66b485dea..9a39a514c 100644
--- a/src/renderer/hooks/useAgentSessionManagement.ts
+++ b/src/renderer/hooks/useAgentSessionManagement.ts
@@ -261,15 +261,16 @@ export function useAgentSessionManagement(
if (s.id !== activeSession.id) return s;
// Create tab from the CURRENT session state (not stale closure value)
- const { session: updatedSession } = createTab(s, {
+ const result = createTab(s, {
agentSessionId,
logs: messages,
name,
starred: isStarred,
saveToHistory: defaultSaveToHistory
});
+ if (!result) return s;
- return { ...updatedSession, inputMode: 'ai' };
+ return { ...result.session, inputMode: 'ai' };
}));
setActiveAgentSessionId(agentSessionId);
} catch (error) {
diff --git a/src/renderer/hooks/useMainKeyboardHandler.ts b/src/renderer/hooks/useMainKeyboardHandler.ts
index 7db12f511..e041ee282 100644
--- a/src/renderer/hooks/useMainKeyboardHandler.ts
+++ b/src/renderer/hooks/useMainKeyboardHandler.ts
@@ -308,12 +308,14 @@ export function useMainKeyboardHandler(): UseMainKeyboardHandlerReturn {
if (ctx.isTabShortcut(e, 'newTab')) {
e.preventDefault();
const result = ctx.createTab(ctx.activeSession, { saveToHistory: ctx.defaultSaveToHistory });
- ctx.setSessions((prev: Session[]) => prev.map((s: Session) =>
- s.id === ctx.activeSession!.id ? result.session : s
- ));
- // Auto-focus the input so user can start typing immediately
- ctx.setActiveFocus('main');
- setTimeout(() => ctx.inputRef.current?.focus(), 50);
+ if (result) {
+ ctx.setSessions((prev: Session[]) => prev.map((s: Session) =>
+ s.id === ctx.activeSession!.id ? result.session : s
+ ));
+ // Auto-focus the input so user can start typing immediately
+ ctx.setActiveFocus('main');
+ setTimeout(() => ctx.inputRef.current?.focus(), 50);
+ }
}
if (ctx.isTabShortcut(e, 'closeTab')) {
e.preventDefault();
diff --git a/src/renderer/hooks/useRemoteIntegration.ts b/src/renderer/hooks/useRemoteIntegration.ts
index 124dde76a..e984c4ec3 100644
--- a/src/renderer/hooks/useRemoteIntegration.ts
+++ b/src/renderer/hooks/useRemoteIntegration.ts
@@ -265,6 +265,7 @@ export function useRemoteIntegration(deps: UseRemoteIntegrationDeps): UseRemoteI
// Use createTab helper
const result = createTab(s, { saveToHistory: defaultSaveToHistory });
+ if (!result) return s;
newTabId = result.tab.id;
return result.session;
}));
diff --git a/src/renderer/hooks/useSettings.ts b/src/renderer/hooks/useSettings.ts
index 0948d3b82..cda1f2bb4 100644
--- a/src/renderer/hooks/useSettings.ts
+++ b/src/renderer/hooks/useSettings.ts
@@ -82,10 +82,6 @@ export interface UseSettingsReturn {
setModelSlug: (value: string) => void;
setApiKey: (value: string) => void;
- // Agent settings
- defaultAgent: string;
- setDefaultAgent: (value: string) => void;
-
// Shell settings
defaultShell: string;
setDefaultShell: (value: string) => void;
@@ -152,6 +148,10 @@ export interface UseSettingsReturn {
checkForUpdatesOnStartup: boolean;
setCheckForUpdatesOnStartup: (value: boolean) => void;
+ // Crash reporting settings
+ crashReportingEnabled: boolean;
+ setCrashReportingEnabled: (value: boolean) => void;
+
// Log Viewer settings
logViewerSelectedLevels: string[];
setLogViewerSelectedLevels: (value: string[]) => void;
@@ -227,9 +227,6 @@ export function useSettings(): UseSettingsReturn {
const [modelSlug, setModelSlugState] = useState('anthropic/claude-3.5-sonnet');
const [apiKey, setApiKeyState] = useState('');
- // Agent Config
- const [defaultAgent, setDefaultAgentState] = useState('claude-code');
-
// Shell Config
const [defaultShell, setDefaultShellState] = useState('zsh');
@@ -272,6 +269,9 @@ export function useSettings(): UseSettingsReturn {
// Update Config
const [checkForUpdatesOnStartup, setCheckForUpdatesOnStartupState] = useState(true); // Default: on
+ // Crash Reporting Config
+ const [crashReportingEnabled, setCrashReportingEnabledState] = useState(true); // Default: on (opt-out)
+
// Log Viewer Config
const [logViewerSelectedLevels, setLogViewerSelectedLevelsState] = useState(['debug', 'info', 'warn', 'error', 'toast']);
@@ -322,11 +322,6 @@ export function useSettings(): UseSettingsReturn {
window.maestro.settings.set('apiKey', value);
}, []);
- const setDefaultAgent = useCallback((value: string) => {
- setDefaultAgentState(value);
- window.maestro.settings.set('defaultAgent', value);
- }, []);
-
const setDefaultShell = useCallback((value: string) => {
setDefaultShellState(value);
window.maestro.settings.set('defaultShell', value);
@@ -453,6 +448,11 @@ export function useSettings(): UseSettingsReturn {
window.maestro.settings.set('checkForUpdatesOnStartup', value);
}, []);
+ const setCrashReportingEnabled = useCallback((value: boolean) => {
+ setCrashReportingEnabledState(value);
+ window.maestro.settings.set('crashReportingEnabled', value);
+ }, []);
+
const setLogViewerSelectedLevels = useCallback((value: string[]) => {
setLogViewerSelectedLevelsState(value);
window.maestro.settings.set('logViewerSelectedLevels', value);
@@ -864,7 +864,6 @@ export function useSettings(): UseSettingsReturn {
const savedLlmProvider = await window.maestro.settings.get('llmProvider');
const savedModelSlug = await window.maestro.settings.get('modelSlug');
const savedApiKey = await window.maestro.settings.get('apiKey');
- const savedDefaultAgent = await window.maestro.settings.get('defaultAgent');
const savedDefaultShell = await window.maestro.settings.get('defaultShell');
const savedGhPath = await window.maestro.settings.get('ghPath');
const savedFontSize = await window.maestro.settings.get('fontSize');
@@ -887,6 +886,7 @@ export function useSettings(): UseSettingsReturn {
const savedAudioFeedbackCommand = await window.maestro.settings.get('audioFeedbackCommand');
const savedToastDuration = await window.maestro.settings.get('toastDuration');
const savedCheckForUpdatesOnStartup = await window.maestro.settings.get('checkForUpdatesOnStartup');
+ const savedCrashReportingEnabled = await window.maestro.settings.get('crashReportingEnabled');
const savedLogViewerSelectedLevels = await window.maestro.settings.get('logViewerSelectedLevels');
const savedCustomAICommands = await window.maestro.settings.get('customAICommands');
const savedGlobalStats = await window.maestro.settings.get('globalStats');
@@ -907,7 +907,6 @@ export function useSettings(): UseSettingsReturn {
if (savedLlmProvider !== undefined) setLlmProviderState(savedLlmProvider);
if (savedModelSlug !== undefined) setModelSlugState(savedModelSlug);
if (savedApiKey !== undefined) setApiKeyState(savedApiKey);
- if (savedDefaultAgent !== undefined) setDefaultAgentState(savedDefaultAgent);
if (savedDefaultShell !== undefined) setDefaultShellState(savedDefaultShell);
if (savedGhPath !== undefined) setGhPathState(savedGhPath);
if (savedFontSize !== undefined) setFontSizeState(savedFontSize);
@@ -929,6 +928,7 @@ export function useSettings(): UseSettingsReturn {
if (savedAudioFeedbackCommand !== undefined) setAudioFeedbackCommandState(savedAudioFeedbackCommand);
if (savedToastDuration !== undefined) setToastDurationState(savedToastDuration);
if (savedCheckForUpdatesOnStartup !== undefined) setCheckForUpdatesOnStartupState(savedCheckForUpdatesOnStartup);
+ if (savedCrashReportingEnabled !== undefined) setCrashReportingEnabledState(savedCrashReportingEnabled);
if (savedLogViewerSelectedLevels !== undefined) setLogViewerSelectedLevelsState(savedLogViewerSelectedLevels);
// Merge saved shortcuts with defaults (in case new shortcuts were added)
@@ -1063,8 +1063,6 @@ export function useSettings(): UseSettingsReturn {
setLlmProvider,
setModelSlug,
setApiKey,
- defaultAgent,
- setDefaultAgent,
defaultShell,
setDefaultShell,
ghPath,
@@ -1113,6 +1111,8 @@ export function useSettings(): UseSettingsReturn {
setToastDuration,
checkForUpdatesOnStartup,
setCheckForUpdatesOnStartup,
+ crashReportingEnabled,
+ setCrashReportingEnabled,
logViewerSelectedLevels,
setLogViewerSelectedLevels,
shortcuts,
@@ -1159,7 +1159,6 @@ export function useSettings(): UseSettingsReturn {
llmProvider,
modelSlug,
apiKey,
- defaultAgent,
defaultShell,
ghPath,
fontFamily,
@@ -1184,6 +1183,7 @@ export function useSettings(): UseSettingsReturn {
audioFeedbackCommand,
toastDuration,
checkForUpdatesOnStartup,
+ crashReportingEnabled,
logViewerSelectedLevels,
shortcuts,
customAICommands,
@@ -1198,7 +1198,6 @@ export function useSettings(): UseSettingsReturn {
setLlmProvider,
setModelSlug,
setApiKey,
- setDefaultAgent,
setDefaultShell,
setGhPath,
setFontFamily,
@@ -1223,6 +1222,7 @@ export function useSettings(): UseSettingsReturn {
setAudioFeedbackCommand,
setToastDuration,
setCheckForUpdatesOnStartup,
+ setCrashReportingEnabled,
setLogViewerSelectedLevels,
setShortcuts,
setCustomAICommands,
diff --git a/src/renderer/main.tsx b/src/renderer/main.tsx
index 1e101bcb5..0e5b08d7f 100644
--- a/src/renderer/main.tsx
+++ b/src/renderer/main.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
+import * as Sentry from '@sentry/electron/renderer';
import MaestroConsole from './App';
import { ErrorBoundary } from './components/ErrorBoundary';
import { LayerStackProvider } from './contexts/LayerStackContext';
@@ -8,6 +9,12 @@ import { WizardProvider } from './components/Wizard';
import { logger } from './utils/logger';
import './index.css';
+// Initialize Sentry for the renderer process
+// The main process handles the enabled/disabled check and initializes Sentry there
+// Renderer Sentry will automatically connect to main process Sentry
+// We initialize unconditionally here - if main process didn't init, this is a no-op
+Sentry.init({});
+
// Set up global error handlers for uncaught exceptions in renderer process
window.addEventListener('error', (event: ErrorEvent) => {
logger.error(
diff --git a/src/renderer/utils/tabHelpers.ts b/src/renderer/utils/tabHelpers.ts
index d4d4390dd..f7c312537 100644
--- a/src/renderer/utils/tabHelpers.ts
+++ b/src/renderer/utils/tabHelpers.ts
@@ -34,7 +34,7 @@ function hasDraft(tab: AITab): boolean {
* const unreadTabs = getNavigableTabs(session, true);
*/
export function getNavigableTabs(session: Session, showUnreadOnly = false): AITab[] {
- if (!session.aiTabs || session.aiTabs.length === 0) {
+ if (!session || !session.aiTabs || session.aiTabs.length === 0) {
return [];
}
@@ -54,7 +54,7 @@ export function getNavigableTabs(session: Session, showUnreadOnly = false): AITa
* @returns The active AITab or undefined if no tabs exist
*/
export function getActiveTab(session: Session): AITab | undefined {
- if (!session.aiTabs || session.aiTabs.length === 0) {
+ if (!session || !session.aiTabs || session.aiTabs.length === 0) {
return undefined;
}
@@ -106,7 +106,11 @@ export interface CreateTabResult {
* logs: existingLogs
* });
*/
-export function createTab(session: Session, options: CreateTabOptions = {}): CreateTabResult {
+export function createTab(session: Session, options: CreateTabOptions = {}): CreateTabResult | null {
+ if (!session) {
+ return null;
+ }
+
const {
agentSessionId = null,
logs = [],
@@ -170,7 +174,7 @@ export interface CloseTabResult {
* }
*/
export function closeTab(session: Session, tabId: string): CloseTabResult | null {
- if (!session.aiTabs || session.aiTabs.length === 0) {
+ if (!session || !session.aiTabs || session.aiTabs.length === 0) {
return null;
}
@@ -345,8 +349,8 @@ export interface SetActiveTabResult {
* }
*/
export function setActiveTab(session: Session, tabId: string): SetActiveTabResult | null {
- // Validate that the tab exists
- if (!session.aiTabs || session.aiTabs.length === 0) {
+ // Validate that the session and tab exists
+ if (!session || !session.aiTabs || session.aiTabs.length === 0) {
return null;
}
@@ -388,7 +392,7 @@ export function setActiveTab(session: Session, tabId: string): SetActiveTabResul
* }
*/
export function getWriteModeTab(session: Session): AITab | undefined {
- if (!session.aiTabs || session.aiTabs.length === 0) {
+ if (!session || !session.aiTabs || session.aiTabs.length === 0) {
return undefined;
}
@@ -416,7 +420,7 @@ export function getWriteModeTab(session: Session): AITab | undefined {
* }
*/
export function getBusyTabs(session: Session): AITab[] {
- if (!session.aiTabs || session.aiTabs.length === 0) {
+ if (!session || !session.aiTabs || session.aiTabs.length === 0) {
return [];
}
@@ -439,7 +443,7 @@ export function getBusyTabs(session: Session): AITab[] {
* }
*/
export function navigateToNextTab(session: Session, showUnreadOnly = false): SetActiveTabResult | null {
- if (!session.aiTabs || session.aiTabs.length < 2) {
+ if (!session || !session.aiTabs || session.aiTabs.length < 2) {
return null;
}
@@ -498,7 +502,7 @@ export function navigateToNextTab(session: Session, showUnreadOnly = false): Set
* }
*/
export function navigateToPrevTab(session: Session, showUnreadOnly = false): SetActiveTabResult | null {
- if (!session.aiTabs || session.aiTabs.length < 2) {
+ if (!session || !session.aiTabs || session.aiTabs.length < 2) {
return null;
}
@@ -559,7 +563,7 @@ export function navigateToPrevTab(session: Session, showUnreadOnly = false): Set
* }
*/
export function navigateToTabByIndex(session: Session, index: number, showUnreadOnly = false): SetActiveTabResult | null {
- if (!session.aiTabs || session.aiTabs.length === 0) {
+ if (!session || !session.aiTabs || session.aiTabs.length === 0) {
return null;
}