From 8baa04b5fc12bef1e867d4f477131b2f996b3515 Mon Sep 17 00:00:00 2001 From: GCWing Date: Sat, 28 Mar 2026 21:50:24 +0800 Subject: [PATCH] fix(session): async metadata resolve and workspace session matching - Replace block_on in build_session_metadata with async resolve_workspace_session_identity - sessionBelongsToWorkspaceNavRow: use isSamePath with normalized remote paths - FlowChatManager: relax path filter and preferred-mode early return for session restore - AppLayout: seed initial session when no history regardless of activeSessionId --- .../core/src/agentic/persistence/manager.rs | 30 ++++++++++--------- src/web-ui/src/app/layout/AppLayout.tsx | 2 +- .../src/flow_chat/services/FlowChatManager.ts | 7 ----- .../src/flow_chat/utils/sessionOrdering.ts | 16 +++++----- 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/crates/core/src/agentic/persistence/manager.rs b/src/crates/core/src/agentic/persistence/manager.rs index 0faec0f4..adbad0b2 100644 --- a/src/crates/core/src/agentic/persistence/manager.rs +++ b/src/crates/core/src/agentic/persistence/manager.rs @@ -564,7 +564,7 @@ impl PersistenceManager { } } - fn build_session_metadata( + async fn build_session_metadata( &self, workspace_path: &Path, session: &Session, @@ -581,17 +581,18 @@ impl PersistenceManager { .or_else(|| existing.map(|value| value.model_name.clone())) .unwrap_or_else(|| "default".to_string()); - let resolved_identity = session - .config - .workspace_path - .as_deref() - .and_then(|workspace_root| { - futures::executor::block_on(resolve_workspace_session_identity( - workspace_root, - session.config.remote_connection_id.as_deref(), - session.config.remote_ssh_host.as_deref(), - )) - }); + let resolved_identity = if let Some(workspace_root) = + session.config.workspace_path.as_deref() + { + resolve_workspace_session_identity( + workspace_root, + session.config.remote_connection_id.as_deref(), + session.config.remote_ssh_host.as_deref(), + ) + .await + } else { + None + }; let workspace_root = resolved_identity .as_ref() @@ -1513,8 +1514,9 @@ impl PersistenceManager { let existing_metadata = self .load_session_metadata(workspace_path, &session.session_id) .await?; - let metadata = - self.build_session_metadata(workspace_path, session, existing_metadata.as_ref()); + let metadata = self + .build_session_metadata(workspace_path, session, existing_metadata.as_ref()) + .await; self.save_session_metadata(workspace_path, &metadata) .await?; diff --git a/src/web-ui/src/app/layout/AppLayout.tsx b/src/web-ui/src/app/layout/AppLayout.tsx index 056de355..e2da431d 100644 --- a/src/web-ui/src/app/layout/AppLayout.tsx +++ b/src/web-ui/src/app/layout/AppLayout.tsx @@ -189,7 +189,7 @@ const AppLayout: React.FC = ({ className = '' }) => { let sessionId: string | undefined; const { flowChatStore } = await import('@/flow_chat/store/FlowChatStore'); - if (!hasHistoricalSessions || !flowChatStore.getState().activeSessionId) { + if (!hasHistoricalSessions) { const initialSessionMode = currentWorkspace.workspaceKind === WorkspaceKind.Assistant ? 'Claw' diff --git a/src/web-ui/src/flow_chat/services/FlowChatManager.ts b/src/web-ui/src/flow_chat/services/FlowChatManager.ts index b49ceaa1..2503ff20 100644 --- a/src/web-ui/src/flow_chat/services/FlowChatManager.ts +++ b/src/web-ui/src/flow_chat/services/FlowChatManager.ts @@ -95,7 +95,6 @@ export class FlowChatManager { remoteSshHost?: string; }) => { const sp = session.workspacePath || workspacePath; - if (sp !== workspacePath) return false; return sessionBelongsToWorkspaceNavRow( { workspacePath: sp, @@ -128,12 +127,6 @@ export class FlowChatManager { return hasHistoricalSessions; } - // If no session matches preferred mode, keep activeSessionId unset for caller to create one. - if (preferredMode && latestSession.mode !== preferredMode) { - this.context.currentWorkspacePath = workspacePath; - return hasHistoricalSessions; - } - if (latestSession.isHistorical) { await this.context.flowChatStore.loadSessionHistory( latestSession.sessionId, diff --git a/src/web-ui/src/flow_chat/utils/sessionOrdering.ts b/src/web-ui/src/flow_chat/utils/sessionOrdering.ts index 191fb93b..af089941 100644 --- a/src/web-ui/src/flow_chat/utils/sessionOrdering.ts +++ b/src/web-ui/src/flow_chat/utils/sessionOrdering.ts @@ -1,5 +1,5 @@ import type { Session } from '../types/flow-chat'; -import { normalizeRemoteWorkspacePath } from '@/shared/utils/pathUtils'; +import { isSamePath, normalizeRemoteWorkspacePath } from '@/shared/utils/pathUtils'; /** Extract `host` from our saved form `ssh-{user}@{host}:{port}` (used when metadata omits `remoteSshHost`). */ function hostFromSshConnectionId(connectionId: string): string | null { @@ -29,8 +29,10 @@ export function sessionBelongsToWorkspaceNavRow( remoteConnectionId?: string | null, remoteSshHost?: string | null ): boolean { - const wp = normalizeRemoteWorkspacePath(workspacePath); - const sp = normalizeRemoteWorkspacePath(session.workspacePath || workspacePath); + const sessionRoot = session.workspacePath || workspacePath; + const pathsMatch = + isSamePath(sessionRoot, workspacePath) || + normalizeRemoteWorkspacePath(sessionRoot) === normalizeRemoteWorkspacePath(workspacePath); const wsConn = remoteConnectionId?.trim() ?? ''; const sessConn = session.remoteConnectionId?.trim() ?? ''; @@ -41,18 +43,18 @@ export function sessionBelongsToWorkspaceNavRow( if (wsHostEff.length > 0) { // Host match alone is insufficient (same server, different remote folders). - if (sessHost === wsHostEff && sp === wp) { + if (sessHost === wsHostEff && pathsMatch) { return true; } - if (sessConnHost === wsHostEff && sp === wp) { + if (sessConnHost === wsHostEff && pathsMatch) { return true; } if (sessConnHost && wsConnHost && sessConnHost === wsConnHost) { - return sp === wp; + return pathsMatch; } } - if (sp !== wp) return false; + if (!pathsMatch) return false; if (wsConn.length > 0 || sessConn.length > 0) { return sessConn === wsConn;