fix(chat): retain clear pane identity with reopened history

This commit is contained in:
Merge Sim
2026-09-06 18:39:23 -07:00
parent fc6a142185
commit 860ad56cfd
2 changed files with 63 additions and 16 deletions
@@ -14,9 +14,13 @@ import {
beforeEach(resetWebSessionTabsSyncTestState)
describe('clear pane identity', () => {
it.each(['agent-session', 'terminal'] as const)(
'replaces a %s pane in its existing local position',
(contentType) => {
it.each(
(['agent-session', 'terminal'] as const).flatMap((contentType) =>
(['absent', 'before', 'after'] as const).map((history) => ({ contentType, history }))
)
)(
'replaces a $contentType pane with reopened history $history the replacement',
({ contentType, history }) => {
const state = makeState({
unifiedTabsByWorktree: {
[WT]: [
@@ -83,13 +87,30 @@ describe('clear pane identity', () => {
],
{ activeTabId: 'agent-session:new-session', activeTabType: 'agent-session' }
)
if (history !== 'absent') {
const oldTab = {
type: 'agent-session' as const,
id: 'agent-session:old-session',
sessionId: 'old-session',
agent: 'codex' as const,
title: 'History',
isActive: false
}
if (history === 'before') {
snapshot.tabs.unshift(oldTab)
} else {
snapshot.tabs.push(oldTab)
}
}
const next = applyWebSessionTabsSnapshot(state, snapshot, ENV, NOW, {
contentScope: 'agent-session',
preserveLocalLayout: true,
terminalPtyMode: 'local'
})
expect(next.unifiedTabsByWorktree?.[WT]).toHaveLength(1)
expect(next.unifiedTabsByWorktree?.[WT]?.[0]).toMatchObject({
expect(next.unifiedTabsByWorktree?.[WT]).toHaveLength(history === 'absent' ? 1 : 2)
expect(
next.unifiedTabsByWorktree?.[WT]?.find((tab) => tab.entityId === 'new-session')
).toMatchObject({
id: 'local-pane',
entityId: 'new-session',
contentType: 'agent-session',
@@ -97,10 +118,19 @@ describe('clear pane identity', () => {
isPinned: true
})
expect(next.groupsByWorktree?.[WT]?.[0]).toMatchObject({
tabOrder: ['local-pane'],
activeTabId: 'local-pane'
})
expect(next.groupsByWorktree?.[WT]?.[0]?.tabOrder[0]).toBe('local-pane')
expect(next.activeTabIdByWorktree?.[WT] ?? state.activeTabIdByWorktree[WT]).toBe('local-pane')
expect(next.tabsByWorktree?.[WT] ?? []).toEqual([])
const repeated = applyWebSessionTabsSnapshot({ ...state, ...next }, snapshot, ENV, NOW + 1, {
contentScope: 'agent-session',
preserveLocalLayout: true,
terminalPtyMode: 'local'
})
expect(repeated.unifiedTabsByWorktree?.[WT] ?? next.unifiedTabsByWorktree?.[WT]).toEqual(
next.unifiedTabsByWorktree?.[WT]
)
}
)
it('gives reopened history its own tab when clear retained its former local ID', () => {
@@ -60,23 +60,40 @@ export function buildMirroredAgentTabs(
now: number
): MirroredAgentTab[] {
const agentTabs = snapshot.tabs.filter(isAgentSessionTab)
const publishedSessions = new Set(agentTabs.map((tab) => tab.sessionId))
const occupiedIds = new Set(currentUnifiedTabs.map((tab) => tab.id))
const assignedIds = new Set<string>()
return agentTabs.map((tab, index) => {
const replacementTabs = new Map<string, Tab>()
const replacementIds = new Set<string>()
for (const tab of agentTabs) {
if (!tab.replacesSessionId) {
continue
}
const existing =
currentUnifiedTabs.find(
(candidate) =>
candidate.contentType === 'agent-session' && candidate.entityId === tab.sessionId
) ??
(tab.replacesSessionId && !publishedSessions.has(tab.replacesSessionId)
? currentUnifiedTabs.find(
(candidate) =>
candidate.structuredSessionId === tab.replacesSessionId ||
(candidate.contentType === 'agent-session' &&
candidate.entityId === tab.replacesSessionId)
)
: undefined)
currentUnifiedTabs.find(
(candidate) =>
!replacementIds.has(candidate.id) &&
(candidate.structuredSessionId === tab.replacesSessionId ||
(candidate.contentType === 'agent-session' &&
candidate.entityId === tab.replacesSessionId))
)
if (existing) {
replacementTabs.set(tab.sessionId, existing)
replacementIds.add(existing.id)
}
}
return agentTabs.map((tab, index) => {
const existing =
replacementTabs.get(tab.sessionId) ??
currentUnifiedTabs.find(
(candidate) =>
!replacementIds.has(candidate.id) &&
candidate.contentType === 'agent-session' &&
candidate.entityId === tab.sessionId
)
const baseId = structuredAgentSessionTabId(tab.sessionId)
let localId = existing?.id ?? baseId
if (!existing || assignedIds.has(localId)) {