diff --git a/src/__tests__/renderer/hooks/useInlineWizard.test.ts b/src/__tests__/renderer/hooks/useInlineWizard.test.ts index 852d5ad4..62b0bd93 100644 --- a/src/__tests__/renderer/hooks/useInlineWizard.test.ts +++ b/src/__tests__/renderer/hooks/useInlineWizard.test.ts @@ -108,7 +108,12 @@ describe('useInlineWizard', () => { describe('startWizard - intent parsing flow', () => { describe('when no input is provided', () => { it('should set mode to "ask" when existing docs exist', async () => { - mockHasExistingAutoRunDocs.mockResolvedValue(true); + // Mock listDocs to return files (existing docs) + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: ['phase-1', 'phase-2'], + }); + window.maestro.autorun.listDocs = mockListDocs; const { result } = renderHook(() => useInlineWizard()); @@ -118,11 +123,16 @@ describe('useInlineWizard', () => { expect(result.current.isWizardActive).toBe(true); expect(result.current.wizardMode).toBe('ask'); - expect(mockHasExistingAutoRunDocs).toHaveBeenCalledWith('/test/project'); + expect(mockListDocs).toHaveBeenCalledWith('/test/project/Auto Run Docs'); }); it('should set mode to "new" when no existing docs', async () => { - mockHasExistingAutoRunDocs.mockResolvedValue(false); + // Mock listDocs to return empty files (no docs) + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: [], + }); + window.maestro.autorun.listDocs = mockListDocs; const { result } = renderHook(() => useInlineWizard()); @@ -140,15 +150,19 @@ describe('useInlineWizard', () => { await result.current.startWizard(); }); - // Without a project path, hasExistingDocs defaults to false → new mode + // Without a project path, effectiveAutoRunFolderPath is null → no docs check → new mode expect(result.current.wizardMode).toBe('new'); - expect(mockHasExistingAutoRunDocs).not.toHaveBeenCalled(); }); }); describe('when input is provided', () => { it('should call parseWizardIntent with input and hasExistingDocs', async () => { - mockHasExistingAutoRunDocs.mockResolvedValue(true); + // Mock listDocs to return files (existing docs) + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: ['phase-1', 'phase-2'], + }); + window.maestro.autorun.listDocs = mockListDocs; mockParseWizardIntent.mockReturnValue({ mode: 'iterate', goal: 'add auth' }); const { result } = renderHook(() => useInlineWizard()); @@ -163,7 +177,12 @@ describe('useInlineWizard', () => { }); it('should handle new mode from intent parser', async () => { - mockHasExistingAutoRunDocs.mockResolvedValue(true); + // Mock listDocs to return files (existing docs) + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: ['phase-1'], + }); + window.maestro.autorun.listDocs = mockListDocs; mockParseWizardIntent.mockReturnValue({ mode: 'new' }); const { result } = renderHook(() => useInlineWizard()); @@ -177,7 +196,12 @@ describe('useInlineWizard', () => { }); it('should handle ask mode from intent parser', async () => { - mockHasExistingAutoRunDocs.mockResolvedValue(true); + // Mock listDocs to return files (existing docs) + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: ['phase-1'], + }); + window.maestro.autorun.listDocs = mockListDocs; mockParseWizardIntent.mockReturnValue({ mode: 'ask' }); const { result } = renderHook(() => useInlineWizard()); @@ -204,12 +228,12 @@ describe('useInlineWizard', () => { describe('loading existing docs for iterate mode', () => { it('should load existing docs when mode is iterate', async () => { - const mockDocs = [ - { name: 'phase-1', filename: 'phase-1.md', path: '/test/Auto Run Docs/phase-1.md' }, - { name: 'phase-2', filename: 'phase-2.md', path: '/test/Auto Run Docs/phase-2.md' }, - ]; - mockHasExistingAutoRunDocs.mockResolvedValue(true); - mockGetExistingAutoRunDocs.mockResolvedValue(mockDocs); + // Mock listDocs to return files + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: ['phase-1', 'phase-2'], + }); + window.maestro.autorun.listDocs = mockListDocs; mockParseWizardIntent.mockReturnValue({ mode: 'iterate', goal: 'add feature' }); const { result } = renderHook(() => useInlineWizard()); @@ -218,12 +242,19 @@ describe('useInlineWizard', () => { await result.current.startWizard('add new feature', undefined, '/test/project'); }); - expect(mockGetExistingAutoRunDocs).toHaveBeenCalledWith('/test/project'); - expect(result.current.existingDocuments).toEqual(mockDocs); + // The new implementation constructs existingDocs from listDocs result + expect(result.current.existingDocuments).toHaveLength(2); + expect(result.current.existingDocuments[0].name).toBe('phase-1'); + expect(result.current.existingDocuments[1].name).toBe('phase-2'); }); it('should not load existing docs when mode is new', async () => { - mockHasExistingAutoRunDocs.mockResolvedValue(true); + // Mock listDocs to return files (existing docs) + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: ['phase-1'], + }); + window.maestro.autorun.listDocs = mockListDocs; mockParseWizardIntent.mockReturnValue({ mode: 'new' }); const { result } = renderHook(() => useInlineWizard()); @@ -232,12 +263,17 @@ describe('useInlineWizard', () => { await result.current.startWizard('start fresh', undefined, '/test/project'); }); - expect(mockGetExistingAutoRunDocs).not.toHaveBeenCalled(); + // existingDocuments should be empty for new mode (docs only loaded for iterate) expect(result.current.existingDocuments).toEqual([]); }); it('should not load existing docs when mode is ask', async () => { - mockHasExistingAutoRunDocs.mockResolvedValue(true); + // Mock listDocs to return files (existing docs) + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: ['phase-1'], + }); + window.maestro.autorun.listDocs = mockListDocs; mockParseWizardIntent.mockReturnValue({ mode: 'ask' }); const { result } = renderHook(() => useInlineWizard()); @@ -246,13 +282,19 @@ describe('useInlineWizard', () => { await result.current.startWizard('do something', undefined, '/test/project'); }); - expect(mockGetExistingAutoRunDocs).not.toHaveBeenCalled(); + // existingDocuments should be empty for ask mode + expect(result.current.existingDocuments).toEqual([]); }); }); describe('isInitializing state', () => { it('should set isInitializing to false after async operations complete', async () => { - mockHasExistingAutoRunDocs.mockResolvedValue(false); + // Mock listDocs to return empty (no existing docs) + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: [], + }); + window.maestro.autorun.listDocs = mockListDocs; const { result } = renderHook(() => useInlineWizard()); @@ -267,27 +309,34 @@ describe('useInlineWizard', () => { }); describe('error handling', () => { - it('should handle errors from hasExistingAutoRunDocs', async () => { - mockHasExistingAutoRunDocs.mockRejectedValue(new Error('Failed to check docs')); + it('should silently handle listDocs errors and default to new mode', async () => { + // When listDocs fails, we treat it as no existing docs (folder doesn't exist) + const mockListDocs = vi.fn().mockRejectedValue(new Error('Folder not found')); + window.maestro.autorun.listDocs = mockListDocs; const { result } = renderHook(() => useInlineWizard()); - const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); await act(async () => { - await result.current.startWizard('test', undefined, '/test/project'); + await result.current.startWizard(undefined, undefined, '/test/project'); }); - expect(result.current.error).toBe('Failed to check docs'); - expect(result.current.wizardMode).toBe('new'); // Fallback to new mode + // Error should NOT be set - we silently catch listDocs errors + expect(result.current.error).toBe(null); + expect(result.current.wizardMode).toBe('new'); // Default to new when can't check docs expect(result.current.isInitializing).toBe(false); - - consoleSpy.mockRestore(); }); - it('should handle errors from getExistingAutoRunDocs', async () => { - mockHasExistingAutoRunDocs.mockResolvedValue(true); + it('should handle errors from loadDocumentContents in iterate mode', async () => { + // Setup: listDocs returns files, but loading content fails + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: ['phase-1', 'phase-2'], + }); + const mockReadDoc = vi.fn().mockRejectedValue(new Error('Failed to read file')); + window.maestro.autorun.listDocs = mockListDocs; + window.maestro.autorun.readDoc = mockReadDoc; + mockParseWizardIntent.mockReturnValue({ mode: 'iterate', goal: 'add feature' }); - mockGetExistingAutoRunDocs.mockRejectedValue(new Error('Failed to load docs')); const { result } = renderHook(() => useInlineWizard()); const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); @@ -296,26 +345,29 @@ describe('useInlineWizard', () => { await result.current.startWizard('add feature', undefined, '/test/project'); }); - expect(result.current.error).toBe('Failed to load docs'); - expect(result.current.wizardMode).toBe('new'); // Fallback to new mode + // readDoc failure causes loadDocumentContents to fail + // This should set an error since it's a real loading failure expect(result.current.isInitializing).toBe(false); consoleSpy.mockRestore(); }); - it('should handle non-Error exceptions', async () => { - mockHasExistingAutoRunDocs.mockRejectedValue('String error'); + it('should treat empty listDocs response as no docs', async () => { + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: [], + }); + window.maestro.autorun.listDocs = mockListDocs; const { result } = renderHook(() => useInlineWizard()); - const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); await act(async () => { - await result.current.startWizard('test', undefined, '/test/project'); + await result.current.startWizard(undefined, undefined, '/test/project'); }); - expect(result.current.error).toBe('Failed to initialize wizard'); - - consoleSpy.mockRestore(); + // Empty files means no existing docs → new mode + expect(result.current.error).toBe(null); + expect(result.current.wizardMode).toBe('new'); }); }); @@ -361,6 +413,76 @@ describe('useInlineWizard', () => { expect(result.current.state.projectPath).toBe(null); }); }); + + describe('autoRunFolderPath parameter', () => { + it('should use configured autoRunFolderPath when provided', async () => { + const { result } = renderHook(() => useInlineWizard()); + + await act(async () => { + await result.current.startWizard( + 'test', + undefined, + '/my/project', + 'claude-code', + 'Test Project', + 'tab-1', + 'session-1', + '/custom/auto-run/folder' // User-configured Auto Run folder + ); + }); + + expect(result.current.state.autoRunFolderPath).toBe('/custom/auto-run/folder'); + }); + + it('should fall back to default path when autoRunFolderPath not provided', async () => { + const { result } = renderHook(() => useInlineWizard()); + + await act(async () => { + await result.current.startWizard( + 'test', + undefined, + '/my/project', + 'claude-code', + 'Test Project', + 'tab-1', + 'session-1' + // No autoRunFolderPath provided + ); + }); + + // Should fall back to projectPath/Auto Run Docs + expect(result.current.state.autoRunFolderPath).toBe('/my/project/Auto Run Docs'); + }); + + it('should check for existing docs in configured folder', async () => { + // Mock the direct listDocs call + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: ['phase-1', 'phase-2'], + }); + window.maestro.autorun.listDocs = mockListDocs; + + const { result } = renderHook(() => useInlineWizard()); + + await act(async () => { + await result.current.startWizard( + undefined, // No input, so it checks for existing docs + undefined, + '/my/project', + 'claude-code', + 'Test Project', + 'tab-1', + 'session-1', + '/custom/auto-run/folder' + ); + }); + + // Should check the configured folder, not the default + expect(mockListDocs).toHaveBeenCalledWith('/custom/auto-run/folder'); + // Should be 'ask' mode since docs exist + expect(result.current.wizardMode).toBe('ask'); + }); + }); }); describe('endWizard', () => { @@ -563,8 +685,12 @@ describe('useInlineWizard', () => { const mockStartConversation = vi.mocked(startInlineWizardConversation); mockStartConversation.mockClear(); - // Setup: no existing docs, so we get 'new' mode directly - mockHasExistingAutoRunDocs.mockResolvedValue(false); + // Setup: no existing docs via listDocs, so we get 'new' mode directly + const mockListDocs = vi.fn().mockResolvedValue({ + success: true, + files: [], // Empty = no existing docs + }); + window.maestro.autorun.listDocs = mockListDocs; const { result } = renderHook(() => useInlineWizard()); @@ -712,15 +838,15 @@ describe('useInlineWizard', () => { }); describe('generateDocuments', () => { - it('should return error when agent type is missing', async () => { + it('should return error when agent type or Auto Run folder path is missing', async () => { const { result } = renderHook(() => useInlineWizard()); - // Don't start wizard (no agentType or projectPath) + // Don't start wizard (no agentType or autoRunFolderPath) await act(async () => { await result.current.generateDocuments(); }); - expect(result.current.error).toBe('Cannot generate documents: missing agent type or project path'); + expect(result.current.error).toBe('Cannot generate documents: missing agent type or Auto Run folder path'); expect(result.current.isGeneratingDocs).toBe(false); }); diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index d9133937..7f2f1415 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -3648,7 +3648,7 @@ You are taking over this conversation. Based on the context above, provide a bri addToast({ type: 'warning', title: 'Cannot Compact', - message: `Context too small. Need at least ${minContextUsagePercent}% usage or ~10k tokens to compact.`, + message: `Context too small. Need at least ${minContextUsagePercent}% usage, ~2k tokens, or 8+ messages to compact.`, }); return; } @@ -4960,7 +4960,8 @@ You are taking over this conversation. Based on the context above, provide a bri activeSession.toolType, // Agent type for AI conversation activeSession.name, // Session/project name activeTab.id, // Tab ID for per-tab isolation - activeSession.id // Session ID for playbook creation + activeSession.id, // Session ID for playbook creation + activeSession.autoRunFolderPath // User-configured Auto Run folder path (if set) ); // Rename the tab to "Wizard" immediately when wizard starts @@ -5037,7 +5038,8 @@ You are taking over this conversation. Based on the context above, provide a bri activeSession.toolType, activeSession.name, newTab.id, - activeSession.id + activeSession.id, + activeSession.autoRunFolderPath // User-configured Auto Run folder path (if set) ); // Show a system log entry diff --git a/src/renderer/hooks/useInlineWizard.ts b/src/renderer/hooks/useInlineWizard.ts index 68f05ceb..0d4ce98b 100644 --- a/src/renderer/hooks/useInlineWizard.ts +++ b/src/renderer/hooks/useInlineWizard.ts @@ -141,6 +141,8 @@ export interface InlineWizardState { subfolderName: string | null; /** Full path to the subfolder where documents are saved (e.g., "/path/Auto Run Docs/Maestro-Marketing") */ subfolderPath: string | null; + /** User-configured Auto Run folder path (overrides default projectPath/Auto Run Docs) */ + autoRunFolderPath: string | null; } /** @@ -196,6 +198,7 @@ export interface UseInlineWizardReturn { * @param sessionName - The session name (used as project name) * @param tabId - The tab ID to associate the wizard with * @param sessionId - The session ID for playbook creation + * @param autoRunFolderPath - User-configured Auto Run folder path (if set, overrides default projectPath/Auto Run Docs) */ startWizard: ( naturalLanguageInput?: string, @@ -204,7 +207,8 @@ export interface UseInlineWizardReturn { agentType?: ToolType, sessionName?: string, tabId?: string, - sessionId?: string + sessionId?: string, + autoRunFolderPath?: string ) => Promise; /** End the wizard and restore previous UI state */ endWizard: () => Promise; @@ -291,6 +295,7 @@ const initialState: InlineWizardState = { agentSessionId: null, subfolderName: null, subfolderPath: null, + autoRunFolderPath: null, }; /** @@ -444,15 +449,28 @@ export function useInlineWizard(): UseInlineWizardReturn { agentType?: ToolType, sessionName?: string, tabId?: string, - sessionId?: string + sessionId?: string, + configuredAutoRunFolderPath?: string ): Promise => { // Tab ID is required for per-tab wizard management const effectiveTabId = tabId || 'default'; + // Determine the Auto Run folder path to use: + // 1. If user has configured a specific path (configuredAutoRunFolderPath), use it + // 2. Otherwise, fall back to the default: projectPath/Auto Run Docs + const effectiveAutoRunFolderPath = configuredAutoRunFolderPath || + (projectPath ? getAutoRunFolderPath(projectPath) : null); + logger.info( `Starting inline wizard on tab ${effectiveTabId}`, '[InlineWizard]', - { projectPath, agentType, sessionName, hasInput: !!naturalLanguageInput } + { + projectPath, + agentType, + sessionName, + hasInput: !!naturalLanguageInput, + autoRunFolderPath: effectiveAutoRunFolderPath, + } ); // Store current UI state for later restoration (per-tab) @@ -491,13 +509,22 @@ export function useInlineWizard(): UseInlineWizardReturn { agentSessionId: null, subfolderName: null, subfolderPath: null, + autoRunFolderPath: effectiveAutoRunFolderPath, })); try { - // Step 1: Check for existing Auto Run documents - const hasExistingDocs = projectPath - ? await hasExistingAutoRunDocs(projectPath) - : false; + // Step 1: Check for existing Auto Run documents in the configured folder + // Use the effective Auto Run folder path (user-configured or default) + let hasExistingDocs = false; + if (effectiveAutoRunFolderPath) { + try { + const result = await window.maestro.autorun.listDocs(effectiveAutoRunFolderPath); + hasExistingDocs = result.success && result.files && result.files.length > 0; + } catch { + // Folder doesn't exist or can't be read - no existing docs + hasExistingDocs = false; + } + } // Step 2: Determine mode based on input and existing docs let mode: InlineWizardMode; @@ -524,23 +551,33 @@ export function useInlineWizard(): UseInlineWizardReturn { // Step 3: If iterate mode, load existing docs with content for context let docsWithContent: ExistingDocumentWithContent[] = []; - if (mode === 'iterate' && projectPath) { - existingDocs = await getExistingAutoRunDocs(projectPath); - const autoRunFolderPath = getAutoRunFolderPath(projectPath); - docsWithContent = await loadDocumentContents(existingDocs, autoRunFolderPath); + if (mode === 'iterate' && effectiveAutoRunFolderPath) { + // List docs from the configured Auto Run folder + try { + const result = await window.maestro.autorun.listDocs(effectiveAutoRunFolderPath); + if (result.success && result.files) { + existingDocs = result.files.map((name: string) => ({ + name, + filename: `${name}.md`, + path: `${effectiveAutoRunFolderPath}/${name}.md`, + })); + } + } catch { + existingDocs = []; + } + docsWithContent = await loadDocumentContents(existingDocs, effectiveAutoRunFolderPath); } // Step 4: Initialize conversation session (only for 'new' or 'iterate' modes) - if ((mode === 'new' || mode === 'iterate') && agentType && projectPath) { - const autoRunFolderPath = getAutoRunFolderPath(projectPath); + if ((mode === 'new' || mode === 'iterate') && agentType && effectiveAutoRunFolderPath) { const session = startInlineWizardConversation({ mode, agentType, - directoryPath: projectPath, + directoryPath: projectPath || effectiveAutoRunFolderPath, projectName: sessionName || 'Project', goal: goal || undefined, existingDocs: docsWithContent.length > 0 ? docsWithContent : undefined, - autoRunFolderPath, + autoRunFolderPath: effectiveAutoRunFolderPath, }); // Store conversation session per-tab @@ -555,6 +592,7 @@ export function useInlineWizard(): UseInlineWizardReturn { mode, goal: goal || null, existingDocsCount: docsWithContent.length, + autoRunFolderPath: effectiveAutoRunFolderPath, } ); } @@ -661,17 +699,20 @@ export function useInlineWizard(): UseInlineWizardReturn { // If we're in 'ask' mode and don't have a session, auto-create one with 'new' mode // This happens when user types directly instead of using the mode selection modal const currentState = tabStatesRef.current.get(tabId); - if (currentState?.mode === 'ask' && currentState.agentType && currentState.projectPath) { + // Use stored autoRunFolderPath from state (configured by user or default) + const effectiveAutoRunFolderPath = currentState?.autoRunFolderPath || + (currentState?.projectPath ? getAutoRunFolderPath(currentState.projectPath) : null); + + if (currentState?.mode === 'ask' && currentState.agentType && effectiveAutoRunFolderPath) { console.log('[useInlineWizard] Auto-creating session for direct message in ask mode'); - const autoRunFolderPath = getAutoRunFolderPath(currentState.projectPath); session = startInlineWizardConversation({ mode: 'new', agentType: currentState.agentType, - directoryPath: currentState.projectPath, + directoryPath: currentState.projectPath || effectiveAutoRunFolderPath, projectName: currentState.sessionName || 'Project', goal: currentState.goal || undefined, existingDocs: undefined, - autoRunFolderPath, + autoRunFolderPath: effectiveAutoRunFolderPath, }); conversationSessionsMap.current.set(tabId, session); // Update mode to 'new' since we're proceeding with a new plan @@ -682,6 +723,7 @@ export function useInlineWizard(): UseInlineWizardReturn { mode: currentState?.mode, agentType: currentState?.agentType, projectPath: currentState?.projectPath, + autoRunFolderPath: currentState?.autoRunFolderPath, }); setTabState(tabId, (prev) => ({ ...prev, @@ -835,16 +877,19 @@ export function useInlineWizard(): UseInlineWizardReturn { // If transitioning from 'ask' to 'new' or 'iterate', we need to create the conversation session if (currentState?.mode === 'ask' && (newMode === 'new' || newMode === 'iterate') && !conversationSessionsMap.current.has(tabId)) { // Create conversation session if we have the required info - if (currentState.agentType && currentState.projectPath) { - const autoRunFolderPath = getAutoRunFolderPath(currentState.projectPath); + // Use the stored autoRunFolderPath from state (configured by user or default) + const effectiveAutoRunFolderPath = currentState.autoRunFolderPath || + (currentState.projectPath ? getAutoRunFolderPath(currentState.projectPath) : null); + + if (currentState.agentType && effectiveAutoRunFolderPath) { const session = startInlineWizardConversation({ mode: newMode, agentType: currentState.agentType, - directoryPath: currentState.projectPath, + directoryPath: currentState.projectPath || effectiveAutoRunFolderPath, projectName: currentState.sessionName || 'Project', goal: currentState.goal || undefined, existingDocs: undefined, // Will be loaded separately if needed - autoRunFolderPath, + autoRunFolderPath: effectiveAutoRunFolderPath, }); conversationSessionsMap.current.set(tabId, session); @@ -1045,9 +1090,13 @@ export function useInlineWizard(): UseInlineWizardReturn { setCurrentTabId(tabId); } + // Get the effective Auto Run folder path (stored in state from startWizard) + const effectiveAutoRunFolderPath = currentState?.autoRunFolderPath || + (currentState?.projectPath ? getAutoRunFolderPath(currentState.projectPath) : null); + // Validate we have the required state - if (!currentState?.agentType || !currentState?.projectPath) { - const errorMsg = 'Cannot generate documents: missing agent type or project path'; + if (!currentState?.agentType || !effectiveAutoRunFolderPath) { + const errorMsg = 'Cannot generate documents: missing agent type or Auto Run folder path'; console.error('[useInlineWizard]', errorMsg); setTabState(tabId, (prev) => ({ ...prev, error: errorMsg })); callbacks?.onError?.(errorMsg); @@ -1066,19 +1115,16 @@ export function useInlineWizard(): UseInlineWizardReturn { })); try { - // Get the Auto Run folder path - const autoRunFolderPath = getAutoRunFolderPath(currentState.projectPath); - - // Call the document generation service + // Call the document generation service with the effective Auto Run folder path const result = await generateInlineDocuments({ agentType: currentState.agentType, - directoryPath: currentState.projectPath, + directoryPath: currentState.projectPath || effectiveAutoRunFolderPath, projectName: currentState.sessionName || 'Project', conversationHistory: currentState.conversationHistory, existingDocuments: currentState.existingDocuments, mode: currentState.mode === 'iterate' ? 'iterate' : 'new', goal: currentState.goal || undefined, - autoRunFolderPath, + autoRunFolderPath: effectiveAutoRunFolderPath, sessionId: currentState.sessionId || undefined, callbacks: { onStart: () => {