mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
Hide sessions that don't have user messages (#13994)
This commit is contained in:
@@ -166,7 +166,7 @@ describe('Session Cleanup Integration', () => {
|
||||
oldSessionFile,
|
||||
JSON.stringify({
|
||||
sessionId: 'old12345',
|
||||
messages: [],
|
||||
messages: [{ type: 'user', content: 'test message' }],
|
||||
startTime: oldDate.toISOString(),
|
||||
lastUpdated: oldDate.toISOString(),
|
||||
}),
|
||||
@@ -181,7 +181,7 @@ describe('Session Cleanup Integration', () => {
|
||||
recentSessionFile,
|
||||
JSON.stringify({
|
||||
sessionId: 'recent789',
|
||||
messages: [],
|
||||
messages: [{ type: 'user', content: 'test message' }],
|
||||
startTime: recentDate.toISOString(),
|
||||
lastUpdated: recentDate.toISOString(),
|
||||
}),
|
||||
@@ -196,7 +196,7 @@ describe('Session Cleanup Integration', () => {
|
||||
currentSessionFile,
|
||||
JSON.stringify({
|
||||
sessionId: 'current123',
|
||||
messages: [],
|
||||
messages: [{ type: 'user', content: 'test message' }],
|
||||
startTime: now.toISOString(),
|
||||
lastUpdated: now.toISOString(),
|
||||
}),
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
SessionSelector,
|
||||
extractFirstUserMessage,
|
||||
formatRelativeTime,
|
||||
hasUserOrAssistantMessage,
|
||||
} from './sessionUtils.js';
|
||||
import type { Config, MessageRecord } from '@google/gemini-cli-core';
|
||||
import { SESSION_FILE_PREFIX } from '@google/gemini-cli-core';
|
||||
@@ -338,6 +339,114 @@ describe('SessionSelector', () => {
|
||||
'Invalid session identifier "999"',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not list sessions with only system messages', async () => {
|
||||
const sessionIdWithUser = randomUUID();
|
||||
const sessionIdSystemOnly = randomUUID();
|
||||
|
||||
// Create test session files
|
||||
const chatsDir = path.join(tmpDir, 'chats');
|
||||
await fs.mkdir(chatsDir, { recursive: true });
|
||||
|
||||
// Session with user message - should be listed
|
||||
const sessionWithUser = {
|
||||
sessionId: sessionIdWithUser,
|
||||
projectHash: 'test-hash',
|
||||
startTime: '2024-01-01T10:00:00.000Z',
|
||||
lastUpdated: '2024-01-01T10:30:00.000Z',
|
||||
messages: [
|
||||
{
|
||||
type: 'user',
|
||||
content: 'Hello world',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Session with only system messages - should NOT be listed
|
||||
const sessionSystemOnly = {
|
||||
sessionId: sessionIdSystemOnly,
|
||||
projectHash: 'test-hash',
|
||||
startTime: '2024-01-01T11:00:00.000Z',
|
||||
lastUpdated: '2024-01-01T11:30:00.000Z',
|
||||
messages: [
|
||||
{
|
||||
type: 'info',
|
||||
content: 'Session started',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T11:00:00.000Z',
|
||||
},
|
||||
{
|
||||
type: 'error',
|
||||
content: 'An error occurred',
|
||||
id: 'msg2',
|
||||
timestamp: '2024-01-01T11:01:00.000Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(
|
||||
chatsDir,
|
||||
`${SESSION_FILE_PREFIX}2024-01-01T10-00-${sessionIdWithUser.slice(0, 8)}.json`,
|
||||
),
|
||||
JSON.stringify(sessionWithUser, null, 2),
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(
|
||||
chatsDir,
|
||||
`${SESSION_FILE_PREFIX}2024-01-01T11-00-${sessionIdSystemOnly.slice(0, 8)}.json`,
|
||||
),
|
||||
JSON.stringify(sessionSystemOnly, null, 2),
|
||||
);
|
||||
|
||||
const sessionSelector = new SessionSelector(config);
|
||||
const sessions = await sessionSelector.listSessions();
|
||||
|
||||
// Should only list the session with user message
|
||||
expect(sessions.length).toBe(1);
|
||||
expect(sessions[0].id).toBe(sessionIdWithUser);
|
||||
});
|
||||
|
||||
it('should list session with gemini message even without user message', async () => {
|
||||
const sessionIdGeminiOnly = randomUUID();
|
||||
|
||||
// Create test session files
|
||||
const chatsDir = path.join(tmpDir, 'chats');
|
||||
await fs.mkdir(chatsDir, { recursive: true });
|
||||
|
||||
// Session with only gemini message - should be listed
|
||||
const sessionGeminiOnly = {
|
||||
sessionId: sessionIdGeminiOnly,
|
||||
projectHash: 'test-hash',
|
||||
startTime: '2024-01-01T10:00:00.000Z',
|
||||
lastUpdated: '2024-01-01T10:30:00.000Z',
|
||||
messages: [
|
||||
{
|
||||
type: 'gemini',
|
||||
content: 'Hello, how can I help?',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(
|
||||
chatsDir,
|
||||
`${SESSION_FILE_PREFIX}2024-01-01T10-00-${sessionIdGeminiOnly.slice(0, 8)}.json`,
|
||||
),
|
||||
JSON.stringify(sessionGeminiOnly, null, 2),
|
||||
);
|
||||
|
||||
const sessionSelector = new SessionSelector(config);
|
||||
const sessions = await sessionSelector.listSessions();
|
||||
|
||||
expect(sessions.length).toBe(1);
|
||||
expect(sessions[0].id).toBe(sessionIdGeminiOnly);
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractFirstUserMessage', () => {
|
||||
@@ -389,6 +498,147 @@ describe('extractFirstUserMessage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasUserOrAssistantMessage', () => {
|
||||
it('should return true when session has user message', () => {
|
||||
const messages = [
|
||||
{
|
||||
type: 'user',
|
||||
content: 'Hello',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
] as MessageRecord[];
|
||||
|
||||
expect(hasUserOrAssistantMessage(messages)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when session has gemini message', () => {
|
||||
const messages = [
|
||||
{
|
||||
type: 'gemini',
|
||||
content: 'Hello, how can I help?',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
] as MessageRecord[];
|
||||
|
||||
expect(hasUserOrAssistantMessage(messages)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when session has both user and gemini messages', () => {
|
||||
const messages = [
|
||||
{
|
||||
type: 'user',
|
||||
content: 'Hello',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
{
|
||||
type: 'gemini',
|
||||
content: 'Hi there!',
|
||||
id: 'msg2',
|
||||
timestamp: '2024-01-01T10:01:00.000Z',
|
||||
},
|
||||
] as MessageRecord[];
|
||||
|
||||
expect(hasUserOrAssistantMessage(messages)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when session only has info messages', () => {
|
||||
const messages = [
|
||||
{
|
||||
type: 'info',
|
||||
content: 'Session started',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
] as MessageRecord[];
|
||||
|
||||
expect(hasUserOrAssistantMessage(messages)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when session only has error messages', () => {
|
||||
const messages = [
|
||||
{
|
||||
type: 'error',
|
||||
content: 'An error occurred',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
] as MessageRecord[];
|
||||
|
||||
expect(hasUserOrAssistantMessage(messages)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when session only has warning messages', () => {
|
||||
const messages = [
|
||||
{
|
||||
type: 'warning',
|
||||
content: 'Warning message',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
] as MessageRecord[];
|
||||
|
||||
expect(hasUserOrAssistantMessage(messages)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when session only has system messages (mixed)', () => {
|
||||
const messages = [
|
||||
{
|
||||
type: 'info',
|
||||
content: 'Session started',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
{
|
||||
type: 'error',
|
||||
content: 'An error occurred',
|
||||
id: 'msg2',
|
||||
timestamp: '2024-01-01T10:01:00.000Z',
|
||||
},
|
||||
{
|
||||
type: 'warning',
|
||||
content: 'Warning message',
|
||||
id: 'msg3',
|
||||
timestamp: '2024-01-01T10:02:00.000Z',
|
||||
},
|
||||
] as MessageRecord[];
|
||||
|
||||
expect(hasUserOrAssistantMessage(messages)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when session has user message among system messages', () => {
|
||||
const messages = [
|
||||
{
|
||||
type: 'info',
|
||||
content: 'Session started',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
{
|
||||
type: 'user',
|
||||
content: 'Hello',
|
||||
id: 'msg2',
|
||||
timestamp: '2024-01-01T10:01:00.000Z',
|
||||
},
|
||||
{
|
||||
type: 'error',
|
||||
content: 'An error occurred',
|
||||
id: 'msg3',
|
||||
timestamp: '2024-01-01T10:02:00.000Z',
|
||||
},
|
||||
] as MessageRecord[];
|
||||
|
||||
expect(hasUserOrAssistantMessage(messages)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for empty messages array', () => {
|
||||
const messages: MessageRecord[] = [];
|
||||
expect(hasUserOrAssistantMessage(messages)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatRelativeTime', () => {
|
||||
it('should format time correctly', () => {
|
||||
const now = new Date();
|
||||
|
||||
@@ -89,6 +89,15 @@ export interface SessionSelectionResult {
|
||||
displayInfo: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a session has at least one user or assistant (gemini) message.
|
||||
* Sessions with only system messages (info, error, warning) are considered empty.
|
||||
* @param messages - The array of message records to check
|
||||
* @returns true if the session has meaningful content
|
||||
*/
|
||||
export const hasUserOrAssistantMessage = (messages: MessageRecord[]): boolean =>
|
||||
messages.some((msg) => msg.type === 'user' || msg.type === 'gemini');
|
||||
|
||||
/**
|
||||
* Cleans and sanitizes message content for display by:
|
||||
* - Converting newlines to spaces
|
||||
@@ -215,6 +224,11 @@ export const getAllSessionFiles = async (
|
||||
return { fileName: file, sessionInfo: null };
|
||||
}
|
||||
|
||||
// Skip sessions that only contain system messages (info, error, warning)
|
||||
if (!hasUserOrAssistantMessage(content.messages)) {
|
||||
return { fileName: file, sessionInfo: null };
|
||||
}
|
||||
|
||||
const firstUserMessage = extractFirstUserMessage(content.messages);
|
||||
const isCurrentSession = currentSessionId
|
||||
? file.includes(currentSessionId.slice(0, 8))
|
||||
|
||||
Reference in New Issue
Block a user