diff --git a/apps/mark/src-tauri/src/session_commands.rs b/apps/mark/src-tauri/src/session_commands.rs index 351cb175..7e5789e9 100644 --- a/apps/mark/src-tauri/src/session_commands.rs +++ b/apps/mark/src-tauri/src/session_commands.rs @@ -180,9 +180,12 @@ pub fn start_session( /// The working directory is read from the session record (set when the /// session was first created), so the frontend doesn't need to pass it. #[tauri::command] +#[allow(clippy::too_many_arguments)] pub fn resume_session( store: tauri::State<'_, Mutex>>>, registry: tauri::State<'_, Arc>, + action_executor: tauri::State<'_, Arc>, + action_registry: tauri::State<'_, Arc>, app_handle: tauri::AppHandle, session_id: String, prompt: String, @@ -201,6 +204,14 @@ pub fn resume_session( let agent_session_id = session.agent_id.clone(); let working_dir = PathBuf::from(&session.working_dir); + // Check if this session is linked to a project note — if so, we need + // to start the MCP server so the agent has access to project tools. + let mcp_project_id = store + .get_project_note_by_session(&session_id) + .ok() + .flatten() + .map(|note| note.project_id); + let transitioned = store .transition_to_running(&session_id) .map_err(|e| e.to_string())?; @@ -215,7 +226,7 @@ pub fn resume_session( status: "running".to_string(), error_message: None, branch_id: None, - project_id: None, + project_id: mcp_project_id.clone(), session_type: None, is_auto_review: false, }, @@ -231,9 +242,17 @@ pub fn resume_session( provider, workspace_name: None, extra_env: vec![], - mcp_project_id: None, - action_executor: None, - action_registry: None, + mcp_project_id: mcp_project_id.clone(), + action_executor: if mcp_project_id.is_some() { + Some(Arc::clone(&action_executor)) + } else { + None + }, + action_registry: if mcp_project_id.is_some() { + Some(Arc::clone(&action_registry)) + } else { + None + }, remote_working_dir: None, image_ids: image_ids.unwrap_or_default(), }, diff --git a/apps/mark/src/lib/features/projects/ProjectSection.svelte b/apps/mark/src/lib/features/projects/ProjectSection.svelte index 566f38af..ac157bb2 100644 --- a/apps/mark/src/lib/features/projects/ProjectSection.svelte +++ b/apps/mark/src/lib/features/projects/ProjectSection.svelte @@ -396,24 +396,42 @@ loadProjectNotes(); let unlistenSession: (() => void) | undefined; - listen<{ sessionId: string; status: string }>('session-status-changed', (event) => { - const { sessionId, status } = event.payload; - const isTracked = activeSessionIds.has(sessionId); - // Also reload if this session belongs to a known project note (handles - // sessions that were already running when the component mounted). - const isKnownNoteSession = projectNotes.some((n) => n.sessionId === sessionId); - if (isTracked || isKnownNoteSession) { - if (status === 'completed' || status === 'error' || status === 'cancelled') { - if (isTracked) { - const next = new Set(activeSessionIds); - next.delete(sessionId); - activeSessionIds = next; + listen<{ sessionId: string; status: string; projectId?: string }>( + 'session-status-changed', + (event) => { + const { sessionId, status, projectId } = event.payload; + const isTracked = activeSessionIds.has(sessionId); + // Also reload if this session belongs to a known project note (handles + // sessions that were already running when the component mounted). + const isKnownNoteSession = projectNotes.some((n) => n.sessionId === sessionId); + + // Handle resumed sessions: when a project note session is resumed, + // the backend emits a "running" event with the projectId. Re-add it + // to active tracking so the row spinner and sidebar spinner appear. + if ( + status === 'running' && + !isTracked && + isKnownNoteSession && + (projectId === project.id || !projectId) + ) { + activeSessionIds = new Set([...activeSessionIds, sessionId]); + sessionRegistry.register(sessionId, project.id, 'note'); + projectStateStore.addRunningSession(project.id, sessionId); + } + + if (isTracked || isKnownNoteSession) { + if (status === 'completed' || status === 'error' || status === 'cancelled') { + if (isTracked) { + const next = new Set(activeSessionIds); + next.delete(sessionId); + activeSessionIds = next; + } + // Refresh notes after session completes + loadProjectNotes(); } - // Refresh notes after session completes - loadProjectNotes(); } } - }).then((unlisten) => { + ).then((unlisten) => { unlistenSession = unlisten; }); diff --git a/crates/acp-client/src/driver.rs b/crates/acp-client/src/driver.rs index 17aac637..d45ca20d 100644 --- a/crates/acp-client/src/driver.rs +++ b/crates/acp-client/src/driver.rs @@ -178,7 +178,7 @@ impl AcpDriver { self } - /// Set MCP servers to inject into the session via `NewSessionRequest`. + /// Set MCP servers to inject into the session via `NewSessionRequest` or `LoadSessionRequest`. pub fn with_mcp_servers(mut self, servers: Vec) -> Self { self.mcp_servers = servers; self @@ -758,10 +758,10 @@ async fn setup_acp_session( ); connection - .load_session(LoadSessionRequest::new( - existing_id.to_string(), - working_dir.to_path_buf(), - )) + .load_session( + LoadSessionRequest::new(existing_id.to_string(), working_dir.to_path_buf()) + .mcp_servers(mcp_servers.to_vec()), + ) .await .map_err(|e| format!("Failed to load ACP session: {e:?}"))?;