fix session rename (#1046)

This commit is contained in:
paisley
2026-05-19 19:04:17 +08:00
committed by GitHub
parent dbe9d3c9bb
commit 2f7466e2dd
3 changed files with 89 additions and 10 deletions

View File

@@ -25,6 +25,7 @@ import {
clearSessionLabelHydrationTracking,
finishSessionLabelHydration,
getSessionLabelHydrationCandidate,
getSessionLabelHydrationVersion,
} from './chat/session-label-hydration';
import {
DEFAULT_CANONICAL_PREFIX,
@@ -147,14 +148,15 @@ function applySessionLabelSummaries(
for (const summary of summaries) {
const labelText = toSessionLabel(summary.firstUserText || '');
if (labelText) {
if (nextLabels[summary.sessionKey] !== labelText) {
if (nextLabels === state.sessionLabels) {
nextLabels = { ...state.sessionLabels };
}
nextLabels[summary.sessionKey] = labelText;
changed = true;
// Only auto-hydrate missing labels. Existing entries include user renames
// and must not be overwritten by transcript-derived titles.
const existingLabel = nextLabels[summary.sessionKey]?.trim();
if (labelText && !existingLabel) {
if (nextLabels === state.sessionLabels) {
nextLabels = { ...state.sessionLabels };
}
nextLabels[summary.sessionKey] = labelText;
changed = true;
}
if (typeof summary.lastTimestamp === 'number' && Number.isFinite(summary.lastTimestamp)) {
@@ -2204,9 +2206,18 @@ export const useChatStore = create<ChatState>((set, get) => ({
throw err;
}
const session = get().sessions.find((entry) => entry.key === key);
if (session) {
finishSessionLabelHydration(
key,
getSessionLabelHydrationVersion(session, get().sessionLastActivity),
'backend-label',
);
}
set((s) => ({
sessions: s.sessions.map((session) =>
session.key === key ? { ...session, label: normalized } : session,
sessions: s.sessions.map((entry) =>
entry.key === key ? { ...entry, label: normalized } : entry,
),
sessionLabels: { ...s.sessionLabels, [key]: normalized },
}));

View File

@@ -193,7 +193,7 @@ export function createSessionActions(
const labelText = firstUser ? getMessageText(firstUser.content).trim() : '';
set((s) => {
const next: Partial<typeof s> = {};
if (labelText) {
if (labelText && !s.sessionLabels[session.key]?.trim()) {
const truncated = labelText.length > 50 ? `${labelText.slice(0, 50)}` : labelText;
next.sessionLabels = { ...s.sessionLabels, [session.key]: truncated };
}

View File

@@ -409,4 +409,72 @@ describe('chat store session label summary hydration', () => {
});
expect(useChatStore.getState().sessionLabels['agent:main:session-a']).toBe('new label');
});
it('preserves user-renamed labels when visible session summaries refresh', async () => {
gatewayRpcMock.mockImplementation(async (method: string, params?: Record<string, unknown>) => {
if (method === 'sessions.list') {
return {
sessions: [
{ key: 'agent:main:session-a', displayName: 'Session A', updatedAt: 1000 },
{ key: 'agent:main:main', displayName: 'Main', updatedAt: 1001 },
],
};
}
if (method === 'chat.history') {
return {
messages: [{ role: 'user', content: 'visible chat', timestamp: Date.now() }],
};
}
throw new Error(`Unexpected gateway RPC: ${method} ${JSON.stringify(params)}`);
});
hostApiFetchMock.mockImplementation(async (path: string) => {
if (path === '/api/sessions/summaries') {
return {
success: true,
summaries: [
{
sessionKey: 'agent:main:session-a',
firstUserText: 'original first message',
lastTimestamp: 1_700_000_000_000,
},
],
};
}
return { success: true, summaries: [] };
});
const { useChatStore } = await import('@/stores/chat');
useChatStore.setState({
currentSessionKey: 'agent:main:main',
currentAgentId: 'main',
sessions: [
{ key: 'agent:main:session-a', displayName: 'Session A', updatedAt: 1000 },
{ key: 'agent:main:main', displayName: 'Main', updatedAt: 1001 },
],
messages: [],
sessionLabels: { 'agent:main:session-a': 'Custom name' },
sessionLastActivity: {},
sending: false,
activeRunId: null,
streamingText: '',
streamingMessage: null,
streamingTools: [],
pendingFinal: false,
lastUserMessageAt: null,
pendingToolImages: [],
error: null,
loading: false,
thinkingLevel: null,
runError: null,
});
await useChatStore.getState().loadHistory(false);
await Promise.resolve();
await Promise.resolve();
expect(useChatStore.getState().sessionLabels['agent:main:session-a']).toBe('Custom name');
});
});