From 58f05d2dbf24bb53e188a10450e5572feba53eb5 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Wed, 11 Mar 2026 10:22:28 +1100 Subject: [PATCH 1/3] fix(mark): restore MCP tools and active state when resuming project sessions When a user replies to a completed project note session, three things were broken: (1) the MCP server wasn't started so tools like add_project_repo and start_repo_session were unavailable, (2) the note row didn't show a spinner, and (3) the sidebar didn't indicate the project was active. Root cause: resume_session hardcoded mcp_project_id to None and the session-status-changed event for "running" wasn't handled by ProjectSection. Now resume_session looks up the project via the linked project note, starts the MCP server when appropriate, and the frontend re-adds resumed sessions to active tracking. Co-Authored-By: Claude Opus 4.6 --- apps/mark/src-tauri/src/session_commands.rs | 26 ++++++++-- .../features/projects/ProjectSection.svelte | 48 +++++++++++++------ 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/apps/mark/src-tauri/src/session_commands.rs b/apps/mark/src-tauri/src/session_commands.rs index 351cb175..700f4f2f 100644 --- a/apps/mark/src-tauri/src/session_commands.rs +++ b/apps/mark/src-tauri/src/session_commands.rs @@ -183,6 +183,8 @@ pub fn start_session( 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 +203,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 +225,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 +241,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; }); From cf432a44ed613552d5ac9ebbd01e85915a6fc911 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Wed, 11 Mar 2026 12:48:36 +1100 Subject: [PATCH 2/3] fix(acp-client): pass MCP servers to agent when resuming sessions via LoadSessionRequest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LoadSessionRequest supports an mcp_servers field, but setup_acp_session was not passing it — only NewSessionRequest included MCP servers. This meant resumed project-note sessions lost access to tools like add_project_repo and start_repo_session even though the MCP server was running locally. Co-Authored-By: Claude Opus 4.6 --- crates/acp-client/src/driver.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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:?}"))?; From d9025325eb51b0d4dac0efab21bf0749212f9525 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Wed, 11 Mar 2026 13:06:35 +1100 Subject: [PATCH 3/3] fix(mark): allow clippy::too_many_arguments on resume_session Co-Authored-By: Claude Opus 4.6 --- apps/mark/src-tauri/src/session_commands.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/mark/src-tauri/src/session_commands.rs b/apps/mark/src-tauri/src/session_commands.rs index 700f4f2f..7e5789e9 100644 --- a/apps/mark/src-tauri/src/session_commands.rs +++ b/apps/mark/src-tauri/src/session_commands.rs @@ -180,6 +180,7 @@ 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>,