fix(cli): prevent duplicate SessionStart systemMessage render (#25827)

Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
Aryan Singh
2026-05-11 22:14:04 +05:30
committed by GitHub
parent 7cd228f5af
commit ecfaac2dc7
4 changed files with 136 additions and 18 deletions
+36
View File
@@ -1267,6 +1267,42 @@ describe('AppContainer State Management', () => {
});
});
describe('SessionStart Hook Rendering', () => {
it('does not render systemMessage directly (avoids duplicate with HookSystemMessage event)', async () => {
const mockAddItem = vi.fn();
mockedUseHistory.mockReturnValue({
history: [],
addItem: mockAddItem,
updateItem: vi.fn(),
clearItems: vi.fn(),
loadHistory: vi.fn(),
});
const fireSessionStartEvent = vi.fn().mockResolvedValue({
systemMessage: 'Hello from SessionStart hook',
getAdditionalContext: vi.fn(() => undefined),
});
vi.spyOn(mockConfig, 'getHookSystem').mockReturnValue({
fireSessionEndEvent: vi.fn().mockResolvedValue(undefined),
fireSessionStartEvent,
} as unknown as ReturnType<Config['getHookSystem']>);
const { unmount } = await act(async () => renderAppContainer());
await waitFor(() => expect(fireSessionStartEvent).toHaveBeenCalled());
// The direct-render path (the bug) would call addItem with the
// systemMessage text and no `source` field. The HookSystemMessage
// event-listener path (the correct one) always sets `source`.
const directRenderCall = mockAddItem.mock.calls.find(
([item]) =>
item?.text === 'Hello from SessionStart hook' && !item?.source,
);
expect(directRenderCall).toBeUndefined();
unmount();
});
});
describe('Token Counting from Session Stats', () => {
it('tracks token counts from session messages', async () => {
// Session stats are provided through the SessionStatsProvider context
-16
View File
@@ -497,16 +497,6 @@ export const AppContainer = (props: AppContainerProps) => {
?.fireSessionStartEvent(sessionStartSource);
if (result) {
if (result.systemMessage) {
historyManager.addItem(
{
type: MessageType.INFO,
text: result.systemMessage,
},
Date.now(),
);
}
const additionalContext = result.getAdditionalContext();
const geminiClient = config.getGeminiClient();
if (additionalContext && geminiClient) {
@@ -549,12 +539,6 @@ export const AppContainer = (props: AppContainerProps) => {
debugLogger.error('Error during cleanup:', e),
);
};
// Disable the dependencies check here. historyManager gets flagged
// but we don't want to react to changes to it because each new history
// item, including the ones from the start session hook will cause a
// re-render and an error when we try to reload config.
//
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [config, resumedSessionData]);
useEffect(