diff --git a/src/components/chat/AgentHistoryPanel.test.tsx b/src/components/chat/AgentHistoryPanel.test.tsx
index 8dc3d6d..8dc4227 100644
--- a/src/components/chat/AgentHistoryPanel.test.tsx
+++ b/src/components/chat/AgentHistoryPanel.test.tsx
@@ -33,8 +33,37 @@ describe("AgentHistoryPanel", () => {
fireEvent.change(screen.getByPlaceholderText("请输入会话标题"), {
target: { value: "新的会话标题" },
});
- fireEvent.click(screen.getByLabelText("确认修改历史会话标题"));
+ fireEvent.click(screen.getByLabelText("确认"));
expect(onRenameSession).toHaveBeenCalledWith("session-1", "新的会话标题");
});
+
+ it("orders history by the first message time instead of the latest update time", () => {
+ renderWithTheme(
+ ,
+ );
+
+ const sessionTitles = screen.getAllByText(/较新的/).map((element) => element.textContent);
+
+ expect(sessionTitles).toEqual(["较新的首条消息", "较新的更新"]);
+ });
});
diff --git a/src/components/chat/AgentHistoryPanel.tsx b/src/components/chat/AgentHistoryPanel.tsx
index ca606c4..496b816 100644
--- a/src/components/chat/AgentHistoryPanel.tsx
+++ b/src/components/chat/AgentHistoryPanel.tsx
@@ -73,7 +73,6 @@ const getSessionGroupLabel = (timestamp: number) => {
};
export const AgentHistoryPanel = ({
-
sessions,
activeSessionId,
isHydrating = false,
@@ -95,11 +94,25 @@ export const AgentHistoryPanel = ({
return sessions.filter((session) => session.title.toLowerCase().includes(normalizedKeyword));
}, [keyword, sessions]);
+ const sortedFilteredSessions = React.useMemo(
+ () =>
+ [...filteredSessions].sort((left, right) => {
+ const createdAtDiff = right.createdAt - left.createdAt;
+ if (createdAtDiff !== 0) return createdAtDiff;
+
+ const updatedAtDiff = right.updatedAt - left.updatedAt;
+ if (updatedAtDiff !== 0) return updatedAtDiff;
+
+ return right.id.localeCompare(left.id);
+ }),
+ [filteredSessions],
+ );
+
const groupedSessions = React.useMemo(() => {
const groups = new Map();
- filteredSessions.forEach((session) => {
- const label = getSessionGroupLabel(session.updatedAt);
+ sortedFilteredSessions.forEach((session) => {
+ const label = getSessionGroupLabel(session.createdAt);
const existing = groups.get(label);
if (existing) {
existing.push(session);
@@ -109,7 +122,7 @@ export const AgentHistoryPanel = ({
});
return Array.from(groups.entries());
- }, [filteredSessions]);
+ }, [sortedFilteredSessions]);
const pendingDeleteSession = filteredSessions.find(
(session) => session.id === pendingDeleteSessionId,
@@ -390,7 +403,7 @@ export const AgentHistoryPanel = ({
{session.title}
- {formatRelativeDate(session.updatedAt)}
+ {formatRelativeDate(session.createdAt)}
)}
diff --git a/src/components/chat/chatStorage.ts b/src/components/chat/chatStorage.ts
index 3c2d794..51d8a34 100644
--- a/src/components/chat/chatStorage.ts
+++ b/src/components/chat/chatStorage.ts
@@ -51,6 +51,28 @@ const sanitizeMessages = (messages: Message[] | undefined) =>
const sanitizeBranchGroups = (branchGroups: BranchGroup[] | undefined) =>
Array.isArray(branchGroups) ? cloneBranchGroups(branchGroups) : [];
+const hasChatContent = (state: {
+ messages: Message[];
+ branchGroups: BranchGroup[];
+ sessionId?: string;
+}) =>
+ state.messages.length > 0 ||
+ state.branchGroups.length > 0 ||
+ Boolean(state.sessionId);
+
+const compareSessionsByAnchorTime = (
+ left: Pick,
+ right: Pick,
+) => {
+ const createdAtDiff = right.createdAt - left.createdAt;
+ if (createdAtDiff !== 0) return createdAtDiff;
+
+ const updatedAtDiff = right.updatedAt - left.updatedAt;
+ if (updatedAtDiff !== 0) return updatedAtDiff;
+
+ return right.id.localeCompare(left.id);
+};
+
const toLoadedChatState = (session: ChatSessionRecord | undefined): LoadedChatState => {
if (!session) return emptyLoadedChatState();
return {
@@ -131,7 +153,7 @@ const getLatestSession = async () => {
const db = await getDb();
const sessions = await db.getAll(SESSION_STORE);
if (sessions.length === 0) return undefined;
- return sessions.sort((left, right) => right.updatedAt - left.updatedAt)[0];
+ return sessions.sort(compareSessionsByAnchorTime)[0];
};
const migrateLegacyLocalStorage = async () => {
@@ -215,10 +237,7 @@ export const saveActiveChatState = async (
): Promise => {
if (typeof window === "undefined") return state.storageSessionId;
- const hasContent =
- state.messages.length > 0 ||
- state.branchGroups.length > 0 ||
- Boolean(state.sessionId);
+ const hasContent = hasChatContent(state);
const db = await getDb();
const existingSession = state.storageSessionId
@@ -241,11 +260,15 @@ export const saveActiveChatState = async (
const storageSessionId = state.storageSessionId ?? createId();
const preferredTitle = state.title?.trim();
const finalTitle = preferredTitle || existingSession?.title || "新对话";
+ const shouldAnchorCreatedAtToFirstMessage =
+ Boolean(existingSession) && !hasChatContent(existingSession) && hasContent;
const nextRecord: ChatSessionRecord = {
id: storageSessionId,
title: finalTitle,
isTitleManuallyEdited: state.isTitleManuallyEdited ?? existingSession?.isTitleManuallyEdited ?? false,
- createdAt: existingSession?.createdAt ?? now,
+ createdAt: shouldAnchorCreatedAtToFirstMessage
+ ? now
+ : existingSession?.createdAt ?? now,
updatedAt: now,
sessionId: state.sessionId,
messages: sanitizeMessages(state.messages),
@@ -268,9 +291,7 @@ export const listChatSessions = async (): Promise => {
const db = await getDb();
const sessions = await db.getAll(SESSION_STORE);
- return sessions
- .sort((left, right) => right.updatedAt - left.updatedAt)
- .map(toSessionSummary);
+ return sessions.sort(compareSessionsByAnchorTime).map(toSessionSummary);
};
export const updateChatSessionTitle = async (
@@ -353,9 +374,7 @@ export const deleteChatSession = async (sessionId: string): Promise right.updatedAt - left.updatedAt,
- )[0];
+ const nextActiveSession = remainingSessions.sort(compareSessionsByAnchorTime)[0];
const meta = await getMeta();
await setMeta({