diff --git a/src/renderer/src/runtime/local-structured-session-tabs-sync.test.ts b/src/renderer/src/runtime/local-structured-session-tabs-sync.test.ts new file mode 100644 index 00000000000..b9a55546c6e --- /dev/null +++ b/src/renderer/src/runtime/local-structured-session-tabs-sync.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from 'vitest' +import type { RuntimeMobileSessionTabsResult } from '../../../shared/runtime-types' +import { projectLocalStructuredSessionTabs } from './local-structured-session-tabs-sync' + +describe('local structured session tab projection', () => { + it('drops terminal topology while retaining structured tabs', () => { + const snapshot = { + worktree: 'workspace-1', + publicationEpoch: 'epoch-1', + snapshotVersion: 1, + activeGroupId: 'structured-group', + activeTabId: 'agent-session:codex-1', + activeTabType: 'agent-session', + tabGroups: [ + { + id: 'terminal-group', + activeTabId: 'terminal-1', + tabOrder: ['terminal-1'] + }, + { + id: 'structured-group', + activeTabId: 'agent-session:codex-1', + tabOrder: ['agent-session:codex-1'] + } + ], + tabGroupLayout: { + type: 'split', + direction: 'horizontal', + first: { type: 'leaf', groupId: 'terminal-group' }, + second: { type: 'leaf', groupId: 'structured-group' } + }, + tabs: [ + { + type: 'terminal', + id: 'terminal-1', + parentTabId: 'terminal-1', + leafId: 'leaf-1', + title: 'Terminal', + status: 'ready', + terminal: 'term-1', + ptyId: 'pty-1', + isActive: false + }, + { + type: 'agent-session', + id: 'agent-session:codex-1', + title: 'Codex Chat', + sessionId: 'codex-1', + agent: 'codex', + isActive: true + } + ] + } satisfies RuntimeMobileSessionTabsResult + + expect(projectLocalStructuredSessionTabs(snapshot)).toMatchObject({ + tabGroups: undefined, + tabGroupLayout: undefined, + tabs: [expect.objectContaining({ type: 'agent-session', agent: 'codex' })] + }) + }) +}) diff --git a/src/renderer/src/runtime/local-structured-session-tabs-sync.ts b/src/renderer/src/runtime/local-structured-session-tabs-sync.ts index 43701c512a7..458abfed1b2 100644 --- a/src/renderer/src/runtime/local-structured-session-tabs-sync.ts +++ b/src/renderer/src/runtime/local-structured-session-tabs-sync.ts @@ -11,20 +11,18 @@ type SessionTabsEvent = | { type: 'snapshots'; snapshots: RuntimeMobileSessionTabsResult[] } | { type: 'end' } -function structuredOnly(snapshot: RuntimeMobileSessionTabsResult): RuntimeMobileSessionTabsResult { +export function projectLocalStructuredSessionTabs( + snapshot: RuntimeMobileSessionTabsResult +): RuntimeMobileSessionTabsResult { const structuredIds = new Set( snapshot.tabs.filter((tab) => tab.type === 'agent-session').map((tab) => tab.id) ) return { ...snapshot, tabs: snapshot.tabs.filter((tab) => structuredIds.has(tab.id)), - tabGroups: snapshot.tabGroups?.map((group) => ({ - ...group, - tabOrder: group.tabOrder.filter((id) => structuredIds.has(id)), - activeTabId: - group.activeTabId && structuredIds.has(group.activeTabId) ? group.activeTabId : null, - recentTabIds: group.recentTabIds?.filter((id) => structuredIds.has(id)) - })) + // Why: replaying local terminal topology under the structured owner clones the workspace layout. + tabGroups: undefined, + tabGroupLayout: undefined } } @@ -34,7 +32,7 @@ function applySnapshots(snapshots: readonly RuntimeMobileSessionTabsResult[]): v for (const snapshot of snapshots) { const patch = applyWebSessionTabsSnapshot( next, - structuredOnly(snapshot), + projectLocalStructuredSessionTabs(snapshot), LOCAL_STRUCTURED_SESSION_OWNER ) next = patch === next ? next : ({ ...next, ...patch } as typeof state)