From 9bb3f3b84cae15760dda898c974b6a18da64b5d3 Mon Sep 17 00:00:00 2001 From: xintaofei Date: Fri, 24 Apr 2026 22:11:53 +0800 Subject: [PATCH] fix(acp): defer auto-connect until historical session id resolves When reopening a historical conversation, the ACP auto-connect fired before the detail fetch resolved external_id, so the backend fell back to session/new and the agent lost prior context. Gate both the auto-connect effect and the focus-triggered reconnect on the session id being ready. --- .../conversations/conversation-detail-panel.tsx | 13 +++++++++++-- src/hooks/use-connection-lifecycle.ts | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/components/conversations/conversation-detail-panel.tsx b/src/components/conversations/conversation-detail-panel.tsx index b3a51b9..da571c1 100644 --- a/src/components/conversations/conversation-detail-panel.tsx +++ b/src/components/conversations/conversation-detail-panel.tsx @@ -202,8 +202,6 @@ const ConversationTabView = memo(function ConversationTabView({ const [hasSentMessage, setHasSentMessage] = useState(false) const hasPersistedConversation = dbConversationId != null - const canAutoConnect = - hasPersistedConversation || (agentsLoaded && usableAgentCount > 0) // Expose the runtime session key to the tab so the aux panel (Diff sidebar) // can look up live turns even before the DB conversation is created. @@ -266,6 +264,17 @@ const ConversationTabView = memo(function ConversationTabView({ }, [effectiveSessionStats, isActive, setSessionStats]) const externalId = detail?.summary.external_id ?? undefined + // For persisted conversations opened from the sidebar, wait until the + // session's external_id has been resolved before auto-connecting. + // Otherwise the auto-connect effect fires with sessionId=undefined and + // the backend falls back to session/new, orphaning the historical + // context. cline doesn't support session resume, so it connects + // immediately regardless. + const awaitingHistoricalSessionId = + hasPersistedConversation && selectedAgent !== "cline" && detailLoading + const canAutoConnect = + (hasPersistedConversation || (agentsLoaded && usableAgentCount > 0)) && + !awaitingHistoricalSessionId const draftStorageKey = useMemo(() => { if (dbConversationId != null) { return buildConversationDraftStorageKey(dbConversationId) diff --git a/src/hooks/use-connection-lifecycle.ts b/src/hooks/use-connection-lifecycle.ts index e2aa1db..a9e93a5 100644 --- a/src/hooks/use-connection-lifecycle.ts +++ b/src/hooks/use-connection-lifecycle.ts @@ -264,6 +264,10 @@ export function useConnectionLifecycle({ }, [removeTask, clearSelectorTask]) const handleFocus = useCallback(() => { + // Respect the caller's readiness gate — e.g. historical conversations + // set isActive=false until the session's external_id resolves, to + // avoid connecting with sessionId=undefined and orphaning context. + if (!isActive) return touchActivity(contextKey) if (!status || status === "disconnected" || status === "error") { setLastAutoConnectError(null) @@ -274,6 +278,7 @@ export function useConnectionLifecycle({ }) } }, [ + isActive, agentType, workingDir, sessionId,