Skip to content

fix(ui): handle headless execution in credits and upgrade dialogs#21850

Merged
gsquared94 merged 2 commits intogoogle-gemini:mainfrom
gsquared94:fix/headless-credits-dialog-browser
Mar 10, 2026
Merged

fix(ui): handle headless execution in credits and upgrade dialogs#21850
gsquared94 merged 2 commits intogoogle-gemini:mainfrom
gsquared94:fix/headless-credits-dialog-browser

Conversation

@gsquared94
Copy link
Contributor

Summary

Fix headless execution handling in AI credits integration dialogs and the /upgrade command. When shouldLaunchBrowser() returns false (CI, SSH, headless environments), the credits "Manage" and "Get Credits" options, as well as the /upgrade command and core handleUpgrade() fallback, now print the URL to the user instead of silently failing.

Details

Three places called openBrowserSecurely() without checking shouldLaunchBrowser() first:

  1. creditsFlowHandler.ts openG1Url() — Used by "Manage" (overage menu) and "Get Credits" (empty wallet menu). Changed return type to Promise<string | undefined> so callers can surface the URL as an info message in chat history.
  2. upgradeCommand.ts — The /upgrade slash command now returns an info message with the URL when browser can't launch.
  3. handler.ts handleUpgrade() — Core-level handler logs the URL via debugLogger.log() since it has no UI access.

This follows the patterns already established in ValidationDialog.tsx and BannedAccountDialog.tsx.

Related Issues

N/A

How to Validate

  1. Run the affected test suites:
    npm test -w @google/gemini-cli -- src/ui/hooks/creditsFlowHandler.test.ts
    npm test -w @google/gemini-cli -- src/ui/commands/upgradeCommand.test.ts
    npm test -w @google/gemini-cli-core -- src/fallback/handler.test.ts
  2. Set CI=1 in your environment and trigger a quota error to see the overage/empty wallet dialogs print URLs instead of trying to open a browser.
  3. With CI=1, run /upgrade and verify it returns the URL as an info message.

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • Linux
      • npm run
      • npx
      • Docker

@gsquared94 gsquared94 requested a review from a team as a code owner March 10, 2026 09:22
Add shouldLaunchBrowser() checks to openG1Url(), /upgrade command, and
handleUpgrade() fallback handler. When browser cannot be launched (CI,
SSH, headless), URLs are printed to the user instead of silently failing.
@gsquared94 gsquared94 force-pushed the fix/headless-credits-dialog-browser branch from 1935531 to 890cadc Compare March 10, 2026 09:24
@gemini-cli gemini-cli bot added the status/need-issue Pull requests that need to have an associated issue. label Mar 10, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the user experience in headless environments (such as CI, SSH, or other non-GUI setups) by preventing silent failures when browser-dependent actions are triggered. Instead of attempting to open a browser, the system now gracefully provides the relevant URLs directly to the user via chat messages or logs, ensuring that critical information like upgrade pages or credit management links remains accessible.

Highlights

  • Headless Environment Handling for Upgrade Command: The /upgrade slash command now checks if a browser can be launched. If not (e.g., in a CI/headless environment), it returns an informational message to the user containing the upgrade URL, rather than attempting to open a browser and potentially failing silently.
  • Headless Environment Handling for AI Credits Dialogs: The 'Manage' and 'Get Credits' options within the AI credits integration dialogs (creditsFlowHandler.ts) have been updated. In headless environments, instead of trying to open a browser, they now return the relevant URL, which is then displayed as an info message in the chat history.
  • Core Fallback Upgrade Handling: The core handleUpgrade() fallback function now also checks for headless execution. If a browser cannot be launched, it logs the upgrade URL using debugLogger.log() for visibility in environments without UI access.
  • Enhanced Test Coverage: New test cases have been added to upgradeCommand.test.ts and creditsFlowHandler.test.ts to specifically validate the behavior of these features in headless environments, ensuring URLs are correctly returned and displayed/logged when browser launching is not possible.
Changelog
  • packages/cli/src/ui/commands/upgradeCommand.test.ts
    • Imported shouldLaunchBrowser from @google/gemini-cli-core.
    • Mocked shouldLaunchBrowser to control test scenarios.
    • Added a new test case to verify that an info message with the URL is returned when shouldLaunchBrowser is false.
  • packages/cli/src/ui/commands/upgradeCommand.ts
    • Imported shouldLaunchBrowser from @google/gemini-cli-core.
    • Implemented a conditional check using shouldLaunchBrowser() before attempting to open a browser.
    • Added logic to return an informational message containing the UPGRADE_URL_PAGE if a browser cannot be launched.
  • packages/cli/src/ui/components/snapshots/ConfigInitDisplay.test.tsx.snap
    • Updated snapshot for ConfigInitDisplay component.
  • packages/cli/src/ui/hooks/creditsFlowHandler.test.ts
    • Imported shouldLaunchBrowser from @google/gemini-cli-core.
    • Mocked shouldLaunchBrowser to simulate headless mode.
    • Added test cases to confirm that 'manage' and 'get credits' actions display the URL in chat history when in headless mode.
  • packages/cli/src/ui/hooks/creditsFlowHandler.ts
    • Imported shouldLaunchBrowser from @google/gemini-cli-core.
    • Modified openG1Url to return the URL as a string if shouldLaunchBrowser() is false, otherwise it opens the browser and returns undefined.
    • Updated handleOverageMenu and handleEmptyWalletMenu to capture the URL returned by openG1Url and add it as an info message to the history manager if available.
  • packages/core/src/fallback/handler.test.ts
    • Mocked shouldLaunchBrowser in secure-browser-launcher.js to return true by default for tests.
  • packages/core/src/fallback/handler.ts
    • Imported shouldLaunchBrowser from ../utils/secure-browser-launcher.js.
    • Added a check in handleUpgrade() to log the UPGRADE_URL_PAGE via debugLogger.log() if shouldLaunchBrowser() returns false.
Activity
  • The author, gsquared94, added and updated tests to cover the new headless execution scenarios.
  • The author validated the changes on MacOS using npm run.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses headless execution for credit and upgrade dialogs by printing URLs instead of attempting to open a browser. The changes are consistently applied across the relevant files and are well-supported by new unit tests. The modification to openG1Url to conditionally return the URL is a clean solution. I've identified one area for improvement regarding test coverage.

}));
vi.mock('../utils/secure-browser-launcher.js', () => ({
openBrowserSecurely: vi.fn(),
shouldLaunchBrowser: vi.fn().mockReturnValue(true),
Copy link
Contributor

Choose a reason for hiding this comment

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

high

While shouldLaunchBrowser is now mocked, the new headless logic in handleUpgrade within packages/core/src/fallback/handler.ts is not covered by a test case. This leaves the new functionality in handleUpgrade untested.

Please add a test case to verify this behavior. When constructing the test, prefer using hardcoded literal values instead of importing constants like UPGRADE_URL_PAGE to ensure tests are self-contained and less brittle. For example:

it('should log URL and not open browser for upgrade intent when headless', async () => {
  const { shouldLaunchBrowser } = await import(
    '../utils/secure-browser-launcher.js'
  );
  vi.mocked(shouldLaunchBrowser).mockReturnValue(false);
  policyHandler.mockResolvedValue('upgrade');

  const result = await handleFallback(
    policyConfig,
    MOCK_PRO_MODEL,
    AUTH_OAUTH,
  );

  expect(result).toBe(false);
  expect(openBrowserSecurely).not.toHaveBeenCalled();
  expect(debugLogger.log).toHaveBeenCalledWith(
    `Cannot open browser in this environment. Please visit: https://example.com/upgrade-page-url`, // Replace with the actual literal URL
  );
});
References
  1. In tests, prefer using hardcoded literal values instead of importing constants to ensure tests are self-contained and less brittle.

@gsquared94 gsquared94 added this pull request to the merge queue Mar 10, 2026
Merged via the queue into google-gemini:main with commit 47e4f6b Mar 10, 2026
27 checks passed
@gsquared94 gsquared94 deleted the fix/headless-credits-dialog-browser branch March 10, 2026 15:07
kunal-10-cloud pushed a commit to kunal-10-cloud/gemini-cli that referenced this pull request Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status/need-issue Pull requests that need to have an associated issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants