diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index 9159a9e0..7ec3db74 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -54,6 +54,7 @@ import { MCP_OAUTH_CALLBACK_PATH } from '@/components/sections/mcp/mcpOAuth'; import { lazyWithChunkRecovery } from '@/lib/chunkLoadRecovery'; import { useI18n } from '@/lib/i18n'; import { applyMobileKeyboardMode } from '@/lib/mobileKeyboardMode'; +import { isEmbeddedSessionChat } from '@/components/layout/contextPanelEmbeddedChat'; import { SyncAppEffects } from '@/apps/AppEffects'; import { resetAppForRuntimeEndpointChange } from '@/apps/runtimeEndpointReset'; import { useAppFontEffects } from '@/apps/useAppFontEffects'; @@ -117,15 +118,11 @@ const normalizeEmbeddedDirectory = (value: string | null | undefined): string => }; const readEmbeddedSessionChatConfig = (): EmbeddedSessionChatConfig | null => { - if (typeof window === 'undefined') { + if (typeof window === 'undefined' || !isEmbeddedSessionChat()) { return null; } const params = new URLSearchParams(window.location.search); - if (params.get('ocPanel') !== 'session-chat') { - return null; - } - const sessionIdRaw = params.get('sessionId'); const sessionId = typeof sessionIdRaw === 'string' ? sessionIdRaw.trim() : ''; if (!sessionId) { @@ -171,7 +168,12 @@ const EmbeddedSessionChatContent: React.FC<{ if (expectedDirectory && activeDirectory !== expectedDirectory) return; const bootstrapKey = `${expectedDirectory}\n${embeddedSessionChat.sessionId}`; - if (bootstrapKeyRef.current === bootstrapKey && currentSessionId === embeddedSessionChat.sessionId) { + // Skip if this session was already bootstrapped and a session is still + // active — allows in-place navigation (e.g. "Open subtask") to change + // currentSessionId without this effect forcing it back. Only re-bootstrap + // when currentSessionId was cleared (store init, draft, delete/archive, + // runtime-switch remount). + if (bootstrapKeyRef.current === bootstrapKey && currentSessionId) { return; } diff --git a/packages/ui/src/components/chat/ChatContainer.tsx b/packages/ui/src/components/chat/ChatContainer.tsx index 6ab7e364..b8babe28 100644 --- a/packages/ui/src/components/chat/ChatContainer.tsx +++ b/packages/ui/src/components/chat/ChatContainer.tsx @@ -49,6 +49,7 @@ import { usePlanDetection } from '@/hooks/usePlanDetection'; import { useI18n } from '@/lib/i18n'; import { isMobileSurfaceRuntime } from '@/lib/runtimeSurface'; import { isVSCodeRuntime } from '@/lib/desktop'; +import { getEmbeddedSessionChatOriginSessionId } from '@/components/layout/contextPanelEmbeddedChat'; const EMPTY_MESSAGES: Array<{ info: Message; parts: Part[] }> = []; const IDLE_SESSION_STATUS = { type: 'idle' as const }; @@ -564,13 +565,22 @@ export const ChatContainer: React.FC = ({ autoOpenDraft = tr const parentSession = useParentSession(currentSessionId, effectiveSessionDirectory); + // In the embedded session-chat iframe, hide "Return to parent" when + // viewing the panel's anchor session (the one recorded in the URL). Going + // up from the anchor would show the primary session that's already in the + // main chat. Drilling into a deeper subtask (currentSessionId ≠ anchor) + // re-enables the button to navigate back to the embedded session. + const embeddedPanelAnchorSessionId = getEmbeddedSessionChatOriginSessionId(); + const hideReturnToParent = + embeddedPanelAnchorSessionId !== null && currentSessionId === embeddedPanelAnchorSessionId; + const handleReturnToParentSession = React.useCallback(() => { if (!parentSession) return; const parentDirectory = (parentSession as Session & { directory?: string | null }).directory ?? null; setCurrentSession(parentSession.id, parentDirectory); }, [parentSession, setCurrentSession]); - const returnToParentButton = parentSession ? ( + const returnToParentButton = parentSession && !hideReturnToParent ? (