diff --git a/public/app.js b/public/app.js index da31d83..cfaef13 100644 --- a/public/app.js +++ b/public/app.js @@ -67,7 +67,7 @@ const SESSION_CACHE_MAX_WEIGHT = 1_500_000; const SIDEBAR_SWIPE_TRIGGER = 72; const SIDEBAR_SWIPE_MAX_VERTICAL_DRIFT = 42; - const OLD_SESSION_COLLAPSE_VISIBLE_LIMIT = 5; + const OLD_SESSION_GROUP_INITIAL_VISIBLE = 3; const OLD_SESSION_COLLAPSE_DAYS = 7; const OLD_SESSION_COLLAPSE_MS = OLD_SESSION_COLLAPSE_DAYS * 24 * 60 * 60 * 1000; @@ -194,6 +194,7 @@ let codexAppApprovalModal = null; let pendingNewSessionRequest = null; let pendingSessionSwitchRequest = null; + let sessionSwitchRequestSeq = 0; let skipDeleteConfirm = localStorage.getItem('cc-web-skip-delete-confirm') === '1'; let pendingInitialSessionLoad = false; let initialSessionListHandled = false; @@ -1630,6 +1631,44 @@ } } + function mergeSessionListSnapshot(snapshot) { + if (!snapshot?.sessionId) return; + const nextMeta = { + id: snapshot.sessionId, + sessionId: snapshot.sessionId, + cwd: snapshot.cwd || '', + projectName: snapshot.cwd ? getPathLeaf(snapshot.cwd) : snapshot.projectName || '', + title: snapshot.title || '新会话', + agent: normalizeAgent(snapshot.agent), + updated: snapshot.updated || new Date().toISOString(), + pinnedAt: snapshot.pinnedAt || null, + hasUnread: !!snapshot.hasUnread, + isRunning: !!snapshot.isRunning, + waitingOnChildren: !!snapshot.waitingOnChildren, + pendingReplyCount: Number(snapshot.pendingReplyCount || 0), + readyReplyCount: Number(snapshot.readyReplyCount || 0), + waitingReplyCount: Number(snapshot.waitingReplyCount || 0), + failedReplyCount: Number(snapshot.failedReplyCount || 0), + oversized: !!snapshot.oversized, + fileBytes: Number(snapshot.fileBytes || 0), + }; + let found = false; + sessions = sessions.map((session) => { + if (session.id !== snapshot.sessionId) return session; + found = true; + return { + ...session, + ...nextMeta, + cwd: nextMeta.cwd || session.cwd || '', + projectName: nextMeta.cwd ? getPathLeaf(nextMeta.cwd) : session.projectName || nextMeta.projectName || '', + title: nextMeta.title || session.title, + }; + }); + if (!found) { + sessions = [nextMeta, ...sessions].sort(compareSessionUpdatedDesc); + } + } + function getSessionCacheDisposition(sessionId) { const entry = sessionCache.get(sessionId); const meta = getSessionMeta(sessionId); @@ -3009,30 +3048,36 @@ return `${normalizeAgent(currentAgent)}:ungrouped`; } - function splitCollapsedOldSessions(regularSessions, pinnedCount, getCollapseKey = () => '') { - const totalCount = pinnedCount + regularSessions.length; - if (totalCount <= OLD_SESSION_COLLAPSE_VISIBLE_LIMIT) { - return { visibleRegularSessions: regularSessions, hiddenOldSessions: [] }; - } + function expandOldSessionGroup(collapseKey) { + if (!collapseKey) return; + expandedOldSessionGroups.add(collapseKey); + } + function shouldAlwaysShowOldSession(session) { + return session.id === currentSessionId || session.isRunning || session.hasUnread || session.waitingOnChildren; + } + + function splitCollapsedOldSessions(sessionItems, collapseKey) { + const isExpanded = collapseKey && expandedOldSessionGroups.has(collapseKey); const nowMs = Date.now(); - const visibleRegularLimit = Math.max(0, OLD_SESSION_COLLAPSE_VISIBLE_LIMIT - pinnedCount); - const visibleRegularSessions = []; + const visibleSessions = []; const hiddenOldSessions = []; - regularSessions.forEach((session, index) => { - const collapseKey = getCollapseKey(session); - const isExpanded = collapseKey && expandedOldSessionGroups.has(collapseKey); - const shouldKeepVisible = session.id === currentSessionId || session.isRunning || session.hasUnread || session.waitingOnChildren; - const canCollapse = !isExpanded && index >= visibleRegularLimit && isOlderThanOldSessionWindow(session, nowMs); - if (canCollapse && !shouldKeepVisible) { + sessionItems.forEach((session) => { + const shouldHideOldSession = ( + !isExpanded + && !shouldAlwaysShowOldSession(session) + && isOlderThanOldSessionWindow(session, nowMs) + && visibleSessions.length >= OLD_SESSION_GROUP_INITIAL_VISIBLE + ); + if (shouldHideOldSession) { hiddenOldSessions.push(session); } else { - visibleRegularSessions.push(session); + visibleSessions.push(session); } }); - return { visibleRegularSessions, hiddenOldSessions }; + return { visibleSessions, hiddenOldSessions }; } function createOldSessionLoadMoreButton(hiddenCount, collapseKey, projectName = '') { @@ -3042,10 +3087,10 @@ button.setAttribute('aria-label', `加载更多${projectName ? ` ${projectName}` : ''} ${hiddenCount} 条 7 天前会话`); button.innerHTML = ` 加载更多 - ${hiddenCount} 条 7 天前会话 + 还有 ${hiddenCount} 条 `; button.addEventListener('click', () => { - if (collapseKey) expandedOldSessionGroups.add(collapseKey); + expandOldSessionGroup(collapseKey); renderSessionList(); }); return button; @@ -3393,7 +3438,8 @@ function setSessionLoading(sessionId, options = {}) { const loading = !!sessionId; const blocking = options.blocking !== false; - activeSessionLoad = loading ? { sessionId, blocking, snapshot: null } : null; + const requestId = loading ? (options.requestId || createSessionSwitchRequestId(sessionId)) : ''; + activeSessionLoad = loading ? { sessionId, blocking, snapshot: null, requestId } : null; const showOverlay = !!(loading && blocking); document.body.classList.toggle('session-loading-active', showOverlay); sessionLoadingOverlay.hidden = !showOverlay; @@ -3413,6 +3459,11 @@ setSessionLoading(null, { blocking: false }); } + function createSessionSwitchRequestId(sessionId) { + sessionSwitchRequestSeq += 1; + return `session-load-${Date.now()}-${sessionSwitchRequestSeq}-${String(sessionId || '').slice(0, 8)}`; + } + function isBlockingSessionLoad(sessionId) { return !!(activeSessionLoad && activeSessionLoad.blocking && @@ -3454,6 +3505,7 @@ sessionId, blocking: options.blocking !== false, label: options.label || '', + requestId: options.requestId || activeSessionLoad?.requestId || createSessionSwitchRequestId(sessionId), }; if (ws && ws.readyState === 1 && wsAuthenticated) { flushPendingSessionSwitch(); @@ -3471,9 +3523,10 @@ setSessionLoading(request.sessionId, { blocking: request.blocking, label: request.label || undefined, + requestId: request.requestId, }); } - ws.send(JSON.stringify({ type: 'load_session', sessionId: request.sessionId })); + ws.send(JSON.stringify({ type: 'load_session', sessionId: request.sessionId, requestId: request.requestId })); } function showCachedSession(sessionId) { @@ -3971,6 +4024,7 @@ sessionId: activeSessionLoad.sessionId, blocking: activeSessionLoad.blocking, label: sessionLoadingLabel?.textContent || '', + requestId: activeSessionLoad.requestId || createSessionSwitchRequestId(activeSessionLoad.sessionId), }; } clearSessionLoading(); @@ -4068,28 +4122,29 @@ break; case 'session_info': - if (pendingNewSessionRequest) pendingNewSessionRequest = null; const snapshot = normalizeSessionSnapshot(msg); - sessions = sessions.map((session) => ( - session.id === snapshot.sessionId - ? { - ...session, - cwd: snapshot.cwd || session.cwd || '', - projectName: snapshot.cwd ? getPathLeaf(snapshot.cwd) : session.projectName || '', - title: snapshot.title || session.title, - pinnedAt: snapshot.pinnedAt || session.pinnedAt || null, - isRunning: snapshot.isRunning, - waitingOnChildren: snapshot.waitingOnChildren, - pendingReplyCount: snapshot.pendingReplyCount, - readyReplyCount: snapshot.readyReplyCount, - waitingReplyCount: snapshot.waitingReplyCount, - failedReplyCount: snapshot.failedReplyCount, - } - : session - )); - if (activeSessionLoad?.sessionId === msg.sessionId) { + const activeLoad = activeSessionLoad; + const pendingNewSession = pendingNewSessionRequest; + const messageRequestId = String(msg.requestId || ''); + const matchesActiveLoad = !!(activeLoad?.sessionId === msg.sessionId + && (!activeLoad.requestId || activeLoad.requestId === messageRequestId)); + const matchesPendingNewSession = !!(pendingNewSession + && (!pendingNewSession.requestId || pendingNewSession.requestId === messageRequestId)); + const canSwitchToSessionInfo = matchesActiveLoad + || matchesPendingNewSession + || msg.sessionId === currentSessionId + || (!currentSessionId && !activeLoad && !pendingNewSession) + || (!messageRequestId && !activeLoad && !pendingNewSession); + mergeSessionListSnapshot(snapshot); + if (matchesActiveLoad) { activeSessionLoad.snapshot = snapshot; } + if (!canSwitchToSessionInfo) { + if (!msg.historyPending) cacheSessionSnapshot(snapshot); + renderSessionList(); + break; + } + if (matchesPendingNewSession) pendingNewSessionRequest = null; applySessionSnapshot(snapshot, { immediate: isBlockingSessionLoad(msg.sessionId), suppressUnreadToast: false, @@ -4099,7 +4154,7 @@ setCurrentSessionRunningState(!!msg.isRunning); } if (!msg.historyPending) { - if (activeSessionLoad?.sessionId === msg.sessionId) { + if (matchesActiveLoad) { finalizeLoadedSession(msg.sessionId); } else { cacheSessionSnapshot(snapshot); @@ -4380,6 +4435,7 @@ agent: request.agent, mode: request.mode, createCwd: true, + requestId: request.requestId, }); }, onCancel: () => { @@ -6515,33 +6571,6 @@ const { pinnedSessions, regularSessions } = splitPinnedSessions(visibleSessions); const { groups: projectGroups, ungroupedSessions } = groupSessionsByProject(regularSessions); - const oldSessionCollapseKeysBySessionId = new Map(); - projectGroups.forEach((group) => { - const oldSessionCollapseKey = getProjectOldSessionCollapseKey(group); - group.sessions.forEach((session) => { - oldSessionCollapseKeysBySessionId.set(session.id, oldSessionCollapseKey); - }); - }); - ungroupedSessions.forEach((session) => { - oldSessionCollapseKeysBySessionId.set(session.id, getUngroupedOldSessionCollapseKey()); - }); - const { visibleRegularSessions, hiddenOldSessions } = isSearchingSessions - ? { visibleRegularSessions: regularSessions, hiddenOldSessions: [] } - : splitCollapsedOldSessions( - regularSessions, - pinnedSessions.length, - (session) => oldSessionCollapseKeysBySessionId.get(session.id) || '' - ); - const hiddenOldSessionCountsByKey = new Map(); - hiddenOldSessions.forEach((session) => { - const oldSessionCollapseKey = oldSessionCollapseKeysBySessionId.get(session.id); - if (!oldSessionCollapseKey) return; - hiddenOldSessionCountsByKey.set( - oldSessionCollapseKey, - (hiddenOldSessionCountsByKey.get(oldSessionCollapseKey) || 0) + 1 - ); - }); - const visibleRegularSessionIds = new Set(visibleRegularSessions.map((session) => session.id)); if (pinnedSessions.length > 0) { const pinnedGroupEl = document.createElement('section'); pinnedGroupEl.className = 'session-project-group session-pinned-group'; @@ -6563,8 +6592,9 @@ projectGroups.forEach((group, groupIndex) => { const groupKey = getProjectCollapseKey(group); const oldSessionCollapseKey = getProjectOldSessionCollapseKey(group); - const visibleGroupSessions = group.sessions.filter((session) => visibleRegularSessionIds.has(session.id)); - const hiddenGroupOldSessionCount = hiddenOldSessionCountsByKey.get(oldSessionCollapseKey) || 0; + const { visibleSessions: visibleGroupSessions, hiddenOldSessions: hiddenGroupOldSessions } = isSearchingSessions + ? { visibleSessions: group.sessions, hiddenOldSessions: [] } + : splitCollapsedOldSessions(group.sessions, oldSessionCollapseKey); const isCollapsed = !isSearchingSessions && collapsedProjectKeys.has(groupKey); const hasActiveSession = group.sessions.some((session) => session.id === currentSessionId); const hasUnreadSession = group.sessions.some((session) => session.hasUnread); @@ -6596,8 +6626,8 @@ for (const s of visibleGroupSessions) { groupBody.appendChild(createSessionListItem(s)); } - if (hiddenGroupOldSessionCount > 0) { - groupBody.appendChild(createOldSessionLoadMoreButton(hiddenGroupOldSessionCount, oldSessionCollapseKey, group.name)); + if (hiddenGroupOldSessions.length > 0) { + groupBody.appendChild(createOldSessionLoadMoreButton(hiddenGroupOldSessions.length, oldSessionCollapseKey, group.name)); } groupEl.appendChild(groupBody); @@ -6614,15 +6644,16 @@ }); const ungroupedCollapseKey = getUngroupedOldSessionCollapseKey(); - const visibleUngroupedSessions = ungroupedSessions.filter((session) => visibleRegularSessionIds.has(session.id)); - const hiddenUngroupedOldSessionCount = hiddenOldSessionCountsByKey.get(ungroupedCollapseKey) || 0; + const { visibleSessions: visibleUngroupedSessions, hiddenOldSessions: hiddenUngroupedOldSessions } = isSearchingSessions + ? { visibleSessions: ungroupedSessions, hiddenOldSessions: [] } + : splitCollapsedOldSessions(ungroupedSessions, ungroupedCollapseKey); for (const s of visibleUngroupedSessions) { sessionList.appendChild(createSessionListItem(s)); } - if (hiddenUngroupedOldSessionCount > 0) { - sessionList.appendChild(createOldSessionLoadMoreButton(hiddenUngroupedOldSessionCount, ungroupedCollapseKey)); + if (hiddenUngroupedOldSessions.length > 0) { + sessionList.appendChild(createOldSessionLoadMoreButton(hiddenUngroupedOldSessions.length, ungroupedCollapseKey)); } } @@ -8583,14 +8614,16 @@ const rawCwd = options.rawCwd !== undefined ? options.rawCwd : (cwd || ''); const agent = normalizeAgent(options.agent || currentAgent); const mode = ['default', 'plan', 'yolo'].includes(options.mode) ? options.mode : currentMode; + const requestId = createSessionSwitchRequestId('new'); pendingNewSessionRequest = { cwd, rawCwd, agent, mode, + requestId, }; if (cwd) saveRecentCwd(cwd); - send({ type: 'new_session', cwd, agent, mode }); + send({ type: 'new_session', cwd, agent, mode, requestId }); } // --- New Session Modal --- diff --git a/public/style.css b/public/style.css index 661ddb7..90d4523 100644 --- a/public/style.css +++ b/public/style.css @@ -200,8 +200,8 @@ html[data-theme='coolvibe'] .session-search-clear:focus-visible { html[data-theme='coolvibe'] .session-project-header { background: linear-gradient(180deg, rgba(247, 251, 252, 0.94), rgba(239, 248, 250, 0.88)); - color: #5f7f87; - border-bottom: 1px solid rgba(191, 220, 228, 0.56); + border-color: rgba(156, 199, 211, 0.9); + color: #335e69; } html[data-theme='coolvibe'] .session-project-count { @@ -337,8 +337,8 @@ html[data-theme='coolvibe'] .theme-card.active { html[data-theme='editorial'] .session-project-header { background: linear-gradient(180deg, rgba(239, 232, 220, 0.94), rgba(246, 241, 232, 0.84)); - color: #7f6f61; - border-bottom: 1px solid rgba(139, 94, 60, 0.12); + border-color: rgba(139, 94, 60, 0.22); + color: #4f4035; } html[data-theme='editorial'] .session-project-count { @@ -1146,15 +1146,18 @@ body.session-loading-active { align-items: center; justify-content: space-between; gap: 8px; - margin: 4px 2px 5px; - padding: 6px 8px 5px; - background: rgba(242, 235, 226, 0.92); + margin: 6px 3px 6px; + padding: 6px 8px; + border: 1px solid rgba(134, 106, 80, 0.22); + border-radius: 8px; + background: rgba(255, 249, 242, 0.88); backdrop-filter: blur(8px); - color: var(--text-muted); - font-size: 11px; + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.72), 0 1px 6px rgba(45, 31, 20, 0.04); + color: var(--text-secondary); + font-size: 12px; font-weight: 800; - letter-spacing: 0.08em; - text-transform: uppercase; + letter-spacing: 0; + text-transform: none; } .session-project-name { min-width: 0; @@ -1162,14 +1165,15 @@ body.session-loading-active { text-overflow: ellipsis; white-space: nowrap; flex: 1; + color: var(--text-primary); } .session-project-toggle { min-width: 0; flex: 1; display: inline-flex; align-items: center; - gap: 5px; - height: 24px; + gap: 6px; + height: 26px; padding: 0; border: 0; background: transparent; @@ -1182,9 +1186,13 @@ body.session-loading-active { } .session-project-toggle:hover, .session-project-toggle:focus-visible { - color: var(--text-secondary); + color: var(--accent); outline: none; } +.session-project-toggle:hover .session-project-name, +.session-project-toggle:focus-visible .session-project-name { + color: var(--accent); +} .session-project-toggle:focus-visible .session-project-name { text-decoration: underline; text-underline-offset: 3px; @@ -1192,8 +1200,8 @@ body.session-loading-active { .session-project-chevron { width: 12px; flex-shrink: 0; - color: var(--text-muted); - font-size: 10px; + color: currentColor; + font-size: 11px; line-height: 1; text-align: center; transform: translateY(-0.5px); @@ -1211,9 +1219,10 @@ body.session-loading-active { min-width: 22px; height: 18px; padding: 0 7px; + border: 1px solid rgba(134, 106, 80, 0.14); border-radius: 999px; - background: var(--bg-tertiary); - color: var(--text-secondary); + background: rgba(45, 31, 20, 0.06); + color: var(--text-primary); font-size: 10px; letter-spacing: 0; } @@ -1244,6 +1253,9 @@ body.session-loading-active { .session-pinned-header { color: var(--accent); } +.session-pinned-header .session-project-name { + color: var(--accent); +} .session-project-sessions[hidden] { display: none; } @@ -1256,6 +1268,9 @@ body.session-loading-active { .session-project-group.has-active-session.collapsed .session-project-header { color: var(--accent); } +.session-project-group.has-active-session.collapsed .session-project-name { + color: var(--accent); +} .session-project-group.has-active-session.collapsed .session-project-count { background: var(--accent-light); color: var(--accent); @@ -1460,23 +1475,24 @@ body.session-loading-active { color: var(--danger); } .session-list-load-more { - width: calc(100% - 4px); - margin: 6px 2px 10px; - padding: 9px 10px; - border: 1px solid var(--border-color); - border-radius: 8px; - background: rgba(255, 249, 242, 0.72); + width: fit-content; + max-width: calc(100% - 18px); + margin: 2px 8px 6px; + padding: 3px 7px; + border: 1px solid rgba(134, 106, 80, 0.18); + border-radius: 6px; + background: transparent; color: var(--text-secondary); cursor: pointer; - display: flex; + display: inline-flex; align-items: center; - justify-content: space-between; - gap: 10px; + justify-content: flex-start; + gap: 6px; text-align: left; transition: background 0.16s, border-color 0.16s, color 0.16s, transform 0.16s; } .session-list-load-more:hover { - background: var(--bg-tertiary); + background: rgba(255, 249, 242, 0.58); border-color: rgba(192, 85, 58, 0.24); color: var(--accent); transform: translateY(-1px); @@ -1487,13 +1503,13 @@ body.session-loading-active { } .session-list-load-more-title { min-width: 0; - font-size: 13px; + font-size: 12px; font-weight: 700; } .session-list-load-more-meta { flex-shrink: 0; color: var(--text-muted); - font-size: 11px; + font-size: 10px; } /* Inline edit in sidebar */ .session-item-edit-input { diff --git a/scripts/regression.js b/scripts/regression.js index 4ec4acc..4c5e71f 100644 --- a/scripts/regression.js +++ b/scripts/regression.js @@ -633,8 +633,9 @@ async function main() { mkdirp(path.join(pickerRoot, 'beta')); fs.writeFileSync(path.join(pickerRoot, 'note.txt'), 'not a directory'); - ws.send(JSON.stringify({ type: 'new_session', agent: 'codex', mode: 'plan' })); + ws.send(JSON.stringify({ type: 'new_session', agent: 'codex', mode: 'plan', requestId: 'reg-new-default' })); const defaultCodexSession = await nextMessage(messages, ws, (msg) => msg.type === 'session_info' && msg.agent === 'codex' && msg.title === 'New Chat'); + assert(defaultCodexSession.requestId === 'reg-new-default', 'new_session session_info should echo requestId'); assert(defaultCodexSession.cwd === homeDir, 'Codex new_session without cwd should default to HOME'); const missingCwd = path.join(tempRoot, 'missing-space', 'nested-project'); @@ -1081,8 +1082,9 @@ async function main() { assert(returnedPendingDetail.status === 200 && returnedPendingDetail.body?.ok, 'Returned pending reply detail should remain queryable from source history'); assert(returnedPendingDetail.body.status === 'returned' && returnedPendingDetail.body.returned === true, 'Returned pending reply detail should report returned status'); - ws.send(JSON.stringify({ type: 'load_session', sessionId: busySourceSession.sessionId })); + ws.send(JSON.stringify({ type: 'load_session', sessionId: busySourceSession.sessionId, requestId: 'reg-load-busy-source' })); const loadedBusySource = await nextMessage(messages, ws, (msg) => msg.type === 'session_info' && msg.sessionId === busySourceSession.sessionId); + assert(loadedBusySource.requestId === 'reg-load-busy-source', 'load_session session_info should echo requestId'); assert(loadedBusySource.isRunning === false, 'Busy source should be idle after background run completed'); assert(loadedBusySource.waitingOnChildren === false && loadedBusySource.pendingReplyCount === 0, 'Busy source should clear waiting state after queued reply is flushed'); diff --git a/server.js b/server.js index 2f1337a..0b3ee4e 100644 --- a/server.js +++ b/server.js @@ -5038,7 +5038,7 @@ wss.on('connection', (ws, req) => { handleNewSession(ws, msg); break; case 'load_session': - handleLoadSession(ws, msg.sessionId); + handleLoadSession(ws, msg); break; case 'load_history_page': handleLoadHistoryPage(ws, msg); @@ -5602,7 +5602,7 @@ function handleSlashCommand(ws, text, sessionId, fallbackAgent) { clearRuntimeSessionId(session); session.updated = new Date().toISOString(); saveSession(session); - wsSend(ws, { + wsSend(ws, attachClientRequestId({ type: 'session_info', sessionId: session.id, messages: [], @@ -5614,7 +5614,7 @@ function handleSlashCommand(ws, text, sessionId, fallbackAgent) { cwd: session.cwd || null, totalCost: session.totalCost || 0, totalUsage: session.totalUsage || null, - }); + }, { sessionId })); } wsSend(ws, { type: 'system_message', message: '会话已清除,上下文已重置。' }); break; @@ -5885,6 +5885,11 @@ function buildSessionInfoPayload(session) { }; } +function attachClientRequestId(payload, source = {}) { + const requestId = String(source?.requestId || '').trim(); + return requestId ? { ...payload, requestId } : payload; +} + function handleNewSession(ws, msg) { const result = createPersistentConversationSession(msg || {}, { defaultAgent: normalizeAgent(msg?.agent), @@ -5902,7 +5907,7 @@ function handleNewSession(ws, msg) { const { session } = result; detachWsFromActiveRuntimes(ws); wsSessionMap.set(ws, session.id); - wsSend(ws, buildSessionInfoPayload(session)); + wsSend(ws, attachClientRequestId(buildSessionInfoPayload(session), msg)); sendSessionList(ws); } @@ -5929,7 +5934,8 @@ function handleLoadHistoryPage(ws, msg = {}) { }); } -function handleLoadSession(ws, sessionId) { +function handleLoadSession(ws, msg) { + const sessionId = sanitizeId(typeof msg === 'string' ? msg : msg?.sessionId); reconcilePendingCrossConversationReplies(); const session = loadSession(sessionId); if (!session) { @@ -5961,7 +5967,7 @@ function handleLoadSession(ws, sessionId) { saveSession(refreshedSession); } - wsSend(ws, { + wsSend(ws, attachClientRequestId({ type: 'session_info', sessionId: refreshedSession.id, messages: recentMessages, @@ -5987,7 +5993,7 @@ function handleLoadSession(ws, sessionId) { waitingReplyCount: waitState.waitingReplyCount, failedReplyCount: waitState.failedReplyCount, pendingReplies: waitState.pendingReplies, - }); + }, msg)); if (olderChunks.length > 0) { olderChunks.forEach((chunk, index) => { @@ -8331,7 +8337,7 @@ function handleImportNativeSession(ws, msg) { }; saveSession(session); wsSessionMap.set(ws, id); - wsSend(ws, { + wsSend(ws, attachClientRequestId({ type: 'session_info', sessionId: id, messages: session.messages, @@ -8347,7 +8353,7 @@ function handleImportNativeSession(ws, msg) { hasUnread: false, historyPending: false, isRunning: false, - }); + }, msg)); sendSessionList(ws); } @@ -8432,7 +8438,7 @@ function handleImportCodexSession(ws, msg) { saveSession(session); wsSessionMap.set(ws, id); - wsSend(ws, { + wsSend(ws, attachClientRequestId({ type: 'session_info', sessionId: id, messages: session.messages, @@ -8448,7 +8454,7 @@ function handleImportCodexSession(ws, msg) { hasUnread: false, historyPending: false, isRunning: false, - }); + }, msg)); sendSessionList(ws); }