Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/cli/src/config/settingsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,15 @@ const SETTINGS_SCHEMA = {
description: 'Hide the application banner',
showInDialog: true,
},
showAuthOnStartup: {
type: 'boolean',
label: 'Show Auth On Startup',
category: 'UI',
requiresRestart: true,
default: true,
description: 'Show the authenticated account and plan on startup.',
showInDialog: true,
},
hideContextSummary: {
type: 'boolean',
label: 'Hide Context Summary',
Expand Down
50 changes: 45 additions & 5 deletions packages/cli/src/ui/AppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
getErrorMessage,
getAllGeminiMdFilenames,
AuthType,
UserAccountManager,
clearCachedCredentialFile,
type ResumedSessionData,
recordExitFail,
Expand Down Expand Up @@ -163,11 +164,50 @@ const SHELL_HEIGHT_PADDING = 10;

export const AppContainer = (props: AppContainerProps) => {
const { config, initializationResult, resumedSessionData } = props;
const settings = useSettings();

const initialHistoryItems = useMemo(() => {
const items: HistoryItem[] = [];
if (resumedSessionData) return items;

// Check if the user has disabled showing auth on startup
if (settings.merged.ui.showAuthOnStartup === false) return items;

// We can't use authState here because it's initialized in useAuthCommand which is called later.
// However, we can check config directly since we know startup just finished.
const authType = config.getContentGeneratorConfig()?.authType;
if (
authType === AuthType.LOGIN_WITH_GOOGLE ||
authType === AuthType.COMPUTE_ADC
) {
try {
const userAccountManager = new UserAccountManager();
const email = userAccountManager.getCachedGoogleAccount();
const tier = config.getUserTier();

if (email) {
let message = `Authenticated as: ${email}`;
if (tier) {
message += ` (Plan: ${tier})`;
}
items.push({
id: Date.now(), // Use timestamp as ID for initial item
type: MessageType.INFO,
text: message,
});
}
} catch (_e) {
// Ignore errors during initial auth check
}
}
return items;
}, [config, resumedSessionData, settings.merged.ui.showAuthOnStartup]);

const historyManager = useHistory({
chatRecordingService: config.getGeminiClient()?.getChatRecordingService(),
initialItems: initialHistoryItems,
});
Comment on lines +169 to 209

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The useMemo hook for initialHistoryItems performs synchronous file I/O via userAccountManager.getCachedGoogleAccount(), which can block the event loop. This is a performance concern and goes against the project's guideline of using asynchronous file operations.

This logic should be moved into a useEffect hook to handle it asynchronously. This would also allow you to use historyManager.addItem(), which correctly generates unique IDs for history items, avoiding the use of Date.now() as a React key.

I'm providing a code suggestion to refactor this. I've added a TODO comment to highlight that the synchronous call still needs to be addressed, ideally by adding an async method to UserAccountManager.

  const historyManager = useHistory({
    chatRecordingService: config.getGeminiClient()?.getChatRecordingService(),
  });
  const { addItem } = historyManager;

  useEffect(() => {
    if (resumedSessionData || settings.merged.ui.showAuthOnStartup === false) {
      return;
    }

    const authType = config.getContentGeneratorConfig()?.authType;
    if (
      authType !== AuthType.LOGIN_WITH_GOOGLE &&
      authType !== AuthType.COMPUTE_ADC
    ) {
      return;
    }

    try {
      // TODO: This should be made asynchronous to avoid blocking the event loop.
      const userAccountManager = new UserAccountManager();
      const email = userAccountManager.getCachedGoogleAccount();
      const tier = config.getUserTier();

      if (email) {
        let message = `Authenticated as: ${email}`;
        if (tier) {
          message += ` (Plan: ${tier})`;
        }
        addItem({
          type: MessageType.INFO,
          text: message,
        });
      }
    } catch (_e) {
      // Ignore errors during initial auth check
    }
  }, [config, resumedSessionData, settings.merged.ui.showAuthOnStartup, addItem]);
References
  1. When selecting an item from a list where simple IDs may not be unique, use a guaranteed unique identifier, such as an index or a composite key, to avoid ambiguity when retrieving the selected item with methods like Array.prototype.find().

useMemoryMonitor(historyManager);
const settings = useSettings();
const isAlternateBuffer = useAlternateBuffer();
const [corgiMode, setCorgiMode] = useState(false);
const [debugMessage, setDebugMessage] = useState<string>('');
Expand Down Expand Up @@ -561,10 +601,10 @@ export const AppContainer = (props: AppContainerProps) => {
) {
await runExitCleanup();
writeToStdout(`
----------------------------------------------------------------
Logging in with Google... Restarting Gemini CLI to continue.
----------------------------------------------------------------
`);
----------------------------------------------------------------
Logging in with Google... Restarting Gemini CLI to continue.
----------------------------------------------------------------
`);
process.exit(RELAUNCH_EXIT_CODE);
}
}
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/ui/hooks/useHistoryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ export interface UseHistoryManagerReturn {
*/
export function useHistory({
chatRecordingService,
initialItems = [],
}: {
chatRecordingService?: ChatRecordingService | null;
initialItems?: HistoryItem[];
} = {}): UseHistoryManagerReturn {
const [history, setHistory] = useState<HistoryItem[]>([]);
const [history, setHistory] = useState<HistoryItem[]>(initialItems);
const messageIdCounterRef = useRef(0);

// Generates a unique message ID based on a timestamp and a counter.
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/core/loggingContentGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export class LoggingContentGenerator implements ContentGenerator {
return this.wrapped;
}

get userTier() {
return this.wrapped.userTier;
}

private logApiRequest(
contents: Content[],
model: string,
Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/core/recordingContentGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import type {
import { appendFileSync } from 'node:fs';
import type { ContentGenerator } from './contentGenerator.js';
import type { FakeResponse } from './fakeContentGenerator.js';
import type { UserTierId } from '../code_assist/types.js';
import { safeJsonStringify } from '../utils/safeJsonStringify.js';

// A ContentGenerator that wraps another content generator and records all the
Expand All @@ -25,13 +24,15 @@ import { safeJsonStringify } from '../utils/safeJsonStringify.js';
//
// Note that only the "interesting" bits of the responses are actually kept.
export class RecordingContentGenerator implements ContentGenerator {
userTier?: UserTierId;

constructor(
private readonly realGenerator: ContentGenerator,
private readonly filePath: string,
) {}

get userTier() {
return this.realGenerator.userTier;
}

async generateContent(
request: GenerateContentParameters,
userPromptId: string,
Expand Down
7 changes: 7 additions & 0 deletions schemas/settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@
"default": false,
"type": "boolean"
},
"showAuthOnStartup": {
"title": "Show Auth On Startup",
"description": "Show the authenticated account and plan on startup.",
"markdownDescription": "Show the authenticated account and plan on startup.\n\n- Category: `UI`\n- Requires restart: `yes`\n- Default: `true`",
"default": true,
"type": "boolean"
},
"hideContextSummary": {
"title": "Hide Context Summary",
"description": "Hide the context summary (GEMINI.md, MCP servers) above the input.",
Expand Down