Compare commits

..

4 Commits

385 changed files with 5219 additions and 14357 deletions
@@ -347,36 +347,6 @@ async function run() {
});
}
}
// Remove status/need-triage from maintainer-only issues since they
// don't need community triage. We always attempt removal rather than
// checking the (potentially stale) label snapshot, because the
// issue-opened-labeler workflow runs concurrently and may add the
// label after our snapshot was taken.
if (isDryRun) {
console.log(
`[DRY RUN] Would remove status/need-triage from ${issueKey}`,
);
} else {
try {
await octokit.rest.issues.removeLabel({
owner: issueInfo.owner,
repo: issueInfo.repo,
issue_number: issueInfo.number,
name: 'status/need-triage',
});
console.log(`Removed status/need-triage from ${issueKey}`);
} catch (removeError) {
// 404 means the label wasn't present — that's fine.
if (removeError.status === 404) {
console.log(
`status/need-triage not present on ${issueKey}, skipping.`,
);
} else {
throw removeError;
}
}
}
} catch (error) {
console.error(`Error processing label for ${issueKey}: ${error.message}`);
}
-2
View File
@@ -95,8 +95,6 @@ jobs:
This PR contains the auto-generated changelog for the ${{ steps.release_info.outputs.VERSION }} release.
Please review and merge.
Related to #18505
branch: 'changelog-${{ steps.release_info.outputs.VERSION }}'
base: 'main'
team-reviewers: 'gemini-cli-docs, gemini-cli-maintainers'
-19
View File
@@ -18,25 +18,6 @@ on GitHub.
| [Preview](preview.md) | Experimental features ready for early feedback. |
| [Stable](latest.md) | Stable, recommended for general use. |
## Announcements: v0.33.0 - 2026-03-11
- **Agent Architecture Enhancements:** Introduced HTTP authentication for A2A
remote agents and authenticated A2A agent card discovery
([#20510](https://github.com/google-gemini/gemini-cli/pull/20510) by
@SandyTao520, [#20622](https://github.com/google-gemini/gemini-cli/pull/20622)
by @SandyTao520).
- **Plan Mode Updates:** Expanded Plan Mode with built-in research subagents,
annotation support for feedback, and a new `copy` subcommand
([#20972](https://github.com/google-gemini/gemini-cli/pull/20972) by @Adib234,
[#20988](https://github.com/google-gemini/gemini-cli/pull/20988) by
@ruomengz).
- **CLI UX & Admin Controls:** Redesigned the header to be compact with an ASCII
icon, inverted context window display to show usage, and enabled a 30-day
default retention for chat history
([#18713](https://github.com/google-gemini/gemini-cli/pull/18713) by
@keithguerin, [#20853](https://github.com/google-gemini/gemini-cli/pull/20853)
by @skeshive).
## Announcements: v0.32.0 - 2026-03-03
- **Generalist Agent:** The generalist agent is now enabled to improve task
+191 -217
View File
@@ -1,6 +1,6 @@
# Latest stable release: v0.33.1
# Latest stable release: v0.32.1
Released: March 12, 2026
Released: March 4, 2026
For most users, our latest stable release is the recommended release. Install
the latest stable version with:
@@ -11,224 +11,198 @@ npm install -g @google/gemini-cli
## Highlights
- **Agent Architecture Enhancements:** Introduced HTTP authentication support
for A2A remote agents, authenticated A2A agent card discovery, and directly
indicated auth-required states.
- **Plan Mode Updates:** Expanded Plan Mode capabilities with built-in research
subagents, annotation support for feedback during iteration, and a new `copy`
subcommand.
- **CLI UX Improvements:** Redesigned the header to be compact with an ASCII
icon, inverted the context window display to show usage, and allowed sub-agent
confirmation requests in the UI while preventing background flicker.
- **ACP & MCP Integrations:** Implemented slash command handling in ACP for
`/memory`, `/init`, `/extensions`, and `/restore`, added an MCPOAuthProvider,
and introduced a `set models` interface for ACP.
- **Admin & Core Stability:** Enabled a 30-day default retention for chat
history, added tool name validation in TOML policy files, and improved tool
parameter extraction.
- **Plan Mode Enhancements**: Significant updates to Plan Mode, including the
ability to open and modify plans in an external editor, adaptations for
complex tasks with multi-select options, and integration tests for plan mode.
- **Agent and Steering Improvements**: The generalist agent has been enabled to
enhance task delegation, model steering is now supported directly within the
workspace, and contiguous parallel admission is enabled for `Kind.Agent`
tools.
- **Interactive Shell**: Interactive shell autocompletion has been introduced,
significantly enhancing the user experience.
- **Core Stability and Performance**: Extensions are now loaded in parallel,
fetch timeouts have been increased, robust A2A streaming reassembly was
implemented, and orphaned processes when terminal closes have been prevented.
- **Billing and Quota Handling**: Implemented G1 AI credits overage flow with
billing telemetry and added support for quota error fallbacks across all
authentication types.
## What's Changed
- fix(patch): cherry-pick 8432bce to release/v0.33.0-pr-22069 to patch version
v0.33.0 and create version 0.33.1 by @gemini-cli-robot in
[#22206](https://github.com/google-gemini/gemini-cli/pull/22206)
- Docs: Update model docs to remove Preview Features. by @jkcinouye in
[#20084](https://github.com/google-gemini/gemini-cli/pull/20084)
- docs: fix typo in installation documentation by @AdityaSharma-Git3207 in
[#20153](https://github.com/google-gemini/gemini-cli/pull/20153)
- docs: add Windows PowerShell equivalents for environments and scripting by
@scidomino in [#20333](https://github.com/google-gemini/gemini-cli/pull/20333)
- fix(core): parse raw ASCII buffer strings in Gaxios errors by @sehoon38 in
[#20626](https://github.com/google-gemini/gemini-cli/pull/20626)
- chore(release): bump version to 0.33.0-nightly.20260227.ba149afa0 by @galz10
in [#20637](https://github.com/google-gemini/gemini-cli/pull/20637)
- fix(github): use robot PAT for automated PRs to pass CLA check by @galz10 in
[#20641](https://github.com/google-gemini/gemini-cli/pull/20641)
- chore/release: bump version to 0.33.0-nightly.20260228.1ca5c05d0 by
@gemini-cli-robot in
[#20644](https://github.com/google-gemini/gemini-cli/pull/20644)
- Changelog for v0.31.0 by @gemini-cli-robot in
[#20634](https://github.com/google-gemini/gemini-cli/pull/20634)
- fix: use full paths for ACP diff payloads by @JagjeevanAK in
[#19539](https://github.com/google-gemini/gemini-cli/pull/19539)
- Changelog for v0.32.0-preview.0 by @gemini-cli-robot in
[#20627](https://github.com/google-gemini/gemini-cli/pull/20627)
- fix: acp/zed race condition between MCP initialisation and prompt by
@kartikangiras in
[#20205](https://github.com/google-gemini/gemini-cli/pull/20205)
- fix(cli): reset themeManager between tests to ensure isolation by
@NTaylorMullen in
[#20598](https://github.com/google-gemini/gemini-cli/pull/20598)
- refactor(core): Extract tool parameter names as constants by @SandyTao520 in
[#20460](https://github.com/google-gemini/gemini-cli/pull/20460)
- fix(cli): resolve autoThemeSwitching when background hasn't changed but theme
mismatches by @sehoon38 in
[#20706](https://github.com/google-gemini/gemini-cli/pull/20706)
- feat(skills): add github-issue-creator skill by @sehoon38 in
[#20709](https://github.com/google-gemini/gemini-cli/pull/20709)
- fix(cli): allow sub-agent confirmation requests in UI while preventing
background flicker by @abhipatel12 in
[#20722](https://github.com/google-gemini/gemini-cli/pull/20722)
- Merge User and Agent Card Descriptions #20849 by @adamfweidman in
[#20850](https://github.com/google-gemini/gemini-cli/pull/20850)
- fix(core): reduce LLM-based loop detection false positives by @SandyTao520 in
[#20701](https://github.com/google-gemini/gemini-cli/pull/20701)
- fix(plan): deflake plan mode integration tests by @Adib234 in
[#20477](https://github.com/google-gemini/gemini-cli/pull/20477)
- Add /unassign support by @scidomino in
[#20864](https://github.com/google-gemini/gemini-cli/pull/20864)
- feat(core): implement HTTP authentication support for A2A remote agents by
- fix(patch): cherry-pick 0659ad1 to release/v0.32.0-pr-21042 to patch version
v0.32.0 and create version 0.32.1 by @gemini-cli-robot in
[#21048](https://github.com/google-gemini/gemini-cli/pull/21048)
- feat(plan): add integration tests for plan mode by @Adib234 in
[#20214](https://github.com/google-gemini/gemini-cli/pull/20214)
- fix(acp): update auth handshake to spec by @skeshive in
[#19725](https://github.com/google-gemini/gemini-cli/pull/19725)
- feat(core): implement robust A2A streaming reassembly and fix task continuity
by @adamfweidman in
[#20091](https://github.com/google-gemini/gemini-cli/pull/20091)
- feat(cli): load extensions in parallel by @scidomino in
[#20229](https://github.com/google-gemini/gemini-cli/pull/20229)
- Plumb the maxAttempts setting through Config args by @kevinjwang1 in
[#20239](https://github.com/google-gemini/gemini-cli/pull/20239)
- fix(cli): skip 404 errors in setup-github file downloads by @h30s in
[#20287](https://github.com/google-gemini/gemini-cli/pull/20287)
- fix(cli): expose model.name setting in settings dialog for persistence by
@achaljhawar in
[#19605](https://github.com/google-gemini/gemini-cli/pull/19605)
- docs: remove legacy cmd examples in favor of powershell by @scidomino in
[#20323](https://github.com/google-gemini/gemini-cli/pull/20323)
- feat(core): Enable model steering in workspace. by @joshualitt in
[#20343](https://github.com/google-gemini/gemini-cli/pull/20343)
- fix: remove trailing comma in issue triage workflow settings json by @Nixxx19
in [#20265](https://github.com/google-gemini/gemini-cli/pull/20265)
- feat(core): implement task tracker foundation and service by @anj-s in
[#19464](https://github.com/google-gemini/gemini-cli/pull/19464)
- test: support tests that include color information by @jacob314 in
[#20220](https://github.com/google-gemini/gemini-cli/pull/20220)
- feat(core): introduce Kind.Agent for sub-agent classification by @abhipatel12
in [#20369](https://github.com/google-gemini/gemini-cli/pull/20369)
- Changelog for v0.30.0 by @gemini-cli-robot in
[#20252](https://github.com/google-gemini/gemini-cli/pull/20252)
- Update changelog workflow to reject nightly builds by @g-samroberts in
[#20248](https://github.com/google-gemini/gemini-cli/pull/20248)
- Changelog for v0.31.0-preview.0 by @gemini-cli-robot in
[#20249](https://github.com/google-gemini/gemini-cli/pull/20249)
- feat(cli): hide workspace policy update dialog and auto-accept by default by
@Abhijit-2592 in
[#20351](https://github.com/google-gemini/gemini-cli/pull/20351)
- feat(core): rename grep_search include parameter to include_pattern by
@SandyTao520 in
[#20510](https://github.com/google-gemini/gemini-cli/pull/20510)
- feat(core): centralize read_file limits and update gemini-3 description by
@aishaneeshah in
[#20619](https://github.com/google-gemini/gemini-cli/pull/20619)
- Do not block CI on evals by @gundermanc in
[#20870](https://github.com/google-gemini/gemini-cli/pull/20870)
- document node limitation for shift+tab by @scidomino in
[#20877](https://github.com/google-gemini/gemini-cli/pull/20877)
- Add install as an option when extension is selected. by @DavidAPierce in
[#20358](https://github.com/google-gemini/gemini-cli/pull/20358)
- Update CODEOWNERS for README.md reviewers by @g-samroberts in
[#20860](https://github.com/google-gemini/gemini-cli/pull/20860)
- feat(core): truncate large MCP tool output by @SandyTao520 in
[#19365](https://github.com/google-gemini/gemini-cli/pull/19365)
- Subagent activity UX. by @gundermanc in
[#17570](https://github.com/google-gemini/gemini-cli/pull/17570)
- style(cli) : Dialog pattern for /hooks Command by @AbdulTawabJuly in
[#17930](https://github.com/google-gemini/gemini-cli/pull/17930)
- feat: redesign header to be compact with ASCII icon by @keithguerin in
[#18713](https://github.com/google-gemini/gemini-cli/pull/18713)
- fix(core): ensure subagents use qualified MCP tool names by @abhipatel12 in
[#20801](https://github.com/google-gemini/gemini-cli/pull/20801)
- feat(core): support authenticated A2A agent card discovery by @SandyTao520 in
[#20622](https://github.com/google-gemini/gemini-cli/pull/20622)
- refactor(cli): fully remove React anti patterns, improve type safety and fix
UX oversights in SettingsDialog.tsx by @psinha40898 in
[#18963](https://github.com/google-gemini/gemini-cli/pull/18963)
- Adding MCPOAuthProvider implementing the MCPSDK OAuthClientProvider by
@Nayana-Parameswarappa in
[#20121](https://github.com/google-gemini/gemini-cli/pull/20121)
- feat(core): add tool name validation in TOML policy files by @allenhutchison
in [#19281](https://github.com/google-gemini/gemini-cli/pull/19281)
- docs: fix broken markdown links in main README.md by @Hamdanbinhashim in
[#20300](https://github.com/google-gemini/gemini-cli/pull/20300)
- refactor(core): replace manual syncPlanModeTools with declarative policy rules
by @jerop in [#20596](https://github.com/google-gemini/gemini-cli/pull/20596)
- fix(core): increase default headers timeout to 5 minutes by @gundermanc in
[#20890](https://github.com/google-gemini/gemini-cli/pull/20890)
- feat(admin): enable 30 day default retention for chat history & remove warning
by @skeshive in
[#20853](https://github.com/google-gemini/gemini-cli/pull/20853)
- feat(plan): support annotating plans with feedback for iteration by @Adib234
in [#20876](https://github.com/google-gemini/gemini-cli/pull/20876)
- Add some dos and don'ts to behavioral evals README. by @gundermanc in
[#20629](https://github.com/google-gemini/gemini-cli/pull/20629)
- fix(core): skip telemetry logging for AbortError exceptions by @yunaseoul in
[#19477](https://github.com/google-gemini/gemini-cli/pull/19477)
- fix(core): restrict "System: Please continue" invalid stream retry to Gemini 2
models by @SandyTao520 in
[#20897](https://github.com/google-gemini/gemini-cli/pull/20897)
- ci(evals): only run evals in CI if prompts or tools changed by @gundermanc in
[#20898](https://github.com/google-gemini/gemini-cli/pull/20898)
- Build binary by @aswinashok44 in
[#18933](https://github.com/google-gemini/gemini-cli/pull/18933)
- Code review fixes as a pr by @jacob314 in
[#20612](https://github.com/google-gemini/gemini-cli/pull/20612)
- fix(ci): handle empty APP_ID in stale PR closer by @bdmorgan in
[#20919](https://github.com/google-gemini/gemini-cli/pull/20919)
- feat(cli): invert context window display to show usage by @keithguerin in
[#20071](https://github.com/google-gemini/gemini-cli/pull/20071)
- fix(plan): clean up session directories and plans on deletion by @jerop in
[#20914](https://github.com/google-gemini/gemini-cli/pull/20914)
- fix(core): enforce optionality for API response fields in code_assist by
@sehoon38 in [#20714](https://github.com/google-gemini/gemini-cli/pull/20714)
- feat(extensions): add support for plan directory in extension manifest by
@mahimashanware in
[#20354](https://github.com/google-gemini/gemini-cli/pull/20354)
- feat(plan): enable built-in research subagents in plan mode by @Adib234 in
[#20972](https://github.com/google-gemini/gemini-cli/pull/20972)
- feat(agents): directly indicate auth required state by @adamfweidman in
[#20986](https://github.com/google-gemini/gemini-cli/pull/20986)
- fix(cli): wait for background auto-update before relaunching by @scidomino in
[#20904](https://github.com/google-gemini/gemini-cli/pull/20904)
- fix: pre-load @scripts/copy_files.js references from external editor prompts
by @kartikangiras in
[#20963](https://github.com/google-gemini/gemini-cli/pull/20963)
- feat(evals): add behavioral evals for ask_user tool by @Adib234 in
[#20620](https://github.com/google-gemini/gemini-cli/pull/20620)
- refactor common settings logic for skills,agents by @ishaanxgupta in
[#17490](https://github.com/google-gemini/gemini-cli/pull/17490)
- Update docs-writer skill with new resource by @g-samroberts in
[#20917](https://github.com/google-gemini/gemini-cli/pull/20917)
- fix(cli): pin clipboardy to ~5.2.x by @scidomino in
[#21009](https://github.com/google-gemini/gemini-cli/pull/21009)
- feat: Implement slash command handling in ACP for
`/memory`,`/init`,`/extensions` and `/restore` by @sripasg in
[#20528](https://github.com/google-gemini/gemini-cli/pull/20528)
- Docs/add hooks reference by @AadithyaAle in
[#20961](https://github.com/google-gemini/gemini-cli/pull/20961)
- feat(plan): add copy subcommand to plan (#20491) by @ruomengz in
[#20988](https://github.com/google-gemini/gemini-cli/pull/20988)
- fix(core): sanitize and length-check MCP tool qualified names by @abhipatel12
in [#20987](https://github.com/google-gemini/gemini-cli/pull/20987)
- Format the quota/limit style guide. by @g-samroberts in
[#21017](https://github.com/google-gemini/gemini-cli/pull/21017)
- fix(core): send shell output to model on cancel by @devr0306 in
[#20501](https://github.com/google-gemini/gemini-cli/pull/20501)
- remove hardcoded tiername when missing tier by @sehoon38 in
[#21022](https://github.com/google-gemini/gemini-cli/pull/21022)
- feat(acp): add set models interface by @skeshive in
[#20991](https://github.com/google-gemini/gemini-cli/pull/20991)
- fix(patch): cherry-pick 0659ad1 to release/v0.33.0-preview.0-pr-21042 to patch
version v0.33.0-preview.0 and create version 0.33.0-preview.1 by
@gemini-cli-robot in
[#21047](https://github.com/google-gemini/gemini-cli/pull/21047)
- fix(patch): cherry-pick 173376b to release/v0.33.0-preview.1-pr-21157 to patch
version v0.33.0-preview.1 and create version 0.33.0-preview.2 by
@gemini-cli-robot in
[#21300](https://github.com/google-gemini/gemini-cli/pull/21300)
- fix(patch): cherry-pick 0135b03 to release/v0.33.0-preview.2-pr-21171
[CONFLICTS] by @gemini-cli-robot in
[#21336](https://github.com/google-gemini/gemini-cli/pull/21336)
- fix(patch): cherry-pick 7ec477d to release/v0.33.0-preview.3-pr-21305 to patch
version v0.33.0-preview.3 and create version 0.33.0-preview.4 by
@gemini-cli-robot in
[#21349](https://github.com/google-gemini/gemini-cli/pull/21349)
- fix(patch): cherry-pick 931e668 to release/v0.33.0-preview.4-pr-21425
[CONFLICTS] by @gemini-cli-robot in
[#21478](https://github.com/google-gemini/gemini-cli/pull/21478)
- fix(patch): cherry-pick 7837194 to release/v0.33.0-preview.5-pr-21487 to patch
version v0.33.0-preview.5 and create version 0.33.0-preview.6 by
@gemini-cli-robot in
[#21720](https://github.com/google-gemini/gemini-cli/pull/21720)
- fix(patch): cherry-pick 4f4431e to release/v0.33.0-preview.7-pr-21750 to patch
version v0.33.0-preview.7 and create version 0.33.0-preview.8 by
@gemini-cli-robot in
[#21782](https://github.com/google-gemini/gemini-cli/pull/21782)
- fix(patch): cherry-pick 9a74271 to release/v0.33.0-preview.8-pr-21236
[CONFLICTS] by @gemini-cli-robot in
[#21788](https://github.com/google-gemini/gemini-cli/pull/21788)
- fix(patch): cherry-pick 936f624 to release/v0.33.0-preview.9-pr-21702 to patch
version v0.33.0-preview.9 and create version 0.33.0-preview.10 by
@gemini-cli-robot in
[#21800](https://github.com/google-gemini/gemini-cli/pull/21800)
- fix(patch): cherry-pick 35ee2a8 to release/v0.33.0-preview.10-pr-21713 by
@gemini-cli-robot in
[#21859](https://github.com/google-gemini/gemini-cli/pull/21859)
- fix(patch): cherry-pick 5dd2dab to release/v0.33.0-preview.11-pr-21871 by
@gemini-cli-robot in
[#21876](https://github.com/google-gemini/gemini-cli/pull/21876)
- fix(patch): cherry-pick e5615f4 to release/v0.33.0-preview.12-pr-21037 to
patch version v0.33.0-preview.12 and create version 0.33.0-preview.13 by
@gemini-cli-robot in
[#21922](https://github.com/google-gemini/gemini-cli/pull/21922)
- fix(patch): cherry-pick 1b69637 to release/v0.33.0-preview.13-pr-21467
[CONFLICTS] by @gemini-cli-robot in
[#21930](https://github.com/google-gemini/gemini-cli/pull/21930)
- fix(patch): cherry-pick 3ff68a9 to release/v0.33.0-preview.14-pr-21884
[CONFLICTS] by @gemini-cli-robot in
[#21952](https://github.com/google-gemini/gemini-cli/pull/21952)
[#20328](https://github.com/google-gemini/gemini-cli/pull/20328)
- feat(plan): support opening and modifying plan in external editor by @Adib234
in [#20348](https://github.com/google-gemini/gemini-cli/pull/20348)
- feat(cli): implement interactive shell autocompletion by @mrpmohiburrahman in
[#20082](https://github.com/google-gemini/gemini-cli/pull/20082)
- fix(core): allow /memory add to work in plan mode by @Jefftree in
[#20353](https://github.com/google-gemini/gemini-cli/pull/20353)
- feat(core): add HTTP 499 to retryable errors and map to RetryableQuotaError by
@bdmorgan in [#20432](https://github.com/google-gemini/gemini-cli/pull/20432)
- feat(core): Enable generalist agent by @joshualitt in
[#19665](https://github.com/google-gemini/gemini-cli/pull/19665)
- Updated tests in TableRenderer.test.tsx to use SVG snapshots by @devr0306 in
[#20450](https://github.com/google-gemini/gemini-cli/pull/20450)
- Refactor Github Action per b/485167538 by @google-admin in
[#19443](https://github.com/google-gemini/gemini-cli/pull/19443)
- fix(github): resolve actionlint and yamllint regressions from #19443 by @jerop
in [#20467](https://github.com/google-gemini/gemini-cli/pull/20467)
- fix: action var usage by @galz10 in
[#20492](https://github.com/google-gemini/gemini-cli/pull/20492)
- feat(core): improve A2A content extraction by @adamfweidman in
[#20487](https://github.com/google-gemini/gemini-cli/pull/20487)
- fix(cli): support quota error fallbacks for all authentication types by
@sehoon38 in [#20475](https://github.com/google-gemini/gemini-cli/pull/20475)
- fix(core): flush transcript for pure tool-call responses to ensure BeforeTool
hooks see complete state by @krishdef7 in
[#20419](https://github.com/google-gemini/gemini-cli/pull/20419)
- feat(plan): adapt planning workflow based on complexity of task by @jerop in
[#20465](https://github.com/google-gemini/gemini-cli/pull/20465)
- fix: prevent orphaned processes from consuming 100% CPU when terminal closes
by @yuvrajangadsingh in
[#16965](https://github.com/google-gemini/gemini-cli/pull/16965)
- feat(core): increase fetch timeout and fix [object Object] error
stringification by @bdmorgan in
[#20441](https://github.com/google-gemini/gemini-cli/pull/20441)
- [Gemma x Gemini CLI] Add an Experimental Gemma Router that uses a LiteRT-LM
shim into the Composite Model Classifier Strategy by @sidwan02 in
[#17231](https://github.com/google-gemini/gemini-cli/pull/17231)
- docs(plan): update documentation regarding supporting editing of plan files
during plan approval by @Adib234 in
[#20452](https://github.com/google-gemini/gemini-cli/pull/20452)
- test(cli): fix flaky ToolResultDisplay overflow test by @jwhelangoog in
[#20518](https://github.com/google-gemini/gemini-cli/pull/20518)
- ui(cli): reduce length of Ctrl+O hint by @jwhelangoog in
[#20490](https://github.com/google-gemini/gemini-cli/pull/20490)
- fix(ui): correct styled table width calculations by @devr0306 in
[#20042](https://github.com/google-gemini/gemini-cli/pull/20042)
- Avoid overaggressive unescaping by @scidomino in
[#20520](https://github.com/google-gemini/gemini-cli/pull/20520)
- feat(telemetry) Instrument traces with more attributes and make them available
to OTEL users by @heaventourist in
[#20237](https://github.com/google-gemini/gemini-cli/pull/20237)
- Add support for policy engine in extensions by @chrstnb in
[#20049](https://github.com/google-gemini/gemini-cli/pull/20049)
- Docs: Update to Terms of Service & FAQ by @jkcinouye in
[#20488](https://github.com/google-gemini/gemini-cli/pull/20488)
- Fix bottom border rendering for search and add a regression test. by @jacob314
in [#20517](https://github.com/google-gemini/gemini-cli/pull/20517)
- fix(core): apply retry logic to CodeAssistServer for all users by @bdmorgan in
[#20507](https://github.com/google-gemini/gemini-cli/pull/20507)
- Fix extension MCP server env var loading by @chrstnb in
[#20374](https://github.com/google-gemini/gemini-cli/pull/20374)
- feat(ui): add 'ctrl+o' hint to truncated content message by @jerop in
[#20529](https://github.com/google-gemini/gemini-cli/pull/20529)
- Fix flicker showing message to press ctrl-O again to collapse. by @jacob314 in
[#20414](https://github.com/google-gemini/gemini-cli/pull/20414)
- fix(cli): hide shortcuts hint while model is thinking or the user has typed a
prompt + add debounce to avoid flicker by @jacob314 in
[#19389](https://github.com/google-gemini/gemini-cli/pull/19389)
- feat(plan): update planning workflow to encourage multi-select with
descriptions of options by @Adib234 in
[#20491](https://github.com/google-gemini/gemini-cli/pull/20491)
- refactor(core,cli): useAlternateBuffer read from config by @psinha40898 in
[#20346](https://github.com/google-gemini/gemini-cli/pull/20346)
- fix(cli): ensure dialogs stay scrolled to bottom in alternate buffer mode by
@jacob314 in [#20527](https://github.com/google-gemini/gemini-cli/pull/20527)
- fix(core): revert auto-save of policies to user space by @Abhijit-2592 in
[#20531](https://github.com/google-gemini/gemini-cli/pull/20531)
- Demote unreliable test. by @gundermanc in
[#20571](https://github.com/google-gemini/gemini-cli/pull/20571)
- fix(core): handle optional response fields from code assist API by @sehoon38
in [#20345](https://github.com/google-gemini/gemini-cli/pull/20345)
- fix(cli): keep thought summary when loading phrases are off by @LyalinDotCom
in [#20497](https://github.com/google-gemini/gemini-cli/pull/20497)
- feat(cli): add temporary flag to disable workspace policies by @Abhijit-2592
in [#20523](https://github.com/google-gemini/gemini-cli/pull/20523)
- Disable expensive and scheduled workflows on personal forks by @dewitt in
[#20449](https://github.com/google-gemini/gemini-cli/pull/20449)
- Moved markdown parsing logic to a separate util file by @devr0306 in
[#20526](https://github.com/google-gemini/gemini-cli/pull/20526)
- fix(plan): prevent agent from using ask_user for shell command confirmation by
@Adib234 in [#20504](https://github.com/google-gemini/gemini-cli/pull/20504)
- fix(core): disable retries for code assist streaming requests by @sehoon38 in
[#20561](https://github.com/google-gemini/gemini-cli/pull/20561)
- feat(billing): implement G1 AI credits overage flow with billing telemetry by
@gsquared94 in
[#18590](https://github.com/google-gemini/gemini-cli/pull/18590)
- feat: better error messages by @gsquared94 in
[#20577](https://github.com/google-gemini/gemini-cli/pull/20577)
- fix(ui): persist expansion in AskUser dialog when navigating options by @jerop
in [#20559](https://github.com/google-gemini/gemini-cli/pull/20559)
- fix(cli): prevent sub-agent tool calls from leaking into UI by @abhipatel12 in
[#20580](https://github.com/google-gemini/gemini-cli/pull/20580)
- fix(cli): Shell autocomplete polish by @jacob314 in
[#20411](https://github.com/google-gemini/gemini-cli/pull/20411)
- Changelog for v0.31.0-preview.1 by @gemini-cli-robot in
[#20590](https://github.com/google-gemini/gemini-cli/pull/20590)
- Add slash command for promoting behavioral evals to CI blocking by @gundermanc
in [#20575](https://github.com/google-gemini/gemini-cli/pull/20575)
- Changelog for v0.30.1 by @gemini-cli-robot in
[#20589](https://github.com/google-gemini/gemini-cli/pull/20589)
- Add low/full CLI error verbosity mode for cleaner UI by @LyalinDotCom in
[#20399](https://github.com/google-gemini/gemini-cli/pull/20399)
- Disable Gemini PR reviews on draft PRs. by @gundermanc in
[#20362](https://github.com/google-gemini/gemini-cli/pull/20362)
- Docs: FAQ update by @jkcinouye in
[#20585](https://github.com/google-gemini/gemini-cli/pull/20585)
- fix(core): reduce intrusive MCP errors and deduplicate diagnostics by
@spencer426 in
[#20232](https://github.com/google-gemini/gemini-cli/pull/20232)
- docs: fix spelling typos in installation guide by @campox747 in
[#20579](https://github.com/google-gemini/gemini-cli/pull/20579)
- Promote stable tests to CI blocking. by @gundermanc in
[#20581](https://github.com/google-gemini/gemini-cli/pull/20581)
- feat(core): enable contiguous parallel admission for Kind.Agent tools by
@abhipatel12 in
[#20583](https://github.com/google-gemini/gemini-cli/pull/20583)
- Enforce import/no-duplicates as error by @Nixxx19 in
[#19797](https://github.com/google-gemini/gemini-cli/pull/19797)
- fix: merge duplicate imports in sdk and test-utils packages (1/4) by @Nixxx19
in [#19777](https://github.com/google-gemini/gemini-cli/pull/19777)
- fix: merge duplicate imports in a2a-server package (2/4) by @Nixxx19 in
[#19781](https://github.com/google-gemini/gemini-cli/pull/19781)
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.32.1...v0.33.1
https://github.com/google-gemini/gemini-cli/compare/v0.31.0...v0.32.1
+185 -448
View File
@@ -1,6 +1,6 @@
# Preview release: v0.34.0-preview.1
# Preview release: v0.33.0-preview.14
Released: March 12, 2026
Released: March 10, 2026
Our preview release includes the latest, new, and experimental features. This
release may not be as stable as our [latest weekly release](latest.md).
@@ -13,459 +13,196 @@ npm install -g @google/gemini-cli@preview
## Highlights
- **Plan Mode Enabled by Default:** Plan Mode is now enabled out-of-the-box,
providing a structured planning workflow and keeping approved plans during
chat compression.
- **Sandboxing Enhancements:** Added experimental LXC container sandbox support
and native gVisor (`runsc`) sandboxing for improved security and isolation.
- **Tracker Visualization and Tools:** Introduced CRUD tools and visualization
for trackers, along with task tracker strategy improvements.
- **Browser Agent Improvements:** Enhanced the browser agent with progress
emission, a new automation overlay, and additional integration tests.
- **CLI and UI Updates:** Standardized semantic focus colors, polished shell
autocomplete rendering, unified keybinding infrastructure, and added custom
footer configuration options.
- **Plan Mode Enhancements**: Added support for annotating plans with feedback
for iteration, enabling built-in research subagents in plan mode, and a new
`copy` subcommand.
- **Agent and Skill Improvements**: Introduced the new `github-issue-creator`
skill, implemented HTTP authentication support for A2A remote agents, and
added support for authenticated A2A agent card discovery.
- **CLI UX/UI Updates**: Redesigned the header to be compact with an ASCII icon,
inverted the context window display to show usage, and directly indicate auth
required state for agents.
- **Core and ACP Enhancements**: Implemented slash command handling in ACP (for
`/memory`, `/init`, `/extensions`, and `/restore`), added a set models
interface to ACP, and centralized `read_file` limits while truncating large
MCP tool output.
## What's Changed
- fix(patch): cherry-pick 45faf4d to release/v0.34.0-preview.0-pr-22148
- fix(patch): cherry-pick 1b69637 to release/v0.33.0-preview.13-pr-21467
[CONFLICTS] by @gemini-cli-robot in
[#22174](https://github.com/google-gemini/gemini-cli/pull/22174)
- feat(cli): add chat resume footer on session quit by @lordshashank in
[#20667](https://github.com/google-gemini/gemini-cli/pull/20667)
- Support bold and other styles in svg snapshots by @jacob314 in
[#20937](https://github.com/google-gemini/gemini-cli/pull/20937)
- fix(core): increase A2A agent timeout to 30 minutes by @adamfweidman in
[#21028](https://github.com/google-gemini/gemini-cli/pull/21028)
- Cleanup old branches. by @jacob314 in
[#19354](https://github.com/google-gemini/gemini-cli/pull/19354)
- chore(release): bump version to 0.34.0-nightly.20260303.34f0c1538 by
[#21930](https://github.com/google-gemini/gemini-cli/pull/21930)
- fix(patch): cherry-pick e5615f4 to release/v0.33.0-preview.12-pr-21037 to
patch version v0.33.0-preview.12 and create version 0.33.0-preview.13 by
@gemini-cli-robot in
[#21034](https://github.com/google-gemini/gemini-cli/pull/21034)
- feat(ui): standardize semantic focus colors and enhance history visibility by
@keithguerin in
[#20745](https://github.com/google-gemini/gemini-cli/pull/20745)
- fix: merge duplicate imports in packages/core (3/4) by @Nixxx19 in
[#20928](https://github.com/google-gemini/gemini-cli/pull/20928)
- Add extra safety checks for proto pollution by @jacob314 in
[#20396](https://github.com/google-gemini/gemini-cli/pull/20396)
- feat(core): Add tracker CRUD tools & visualization by @anj-s in
[#19489](https://github.com/google-gemini/gemini-cli/pull/19489)
- Revert "fix(ui): persist expansion in AskUser dialog when navigating options"
by @jacob314 in
[#21042](https://github.com/google-gemini/gemini-cli/pull/21042)
- Changelog for v0.33.0-preview.0 by @gemini-cli-robot in
[#21030](https://github.com/google-gemini/gemini-cli/pull/21030)
- fix: model persistence for all scenarios by @sripasg in
[#21051](https://github.com/google-gemini/gemini-cli/pull/21051)
- chore/release: bump version to 0.34.0-nightly.20260304.28af4e127 by
[#21922](https://github.com/google-gemini/gemini-cli/pull/21922)
- fix(patch): cherry-pick 7ec477d to release/v0.33.0-preview.3-pr-21305 to patch
version v0.33.0-preview.3 and create version 0.33.0-preview.4 by
@gemini-cli-robot in
[#21054](https://github.com/google-gemini/gemini-cli/pull/21054)
- Consistently guard restarts against concurrent auto updates by @scidomino in
[#21016](https://github.com/google-gemini/gemini-cli/pull/21016)
- Defensive coding to reduce the risk of Maximum update depth errors by
@jacob314 in [#20940](https://github.com/google-gemini/gemini-cli/pull/20940)
- fix(cli): Polish shell autocomplete rendering to be a little more shell native
feeling. by @jacob314 in
[#20931](https://github.com/google-gemini/gemini-cli/pull/20931)
- Docs: Update plan mode docs by @jkcinouye in
[#19682](https://github.com/google-gemini/gemini-cli/pull/19682)
- fix(mcp): Notifications/tools/list_changed support not working by @jacob314 in
[#21050](https://github.com/google-gemini/gemini-cli/pull/21050)
- fix(cli): register extension lifecycle events in DebugProfiler by
@fayerman-source in
[#20101](https://github.com/google-gemini/gemini-cli/pull/20101)
- chore(dev): update vscode settings for typescriptreact by @rohit-4321 in
[#19907](https://github.com/google-gemini/gemini-cli/pull/19907)
- fix(cli): enable multi-arch docker builds for sandbox by @ru-aish in
[#19821](https://github.com/google-gemini/gemini-cli/pull/19821)
- Changelog for v0.32.0 by @gemini-cli-robot in
[#21033](https://github.com/google-gemini/gemini-cli/pull/21033)
- Changelog for v0.33.0-preview.1 by @gemini-cli-robot in
[#21058](https://github.com/google-gemini/gemini-cli/pull/21058)
- feat(core): improve @scripts/copy_files.js autocomplete to prioritize
filenames by @sehoon38 in
[#21064](https://github.com/google-gemini/gemini-cli/pull/21064)
- feat(sandbox): add experimental LXC container sandbox support by @h30s in
[#20735](https://github.com/google-gemini/gemini-cli/pull/20735)
- feat(evals): add overall pass rate row to eval nightly summary table by
@gundermanc in
[#20905](https://github.com/google-gemini/gemini-cli/pull/20905)
- feat(telemetry): include language in telemetry and fix accepted lines
computation by @gundermanc in
[#21126](https://github.com/google-gemini/gemini-cli/pull/21126)
- Changelog for v0.32.1 by @gemini-cli-robot in
[#21055](https://github.com/google-gemini/gemini-cli/pull/21055)
- feat(core): add robustness tests, logging, and metrics for CodeAssistServer
SSE parsing by @yunaseoul in
[#21013](https://github.com/google-gemini/gemini-cli/pull/21013)
- feat: add issue assignee workflow by @kartikangiras in
[#21003](https://github.com/google-gemini/gemini-cli/pull/21003)
- fix: improve error message when OAuth succeeds but project ID is required by
@Nixxx19 in [#21070](https://github.com/google-gemini/gemini-cli/pull/21070)
- feat(loop-reduction): implement iterative loop detection and model feedback by
[#21349](https://github.com/google-gemini/gemini-cli/pull/21349)
- fix(patch): cherry-pick 0135b03 to release/v0.33.0-preview.2-pr-21171
[CONFLICTS] by @gemini-cli-robot in
[#21336](https://github.com/google-gemini/gemini-cli/pull/21336)
- fix(patch): cherry-pick 173376b to release/v0.33.0-preview.1-pr-21157 to patch
version v0.33.0-preview.1 and create version 0.33.0-preview.2 by
@gemini-cli-robot in
[#21300](https://github.com/google-gemini/gemini-cli/pull/21300)
- fix(patch): cherry-pick 173376b to release/v0.33.0-preview.1-pr-21157 to patch
version v0.33.0-preview.1 and create version 0.33.0-preview.2 by
@gemini-cli-robot in
[#21300](https://github.com/google-gemini/gemini-cli/pull/21300)
- fix(patch): cherry-pick 0659ad1 to release/v0.33.0-preview.0-pr-21042 to patch
version v0.33.0-preview.0 and create version 0.33.0-preview.1 by
@gemini-cli-robot in
[#21047](https://github.com/google-gemini/gemini-cli/pull/21047)
- Docs: Update model docs to remove Preview Features. by @jkcinouye in
[#20084](https://github.com/google-gemini/gemini-cli/pull/20084)
- docs: fix typo in installation documentation by @AdityaSharma-Git3207 in
[#20153](https://github.com/google-gemini/gemini-cli/pull/20153)
- docs: add Windows PowerShell equivalents for environments and scripting by
@scidomino in [#20333](https://github.com/google-gemini/gemini-cli/pull/20333)
- fix(core): parse raw ASCII buffer strings in Gaxios errors by @sehoon38 in
[#20626](https://github.com/google-gemini/gemini-cli/pull/20626)
- chore(release): bump version to 0.33.0-nightly.20260227.ba149afa0 by @galz10
in [#20637](https://github.com/google-gemini/gemini-cli/pull/20637)
- fix(github): use robot PAT for automated PRs to pass CLA check by @galz10 in
[#20641](https://github.com/google-gemini/gemini-cli/pull/20641)
- chore/release: bump version to 0.33.0-nightly.20260228.1ca5c05d0 by
@gemini-cli-robot in
[#20644](https://github.com/google-gemini/gemini-cli/pull/20644)
- Changelog for v0.31.0 by @gemini-cli-robot in
[#20634](https://github.com/google-gemini/gemini-cli/pull/20634)
- fix: use full paths for ACP diff payloads by @JagjeevanAK in
[#19539](https://github.com/google-gemini/gemini-cli/pull/19539)
- Changelog for v0.32.0-preview.0 by @gemini-cli-robot in
[#20627](https://github.com/google-gemini/gemini-cli/pull/20627)
- fix: acp/zed race condition between MCP initialisation and prompt by
@kartikangiras in
[#20205](https://github.com/google-gemini/gemini-cli/pull/20205)
- fix(cli): reset themeManager between tests to ensure isolation by
@NTaylorMullen in
[#20598](https://github.com/google-gemini/gemini-cli/pull/20598)
- refactor(core): Extract tool parameter names as constants by @SandyTao520 in
[#20460](https://github.com/google-gemini/gemini-cli/pull/20460)
- fix(cli): resolve autoThemeSwitching when background hasn't changed but theme
mismatches by @sehoon38 in
[#20706](https://github.com/google-gemini/gemini-cli/pull/20706)
- feat(skills): add github-issue-creator skill by @sehoon38 in
[#20709](https://github.com/google-gemini/gemini-cli/pull/20709)
- fix(cli): allow sub-agent confirmation requests in UI while preventing
background flicker by @abhipatel12 in
[#20722](https://github.com/google-gemini/gemini-cli/pull/20722)
- Merge User and Agent Card Descriptions #20849 by @adamfweidman in
[#20850](https://github.com/google-gemini/gemini-cli/pull/20850)
- fix(core): reduce LLM-based loop detection false positives by @SandyTao520 in
[#20701](https://github.com/google-gemini/gemini-cli/pull/20701)
- fix(plan): deflake plan mode integration tests by @Adib234 in
[#20477](https://github.com/google-gemini/gemini-cli/pull/20477)
- Add /unassign support by @scidomino in
[#20864](https://github.com/google-gemini/gemini-cli/pull/20864)
- feat(core): implement HTTP authentication support for A2A remote agents by
@SandyTao520 in
[#20510](https://github.com/google-gemini/gemini-cli/pull/20510)
- feat(core): centralize read_file limits and update gemini-3 description by
@aishaneeshah in
[#20763](https://github.com/google-gemini/gemini-cli/pull/20763)
- chore(github): require prompt approvers for agent prompt files by @gundermanc
in [#20896](https://github.com/google-gemini/gemini-cli/pull/20896)
- Docs: Create tools reference by @jkcinouye in
[#19470](https://github.com/google-gemini/gemini-cli/pull/19470)
- fix(core, a2a-server): prevent hang during OAuth in non-interactive sessions
by @spencer426 in
[#21045](https://github.com/google-gemini/gemini-cli/pull/21045)
- chore(cli): enable deprecated settings removal by default by @yashodipmore in
[#20682](https://github.com/google-gemini/gemini-cli/pull/20682)
- feat(core): Disable fast ack helper for hints. by @joshualitt in
[#21011](https://github.com/google-gemini/gemini-cli/pull/21011)
- fix(ui): suppress redundant failure note when tool error note is shown by
@NTaylorMullen in
[#21078](https://github.com/google-gemini/gemini-cli/pull/21078)
- docs: document planning workflows with Conductor example by @jerop in
[#21166](https://github.com/google-gemini/gemini-cli/pull/21166)
- feat(release): ship esbuild bundle in npm package by @genneth in
[#19171](https://github.com/google-gemini/gemini-cli/pull/19171)
- fix(extensions): preserve symlinks in extension source path while enforcing
folder trust by @galz10 in
[#20867](https://github.com/google-gemini/gemini-cli/pull/20867)
- fix(cli): defer tool exclusions to policy engine in non-interactive mode by
@EricRahm in [#20639](https://github.com/google-gemini/gemini-cli/pull/20639)
- fix(ui): removed double padding on rendered content by @devr0306 in
[#21029](https://github.com/google-gemini/gemini-cli/pull/21029)
- fix(core): truncate excessively long lines in grep search output by
@gundermanc in
[#21147](https://github.com/google-gemini/gemini-cli/pull/21147)
- feat: add custom footer configuration via `/footer` by @jackwotherspoon in
[#19001](https://github.com/google-gemini/gemini-cli/pull/19001)
- perf(core): fix OOM crash in long-running sessions by @WizardsForgeGames in
[#19608](https://github.com/google-gemini/gemini-cli/pull/19608)
- refactor(cli): categorize built-in themes into dark/ and light/ directories by
@JayadityaGit in
[#18634](https://github.com/google-gemini/gemini-cli/pull/18634)
- fix(core): explicitly allow codebase_investigator and cli_help in read-only
mode by @Adib234 in
[#21157](https://github.com/google-gemini/gemini-cli/pull/21157)
- test: add browser agent integration tests by @kunal-10-cloud in
[#21151](https://github.com/google-gemini/gemini-cli/pull/21151)
- fix(cli): fix enabling kitty codes on Windows Terminal by @scidomino in
[#21136](https://github.com/google-gemini/gemini-cli/pull/21136)
- refactor(core): extract shared OAuth flow primitives from MCPOAuthProvider by
@SandyTao520 in
[#20895](https://github.com/google-gemini/gemini-cli/pull/20895)
- fix(ui): add partial output to cancelled shell UI by @devr0306 in
[#21178](https://github.com/google-gemini/gemini-cli/pull/21178)
- fix(cli): replace hardcoded keybinding strings with dynamic formatters by
@scidomino in [#21159](https://github.com/google-gemini/gemini-cli/pull/21159)
- DOCS: Update quota and pricing page by @g-samroberts in
[#21194](https://github.com/google-gemini/gemini-cli/pull/21194)
- feat(telemetry): implement Clearcut logging for startup statistics by
@yunaseoul in [#21172](https://github.com/google-gemini/gemini-cli/pull/21172)
- feat(triage): add area/documentation to issue triage by @g-samroberts in
[#21222](https://github.com/google-gemini/gemini-cli/pull/21222)
- Fix so shell calls are formatted by @jacob314 in
[#21237](https://github.com/google-gemini/gemini-cli/pull/21237)
- feat(cli): add native gVisor (runsc) sandboxing support by @Zheyuan-Lin in
[#21062](https://github.com/google-gemini/gemini-cli/pull/21062)
- docs: use absolute paths for internal links in plan-mode.md by @jerop in
[#21299](https://github.com/google-gemini/gemini-cli/pull/21299)
- fix(core): prevent unhandled AbortError crash during stream loop detection by
@7hokerz in [#21123](https://github.com/google-gemini/gemini-cli/pull/21123)
- fix:reorder env var redaction checks to scan values first by @kartikangiras in
[#21059](https://github.com/google-gemini/gemini-cli/pull/21059)
- fix(acp): rename --experimental-acp to --acp & remove Zed-specific refrences
[#20619](https://github.com/google-gemini/gemini-cli/pull/20619)
- Do not block CI on evals by @gundermanc in
[#20870](https://github.com/google-gemini/gemini-cli/pull/20870)
- document node limitation for shift+tab by @scidomino in
[#20877](https://github.com/google-gemini/gemini-cli/pull/20877)
- Add install as an option when extension is selected. by @DavidAPierce in
[#20358](https://github.com/google-gemini/gemini-cli/pull/20358)
- Update CODEOWNERS for README.md reviewers by @g-samroberts in
[#20860](https://github.com/google-gemini/gemini-cli/pull/20860)
- feat(core): truncate large MCP tool output by @SandyTao520 in
[#19365](https://github.com/google-gemini/gemini-cli/pull/19365)
- Subagent activity UX. by @gundermanc in
[#17570](https://github.com/google-gemini/gemini-cli/pull/17570)
- style(cli) : Dialog pattern for /hooks Command by @AbdulTawabJuly in
[#17930](https://github.com/google-gemini/gemini-cli/pull/17930)
- feat: redesign header to be compact with ASCII icon by @keithguerin in
[#18713](https://github.com/google-gemini/gemini-cli/pull/18713)
- fix(core): ensure subagents use qualified MCP tool names by @abhipatel12 in
[#20801](https://github.com/google-gemini/gemini-cli/pull/20801)
- feat(core): support authenticated A2A agent card discovery by @SandyTao520 in
[#20622](https://github.com/google-gemini/gemini-cli/pull/20622)
- refactor(cli): fully remove React anti patterns, improve type safety and fix
UX oversights in SettingsDialog.tsx by @psinha40898 in
[#18963](https://github.com/google-gemini/gemini-cli/pull/18963)
- Adding MCPOAuthProvider implementing the MCPSDK OAuthClientProvider by
@Nayana-Parameswarappa in
[#20121](https://github.com/google-gemini/gemini-cli/pull/20121)
- feat(core): add tool name validation in TOML policy files by @allenhutchison
in [#19281](https://github.com/google-gemini/gemini-cli/pull/19281)
- docs: fix broken markdown links in main README.md by @Hamdanbinhashim in
[#20300](https://github.com/google-gemini/gemini-cli/pull/20300)
- refactor(core): replace manual syncPlanModeTools with declarative policy rules
by @jerop in [#20596](https://github.com/google-gemini/gemini-cli/pull/20596)
- fix(core): increase default headers timeout to 5 minutes by @gundermanc in
[#20890](https://github.com/google-gemini/gemini-cli/pull/20890)
- feat(admin): enable 30 day default retention for chat history & remove warning
by @skeshive in
[#21171](https://github.com/google-gemini/gemini-cli/pull/21171)
- feat(core): fallback to 2.5 models with no access for toolcalls by @sehoon38
in [#21283](https://github.com/google-gemini/gemini-cli/pull/21283)
- test(core): improve testing for API request/response parsing by @sehoon38 in
[#21227](https://github.com/google-gemini/gemini-cli/pull/21227)
- docs(links): update docs-writer skill and fix broken link by @g-samroberts in
[#21314](https://github.com/google-gemini/gemini-cli/pull/21314)
- Fix code colorizer ansi escape bug. by @jacob314 in
[#21321](https://github.com/google-gemini/gemini-cli/pull/21321)
- remove wildcard behavior on keybindings by @scidomino in
[#21315](https://github.com/google-gemini/gemini-cli/pull/21315)
- feat(acp): Add support for AI Gateway auth by @skeshive in
[#21305](https://github.com/google-gemini/gemini-cli/pull/21305)
- fix(theme): improve theme color contrast for macOS Terminal.app by @clocky in
[#21175](https://github.com/google-gemini/gemini-cli/pull/21175)
- feat (core): Implement tracker related SI changes by @anj-s in
[#19964](https://github.com/google-gemini/gemini-cli/pull/19964)
- Changelog for v0.33.0-preview.2 by @gemini-cli-robot in
[#21333](https://github.com/google-gemini/gemini-cli/pull/21333)
- Changelog for v0.33.0-preview.3 by @gemini-cli-robot in
[#21347](https://github.com/google-gemini/gemini-cli/pull/21347)
- docs: format release times as HH:MM UTC by @pavan-sh in
[#20726](https://github.com/google-gemini/gemini-cli/pull/20726)
- fix(cli): implement --all flag for extensions uninstall by @sehoon38 in
[#21319](https://github.com/google-gemini/gemini-cli/pull/21319)
- docs: fix incorrect relative links to command reference by @kanywst in
[#20964](https://github.com/google-gemini/gemini-cli/pull/20964)
- documentiong ensures ripgrep by @Jatin24062005 in
[#21298](https://github.com/google-gemini/gemini-cli/pull/21298)
- fix(core): handle AbortError thrown during processTurn by @MumuTW in
[#21296](https://github.com/google-gemini/gemini-cli/pull/21296)
- docs(cli): clarify ! command output visibility in shell commands tutorial by
@MohammedADev in
[#21041](https://github.com/google-gemini/gemini-cli/pull/21041)
- fix: logic for task tracker strategy and remove tracker tools by @anj-s in
[#21355](https://github.com/google-gemini/gemini-cli/pull/21355)
- fix(partUtils): display media type and size for inline data parts by @Aboudjem
in [#21358](https://github.com/google-gemini/gemini-cli/pull/21358)
- Fix(accessibility): add screen reader support to RewindViewer by @Famous077 in
[#20750](https://github.com/google-gemini/gemini-cli/pull/20750)
- fix(hooks): propagate stopHookActive in AfterAgent retry path (#20426) by
@Aarchi-07 in [#20439](https://github.com/google-gemini/gemini-cli/pull/20439)
- fix(core): deduplicate GEMINI.md files by device/inode on case-insensitive
filesystems (#19904) by @Nixxx19 in
[#19915](https://github.com/google-gemini/gemini-cli/pull/19915)
- feat(core): add concurrency safety guidance for subagent delegation (#17753)
by @abhipatel12 in
[#21278](https://github.com/google-gemini/gemini-cli/pull/21278)
- feat(ui): dynamically generate all keybinding hints by @scidomino in
[#21346](https://github.com/google-gemini/gemini-cli/pull/21346)
- feat(core): implement unified KeychainService and migrate token storage by
@ehedlund in [#21344](https://github.com/google-gemini/gemini-cli/pull/21344)
- fix(cli): gracefully handle --resume when no sessions exist by @SandyTao520 in
[#21429](https://github.com/google-gemini/gemini-cli/pull/21429)
- fix(plan): keep approved plan during chat compression by @ruomengz in
[#21284](https://github.com/google-gemini/gemini-cli/pull/21284)
- feat(core): implement generic CacheService and optimize setupUser by @sehoon38
in [#21374](https://github.com/google-gemini/gemini-cli/pull/21374)
- Update quota and pricing documentation with subscription tiers by @srithreepo
in [#21351](https://github.com/google-gemini/gemini-cli/pull/21351)
- fix(core): append correct OTLP paths for HTTP exporters by
@sebastien-prudhomme in
[#16836](https://github.com/google-gemini/gemini-cli/pull/16836)
- Changelog for v0.33.0-preview.4 by @gemini-cli-robot in
[#21354](https://github.com/google-gemini/gemini-cli/pull/21354)
- feat(cli): implement dot-prefixing for slash command conflicts by @ehedlund in
[#20979](https://github.com/google-gemini/gemini-cli/pull/20979)
- refactor(core): standardize MCP tool naming to mcp\_ FQN format by
@abhipatel12 in
[#21425](https://github.com/google-gemini/gemini-cli/pull/21425)
- feat(cli): hide gemma settings from display and mark as experimental by
@abhipatel12 in
[#21471](https://github.com/google-gemini/gemini-cli/pull/21471)
- feat(skills): refine string-reviewer guidelines and description by @clocky in
[#20368](https://github.com/google-gemini/gemini-cli/pull/20368)
- fix(core): whitelist TERM and COLORTERM in environment sanitization by
@deadsmash07 in
[#20514](https://github.com/google-gemini/gemini-cli/pull/20514)
- fix(billing): fix overage strategy lifecycle and settings integration by
@gsquared94 in
[#21236](https://github.com/google-gemini/gemini-cli/pull/21236)
- fix: expand paste placeholders in TextInput on submit by @Jefftree in
[#19946](https://github.com/google-gemini/gemini-cli/pull/19946)
- fix(core): add in-memory cache to ChatRecordingService to prevent OOM by
@SandyTao520 in
[#21502](https://github.com/google-gemini/gemini-cli/pull/21502)
- feat(cli): overhaul thinking UI by @keithguerin in
[#18725](https://github.com/google-gemini/gemini-cli/pull/18725)
- fix(ui): unify Ctrl+O expansion hint experience across buffer modes by
@jwhelangoog in
[#21474](https://github.com/google-gemini/gemini-cli/pull/21474)
- fix(cli): correct shell height reporting by @jacob314 in
[#21492](https://github.com/google-gemini/gemini-cli/pull/21492)
- Make test suite pass when the GEMINI_SYSTEM_MD env variable or
GEMINI_WRITE_SYSTEM_MD variable happens to be set locally/ by @jacob314 in
[#21480](https://github.com/google-gemini/gemini-cli/pull/21480)
- Disallow underspecified types by @gundermanc in
[#21485](https://github.com/google-gemini/gemini-cli/pull/21485)
- refactor(cli): standardize on 'reload' verb for all components by @keithguerin
in [#20654](https://github.com/google-gemini/gemini-cli/pull/20654)
- feat(cli): Invert quota language to 'percent used' by @keithguerin in
[#20100](https://github.com/google-gemini/gemini-cli/pull/20100)
- Docs: Add documentation for notifications (experimental)(macOS) by @jkcinouye
in [#21163](https://github.com/google-gemini/gemini-cli/pull/21163)
- Code review comments as a pr by @jacob314 in
[#21209](https://github.com/google-gemini/gemini-cli/pull/21209)
- feat(cli): unify /chat and /resume command UX by @LyalinDotCom in
[#20256](https://github.com/google-gemini/gemini-cli/pull/20256)
- docs: fix typo 'allowslisted' -> 'allowlisted' in mcp-server.md by
@Gyanranjan-Priyam in
[#21665](https://github.com/google-gemini/gemini-cli/pull/21665)
- fix(core): display actual graph output in tracker_visualize tool by @anj-s in
[#21455](https://github.com/google-gemini/gemini-cli/pull/21455)
- fix(core): sanitize SSE-corrupted JSON and domain strings in error
classification by @gsquared94 in
[#21702](https://github.com/google-gemini/gemini-cli/pull/21702)
- Docs: Make documentation links relative by @diodesign in
[#21490](https://github.com/google-gemini/gemini-cli/pull/21490)
- feat(cli): expose /tools desc as explicit subcommand for discoverability by
@aworki in [#21241](https://github.com/google-gemini/gemini-cli/pull/21241)
- feat(cli): add /compact alias for /compress command by @jackwotherspoon in
[#21711](https://github.com/google-gemini/gemini-cli/pull/21711)
- feat(plan): enable Plan Mode by default by @jerop in
[#21713](https://github.com/google-gemini/gemini-cli/pull/21713)
- feat(core): Introduce `AgentLoopContext`. by @joshualitt in
[#21198](https://github.com/google-gemini/gemini-cli/pull/21198)
- fix(core): resolve symlinks for non-existent paths during validation by
@Adib234 in [#21487](https://github.com/google-gemini/gemini-cli/pull/21487)
- docs: document tool exclusion from memory via deny policy by @Abhijit-2592 in
[#21428](https://github.com/google-gemini/gemini-cli/pull/21428)
- perf(core): cache loadApiKey to reduce redundant keychain access by @sehoon38
in [#21520](https://github.com/google-gemini/gemini-cli/pull/21520)
- feat(cli): implement /upgrade command by @sehoon38 in
[#21511](https://github.com/google-gemini/gemini-cli/pull/21511)
- Feat/browser agent progress emission by @kunal-10-cloud in
[#21218](https://github.com/google-gemini/gemini-cli/pull/21218)
- fix(settings): display objects as JSON instead of [object Object] by
@Zheyuan-Lin in
[#21458](https://github.com/google-gemini/gemini-cli/pull/21458)
- Unmarshall update by @DavidAPierce in
[#21721](https://github.com/google-gemini/gemini-cli/pull/21721)
- Update mcp's list function to check for disablement. by @DavidAPierce in
[#21148](https://github.com/google-gemini/gemini-cli/pull/21148)
- robustness(core): static checks to validate history is immutable by @jacob314
in [#21228](https://github.com/google-gemini/gemini-cli/pull/21228)
- refactor(cli): better react patterns for BaseSettingsDialog by @psinha40898 in
[#21206](https://github.com/google-gemini/gemini-cli/pull/21206)
- feat(security): implement robust IP validation and safeFetch foundation by
@alisa-alisa in
[#21401](https://github.com/google-gemini/gemini-cli/pull/21401)
- feat(core): improve subagent result display by @joshualitt in
[#20378](https://github.com/google-gemini/gemini-cli/pull/20378)
- docs: fix broken markdown syntax and anchor links in /tools by @campox747 in
[#20902](https://github.com/google-gemini/gemini-cli/pull/20902)
- feat(policy): support subagent-specific policies in TOML by @akh64bit in
[#21431](https://github.com/google-gemini/gemini-cli/pull/21431)
- Add script to speed up reviewing PRs adding a worktree. by @jacob314 in
[#21748](https://github.com/google-gemini/gemini-cli/pull/21748)
- fix(core): prevent infinite recursion in symlink resolution by @Adib234 in
[#21750](https://github.com/google-gemini/gemini-cli/pull/21750)
- fix(docs): fix headless mode docs by @ame2en in
[#21287](https://github.com/google-gemini/gemini-cli/pull/21287)
- feat/redesign header compact by @jacob314 in
[#20922](https://github.com/google-gemini/gemini-cli/pull/20922)
- refactor: migrate to useKeyMatchers hook by @scidomino in
[#21753](https://github.com/google-gemini/gemini-cli/pull/21753)
- perf(cli): cache loadSettings to reduce redundant disk I/O at startup by
@sehoon38 in [#21521](https://github.com/google-gemini/gemini-cli/pull/21521)
- fix(core): resolve Windows line ending and path separation bugs across CLI by
@muhammadusman586 in
[#21068](https://github.com/google-gemini/gemini-cli/pull/21068)
- docs: fix heading formatting in commands.md and phrasing in tools-api.md by
@campox747 in [#20679](https://github.com/google-gemini/gemini-cli/pull/20679)
- refactor(ui): unify keybinding infrastructure and support string
initialization by @scidomino in
[#21776](https://github.com/google-gemini/gemini-cli/pull/21776)
- Add support for updating extension sources and names by @chrstnb in
[#21715](https://github.com/google-gemini/gemini-cli/pull/21715)
- fix(core): handle GUI editor non-zero exit codes gracefully by @reyyanxahmed
in [#20376](https://github.com/google-gemini/gemini-cli/pull/20376)
- fix(core): destroy PTY on kill() and exception to prevent fd leak by @nbardy
in [#21693](https://github.com/google-gemini/gemini-cli/pull/21693)
- fix(docs): update theme screenshots and add missing themes by @ashmod in
[#20689](https://github.com/google-gemini/gemini-cli/pull/20689)
- refactor(cli): rename 'return' key to 'enter' internally by @scidomino in
[#21796](https://github.com/google-gemini/gemini-cli/pull/21796)
- build(release): restrict npm bundling to non-stable tags by @sehoon38 in
[#21821](https://github.com/google-gemini/gemini-cli/pull/21821)
- fix(core): override toolRegistry property for sub-agent schedulers by
@gsquared94 in
[#21766](https://github.com/google-gemini/gemini-cli/pull/21766)
- fix(cli): make footer items equally spaced by @jacob314 in
[#21843](https://github.com/google-gemini/gemini-cli/pull/21843)
- docs: clarify global policy rules application in plan mode by @jerop in
[#21864](https://github.com/google-gemini/gemini-cli/pull/21864)
- fix(core): ensure correct flash model steering in plan mode implementation
phase by @jerop in
[#21871](https://github.com/google-gemini/gemini-cli/pull/21871)
- fix(core): update @a2a-js/sdk to 0.3.11 by @adamfweidman in
[#21875](https://github.com/google-gemini/gemini-cli/pull/21875)
- refactor(core): improve API response error logging when retry by @yunaseoul in
[#21784](https://github.com/google-gemini/gemini-cli/pull/21784)
- fix(ui): handle headless execution in credits and upgrade dialogs by
@gsquared94 in
[#21850](https://github.com/google-gemini/gemini-cli/pull/21850)
- fix(core): treat retryable errors with >5 min delay as terminal quota errors
by @gsquared94 in
[#21881](https://github.com/google-gemini/gemini-cli/pull/21881)
- feat(telemetry): add specific PR, issue, and custom tracking IDs for GitHub
Actions by @cocosheng-g in
[#21129](https://github.com/google-gemini/gemini-cli/pull/21129)
- feat(core): add OAuth2 Authorization Code auth provider for A2A agents by
@SandyTao520 in
[#21496](https://github.com/google-gemini/gemini-cli/pull/21496)
- feat(cli): give visibility to /tools list command in the TUI and follow the
subcommand pattern of other commands by @JayadityaGit in
[#21213](https://github.com/google-gemini/gemini-cli/pull/21213)
- Handle dirty worktrees better and warn about running scripts/review.sh on
untrusted code. by @jacob314 in
[#21791](https://github.com/google-gemini/gemini-cli/pull/21791)
- feat(policy): support auto-add to policy by default and scoped persistence by
@spencer426 in
[#20361](https://github.com/google-gemini/gemini-cli/pull/20361)
- fix(core): handle AbortError when ESC cancels tool execution by @PrasannaPal21
in [#20863](https://github.com/google-gemini/gemini-cli/pull/20863)
- fix(release): Improve Patch Release Workflow Comments: Clearer Approval
Guidance by @jerop in
[#21894](https://github.com/google-gemini/gemini-cli/pull/21894)
- docs: clarify telemetry setup and comprehensive data map by @jerop in
[#21879](https://github.com/google-gemini/gemini-cli/pull/21879)
- feat(core): add per-model token usage to stream-json output by @yongruilin in
[#21839](https://github.com/google-gemini/gemini-cli/pull/21839)
- docs: remove experimental badge from plan mode in sidebar by @jerop in
[#21906](https://github.com/google-gemini/gemini-cli/pull/21906)
- fix(cli): prevent race condition in loop detection retry by @skyvanguard in
[#17916](https://github.com/google-gemini/gemini-cli/pull/17916)
- Add behavioral evals for tracker by @anj-s in
[#20069](https://github.com/google-gemini/gemini-cli/pull/20069)
- fix(auth): update terminology to 'sign in' and 'sign out' by @clocky in
[#20892](https://github.com/google-gemini/gemini-cli/pull/20892)
- docs(mcp): standardize mcp tool fqn documentation by @abhipatel12 in
[#21664](https://github.com/google-gemini/gemini-cli/pull/21664)
- fix(ui): prevent empty tool-group border stubs after filtering by @Aaxhirrr in
[#21852](https://github.com/google-gemini/gemini-cli/pull/21852)
- make command names consistent by @scidomino in
[#21907](https://github.com/google-gemini/gemini-cli/pull/21907)
- refactor: remove agent_card_requires_auth config flag by @adamfweidman in
[#21914](https://github.com/google-gemini/gemini-cli/pull/21914)
- feat(a2a): implement standardized normalization and streaming reassembly by
@alisa-alisa in
[#21402](https://github.com/google-gemini/gemini-cli/pull/21402)
- feat(cli): enable skill activation via slash commands by @NTaylorMullen in
[#21758](https://github.com/google-gemini/gemini-cli/pull/21758)
- docs(cli): mention per-model token usage in stream-json result event by
@yongruilin in
[#21908](https://github.com/google-gemini/gemini-cli/pull/21908)
- fix(plan): prevent plan truncation in approval dialog by supporting
unconstrained heights by @Adib234 in
[#21037](https://github.com/google-gemini/gemini-cli/pull/21037)
- feat(a2a): switch from callback-based to event-driven tool scheduler by
@cocosheng-g in
[#21467](https://github.com/google-gemini/gemini-cli/pull/21467)
- feat(voice): implement speech-friendly response formatter by @Solventerritory
in [#20989](https://github.com/google-gemini/gemini-cli/pull/20989)
- feat: add pulsating blue border automation overlay to browser agent by
@kunal-10-cloud in
[#21173](https://github.com/google-gemini/gemini-cli/pull/21173)
- Add extensionRegistryURI setting to change where the registry is read from by
@kevinjwang1 in
[#20463](https://github.com/google-gemini/gemini-cli/pull/20463)
- fix: patch gaxios v7 Array.toString() stream corruption by @gsquared94 in
[#21884](https://github.com/google-gemini/gemini-cli/pull/21884)
- fix: prevent hangs in non-interactive mode and improve agent guidance by
@cocosheng-g in
[#20893](https://github.com/google-gemini/gemini-cli/pull/20893)
- Add ExtensionDetails dialog and support install by @chrstnb in
[#20845](https://github.com/google-gemini/gemini-cli/pull/20845)
- chore/release: bump version to 0.34.0-nightly.20260310.4653b126f by
@gemini-cli-robot in
[#21816](https://github.com/google-gemini/gemini-cli/pull/21816)
- Changelog for v0.33.0-preview.13 by @gemini-cli-robot in
[#21927](https://github.com/google-gemini/gemini-cli/pull/21927)
- fix(cli): stabilize prompt layout to prevent jumping when typing by
@NTaylorMullen in
[#21081](https://github.com/google-gemini/gemini-cli/pull/21081)
- fix: preserve prompt text when cancelling streaming by @Nixxx19 in
[#21103](https://github.com/google-gemini/gemini-cli/pull/21103)
- fix: robust UX for remote agent errors by @Shyam-Raghuwanshi in
[#20307](https://github.com/google-gemini/gemini-cli/pull/20307)
- feat: implement background process logging and cleanup by @galz10 in
[#21189](https://github.com/google-gemini/gemini-cli/pull/21189)
- Changelog for v0.33.0-preview.14 by @gemini-cli-robot in
[#21938](https://github.com/google-gemini/gemini-cli/pull/21938)
[#20853](https://github.com/google-gemini/gemini-cli/pull/20853)
- feat(plan): support annotating plans with feedback for iteration by @Adib234
in [#20876](https://github.com/google-gemini/gemini-cli/pull/20876)
- Add some dos and don'ts to behavioral evals README. by @gundermanc in
[#20629](https://github.com/google-gemini/gemini-cli/pull/20629)
- fix(core): skip telemetry logging for AbortError exceptions by @yunaseoul in
[#19477](https://github.com/google-gemini/gemini-cli/pull/19477)
- fix(core): restrict "System: Please continue" invalid stream retry to Gemini 2
models by @SandyTao520 in
[#20897](https://github.com/google-gemini/gemini-cli/pull/20897)
- ci(evals): only run evals in CI if prompts or tools changed by @gundermanc in
[#20898](https://github.com/google-gemini/gemini-cli/pull/20898)
- Build binary by @aswinashok44 in
[#18933](https://github.com/google-gemini/gemini-cli/pull/18933)
- Code review fixes as a pr by @jacob314 in
[#20612](https://github.com/google-gemini/gemini-cli/pull/20612)
- fix(ci): handle empty APP_ID in stale PR closer by @bdmorgan in
[#20919](https://github.com/google-gemini/gemini-cli/pull/20919)
- feat(cli): invert context window display to show usage by @keithguerin in
[#20071](https://github.com/google-gemini/gemini-cli/pull/20071)
- fix(plan): clean up session directories and plans on deletion by @jerop in
[#20914](https://github.com/google-gemini/gemini-cli/pull/20914)
- fix(core): enforce optionality for API response fields in code_assist by
@sehoon38 in [#20714](https://github.com/google-gemini/gemini-cli/pull/20714)
- feat(extensions): add support for plan directory in extension manifest by
@mahimashanware in
[#20354](https://github.com/google-gemini/gemini-cli/pull/20354)
- feat(plan): enable built-in research subagents in plan mode by @Adib234 in
[#20972](https://github.com/google-gemini/gemini-cli/pull/20972)
- feat(agents): directly indicate auth required state by @adamfweidman in
[#20986](https://github.com/google-gemini/gemini-cli/pull/20986)
- fix(cli): wait for background auto-update before relaunching by @scidomino in
[#20904](https://github.com/google-gemini/gemini-cli/pull/20904)
- fix: pre-load @scripts/copy_files.js references from external editor prompts
by @kartikangiras in
[#20963](https://github.com/google-gemini/gemini-cli/pull/20963)
- feat(evals): add behavioral evals for ask_user tool by @Adib234 in
[#20620](https://github.com/google-gemini/gemini-cli/pull/20620)
- refactor common settings logic for skills,agents by @ishaanxgupta in
[#17490](https://github.com/google-gemini/gemini-cli/pull/17490)
- Update docs-writer skill with new resource by @g-samroberts in
[#20917](https://github.com/google-gemini/gemini-cli/pull/20917)
- fix(cli): pin clipboardy to ~5.2.x by @scidomino in
[#21009](https://github.com/google-gemini/gemini-cli/pull/21009)
- feat: Implement slash command handling in ACP for
`/memory`,`/init`,`/extensions` and `/restore` by @sripasg in
[#20528](https://github.com/google-gemini/gemini-cli/pull/20528)
- Docs/add hooks reference by @AadithyaAle in
[#20961](https://github.com/google-gemini/gemini-cli/pull/20961)
- feat(plan): add copy subcommand to plan (#20491) by @ruomengz in
[#20988](https://github.com/google-gemini/gemini-cli/pull/20988)
- fix(core): sanitize and length-check MCP tool qualified names by @abhipatel12
in [#20987](https://github.com/google-gemini/gemini-cli/pull/20987)
- Format the quota/limit style guide. by @g-samroberts in
[#21017](https://github.com/google-gemini/gemini-cli/pull/21017)
- fix(core): send shell output to model on cancel by @devr0306 in
[#20501](https://github.com/google-gemini/gemini-cli/pull/20501)
- remove hardcoded tiername when missing tier by @sehoon38 in
[#21022](https://github.com/google-gemini/gemini-cli/pull/21022)
- feat(acp): add set models interface by @skeshive in
[#20991](https://github.com/google-gemini/gemini-cli/pull/20991)
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.33.0-preview.15...v0.34.0-preview.1
https://github.com/google-gemini/gemini-cli/compare/v0.32.0-preview.0...v0.33.0-preview.14
+1 -18
View File
@@ -26,20 +26,6 @@ policies.
the CLI will use an available fallback model for the current turn or the
remainder of the session.
### Local Model Routing (Experimental)
Gemini CLI supports using a local model for routing decisions. When configured,
Gemini CLI will use a locally-running **Gemma** model to make routing decisions
(instead of sending routing decisions to a hosted model). This feature can help
reduce costs associated with hosted model usage while offering similar routing
decision latency and quality.
In order to use this feature, the local Gemma model **must** be served behind a
Gemini API and accessible via HTTP at an endpoint configured in `settings.json`.
For more details on how to configure local model routing, see
[Local Model Routing](../core/local-model-routing.md).
### Model selection precedence
The model used by Gemini CLI is determined by the following order of precedence:
@@ -52,8 +38,5 @@ The model used by Gemini CLI is determined by the following order of precedence:
3. **`model.name` in `settings.json`:** If neither of the above are set, the
model specified in the `model.name` property of your `settings.json` file
will be used.
4. **Local model (experimental):** If the Gemma local model router is enabled
in your `settings.json` file, the CLI will use the local Gemma model
(instead of Gemini models) to route the request to an appropriate model.
5. **Default model:** If none of the above are set, the default model will be
4. **Default model:** If none of the above are set, the default model will be
used. The default model is `auto`
-79
View File
@@ -1,79 +0,0 @@
# Model steering (experimental)
Model steering lets you provide real-time guidance and feedback to Gemini CLI
while it is actively executing a task. This lets you correct course, add missing
context, or skip unnecessary steps without having to stop and restart the agent.
> **Note:** This is a preview feature under active development. Preview features
> may only be available in the **Preview** channel or may need to be enabled
> under `/settings`.
Model steering is particularly useful during complex [Plan Mode](./plan-mode.md)
workflows or long-running subagent executions where you want to ensure the agent
stays on the right track.
## Enabling model steering
Model steering is an experimental feature and is disabled by default. You can
enable it using the `/settings` command or by updating your `settings.json`
file.
1. Type `/settings` in the Gemini CLI.
2. Search for **Model Steering**.
3. Set the value to **true**.
Alternatively, add the following to your `settings.json`:
```json
{
"experimental": {
"modelSteering": true
}
}
```
## Using model steering
When model steering is enabled, Gemini CLI treats any text you type while the
agent is working as a steering hint.
1. Start a task (for example, "Refactor the database service").
2. While the agent is working (the spinner is visible), type your feedback in
the input box.
3. Press **Enter**.
Gemini CLI acknowledges your hint with a brief message and injects it directly
into the model's context for the very next turn. The model then re-evaluates its
current plan and adjusts its actions accordingly.
### Common use cases
You can use steering hints to guide the model in several ways:
- **Correcting a path:** "Actually, the utilities are in `src/common/utils`."
- **Skipping a step:** "Skip the unit tests for now and just focus on the
implementation."
- **Adding context:** "The `User` type is defined in `packages/core/types.ts`."
- **Redirecting the effort:** "Stop searching the codebase and start drafting
the plan now."
- **Handling ambiguity:** "Use the existing `Logger` class instead of creating a
new one."
## How it works
When you submit a steering hint, Gemini CLI performs the following actions:
1. **Immediate acknowledgment:** It uses a small, fast model to generate a
one-sentence acknowledgment so you know your hint was received.
2. **Context injection:** It prepends an internal instruction to your hint that
tells the main agent to:
- Re-evaluate the active plan.
- Classify the update (for example, as a new task or extra context).
- Apply minimal-diff changes to affected tasks.
3. **Real-time update:** The hint is delivered to the agent at the beginning of
its next turn, ensuring the most immediate course correction possible.
## Next steps
- Tackle complex tasks with [Plan Mode](./plan-mode.md).
- Build custom [Agent Skills](./skills.md).
+14 -99
View File
@@ -61,44 +61,20 @@ Gemini CLI takes action.
[`ask_user`](../tools/ask-user.md). Provide your preferences to help guide
the design.
3. **Review the plan:** Once Gemini CLI has a proposed strategy, it creates a
detailed implementation plan as a Markdown file in your plans directory.
- **View:** You can open and read this file to understand the proposed
changes.
- **Edit:** Press `Ctrl+X` to open the plan directly in your configured
external editor.
detailed implementation plan as a Markdown file in your plans directory. You
can open and read this file to understand the proposed changes.
4. **Approve or iterate:** Gemini CLI will present the finalized plan for your
approval.
- **Approve:** If you're satisfied with the plan, approve it to start the
implementation immediately: **Yes, automatically accept edits** or **Yes,
manually accept edits**.
- **Iterate:** If the plan needs adjustments, provide feedback in the input
box or [edit the plan file directly](#collaborative-plan-editing). Gemini
CLI will refine the strategy and update the plan.
- **Iterate:** If the plan needs adjustments, provide feedback. Gemini CLI
will refine the strategy and update the plan.
- **Cancel:** You can cancel your plan with `Esc`.
For more complex or specialized planning tasks, you can
[customize the planning workflow with skills](#custom-planning-with-skills).
### Collaborative plan editing
You can collaborate with Gemini CLI by making direct changes or leaving comments
in the implementation plan. This is often faster and more precise than
describing complex changes in natural language.
1. **Open the plan:** Press `Ctrl+X` when Gemini CLI presents a plan for
review.
2. **Edit or comment:** The plan opens in your configured external editor (for
example, VS Code or Vim). You can:
- **Modify steps:** Directly reorder, delete, or rewrite implementation
steps.
- **Leave comments:** Add inline questions or feedback (for example, "Wait,
shouldn't we use the existing `Logger` class here?").
3. **Save and close:** Save your changes and close the editor.
4. **Review and refine:** Gemini CLI automatically detects the changes, reviews
your comments, and adjusts the implementation strategy. It then presents the
refined plan for your final approval.
## How to exit Plan Mode
You can exit Plan Mode at any time, whether you have finalized a plan or want to
@@ -109,6 +85,16 @@ switch back to another mode.
- **Keyboard shortcut:** Press `Shift+Tab` to cycle to the desired mode.
- **Natural language:** Ask Gemini CLI to "exit plan mode" or "stop planning."
## Customization and best practices
Plan Mode is secure by default, but you can adapt it to fit your specific
workflows. You can customize how Gemini CLI plans by using skills, adjusting
safety policies, or changing where plans are stored.
## Commands
- **`/plan copy`**: Copy the currently approved plan to your clipboard.
## Tool Restrictions
Plan Mode enforces strict safety policies to prevent accidental changes.
@@ -136,12 +122,6 @@ These are the only allowed tools:
- **Skills:** [`activate_skill`](../cli/skills.md) (allows loading specialized
instructions and resources in a read-only manner)
## Customization and best practices
Plan Mode is secure by default, but you can adapt it to fit your specific
workflows. You can customize how Gemini CLI plans by using skills, adjusting
safety policies, changing where plans are stored, or adding hooks.
### Custom planning with skills
You can use [Agent Skills](../cli/skills.md) to customize how Gemini CLI
@@ -290,71 +270,6 @@ modes = ["plan"]
argsPattern = "\"file_path\":\"[^\"]+[\\\\/]+\\.gemini[\\\\/]+plans[\\\\/]+[\\w-]+\\.md\""
```
### Using hooks with Plan Mode
You can use the [hook system](../hooks/writing-hooks.md) to automate parts of
the planning workflow or enforce additional checks when Gemini CLI transitions
into or out of Plan Mode.
Hooks such as `BeforeTool` or `AfterTool` can be configured to intercept the
`enter_plan_mode` and `exit_plan_mode` tool calls.
> [!WARNING] When hooks are triggered by **tool executions**, they do **not**
> run when you manually toggle Plan Mode using the `/plan` command or the
> `Shift+Tab` keyboard shortcut. If you need hooks to execute on mode changes,
> ensure the transition is initiated by the agent (e.g., by asking "start a plan
> for...").
#### Example: Archive approved plans to GCS (`AfterTool`)
If your organizational policy requires a record of all execution plans, you can
use an `AfterTool` hook to securely copy the plan artifact to Google Cloud
Storage whenever Gemini CLI exits Plan Mode to start the implementation.
**`.gemini/hooks/archive-plan.sh`:**
```bash
#!/usr/bin/env bash
# Extract the plan path from the tool input JSON
plan_path=$(jq -r '.tool_input.plan_path // empty')
if [ -f "$plan_path" ]; then
# Generate a unique filename using a timestamp
filename="$(date +%s)_$(basename "$plan_path")"
# Upload the plan to GCS in the background so it doesn't block the CLI
gsutil cp "$plan_path" "gs://my-audit-bucket/gemini-plans/$filename" > /dev/null 2>&1 &
fi
# AfterTool hooks should generally allow the flow to continue
echo '{"decision": "allow"}'
```
To register this `AfterTool` hook, add it to your `settings.json`:
```json
{
"hooks": {
"AfterTool": [
{
"matcher": "exit_plan_mode",
"hooks": [
{
"name": "archive-plan",
"type": "command",
"command": "./.gemini/hooks/archive-plan.sh"
}
]
}
]
}
}
```
## Commands
- **`/plan copy`**: Copy the currently approved plan to your clipboard.
## Planning workflows
Plan Mode provides building blocks for structured research and design. These are
-1
View File
@@ -55,7 +55,6 @@ they appear in the UI.
| Show Home Directory Warning | `ui.showHomeDirectoryWarning` | Show a warning when running Gemini CLI in the home directory. | `true` |
| Show Compatibility Warnings | `ui.showCompatibilityWarnings` | Show warnings about terminal or OS compatibility issues. | `true` |
| Hide Tips | `ui.hideTips` | Hide helpful tips in the UI | `false` |
| Escape Pasted @ Symbols | `ui.escapePastedAtSymbols` | When enabled, @ symbols in pasted text are escaped to prevent unintended @path expansion. | `false` |
| Show Shortcuts Hint | `ui.showShortcutsHint` | Show the "? for shortcuts" hint above the input. | `true` |
| Hide Banner | `ui.hideBanner` | Hide the application banner | `false` |
| Hide Context Summary | `ui.hideContextSummary` | Hide the context summary (GEMINI.md, MCP servers) above the input. | `false` |
-45
View File
@@ -45,7 +45,6 @@ Environment variables can override these settings.
| `logPrompts` | `GEMINI_TELEMETRY_LOG_PROMPTS` | Include prompts in telemetry logs | `true`/`false` | `true` |
| `useCollector` | `GEMINI_TELEMETRY_USE_COLLECTOR` | Use external OTLP collector (advanced) | `true`/`false` | `false` |
| `useCliAuth` | `GEMINI_TELEMETRY_USE_CLI_AUTH` | Use CLI credentials for telemetry (GCP target only) | `true`/`false` | `false` |
| - | `GEMINI_CLI_SURFACE` | Optional custom label for traffic reporting | string | - |
**Note on boolean environment variables:** For boolean settings like `enabled`,
setting the environment variable to `true` or `1` enables the feature.
@@ -217,50 +216,6 @@ recommend using file-based output for local development.
For advanced local telemetry setups (such as Jaeger or Genkit), see the
[Local development guide](../local-development.md#viewing-traces).
## Client identification
Gemini CLI includes identifiers in its `User-Agent` header to help you
differentiate and report on API traffic from different environments (for
example, identifying calls from Gemini Code Assist versus a standard terminal).
### Automatic identification
Most integrated environments are identified automatically without additional
configuration. The identifier is included as a prefix to the `User-Agent` and as
a "surface" tag in the parenthetical metadata.
| Environment | User-Agent Prefix | Surface Tag |
| :---------------------------------- | :--------------------------- | :---------- |
| **Gemini Code Assist (Agent Mode)** | `GeminiCLI-a2a-server` | `vscode` |
| **Zed (via ACP)** | `GeminiCLI-acp-zed` | `zed` |
| **XCode (via ACP)** | `GeminiCLI-acp-xcode` | `xcode` |
| **IntelliJ IDEA (via ACP)** | `GeminiCLI-acp-intellijidea` | `jetbrains` |
| **Standard Terminal** | `GeminiCLI` | `terminal` |
**Example User-Agent:**
`GeminiCLI-a2a-server/0.34.0/gemini-pro (linux; x64; vscode)`
### Custom identification
You can provide a custom identifier for your own scripts or automation by
setting the `GEMINI_CLI_SURFACE` environment variable. This is useful for
tracking specific internal tools or distribution channels in your GCP logs.
**macOS/Linux**
```bash
export GEMINI_CLI_SURFACE="my-custom-tool"
```
**Windows (PowerShell)**
```powershell
$env:GEMINI_CLI_SURFACE="my-custom-tool"
```
When set, the value appears at the end of the `User-Agent` parenthetical:
`GeminiCLI/0.34.0/gemini-pro (linux; x64; my-custom-tool)`
## Logs, metrics, and traces
This section describes the structure of logs, metrics, and traces generated by
-89
View File
@@ -1,89 +0,0 @@
# Use Plan Mode with model steering for complex tasks
Architecting a complex solution requires precision. By combining Plan Mode's
structured environment with model steering's real-time feedback, you can guide
Gemini CLI through the research and design phases to ensure the final
implementation plan is exactly what you need.
> **Note:** This is a preview feature under active development. Preview features
> may only be available in the **Preview** channel or may need to be enabled
> under `/settings`.
## Prerequisites
- Gemini CLI installed and authenticated.
- [Plan Mode](../plan-mode.md) enabled in your settings.
- [Model steering](../model-steering.md) enabled in your settings.
## Why combine Plan Mode and model steering?
[Plan Mode](../plan-mode.md) typically follows a linear path: research, propose,
and draft. Adding model steering lets you:
1. **Direct the research:** Correct the agent if it's looking in the wrong
directory or missing a key dependency.
2. **Iterate mid-draft:** Suggest a different architectural pattern while the
agent is still writing the plan.
3. **Speed up the loop:** Avoid waiting for a full research turn to finish
before providing critical context.
## Step 1: Start a complex task
Enter Plan Mode and start a task that requires research.
**Prompt:** `/plan I want to implement a new notification service using Redis.`
Gemini CLI enters Plan Mode and starts researching your existing codebase to
identify where the new service should live.
## Step 2: Steer the research phase
As you see the agent calling tools like `list_directory` or `grep_search`, you
might realize it's missing the relevant context.
**Action:** While the spinner is active, type your hint:
`"Don't forget to check packages/common/queues for the existing Redis config."`
**Result:** Gemini CLI acknowledges your hint and immediately incorporates it
into its research. You'll see it start exploring the directory you suggested in
its very next turn.
## Step 3: Refine the design mid-turn
After research, the agent starts drafting the implementation plan. If you notice
it's proposing a design that doesn't align with your goals, steer it.
**Action:** Type:
`"Actually, let's use a Publisher/Subscriber pattern instead of a simple queue for this service."`
**Result:** The agent stops drafting the current version of the plan,
re-evaluates the design based on your feedback, and starts a new draft that uses
the Pub/Sub pattern.
## Step 4: Approve and implement
Once the agent has used your hints to craft the perfect plan, review the final
`.md` file.
**Action:** Type: `"Looks perfect. Let's start the implementation."`
Gemini CLI exits Plan Mode and transitions to the implementation phase. Because
the plan was refined in real-time with your feedback, the agent can now execute
each step with higher confidence and fewer errors.
## Tips for effective steering
- **Be specific:** Instead of "do it differently," try "use the existing
`Logger` class in `src/utils`."
- **Steer early:** Providing feedback during the research phase is more
efficient than waiting for the final plan to be drafted.
- **Use for context:** Steering is a great way to provide knowledge that might
not be obvious from reading the code (e.g., "We are planning to deprecate this
module next month").
## Next steps
- Explore [Agent Skills](../skills.md) to add specialized expertise to your
planning turns.
- See the [Model steering reference](../model-steering.md) for technical
details.
-2
View File
@@ -15,8 +15,6 @@ requests sent from `packages/cli`. For a general overview of Gemini CLI, see the
modular GEMINI.md import feature using @file.md syntax.
- **[Policy Engine](../reference/policy-engine.md):** Use the Policy Engine for
fine-grained control over tool execution.
- **[Local Model Routing (experimental)](./local-model-routing.md):** Learn how
to enable use of a local Gemma model for model routing decisions.
## Role of the core
-193
View File
@@ -1,193 +0,0 @@
# Local Model Routing (experimental)
Gemini CLI supports using a local model for
[routing decisions](../cli/model-routing.md). When configured, Gemini CLI will
use a locally-running **Gemma** model to make routing decisions (instead of
sending routing decisions to a hosted model).
This feature can help reduce costs associated with hosted model usage while
offering similar routing decision latency and quality.
> **Note: Local model routing is currently an experimental feature.**
## Setup
Using a Gemma model for routing decisions requires that an implementation of a
Gemma model be running locally on your machine, served behind an HTTP endpoint
and accessed via the Gemini API.
To serve the Gemma model, follow these steps:
### Download the LiteRT-LM runtime
The [LiteRT-LM](https://github.com/google-ai-edge/LiteRT-LM) runtime offers
pre-built binaries for locally-serving models. Download the binary appropriate
for your system.
#### Windows
1. Download
[lit.windows_x86_64.exe](https://github.com/google-ai-edge/LiteRT-LM/releases/download/v0.9.0-alpha03/lit.windows_x86_64.exe).
2. Using GPU on Windows requires the DirectXShaderCompiler. Download the
[dxc zip from the latest release](https://github.com/microsoft/DirectXShaderCompiler/releases/download/v1.8.2505.1/dxc_2025_07_14.zip).
Unzip the archive and from the architecture-appropriate `bin\` directory, and
copy the `dxil.dll` and `dxcompiler.dll` into the same location as you saved
`lit.windows_x86_64.exe`.
3. (Optional) Test starting the runtime:
`.\lit.windows_x86_64.exe serve --verbose`
#### Linux
1. Download
[lit.linux_x86_64](https://github.com/google-ai-edge/LiteRT-LM/releases/download/v0.9.0-alpha03/lit.linux_x86_64).
2. Ensure the binary is executable: `chmod a+x lit.linux_x86_64`
3. (Optional) Test starting the runtime: `./lit.linux_x86_64 serve --verbose`
#### MacOS
1. Download
[lit-macos-arm64](https://github.com/google-ai-edge/LiteRT-LM/releases/download/v0.9.0-alpha03/lit.macos_arm64).
2. Ensure the binary is executable: `chmod a+x lit.macos_arm64`
3. (Optional) Test starting the runtime: `./lit.macos_arm64 serve --verbose`
> **Note**: MacOS can be configured to only allows binaries from "App Store &
> Known Developers". If you encounter an error message when attempting to run
> the binary, you will need to allow the application. One option is to visit
> `System Settings -> Privacy & Security`, scroll to `Security`, and click
> `"Allow Anyway"` for `"lit.macos_arm64"`. Another option is to run
> `xattr -d com.apple.quarantine lit.macos_arm64` from the commandline.
### Download the Gemma Model
Before using Gemma, you will need to download the model (and agree to the Terms
of Service).
This can be done via the LiteRT-LM runtime.
#### Windows
```bash
$ .\lit.windows_x86_64.exe pull gemma3-1b-gpu-custom
[Legal] The model you are about to download is governed by
the Gemma Terms of Use and Prohibited Use Policy. Please review these terms and ensure you agree before continuing.
Full Terms: https://ai.google.dev/gemma/terms
Prohibited Use Policy: https://ai.google.dev/gemma/prohibited_use_policy
Do you accept these terms? (Y/N): Y
Terms accepted.
Downloading model 'gemma3-1b-gpu-custom' ...
Downloading... 968.6 MB
Download complete.
```
#### Linux
```bash
$ ./lit.linux_x86_64 pull gemma3-1b-gpu-custom
[Legal] The model you are about to download is governed by
the Gemma Terms of Use and Prohibited Use Policy. Please review these terms and ensure you agree before continuing.
Full Terms: https://ai.google.dev/gemma/terms
Prohibited Use Policy: https://ai.google.dev/gemma/prohibited_use_policy
Do you accept these terms? (Y/N): Y
Terms accepted.
Downloading model 'gemma3-1b-gpu-custom' ...
Downloading... 968.6 MB
Download complete.
```
#### MacOS
```bash
$ ./lit.lit.macos_arm64 pull gemma3-1b-gpu-custom
[Legal] The model you are about to download is governed by
the Gemma Terms of Use and Prohibited Use Policy. Please review these terms and ensure you agree before continuing.
Full Terms: https://ai.google.dev/gemma/terms
Prohibited Use Policy: https://ai.google.dev/gemma/prohibited_use_policy
Do you accept these terms? (Y/N): Y
Terms accepted.
Downloading model 'gemma3-1b-gpu-custom' ...
Downloading... 968.6 MB
Download complete.
```
### Start LiteRT-LM Runtime
Using the command appropriate to your system, start the LiteRT-LM runtime.
Configure the port that you want to use for your Gemma model. For the purposes
of this document, we will use port `9379`.
Example command for MacOS: `./lit.macos_arm64 serve --port=9379 --verbose`
### (Optional) Verify Model Serving
Send a quick prompt to the model via HTTP to validate successful model serving.
This will cause the runtime to download the model and run it once.
You should see a short joke in the server output as an indicator of success.
#### Windows
```
# Run this in PowerShell to send a request to the server
$uri = "http://localhost:9379/v1beta/models/gemma3-1b-gpu-custom:generateContent"
$body = @{contents = @( @{
role = "user"
parts = @( @{ text = "Tell me a joke." } )
})} | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri $uri -Method Post -Body $body -ContentType "application/json"
```
#### Linux/MacOS
```bash
$ curl "http://localhost:9379/v1beta/models/gemma3-1b-gpu-custom:generateContent" \
-H 'Content-Type: application/json' \
-X POST \
-d '{"contents":[{"role":"user","parts":[{"text":"Tell me a joke."}]}]}'
```
## Configuration
To use a local Gemma model for routing, you must explicitly enable it in your
`settings.json`:
```json
{
"experimental": {
"gemmaModelRouter": {
"enabled": true,
"classifier": {
"host": "http://localhost:9379",
"model": "gemma3-1b-gpu-custom"
}
}
}
}
```
> Use the port you started your LiteRT-LM runtime on in the setup steps.
### Configuration schema
| Field | Type | Required | Description |
| :----------------- | :------ | :------- | :----------------------------------------------------------------------------------------- |
| `enabled` | boolean | Yes | Must be `true` to enable the feature. |
| `classifier` | object | Yes | The configuration for the local model endpoint. It includes the host and model specifiers. |
| `classifier.host` | string | Yes | The URL to the local model server. Should be `http://localhost:<port>`. |
| `classifier.model` | string | Yes | The model name to use for decisions. Must be `"gemma3-1b-gpu-custom"`. |
> **Note: You will need to restart after configuration changes for local model
> routing to take effect.**
+1 -165
View File
@@ -92,13 +92,6 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `[]`
- **Requires restart:** Yes
#### `adminPolicyPaths`
- **`adminPolicyPaths`** (array):
- **Description:** Additional admin policy files or directories to load.
- **Default:** `[]`
- **Requires restart:** Yes
#### `general`
- **`general.preferredEditor`** (string):
@@ -245,11 +238,6 @@ their corresponding top-level category object in your `settings.json` file.
- **Description:** Hide helpful tips in the UI
- **Default:** `false`
- **`ui.escapePastedAtSymbols`** (boolean):
- **Description:** When enabled, @ symbols in pasted text are escaped to
prevent unintended @path expansion.
- **Default:** `false`
- **`ui.showShortcutsHint`** (boolean):
- **Description:** Show the "? for shortcuts" hint above the input.
- **Default:** `true`
@@ -677,141 +665,6 @@ their corresponding top-level category object in your `settings.json` file.
used.
- **Default:** `[]`
- **`modelConfigs.modelDefinitions`** (object):
- **Description:** Registry of model metadata, including tier, family, and
features.
- **Default:**
```json
{
"gemini-3.1-pro-preview": {
"tier": "pro",
"family": "gemini-3",
"isPreview": true,
"dialogLocation": "manual",
"features": {
"thinking": true,
"multimodalToolUse": true
}
},
"gemini-3.1-pro-preview-customtools": {
"tier": "pro",
"family": "gemini-3",
"isPreview": true,
"features": {
"thinking": true,
"multimodalToolUse": true
}
},
"gemini-3-pro-preview": {
"tier": "pro",
"family": "gemini-3",
"isPreview": true,
"dialogLocation": "manual",
"features": {
"thinking": true,
"multimodalToolUse": true
}
},
"gemini-3-flash-preview": {
"tier": "flash",
"family": "gemini-3",
"isPreview": true,
"dialogLocation": "manual",
"features": {
"thinking": false,
"multimodalToolUse": true
}
},
"gemini-2.5-pro": {
"tier": "pro",
"family": "gemini-2.5",
"isPreview": false,
"dialogLocation": "manual",
"features": {
"thinking": false,
"multimodalToolUse": false
}
},
"gemini-2.5-flash": {
"tier": "flash",
"family": "gemini-2.5",
"isPreview": false,
"dialogLocation": "manual",
"features": {
"thinking": false,
"multimodalToolUse": false
}
},
"gemini-2.5-flash-lite": {
"tier": "flash-lite",
"family": "gemini-2.5",
"isPreview": false,
"dialogLocation": "manual",
"features": {
"thinking": false,
"multimodalToolUse": false
}
},
"auto": {
"tier": "auto",
"isPreview": true,
"features": {
"thinking": true,
"multimodalToolUse": false
}
},
"pro": {
"tier": "pro",
"isPreview": false,
"features": {
"thinking": true,
"multimodalToolUse": false
}
},
"flash": {
"tier": "flash",
"isPreview": false,
"features": {
"thinking": false,
"multimodalToolUse": false
}
},
"flash-lite": {
"tier": "flash-lite",
"isPreview": false,
"features": {
"thinking": false,
"multimodalToolUse": false
}
},
"auto-gemini-3": {
"displayName": "Auto (Gemini 3)",
"tier": "auto",
"isPreview": true,
"dialogLocation": "main",
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash",
"features": {
"thinking": true,
"multimodalToolUse": false
}
},
"auto-gemini-2.5": {
"displayName": "Auto (Gemini 2.5)",
"tier": "auto",
"isPreview": false,
"dialogLocation": "main",
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash",
"features": {
"thinking": false,
"multimodalToolUse": false
}
}
}
```
- **Requires restart:** Yes
#### `agents`
- **`agents.overrides`** (object):
@@ -841,10 +694,6 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`agents.browser.disableUserInput`** (boolean):
- **Description:** Disable user input on browser window during automation.
- **Default:** `true`
#### `context`
- **`context.fileName`** (string | string[]):
@@ -907,7 +756,7 @@ their corresponding top-level category object in your `settings.json` file.
#### `tools`
- **`tools.sandbox`** (string):
- **`tools.sandbox`** (boolean | string):
- **Description:** Sandbox execution environment. Set to a boolean to enable
or disable the sandbox, provide a string path to a sandbox profile, or
specify an explicit sandbox command (e.g., "docker", "podman", "lxc").
@@ -1203,12 +1052,6 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.dynamicModelConfiguration`** (boolean):
- **Description:** Enable dynamic model configuration (definitions,
resolutions, and chains) via settings.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.gemmaModelRouter.enabled`** (boolean):
- **Description:** Enable the Gemma Model Router (experimental). Requires a
local endpoint serving Gemma via the Gemini API using LiteRT-LM shim.
@@ -1534,13 +1377,6 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
- Useful for shared compute environments or keeping CLI state isolated.
- Example: `export GEMINI_CLI_HOME="/path/to/user/config"` (Windows
PowerShell: `$env:GEMINI_CLI_HOME="C:\path\to\user\config"`)
- **`GEMINI_CLI_SURFACE`**:
- Specifies a custom label to include in the `User-Agent` header for API
traffic reporting.
- This is useful for tracking specific internal tools or distribution
channels.
- Example: `export GEMINI_CLI_SURFACE="my-custom-tool"` (Windows PowerShell:
`$env:GEMINI_CLI_SURFACE="my-custom-tool"`)
- **`GOOGLE_API_KEY`**:
- Your Google Cloud API key.
- Required for using Vertex AI in express mode.
+15 -46
View File
@@ -130,9 +130,10 @@ available combinations.
## Customizing Keybindings
You can add alternative keybindings or remove default keybindings by creating a
You can add alternative keybindings for commands by creating or modifying the
`keybindings.json` file in your home gemini directory (typically
`~/.gemini/keybindings.json`).
`~/.gemini/keybindings.json`). This allows you to bind commands to additional
key combinations. Note that default keybindings cannot be removed.
### Configuration Format
@@ -142,58 +143,29 @@ a `key` combination.
```json
[
{
"command": "edit.clear",
"key": "cmd+l"
},
{
// prefix "-" to unbind a key
"command": "-app.toggleYolo",
"key": "ctrl+y"
},
{
"command": "input.submit",
"key": "ctrl+y"
"key": "cmd+s"
},
{
// multiple modifiers
"command": "cursor.right",
"key": "shift+alt+a"
},
{
// Some mac keyboards send "Å" instead of "shift+option+a"
"command": "cursor.right",
"key": "Å"
},
{
// some base keys have special multi-char names
"command": "cursor.right",
"key": "shift+pageup"
"command": "edit.clear",
"key": "ctrl+l"
}
]
```
- **Unbinding** To remove an existing or default keybinding, prefix a minus sign
(`-`) to the `command` name.
- **No Auto-unbinding** The same key can be bound to multiple commands in
different contexts at the same time. Therefore, creating a binding does not
automatically unbind the key from other commands.
### Keyboard Rules
- **Explicit Modifiers**: Key matching is explicit. For example, a binding for
`ctrl+f` will only trigger on exactly `ctrl+f`, not `ctrl+shift+f` or
`alt+ctrl+f`.
`alt+ctrl+f`. You must specify the exact modifier keys (`ctrl`, `shift`,
`alt`/`opt`/`option`, `cmd`/`meta`).
- **Literal Characters**: Terminals often translate complex key combinations
(especially on macOS with the `Option` key) into special characters, losing
modifier and keystroke information along the way. For example,`shift+5` might
be sent as `%`. In these cases, you must bind to the literal character `%` as
bindings to `shift+5` will never fire. To see precisely what is being sent,
enable `Debug Keystroke Logging` and hit f12 to open the debug log console.
- **Key Modifiers**: The supported key modifiers are:
- `ctrl`
- `shift`,
- `alt` (synonyms: `opt`, `option`)
- `cmd` (synonym: `meta`)
- **Base Key**: The base key can be any single unicode code point or any of the
following special keys:
(especially on macOS with the `Option` key) into special characters. To handle
this reliably across all operating systems and SSH sessions, you can bind
directly to the literal character produced. For example, instead of trying to
bind `shift+5`, bind directly to `%`.
- **Special Keys**: Supported special keys include:
- **Navigation**: `up`, `down`, `left`, `right`, `home`, `end`, `pageup`,
`pagedown`
- **Actions**: `enter`, `escape`, `tab`, `space`, `backspace`, `delete`,
@@ -229,9 +201,6 @@ a `key` combination.
the numbered radio option and confirm when the full number is entered.
- `Ctrl + O`: Expand or collapse paste placeholders (`[Pasted Text: X lines]`)
inline when the cursor is over the placeholder.
- `Ctrl + X` (while a plan is presented): Open the plan in an external editor to
[collaboratively edit or comment](../cli/plan-mode.md#collaborative-plan-editing)
on the implementation strategy.
- `Double-click` on a paste placeholder (alternate buffer mode only): Expand to
view full content inline. Double-click again to collapse.
+7 -33
View File
@@ -191,13 +191,9 @@ User, and (if configured) Admin directories.
#### System-wide policies (Admin)
Administrators can enforce system-wide policies (Tier 4) that override all user
and default settings. These policies can be loaded from standard system
locations or supplemental paths.
##### Standard Locations
These are the default paths the CLI searches for admin policies:
Administrators can enforce system-wide policies (Tier 3) that override all user
and default settings. These policies must be placed in specific, secure
directories:
| OS | Policy Directory Path |
| :---------- | :------------------------------------------------ |
@@ -205,25 +201,10 @@ These are the default paths the CLI searches for admin policies:
| **macOS** | `/Library/Application Support/GeminiCli/policies` |
| **Windows** | `C:\ProgramData\gemini-cli\policies` |
##### Supplemental Admin Policies
**Security Requirements:**
Administrators can also specify supplemental policy paths using:
- The `--admin-policy` command-line flag.
- The `adminPolicyPaths` setting in a system settings file.
These supplemental policies are assigned the same **Admin** tier (Base 4) as
policies in standard locations.
**Security Guard**: Supplemental admin policies are **ignored** if any `.toml`
policy files are found in the standard system location. This prevents flag-based
overrides when a central system policy has already been established.
#### Security Requirements
To prevent privilege escalation, the CLI enforces strict security checks on the
**standard system policy directory**. If checks fail, the policies in that
directory are **ignored**.
To prevent privilege escalation, the CLI enforces strict security checks on
admin directories. If checks fail, system policies are **ignored**.
- **Linux / macOS:** Must be owned by `root` (UID 0) and NOT writable by group
or others (e.g., `chmod 755`).
@@ -233,11 +214,6 @@ directory are **ignored**.
for non-admin groups. You may need to "Disable inheritance" in Advanced
Security Settings._
**Note:** Supplemental admin policies (provided via `--admin-policy` or
`adminPolicyPaths` settings) are **NOT** subject to these strict ownership
checks, as they are explicitly provided by the user or administrator in their
current execution context.
### TOML rule schema
Here is a breakdown of the fields available in a TOML policy rule:
@@ -342,9 +318,7 @@ policies, as it is much more robust than manually writing Fully Qualified Names
**1. Targeting a specific tool on a server**
Combine `mcpName` and `toolName` to target a single operation. When using
`mcpName`, the `toolName` field should strictly be the simple name of the tool
(e.g., `search`), **not** the Fully Qualified Name (e.g., `mcp_server_search`).
Combine `mcpName` and `toolName` to target a single operation.
```toml
# Allows the `search` tool on the `my-jira-server` MCP
-15
View File
@@ -124,21 +124,6 @@ topics on:
`advanced.excludedEnvVars` setting in your `settings.json` to exclude fewer
variables.
- **Warning: `npm WARN deprecated node-domexception@1.0.0` or
`npm WARN deprecated glob` during install/update**
- **Issue:** When installing or updating the Gemini CLI globally via
`npm install -g @google/gemini-cli` or `npm update -g @google/gemini-cli`,
you might see deprecation warnings regarding `node-domexception` or old
versions of `glob`.
- **Cause:** These warnings occur because some dependencies (or their
sub-dependencies, like `google-auth-library`) rely on older package
versions. Since Gemini CLI requires Node.js 20 or higher, the platform's
native features (like the native `DOMException`) are used, making these
warnings purely informational.
- **Solution:** These warnings are harmless and can be safely ignored. Your
installation or update will complete successfully and function properly
without any action required.
## Exit codes
The Gemini CLI uses specific exit codes to indicate the reason for termination.
-10
View File
@@ -47,11 +47,6 @@
"label": "Plan tasks with todos",
"slug": "docs/cli/tutorials/task-planning"
},
{
"label": "Use Plan Mode with model steering",
"badge": "🔬",
"slug": "docs/cli/tutorials/plan-mode-steering"
},
{
"label": "Web search and fetch",
"slug": "docs/cli/tutorials/web-tools"
@@ -111,11 +106,6 @@
{ "label": "MCP servers", "slug": "docs/tools/mcp-server" },
{ "label": "Model routing", "slug": "docs/cli/model-routing" },
{ "label": "Model selection", "slug": "docs/cli/model" },
{
"label": "Model steering",
"badge": "🔬",
"slug": "docs/cli/model-steering"
},
{
"label": "Notifications",
"badge": "🔬",
-8
View File
@@ -120,14 +120,6 @@ tools to detect if they are being run from within the Gemini CLI.
## Command restrictions
<!-- prettier-ignore -->
> [!WARNING]
> The `tools.core` setting is an **allowlist for _all_ built-in
> tools**, not just shell commands. When you set `tools.core` to any value,
> _only_ the tools explicitly listed will be enabled. This includes all built-in
> tools like `read_file`, `write_file`, `glob`, `grep_search`, `list_directory`,
> `replace`, etc.
You can restrict the commands that can be executed by the `run_shell_command`
tool by using the `tools.core` and `tools.exclude` settings in your
configuration file.
+4 -9
View File
@@ -82,14 +82,11 @@ const commonAliases = {
const cliConfig = {
...baseConfig,
banner: {
js: `const require = (await import('node:module')).createRequire(import.meta.url); const __chunk_filename = (await import('node:url')).fileURLToPath(import.meta.url); const __chunk_dirname = (await import('node:path')).dirname(__chunk_filename);`,
js: `const require = (await import('node:module')).createRequire(import.meta.url); globalThis.__filename = (await import('node:url')).fileURLToPath(import.meta.url); globalThis.__dirname = (await import('node:path')).dirname(globalThis.__filename);`,
},
entryPoints: { gemini: 'packages/cli/index.ts' },
outdir: 'bundle',
splitting: true,
entryPoints: ['packages/cli/index.ts'],
outfile: 'bundle/gemini.js',
define: {
__filename: '__chunk_filename',
__dirname: '__chunk_dirname',
'process.env.CLI_VERSION': JSON.stringify(pkg.version),
'process.env.GEMINI_SANDBOX_IMAGE_DEFAULT': JSON.stringify(
pkg.config?.sandboxImageUri,
@@ -106,13 +103,11 @@ const cliConfig = {
const a2aServerConfig = {
...baseConfig,
banner: {
js: `const require = (await import('node:module')).createRequire(import.meta.url); const __chunk_filename = (await import('node:url')).fileURLToPath(import.meta.url); const __chunk_dirname = (await import('node:path')).dirname(__chunk_filename);`,
js: `const require = (await import('node:module')).createRequire(import.meta.url); globalThis.__filename = (await import('node:url')).fileURLToPath(import.meta.url); globalThis.__dirname = (await import('node:path')).dirname(globalThis.__filename);`,
},
entryPoints: ['packages/a2a-server/src/http/server.ts'],
outfile: 'packages/a2a-server/dist/a2a-server.mjs',
define: {
__filename: '__chunk_filename',
__dirname: '__chunk_dirname',
'process.env.CLI_VERSION': JSON.stringify(pkg.version),
},
plugins: createWasmPlugins(),
+5
View File
@@ -35,6 +35,11 @@ const commonRestrictedSyntaxRules = [
message:
'Do not throw string literals or non-Error objects. Throw new Error("...") instead.',
},
{
selector: 'CallExpression[callee.name="fetch"]',
message:
'Use safeFetch() from "@/utils/fetch" instead of the global fetch() to ensure SSRF protection. If you are implementing a custom security layer, use an eslint-disable comment and explain why.',
},
];
export default tseslint.config(
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

@@ -1,64 +0,0 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
/**
* integration test to ensure no node.js deprecation warnings are emitted.
* must run for all supported node versions as warnings may vary by version.
*/
describe('deprecation-warnings', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it.each([
{ command: '--version', description: 'running --version' },
{ command: '--help', description: 'running with --help' },
])(
'should not emit any deprecation warnings when $description',
async ({ command, description }) => {
await rig.setup(
`should not emit any deprecation warnings when ${description}`,
);
const { stderr, exitCode } = await rig.runWithStreams([command]);
// node.js deprecation warnings: (node:12345) [DEP0040] DeprecationWarning: ...
const deprecationWarningPattern = /\[DEP\d+\].*DeprecationWarning/i;
const hasDeprecationWarning = deprecationWarningPattern.test(stderr);
if (hasDeprecationWarning) {
const deprecationMatches = stderr.match(
/\[DEP\d+\].*DeprecationWarning:.*/gi,
);
const warnings = deprecationMatches
? deprecationMatches.map((m) => m.trim()).join('\n')
: 'Unknown deprecation warning format';
throw new Error(
`Deprecation warnings detected in CLI output:\n${warnings}\n\n` +
`Full stderr:\n${stderr}\n\n` +
`This test ensures no deprecated Node.js modules are used. ` +
`Please update dependencies to use non-deprecated alternatives.`,
);
}
// only check exit code if no deprecation warnings found
if (exitCode !== 0) {
throw new Error(
`CLI exited with code ${exitCode} (expected 0). This may indicate a setup issue.\n` +
`Stderr: ${stderr}`,
);
}
},
);
});
-2
View File
@@ -1,2 +0,0 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"run_shell_command","args":{"command":"ls -F"}}}]},"finishReason":"STOP","index":0}]},{"candidates":[{"content":{"parts":[{"text":"I ran ls -F"}]},"finishReason":"STOP","index":0}]}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I ran ls -F"}]},"finishReason":"STOP","index":0}]}]}
-81
View File
@@ -1,81 +0,0 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { join } from 'node:path';
import { TestRig, GEMINI_DIR } from './test-helper.js';
import fs from 'node:fs';
describe('User Policy Regression Repro', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => {
if (rig) {
await rig.cleanup();
}
});
it('should respect policies in ~/.gemini/policies/allowed-tools.toml', async () => {
rig.setup('user-policy-test', {
fakeResponsesPath: join(import.meta.dirname, 'user-policy.responses'),
});
// Create ~/.gemini/policies/allowed-tools.toml
const userPoliciesDir = join(rig.homeDir!, GEMINI_DIR, 'policies');
fs.mkdirSync(userPoliciesDir, { recursive: true });
fs.writeFileSync(
join(userPoliciesDir, 'allowed-tools.toml'),
`
[[rule]]
toolName = "run_shell_command"
commandPrefix = "ls -F"
decision = "allow"
priority = 100
`,
);
// Run gemini with a prompt that triggers ls -F
// approvalMode: 'default' in headless mode will DENY if it hits ASK_USER
const result = await rig.run({
args: ['-p', 'Run ls -F', '--model', 'gemini-3.1-pro-preview'],
approvalMode: 'default',
});
expect(result).toContain('I ran ls -F');
expect(result).not.toContain('Tool execution denied by policy');
expect(result).not.toContain('Tool "run_shell_command" not found');
const toolLogs = rig.readToolLogs();
const lsLog = toolLogs.find(
(l) =>
l.toolRequest.name === 'run_shell_command' &&
l.toolRequest.args.includes('ls -F'),
);
expect(lsLog).toBeDefined();
expect(lsLog?.toolRequest.success).toBe(true);
});
it('should FAIL if policy is not present (sanity check)', async () => {
rig.setup('user-policy-sanity-check', {
fakeResponsesPath: join(import.meta.dirname, 'user-policy.responses'),
});
// DO NOT create the policy file here
// Run gemini with a prompt that triggers ls -F
const result = await rig.run({
args: ['-p', 'Run ls -F', '--model', 'gemini-3.1-pro-preview'],
approvalMode: 'default',
});
// In non-interactive mode, it should be denied
expect(result).toContain('Tool "run_shell_command" not found');
});
});
+142 -223
View File
@@ -1318,9 +1318,9 @@
}
},
"node_modules/@google-cloud/storage": {
"version": "7.19.0",
"resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.19.0.tgz",
"integrity": "sha512-n2FjE7NAOYyshogdc7KQOl/VZb4sneqPjWouSyia9CMDdMhRX5+RIbqalNmC7LOLzuLAN89VlF2HvG8na9G+zQ==",
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.17.0.tgz",
"integrity": "sha512-5m9GoZqKh52a1UqkxDBu/+WVFDALNtHg5up5gNmNbXQWBcV813tzJKsyDtKjOPrlR1em1TxtD7NSPCrObH7koQ==",
"license": "Apache-2.0",
"dependencies": {
"@google-cloud/paginator": "^5.0.0",
@@ -1329,7 +1329,7 @@
"abort-controller": "^3.0.0",
"async-retry": "^1.3.3",
"duplexify": "^4.1.3",
"fast-xml-parser": "^5.3.4",
"fast-xml-parser": "^4.4.1",
"gaxios": "^6.0.2",
"google-auth-library": "^9.6.3",
"html-entities": "^2.5.2",
@@ -1516,9 +1516,9 @@
}
},
"node_modules/@hono/node-server": {
"version": "1.19.11",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz",
"integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==",
"version": "1.19.9",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz",
"integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==",
"license": "MIT",
"engines": {
"node": ">=18.14.1"
@@ -2089,9 +2089,9 @@
}
},
"node_modules/@modelcontextprotocol/sdk/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@@ -3039,9 +3039,9 @@
"license": "BSD-3-Clause"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
"integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz",
"integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==",
"cpu": [
"arm"
],
@@ -3052,9 +3052,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
"integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz",
"integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==",
"cpu": [
"arm64"
],
@@ -3065,9 +3065,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
"integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz",
"integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==",
"cpu": [
"arm64"
],
@@ -3078,9 +3078,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
"integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz",
"integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==",
"cpu": [
"x64"
],
@@ -3091,9 +3091,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
"integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz",
"integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==",
"cpu": [
"arm64"
],
@@ -3104,9 +3104,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
"integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz",
"integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==",
"cpu": [
"x64"
],
@@ -3117,9 +3117,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
"integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz",
"integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==",
"cpu": [
"arm"
],
@@ -3130,9 +3130,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
"integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz",
"integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==",
"cpu": [
"arm"
],
@@ -3143,9 +3143,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
"integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz",
"integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==",
"cpu": [
"arm64"
],
@@ -3156,9 +3156,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
"integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz",
"integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==",
"cpu": [
"arm64"
],
@@ -3169,22 +3169,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
"integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
"cpu": [
"loong64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
"integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz",
"integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==",
"cpu": [
"loong64"
],
@@ -3195,22 +3182,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
"integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
"integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz",
"integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==",
"cpu": [
"ppc64"
],
@@ -3221,9 +3195,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
"integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz",
"integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==",
"cpu": [
"riscv64"
],
@@ -3234,9 +3208,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
"integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz",
"integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==",
"cpu": [
"riscv64"
],
@@ -3247,9 +3221,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
"integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz",
"integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==",
"cpu": [
"s390x"
],
@@ -3260,9 +3234,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
"integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz",
"integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==",
"cpu": [
"x64"
],
@@ -3273,9 +3247,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
"integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz",
"integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==",
"cpu": [
"x64"
],
@@ -3285,23 +3259,10 @@
"linux"
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
"integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"openbsd"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
"integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz",
"integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==",
"cpu": [
"arm64"
],
@@ -3312,9 +3273,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
"integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz",
"integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==",
"cpu": [
"arm64"
],
@@ -3325,9 +3286,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
"integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz",
"integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==",
"cpu": [
"ia32"
],
@@ -3338,9 +3299,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
"integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz",
"integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==",
"cpu": [
"x64"
],
@@ -3351,9 +3312,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
"integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz",
"integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==",
"cpu": [
"x64"
],
@@ -3408,9 +3369,9 @@
}
},
"node_modules/@secretlint/config-loader/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5270,9 +5231,9 @@
}
},
"node_modules/ajv": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5304,9 +5265,9 @@
}
},
"node_modules/ajv-formats/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@@ -8314,12 +8275,12 @@
}
},
"node_modules/express-rate-limit": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz",
"integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==",
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz",
"integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==",
"license": "MIT",
"dependencies": {
"ip-address": "10.1.0"
"ip-address": "10.0.1"
},
"engines": {
"node": ">= 16"
@@ -8461,25 +8422,10 @@
],
"license": "BSD-3-Clause"
},
"node_modules/fast-xml-builder": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.2.tgz",
"integrity": "sha512-NJAmiuVaJEjVa7TjLZKlYd7RqmzOC91EtPFXHvlTcqBVo50Qh7XV5IwvXi1c7NRz2Q/majGX9YLcwJtWgHjtkA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"dependencies": {
"path-expression-matcher": "^1.1.3"
}
},
"node_modules/fast-xml-parser": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.3.tgz",
"integrity": "sha512-Ymnuefk6VzAhT3SxLzVUw+nMio/wB1NGypHkgetwtXcK1JfryaHk4DWQFGVwQ9XgzyS5iRZ7C2ZGI4AMsdMZ6A==",
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz",
"integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==",
"funding": [
{
"type": "github",
@@ -8488,9 +8434,7 @@
],
"license": "MIT",
"dependencies": {
"fast-xml-builder": "^1.1.2",
"path-expression-matcher": "^1.1.3",
"strnum": "^2.1.2"
"strnum": "^1.1.1"
},
"bin": {
"fxparser": "src/cli/cli.js"
@@ -9555,9 +9499,9 @@
}
},
"node_modules/hono": {
"version": "4.12.7",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz",
"integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==",
"version": "4.11.9",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.11.9.tgz",
"integrity": "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ==",
"license": "MIT",
"engines": {
"node": ">=16.9.0"
@@ -10006,9 +9950,9 @@
}
},
"node_modules/ip-address": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
"integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==",
"license": "MIT",
"engines": {
"node": ">= 12"
@@ -10792,7 +10736,6 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"license": "MIT"
},
"node_modules/json-schema-typed": {
@@ -12871,21 +12814,6 @@
"node": ">=8"
}
},
"node_modules/path-expression-matcher": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz",
"integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -13919,9 +13847,9 @@
}
},
"node_modules/rollup": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
"integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz",
"integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==",
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.8"
@@ -13934,31 +13862,28 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.59.0",
"@rollup/rollup-android-arm64": "4.59.0",
"@rollup/rollup-darwin-arm64": "4.59.0",
"@rollup/rollup-darwin-x64": "4.59.0",
"@rollup/rollup-freebsd-arm64": "4.59.0",
"@rollup/rollup-freebsd-x64": "4.59.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
"@rollup/rollup-linux-arm-musleabihf": "4.59.0",
"@rollup/rollup-linux-arm64-gnu": "4.59.0",
"@rollup/rollup-linux-arm64-musl": "4.59.0",
"@rollup/rollup-linux-loong64-gnu": "4.59.0",
"@rollup/rollup-linux-loong64-musl": "4.59.0",
"@rollup/rollup-linux-ppc64-gnu": "4.59.0",
"@rollup/rollup-linux-ppc64-musl": "4.59.0",
"@rollup/rollup-linux-riscv64-gnu": "4.59.0",
"@rollup/rollup-linux-riscv64-musl": "4.59.0",
"@rollup/rollup-linux-s390x-gnu": "4.59.0",
"@rollup/rollup-linux-x64-gnu": "4.59.0",
"@rollup/rollup-linux-x64-musl": "4.59.0",
"@rollup/rollup-openbsd-x64": "4.59.0",
"@rollup/rollup-openharmony-arm64": "4.59.0",
"@rollup/rollup-win32-arm64-msvc": "4.59.0",
"@rollup/rollup-win32-ia32-msvc": "4.59.0",
"@rollup/rollup-win32-x64-gnu": "4.59.0",
"@rollup/rollup-win32-x64-msvc": "4.59.0",
"@rollup/rollup-android-arm-eabi": "4.53.2",
"@rollup/rollup-android-arm64": "4.53.2",
"@rollup/rollup-darwin-arm64": "4.53.2",
"@rollup/rollup-darwin-x64": "4.53.2",
"@rollup/rollup-freebsd-arm64": "4.53.2",
"@rollup/rollup-freebsd-x64": "4.53.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.53.2",
"@rollup/rollup-linux-arm-musleabihf": "4.53.2",
"@rollup/rollup-linux-arm64-gnu": "4.53.2",
"@rollup/rollup-linux-arm64-musl": "4.53.2",
"@rollup/rollup-linux-loong64-gnu": "4.53.2",
"@rollup/rollup-linux-ppc64-gnu": "4.53.2",
"@rollup/rollup-linux-riscv64-gnu": "4.53.2",
"@rollup/rollup-linux-riscv64-musl": "4.53.2",
"@rollup/rollup-linux-s390x-gnu": "4.53.2",
"@rollup/rollup-linux-x64-gnu": "4.53.2",
"@rollup/rollup-linux-x64-musl": "4.53.2",
"@rollup/rollup-openharmony-arm64": "4.53.2",
"@rollup/rollup-win32-arm64-msvc": "4.53.2",
"@rollup/rollup-win32-ia32-msvc": "4.53.2",
"@rollup/rollup-win32-x64-gnu": "4.53.2",
"@rollup/rollup-win32-x64-msvc": "4.53.2",
"fsevents": "~2.3.2"
}
},
@@ -14492,9 +14417,9 @@
}
},
"node_modules/simple-git": {
"version": "3.33.0",
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.33.0.tgz",
"integrity": "sha512-D4V/tGC2sjsoNhoMybKyGoE+v8A60hRawKQ1iFRA1zwuDgGZCBJ4ByOzZ5J8joBbi4Oam0qiPH+GhzmSBwbJng==",
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz",
"integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==",
"license": "MIT",
"dependencies": {
"@kwsites/file-exists": "^1.1.1",
@@ -14997,9 +14922,9 @@
}
},
"node_modules/strnum": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz",
"integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz",
"integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==",
"funding": [
{
"type": "github",
@@ -15179,9 +15104,9 @@
}
},
"node_modules/systeminformation": {
"version": "5.31.4",
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.31.4.tgz",
"integrity": "sha512-lZppDyQx91VdS5zJvAyGkmwe+Mq6xY978BDUG2wRkWE+jkmUF5ti8cvOovFQoN5bvSFKCXVkyKEaU5ec3SJiRg==",
"version": "5.30.2",
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.30.2.tgz",
"integrity": "sha512-Rrt5oFTWluUVuPlbtn3o9ja+nvjdF3Um4DG0KxqfYvpzcx7Q9plZBTjJiJy9mAouua4+OI7IUGBaG9Zyt9NgxA==",
"license": "MIT",
"os": [
"darwin",
@@ -15222,9 +15147,9 @@
}
},
"node_modules/table/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -15945,9 +15870,9 @@
}
},
"node_modules/underscore": {
"version": "1.13.8",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz",
"integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==",
"version": "1.13.7",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz",
"integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==",
"dev": true,
"license": "MIT"
},
@@ -17341,9 +17266,9 @@
}
},
"packages/core/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@@ -17391,12 +17316,6 @@
"node": ">= 4"
}
},
"packages/core/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"packages/core/node_modules/mime": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz",
@@ -26,7 +26,7 @@ describe('Task Event-Driven Scheduler', () => {
mockConfig = createMockConfig({
isEventDrivenSchedulerEnabled: () => true,
}) as Config;
messageBus = mockConfig.messageBus;
messageBus = mockConfig.getMessageBus();
mockEventBus = {
publish: vi.fn(),
on: vi.fn(),
@@ -360,7 +360,7 @@ describe('Task Event-Driven Scheduler', () => {
isEventDrivenSchedulerEnabled: () => true,
getApprovalMode: () => ApprovalMode.YOLO,
}) as Config;
const yoloMessageBus = yoloConfig.messageBus;
const yoloMessageBus = yoloConfig.getMessageBus();
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', yoloConfig, mockEventBus);
+7 -14
View File
@@ -5,7 +5,6 @@
*/
import {
type AgentLoopContext,
Scheduler,
type GeminiClient,
GeminiEventType,
@@ -115,8 +114,7 @@ export class Task {
this.scheduler = this.setupEventDrivenScheduler();
const loopContext: AgentLoopContext = this.config;
this.geminiClient = loopContext.geminiClient;
this.geminiClient = this.config.getGeminiClient();
this.pendingToolConfirmationDetails = new Map();
this.taskState = 'submitted';
this.eventBus = eventBus;
@@ -145,8 +143,7 @@ export class Task {
// process. This is not scoped to the individual task but reflects the global connection
// state managed within the @gemini-cli/core module.
async getMetadata(): Promise<TaskMetadata> {
const loopContext: AgentLoopContext = this.config;
const toolRegistry = loopContext.toolRegistry;
const toolRegistry = this.config.getToolRegistry();
const mcpServers = this.config.getMcpClientManager()?.getMcpServers() || {};
const serverStatuses = getAllMCPServerStatuses();
const servers = Object.keys(mcpServers).map((serverName) => ({
@@ -379,8 +376,7 @@ export class Task {
private messageBusListener?: (message: ToolCallsUpdateMessage) => void;
private setupEventDrivenScheduler(): Scheduler {
const loopContext: AgentLoopContext = this.config;
const messageBus = loopContext.messageBus;
const messageBus = this.config.getMessageBus();
const scheduler = new Scheduler({
schedulerId: this.id,
context: this.config,
@@ -399,11 +395,9 @@ export class Task {
dispose(): void {
if (this.messageBusListener) {
const loopContext: AgentLoopContext = this.config;
loopContext.messageBus.unsubscribe(
MessageBusType.TOOL_CALLS_UPDATE,
this.messageBusListener,
);
this.config
.getMessageBus()
.unsubscribe(MessageBusType.TOOL_CALLS_UPDATE, this.messageBusListener);
this.messageBusListener = undefined;
}
@@ -954,8 +948,7 @@ export class Task {
try {
if (correlationId) {
const loopContext: AgentLoopContext = this.config;
await loopContext.messageBus.publish({
await this.config.getMessageBus().publish({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId,
confirmed:
@@ -59,9 +59,6 @@ describe('a2a-server memory commands', () => {
} as unknown as ToolRegistry;
mockConfig = {
get toolRegistry() {
return mockToolRegistry;
},
getToolRegistry: vi.fn().mockReturnValue(mockToolRegistry),
} as unknown as Config;
@@ -171,6 +168,7 @@ describe('a2a-server memory commands', () => {
]);
expect(mockAddMemory).toHaveBeenCalledWith(fact);
expect(mockConfig.getToolRegistry).toHaveBeenCalled();
expect(mockToolRegistry.getTool).toHaveBeenCalledWith('save_memory');
expect(mockSaveMemoryTool.buildAndExecute).toHaveBeenCalledWith(
{ fact },
+1 -3
View File
@@ -15,7 +15,6 @@ import type {
CommandContext,
CommandExecutionResponse,
} from './types.js';
import type { AgentLoopContext } from '@google/gemini-cli-core';
const DEFAULT_SANITIZATION_CONFIG = {
allowedEnvironmentVariables: [],
@@ -96,8 +95,7 @@ export class AddMemoryCommand implements Command {
return { name: this.name, data: result.content };
}
const loopContext: AgentLoopContext = context.config;
const toolRegistry = loopContext.toolRegistry;
const toolRegistry = context.config.getToolRegistry();
const tool = toolRegistry.getTool(result.toolName);
if (tool) {
const abortController = new AbortController();
@@ -91,15 +91,6 @@ describe('loadConfig', () => {
expect(fetchAdminControlsOnce).not.toHaveBeenCalled();
});
it('should pass clientName as a2a-server to Config', async () => {
await loadConfig(mockSettings, mockExtensionLoader, taskId);
expect(Config).toHaveBeenCalledWith(
expect.objectContaining({
clientName: 'a2a-server',
}),
);
});
describe('when admin controls experiment is enabled', () => {
beforeEach(() => {
// We need to cast to any here to modify the mock implementation
-1
View File
@@ -62,7 +62,6 @@ export async function loadConfig(
const configParams: ConfigParameters = {
sessionId: taskId,
clientName: 'a2a-server',
model: PREVIEW_GEMINI_MODEL,
embeddingModel: DEFAULT_GEMINI_EMBEDDING_MODEL,
sandbox: undefined, // Sandbox might not be relevant for a server-side agent
+5 -21
View File
@@ -16,7 +16,6 @@ import {
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
GeminiClient,
HookSystem,
type MessageBus,
PolicyDecision,
tmpdir,
type Config,
@@ -32,27 +31,9 @@ export function createMockConfig(
const tmpDir = tmpdir();
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const mockConfig = {
get config() {
get toolRegistry(): ToolRegistry {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return this as unknown as Config;
},
get toolRegistry() {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const config = this as unknown as Config;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return config.getToolRegistry?.() as unknown as ToolRegistry;
},
get messageBus() {
return (
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(this as unknown as Config).getMessageBus?.() as unknown as MessageBus
);
},
get geminiClient() {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const config = this as unknown as Config;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return config.getGeminiClient?.() as unknown as GeminiClient;
return (this as unknown as Config).getToolRegistry();
},
getToolRegistry: vi.fn().mockReturnValue({
getTool: vi.fn(),
@@ -100,6 +81,9 @@ export function createMockConfig(
...overrides,
} as unknown as Config;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(mockConfig as unknown as { config: Config; promptId: string }).config =
mockConfig;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(mockConfig as unknown as { config: Config; promptId: string }).promptId =
'test-prompt-id';
+4 -6
View File
@@ -57,17 +57,15 @@ function hasMeta(obj: unknown): obj is { _meta?: Record<string, unknown> } {
return typeof obj === 'object' && obj !== null && '_meta' in obj;
}
import type { Content, Part, FunctionCall } from '@google/genai';
import {
SettingScope,
loadSettings,
type LoadedSettings,
} from '../config/settings.js';
import type { LoadedSettings } from '../config/settings.js';
import { SettingScope, loadSettings } from '../config/settings.js';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { z } from 'zod';
import { randomUUID } from 'node:crypto';
import { loadCliConfig, type CliArgs } from '../config/config.js';
import type { CliArgs } from '../config/config.js';
import { loadCliConfig } from '../config/config.js';
import { runExitCleanup } from '../utils/cleanup.js';
import { SessionSelector } from '../utils/sessionUtils.js';
+2 -1
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { listExtensions, type Config } from '@google/gemini-cli-core';
import { listExtensions } from '@google/gemini-cli-core';
import { SettingScope } from '../../config/settings.js';
import {
ExtensionManager,
@@ -18,6 +18,7 @@ import type {
CommandContext,
CommandExecutionResponse,
} from './types.js';
import type { Config } from '@google/gemini-cli-core';
export class ExtensionsCommand implements Command {
readonly name = 'extensions';
@@ -17,10 +17,8 @@ import {
import { handleInstall, installCommand } from './install.js';
import yargs from 'yargs';
import * as core from '@google/gemini-cli-core';
import {
ExtensionManager,
type inferInstallMetadata,
} from '../../config/extension-manager.js';
import type { inferInstallMetadata } from '../../config/extension-manager.js';
import { ExtensionManager } from '../../config/extension-manager.js';
import type {
promptForConsentNonInteractive,
requestConsentNonInteractive,
+2 -1
View File
@@ -6,7 +6,8 @@
import { describe, it, expect, vi } from 'vitest';
import { mcpCommand } from './mcp.js';
import yargs, { type Argv } from 'yargs';
import { type Argv } from 'yargs';
import yargs from 'yargs';
describe('mcp command', () => {
it('should have correct command definition', () => {
@@ -5,10 +5,11 @@
*/
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import { coreEvents, type Config } from '@google/gemini-cli-core';
import { coreEvents } from '@google/gemini-cli-core';
import { handleList, listCommand } from './list.js';
import { loadSettings, type LoadedSettings } from '../../config/settings.js';
import { loadCliConfig } from '../../config/config.js';
import type { Config } from '@google/gemini-cli-core';
import chalk from 'chalk';
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
+4 -59
View File
@@ -1773,7 +1773,7 @@ describe('loadCliConfig model selection', () => {
});
it('always prefers model from argv', async () => {
process.argv = ['node', 'script.js', '--model', 'gemini-2.5-flash'];
process.argv = ['node', 'script.js', '--model', 'gemini-2.5-flash-preview'];
const argv = await parseArguments(createTestMergedSettings());
const config = await loadCliConfig(
createTestMergedSettings({
@@ -1785,11 +1785,11 @@ describe('loadCliConfig model selection', () => {
argv,
);
expect(config.getModel()).toBe('gemini-2.5-flash');
expect(config.getModel()).toBe('gemini-2.5-flash-preview');
});
it('selects the model from argv if provided', async () => {
process.argv = ['node', 'script.js', '--model', 'gemini-2.5-flash'];
process.argv = ['node', 'script.js', '--model', 'gemini-2.5-flash-preview'];
const argv = await parseArguments(createTestMergedSettings());
const config = await loadCliConfig(
createTestMergedSettings({
@@ -1799,7 +1799,7 @@ describe('loadCliConfig model selection', () => {
argv,
);
expect(config.getModel()).toBe('gemini-2.5-flash');
expect(config.getModel()).toBe('gemini-2.5-flash-preview');
});
it('selects the default auto model if provided via auto alias', async () => {
@@ -3616,58 +3616,3 @@ describe('loadCliConfig mcpEnabled', () => {
});
});
});
describe('loadCliConfig acpMode and clientName', () => {
beforeEach(() => {
vi.resetAllMocks();
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([]);
});
afterEach(() => {
vi.unstubAllEnvs();
});
it('should set acpMode to true and detect clientName when --acp flag is used', async () => {
process.argv = ['node', 'script.js', '--acp'];
vi.stubEnv('TERM_PROGRAM', 'vscode');
vi.stubEnv('VSCODE_GIT_ASKPASS_MAIN', '');
vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', '');
const argv = await parseArguments(createTestMergedSettings());
const config = await loadCliConfig(
createTestMergedSettings(),
'test-session',
argv,
);
expect(config.getAcpMode()).toBe(true);
expect(config.getClientName()).toBe('acp-vscode');
});
it('should set acpMode to true but leave clientName undefined for generic terminals', async () => {
process.argv = ['node', 'script.js', '--acp'];
vi.stubEnv('TERM_PROGRAM', 'iTerm.app'); // Generic terminal
vi.stubEnv('VSCODE_GIT_ASKPASS_MAIN', '');
vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', '');
const argv = await parseArguments(createTestMergedSettings());
const config = await loadCliConfig(
createTestMergedSettings(),
'test-session',
argv,
);
expect(config.getAcpMode()).toBe(true);
expect(config.getClientName()).toBeUndefined();
});
it('should set acpMode to false and clientName to undefined by default', async () => {
process.argv = ['node', 'script.js'];
const argv = await parseArguments(createTestMergedSettings());
const config = await loadCliConfig(
createTestMergedSettings(),
'test-session',
argv,
);
expect(config.getAcpMode()).toBe(false);
expect(config.getClientName()).toBeUndefined();
});
});
+26 -61
View File
@@ -31,8 +31,6 @@ import {
type HierarchicalMemory,
coreEvents,
GEMINI_MODEL_ALIAS_AUTO,
isValidModelOrAlias,
getValidModelsAndAliases,
getAdminErrorMessage,
isHeadlessMode,
Config,
@@ -42,7 +40,6 @@ import {
type HookDefinition,
type HookEventName,
type OutputFormat,
detectIdeFromEnv,
} from '@google/gemini-cli-core';
import {
type Settings,
@@ -79,7 +76,6 @@ export interface CliArgs {
yolo: boolean | undefined;
approvalMode: string | undefined;
policy: string[] | undefined;
adminPolicy: string[] | undefined;
allowedMcpServerNames: string[] | undefined;
allowedTools: string[] | undefined;
acp?: boolean;
@@ -101,21 +97,6 @@ export interface CliArgs {
isCommand: boolean | undefined;
}
/**
* Helper to coerce comma-separated or multiple flag values into a flat array.
*/
const coerceCommaSeparated = (values: string[]): string[] => {
if (values.length === 1 && values[0] === '') {
return [''];
}
return values.flatMap((v) =>
v
.split(',')
.map((s) => s.trim())
.filter(Boolean),
);
};
export async function parseArguments(
settings: MergedSettings,
): Promise<CliArgs> {
@@ -185,15 +166,14 @@ export async function parseArguments(
nargs: 1,
description:
'Additional policy files or directories to load (comma-separated or multiple --policy)',
coerce: coerceCommaSeparated,
})
.option('admin-policy', {
type: 'array',
string: true,
nargs: 1,
description:
'Additional admin policy files or directories to load (comma-separated or multiple --admin-policy)',
coerce: coerceCommaSeparated,
coerce: (policies: string[]) =>
// Handle comma-separated values
policies.flatMap((p) =>
p
.split(',')
.map((s) => s.trim())
.filter(Boolean),
),
})
.option('acp', {
type: 'boolean',
@@ -209,7 +189,11 @@ export async function parseArguments(
string: true,
nargs: 1,
description: 'Allowed MCP server names',
coerce: coerceCommaSeparated,
coerce: (mcpServerNames: string[]) =>
// Handle comma-separated values
mcpServerNames.flatMap((mcpServerName) =>
mcpServerName.split(',').map((m) => m.trim()),
),
})
.option('allowed-tools', {
type: 'array',
@@ -217,7 +201,9 @@ export async function parseArguments(
nargs: 1,
description:
'[DEPRECATED: Use Policy Engine instead See https://geminicli.com/docs/core/policy-engine] Tools that are allowed to run without confirmation',
coerce: coerceCommaSeparated,
coerce: (tools: string[]) =>
// Handle comma-separated values
tools.flatMap((tool) => tool.split(',').map((t) => t.trim())),
})
.option('extensions', {
alias: 'e',
@@ -226,7 +212,11 @@ export async function parseArguments(
nargs: 1,
description:
'A list of extensions to use. If not provided, all extensions are used.',
coerce: coerceCommaSeparated,
coerce: (extensions: string[]) =>
// Handle comma-separated values
extensions.flatMap((extension) =>
extension.split(',').map((e) => e.trim()),
),
})
.option('list-extensions', {
alias: 'l',
@@ -268,7 +258,9 @@ export async function parseArguments(
nargs: 1,
description:
'Additional directories to include in the workspace (comma-separated or multiple --include-directories)',
coerce: coerceCommaSeparated,
coerce: (dirs: string[]) =>
// Handle comma-separated values
dirs.flatMap((dir) => dir.split(',').map((d) => d.trim())),
})
.option('screen-reader', {
type: 'boolean',
@@ -651,8 +643,7 @@ export async function loadCliConfig(
...settings.mcp,
allowed: argv.allowedMcpServerNames ?? settings.mcp?.allowed,
},
policyPaths: argv.policy ?? settings.policyPaths,
adminPolicyPaths: argv.adminPolicy ?? settings.adminPolicyPaths,
policyPaths: argv.policy,
};
const { workspacePoliciesDir, policyUpdateConfirmationRequest } =
@@ -673,18 +664,6 @@ export async function loadCliConfig(
const specifiedModel =
argv.model || process.env['GEMINI_MODEL'] || settings.model?.name;
// Validate the model if one was explicitly specified
if (specifiedModel && specifiedModel !== GEMINI_MODEL_ALIAS_AUTO) {
if (!isValidModelOrAlias(specifiedModel)) {
const validModels = getValidModelsAndAliases();
throw new FatalConfigError(
`Invalid model: "${specifiedModel}"\n\n` +
`Valid models and aliases:\n${validModels.map((m) => ` - ${m}`).join('\n')}\n\n` +
`Use /model to switch models interactively.`,
);
}
}
const resolvedModel =
specifiedModel === GEMINI_MODEL_ALIAS_AUTO
? defaultModel
@@ -725,21 +704,8 @@ export async function loadCliConfig(
}
}
const isAcpMode = !!argv.acp || !!argv.experimentalAcp;
let clientName: string | undefined = undefined;
if (isAcpMode) {
const ide = detectIdeFromEnv();
if (
ide &&
(ide.name !== 'vscode' || process.env['TERM_PROGRAM'] === 'vscode')
) {
clientName = `acp-${ide.name}`;
}
}
return new Config({
acpMode: isAcpMode,
clientName,
acpMode: !!argv.acp || !!argv.experimentalAcp,
sessionId,
clientVersion: await getVersion(),
embeddingModel: DEFAULT_GEMINI_EMBEDDING_MODEL,
@@ -857,7 +823,6 @@ export async function loadCliConfig(
disableLLMCorrection: settings.tools?.disableLLMCorrection,
rawOutput: argv.rawOutput,
acceptRawOutputRisk: argv.acceptRawOutputRisk,
dynamicModelConfiguration: settings.experimental?.dynamicModelConfiguration,
modelConfigServiceConfig: settings.modelConfigs,
// TODO: loading of hooks based on workspace trust
enableHooks: settings.hooksConfig.enabled,
@@ -9,16 +9,16 @@ import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { ExtensionManager } from './extension-manager.js';
import { createTestMergedSettings, type MergedSettings } from './settings.js';
import { createTestMergedSettings } from './settings.js';
import { createExtension } from '../test-utils/createExtension.js';
import { EXTENSIONS_DIRECTORY_NAME } from './extensions/variables.js';
import { themeManager } from '../ui/themes/theme-manager.js';
import {
TrustLevel,
loadTrustedFolders,
isWorkspaceTrusted,
} from './trustedFolders.js';
import { getRealPath, type CustomTheme } from '@google/gemini-cli-core';
import { getRealPath } from '@google/gemini-cli-core';
import type { MergedSettings } from './settings.js';
const mockHomedir = vi.hoisted(() => vi.fn(() => '/tmp/mock-home'));
@@ -39,26 +39,6 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
};
});
const testTheme: CustomTheme = {
type: 'custom',
name: 'MyTheme',
background: {
primary: '#282828',
diff: { added: '#2b3312', removed: '#341212' },
},
text: {
primary: '#ebdbb2',
secondary: '#a89984',
link: '#83a598',
accent: '#d3869b',
},
status: {
success: '#b8bb26',
warning: '#fabd2f',
error: '#fb4934',
},
};
describe('ExtensionManager', () => {
let tempHomeDir: string;
let tempWorkspaceDir: string;
@@ -86,7 +66,6 @@ describe('ExtensionManager', () => {
});
afterEach(() => {
themeManager.clearExtensionThemes();
try {
fs.rmSync(tempHomeDir, { recursive: true, force: true });
} catch (_e) {
@@ -506,45 +485,4 @@ describe('ExtensionManager', () => {
).rejects.toThrow(/already installed/);
});
});
describe('early theme registration', () => {
it('should register themes with ThemeManager during loadExtensions for active extensions', async () => {
createExtension({
extensionsDir: userExtensionsDir,
name: 'themed-ext',
version: '1.0.0',
themes: [testTheme],
});
await extensionManager.loadExtensions();
expect(themeManager.getCustomThemeNames()).toContain(
'MyTheme (themed-ext)',
);
});
it('should not register themes for inactive extensions', async () => {
createExtension({
extensionsDir: userExtensionsDir,
name: 'disabled-ext',
version: '1.0.0',
themes: [testTheme],
});
// Disable the extension by creating an enablement override
const manager = new ExtensionManager({
enabledExtensionOverrides: ['none'],
settings: createTestMergedSettings(),
workspaceDir: tempWorkspaceDir,
requestConsent: vi.fn().mockResolvedValue(true),
requestSetting: null,
});
await manager.loadExtensions();
expect(themeManager.getCustomThemeNames()).not.toContain(
'MyTheme (disabled-ext)',
);
});
});
});
+1 -8
View File
@@ -564,7 +564,7 @@ Would you like to attempt to install via "git clone" instead?`,
protected override async startExtension(extension: GeminiCLIExtension) {
await super.startExtension(extension);
if (extension.themes && !themeManager.hasExtensionThemes(extension.name)) {
if (extension.themes) {
themeManager.registerExtensionThemes(extension.name, extension.themes);
}
}
@@ -624,13 +624,6 @@ Would you like to attempt to install via "git clone" instead?`,
this.loadedExtensions = builtExtensions;
// Register extension themes early so they're available at startup.
for (const ext of this.loadedExtensions) {
if (ext.isActive && ext.themes) {
themeManager.registerExtensionThemes(ext.name, ext.themes);
}
}
await Promise.all(
this.loadedExtensions.map((ext) => this.maybeStartExtension(ext)),
);
@@ -15,10 +15,11 @@ import {
type ExtensionUpdateStatus,
} from '../../ui/state/extensions.js';
import { ExtensionStorage } from './storage.js';
import { copyExtension, type ExtensionManager } from '../extension-manager.js';
import { copyExtension } from '../extension-manager.js';
import { checkForExtensionUpdate } from './github.js';
import { loadInstallMetadata } from '../extension.js';
import * as fs from 'node:fs';
import type { ExtensionManager } from '../extension-manager.js';
import type { GeminiCLIExtension } from '@google/gemini-cli-core';
// Mock dependencies
-1
View File
@@ -61,7 +61,6 @@ export async function createPolicyEngineConfig(
tools: settings.tools,
mcpServers: settings.mcpServers,
policyPaths: settings.policyPaths,
adminPolicyPaths: settings.adminPolicyPaths,
workspacePoliciesDir,
};
+13 -180
View File
@@ -90,13 +90,7 @@ describe('loadSandboxConfig', () => {
process.env['GEMINI_SANDBOX'] = 'docker';
mockedCommandExistsSync.mockReturnValue(true);
const config = await loadSandboxConfig({}, {});
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'docker',
image: 'default/image',
});
expect(config).toEqual({ command: 'docker', image: 'default/image' });
expect(mockedCommandExistsSync).toHaveBeenCalledWith('docker');
});
@@ -119,13 +113,7 @@ describe('loadSandboxConfig', () => {
process.env['GEMINI_SANDBOX'] = 'lxc';
mockedCommandExistsSync.mockReturnValue(true);
const config = await loadSandboxConfig({}, {});
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'lxc',
image: 'default/image',
});
expect(config).toEqual({ command: 'lxc', image: 'default/image' });
expect(mockedCommandExistsSync).toHaveBeenCalledWith('lxc');
});
@@ -146,9 +134,6 @@ describe('loadSandboxConfig', () => {
);
const config = await loadSandboxConfig({}, { sandbox: true });
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'sandbox-exec',
image: 'default/image',
});
@@ -159,9 +144,6 @@ describe('loadSandboxConfig', () => {
mockedCommandExistsSync.mockReturnValue(true); // all commands exist
const config = await loadSandboxConfig({}, { sandbox: true });
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'sandbox-exec',
image: 'default/image',
});
@@ -171,26 +153,14 @@ describe('loadSandboxConfig', () => {
mockedOsPlatform.mockReturnValue('linux');
mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'docker');
const config = await loadSandboxConfig({ tools: { sandbox: true } }, {});
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'docker',
image: 'default/image',
});
expect(config).toEqual({ command: 'docker', image: 'default/image' });
});
it('should use podman if available and docker is not', async () => {
mockedOsPlatform.mockReturnValue('linux');
mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'podman');
const config = await loadSandboxConfig({}, { sandbox: true });
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'podman',
image: 'default/image',
});
expect(config).toEqual({ command: 'podman', image: 'default/image' });
});
it('should throw if sandbox: true but no command is found', async () => {
@@ -207,13 +177,7 @@ describe('loadSandboxConfig', () => {
it('should use the specified command if it exists', async () => {
mockedCommandExistsSync.mockReturnValue(true);
const config = await loadSandboxConfig({}, { sandbox: 'podman' });
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'podman',
image: 'default/image',
});
expect(config).toEqual({ command: 'podman', image: 'default/image' });
expect(mockedCommandExistsSync).toHaveBeenCalledWith('podman');
});
@@ -241,26 +205,14 @@ describe('loadSandboxConfig', () => {
process.env['GEMINI_SANDBOX'] = 'docker';
mockedCommandExistsSync.mockReturnValue(true);
const config = await loadSandboxConfig({}, {});
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'docker',
image: 'env/image',
});
expect(config).toEqual({ command: 'docker', image: 'env/image' });
});
it('should use image from package.json if env var is not set', async () => {
process.env['GEMINI_SANDBOX'] = 'docker';
mockedCommandExistsSync.mockReturnValue(true);
const config = await loadSandboxConfig({}, {});
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'docker',
image: 'default/image',
});
expect(config).toEqual({ command: 'docker', image: 'default/image' });
});
it('should return undefined if command is found but no image is configured', async () => {
@@ -282,115 +234,20 @@ describe('loadSandboxConfig', () => {
'should enable sandbox for value: %s',
async (value) => {
const config = await loadSandboxConfig({}, { sandbox: value });
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'docker',
image: 'default/image',
});
expect(config).toEqual({ command: 'docker', image: 'default/image' });
},
);
it.each([false, 'false', '0', undefined, null, ''])(
'should disable sandbox for value: %s',
async (value) => {
// `null` is not a valid type for the arg, but good to test falsiness
// \`null\` is not a valid type for the arg, but good to test falsiness
const config = await loadSandboxConfig({}, { sandbox: value });
expect(config).toBeUndefined();
},
);
});
describe('with SandboxConfig object in settings', () => {
beforeEach(() => {
mockedOsPlatform.mockReturnValue('linux');
mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'docker');
});
it('should support object structure with enabled: true', async () => {
const config = await loadSandboxConfig(
{
tools: {
sandbox: {
enabled: true,
allowedPaths: ['/tmp'],
networkAccess: true,
},
},
},
{},
);
expect(config).toEqual({
enabled: true,
allowedPaths: ['/tmp'],
networkAccess: true,
command: 'docker',
image: 'default/image',
});
});
it('should support object structure with explicit command', async () => {
mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'podman');
const config = await loadSandboxConfig(
{
tools: {
sandbox: {
enabled: true,
command: 'podman',
},
},
},
{},
);
expect(config?.command).toBe('podman');
});
it('should support object structure with custom image', async () => {
const config = await loadSandboxConfig(
{
tools: {
sandbox: {
enabled: true,
image: 'custom/image',
},
},
},
{},
);
expect(config?.image).toBe('custom/image');
});
it('should return undefined if enabled is false in object', async () => {
const config = await loadSandboxConfig(
{
tools: {
sandbox: {
enabled: false,
},
},
},
{},
);
expect(config).toBeUndefined();
});
it('should prioritize CLI flag over settings object', async () => {
const config = await loadSandboxConfig(
{
tools: {
sandbox: {
enabled: true,
allowedPaths: ['/settings-path'],
},
},
},
{ sandbox: false },
);
expect(config).toBeUndefined();
});
});
describe('with sandbox: runsc (gVisor)', () => {
beforeEach(() => {
mockedOsPlatform.mockReturnValue('linux');
@@ -400,13 +257,7 @@ describe('loadSandboxConfig', () => {
it('should use runsc via CLI argument on Linux', async () => {
const config = await loadSandboxConfig({}, { sandbox: 'runsc' });
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'runsc',
image: 'default/image',
});
expect(config).toEqual({ command: 'runsc', image: 'default/image' });
expect(mockedCommandExistsSync).toHaveBeenCalledWith('runsc');
expect(mockedCommandExistsSync).toHaveBeenCalledWith('docker');
});
@@ -415,13 +266,7 @@ describe('loadSandboxConfig', () => {
process.env['GEMINI_SANDBOX'] = 'runsc';
const config = await loadSandboxConfig({}, {});
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'runsc',
image: 'default/image',
});
expect(config).toEqual({ command: 'runsc', image: 'default/image' });
expect(mockedCommandExistsSync).toHaveBeenCalledWith('runsc');
expect(mockedCommandExistsSync).toHaveBeenCalledWith('docker');
});
@@ -432,13 +277,7 @@ describe('loadSandboxConfig', () => {
{},
);
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'runsc',
image: 'default/image',
});
expect(config).toEqual({ command: 'runsc', image: 'default/image' });
expect(mockedCommandExistsSync).toHaveBeenCalledWith('runsc');
expect(mockedCommandExistsSync).toHaveBeenCalledWith('docker');
});
@@ -450,13 +289,7 @@ describe('loadSandboxConfig', () => {
{ sandbox: 'podman' },
);
expect(config).toEqual({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'runsc',
image: 'default/image',
});
expect(config).toEqual({ command: 'runsc', image: 'default/image' });
});
it('should reject runsc on macOS (Linux-only)', async () => {
+5 -30
View File
@@ -23,7 +23,7 @@ const __dirname = path.dirname(__filename);
interface SandboxCliArgs {
sandbox?: boolean | string | null;
}
const VALID_SANDBOX_COMMANDS = [
const VALID_SANDBOX_COMMANDS: ReadonlyArray<SandboxConfig['command']> = [
'docker',
'podman',
'sandbox-exec',
@@ -31,10 +31,8 @@ const VALID_SANDBOX_COMMANDS = [
'lxc',
];
function isSandboxCommand(
value: string,
): value is Exclude<SandboxConfig['command'], undefined> {
return VALID_SANDBOX_COMMANDS.includes(value);
function isSandboxCommand(value: string): value is SandboxConfig['command'] {
return (VALID_SANDBOX_COMMANDS as readonly string[]).includes(value);
}
function getSandboxCommand(
@@ -118,36 +116,13 @@ export async function loadSandboxConfig(
argv: SandboxCliArgs,
): Promise<SandboxConfig | undefined> {
const sandboxOption = argv.sandbox ?? settings.tools?.sandbox;
let sandboxValue: boolean | string | null | undefined;
let allowedPaths: string[] = [];
let networkAccess = false;
let customImage: string | undefined;
if (
typeof sandboxOption === 'object' &&
sandboxOption !== null &&
!Array.isArray(sandboxOption)
) {
const config = sandboxOption;
sandboxValue = config.enabled ? (config.command ?? true) : false;
allowedPaths = config.allowedPaths ?? [];
networkAccess = config.networkAccess ?? false;
customImage = config.image;
} else if (typeof sandboxOption !== 'object' || sandboxOption === null) {
sandboxValue = sandboxOption;
}
const command = getSandboxCommand(sandboxValue);
const command = getSandboxCommand(sandboxOption);
const packageJson = await getPackageJson(__dirname);
const image =
process.env['GEMINI_SANDBOX_IMAGE'] ??
process.env['GEMINI_SANDBOX_IMAGE_DEFAULT'] ??
customImage ??
packageJson?.config?.sandboxImageUri;
return command && image
? { enabled: true, allowedPaths, networkAccess, command, image }
: undefined;
return command && image ? { command, image } : undefined;
}
+18 -131
View File
@@ -18,7 +18,6 @@ import {
type AuthType,
type AgentOverride,
type CustomTheme,
type SandboxConfig,
} from '@google/gemini-cli-core';
import type { SessionRetentionSettings } from './settings.js';
import { DEFAULT_MIN_RETENTION } from '../utils/sessionCleanup.js';
@@ -135,18 +134,6 @@ export interface SettingsSchema {
export type MemoryImportFormat = 'tree' | 'flat';
export type DnsResolutionOrder = 'ipv4first' | 'verbatim';
const pathArraySetting = (label: string, description: string) => ({
type: 'array' as const,
label,
category: 'Advanced' as const,
requiresRestart: true as const,
default: [] as string[],
description,
showInDialog: false as const,
items: { type: 'string' as const },
mergeStrategy: MergeStrategy.UNION,
});
/**
* The canonical schema for all settings.
* The structure of this object defines the structure of the `Settings` type.
@@ -169,15 +156,17 @@ const SETTINGS_SCHEMA = {
},
},
policyPaths: pathArraySetting(
'Policy Paths',
'Additional policy files or directories to load.',
),
adminPolicyPaths: pathArraySetting(
'Admin Policy Paths',
'Additional admin policy files or directories to load.',
),
policyPaths: {
type: 'array',
label: 'Policy Paths',
category: 'Advanced',
requiresRestart: true,
default: [] as string[],
description: 'Additional policy files or directories to load.',
showInDialog: false,
items: { type: 'string' },
mergeStrategy: MergeStrategy.UNION,
},
general: {
type: 'object',
@@ -540,16 +529,6 @@ const SETTINGS_SCHEMA = {
description: 'Hide helpful tips in the UI',
showInDialog: true,
},
escapePastedAtSymbols: {
type: 'boolean',
label: 'Escape Pasted @ Symbols',
category: 'UI',
requiresRestart: false,
default: false,
description:
'When enabled, @ symbols in pasted text are escaped to prevent unintended @path expansion.',
showInDialog: true,
},
showShortcutsHint: {
type: 'boolean',
label: 'Show Shortcuts Hint',
@@ -1039,20 +1018,6 @@ const SETTINGS_SCHEMA = {
'Apply specific configuration overrides based on matches, with a primary key of model (or alias). The most specific match will be used.',
showInDialog: false,
},
modelDefinitions: {
type: 'object',
label: 'Model Definitions',
category: 'Model',
requiresRestart: true,
default: DEFAULT_MODEL_CONFIGS.modelDefinitions,
description:
'Registry of model metadata, including tier, family, and features.',
showInDialog: false,
additionalProperties: {
type: 'object',
ref: 'ModelDefinition',
},
},
},
},
@@ -1131,16 +1096,6 @@ const SETTINGS_SCHEMA = {
description: 'Model override for the visual agent.',
showInDialog: false,
},
disableUserInput: {
type: 'boolean',
label: 'Disable User Input',
category: 'Advanced',
requiresRestart: false,
default: true,
description:
'Disable user input on browser window during automation.',
showInDialog: false,
},
},
},
},
@@ -1298,8 +1253,8 @@ const SETTINGS_SCHEMA = {
label: 'Sandbox',
category: 'Tools',
requiresRestart: true,
default: undefined as boolean | string | SandboxConfig | undefined,
ref: 'BooleanOrStringOrObject',
default: undefined as boolean | string | undefined,
ref: 'BooleanOrString',
description: oneLine`
Sandbox execution environment.
Set to a boolean to enable or disable the sandbox, provide a string path to a sandbox profile,
@@ -1924,16 +1879,6 @@ const SETTINGS_SCHEMA = {
'Enable web fetch behavior that bypasses LLM summarization.',
showInDialog: true,
},
dynamicModelConfiguration: {
type: 'boolean',
label: 'Dynamic Model Configuration',
category: 'Experimental',
requiresRestart: true,
default: false,
description:
'Enable dynamic model configuration (definitions, resolutions, and chains) via settings.',
showInDialog: false,
},
gemmaModelRouter: {
type: 'object',
label: 'Gemma Model Router',
@@ -2663,44 +2608,9 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record<
description: 'Accepts either a single string or an array of strings.',
anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],
},
BooleanOrStringOrObject: {
description:
'Accepts either a boolean flag, a string command name, or a configuration object.',
anyOf: [
{ type: 'boolean' },
{ type: 'string' },
{
type: 'object',
description: 'Sandbox configuration object.',
additionalProperties: false,
properties: {
enabled: {
type: 'boolean',
description: 'Enables or disables the sandbox.',
},
command: {
type: 'string',
description:
'The sandbox command to use (docker, podman, sandbox-exec, runsc, lxc).',
enum: ['docker', 'podman', 'sandbox-exec', 'runsc', 'lxc'],
},
image: {
type: 'string',
description: 'The sandbox image to use.',
},
allowedPaths: {
type: 'array',
description:
'A list of absolute host paths that should be accessible within the sandbox.',
items: { type: 'string' },
},
networkAccess: {
type: 'boolean',
description: 'Whether the sandbox should have internet access.',
},
},
},
],
BooleanOrString: {
description: 'Accepts either a boolean flag or a string command name.',
anyOf: [{ type: 'boolean' }, { type: 'string' }],
},
HookDefinitionArray: {
type: 'array',
@@ -2750,25 +2660,6 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record<
},
},
},
ModelDefinition: {
type: 'object',
description: 'Model metadata registry entry.',
properties: {
displayName: { type: 'string' },
tier: { enum: ['pro', 'flash', 'flash-lite', 'custom', 'auto'] },
family: { type: 'string' },
isPreview: { type: 'boolean' },
dialogLocation: { enum: ['main', 'manual'] },
dialogDescription: { type: 'string' },
features: {
type: 'object',
properties: {
thinking: { type: 'boolean' },
multimodalToolUse: { type: 'boolean' },
},
},
},
},
};
export function getSettingsSchema(): SettingsSchemaType {
@@ -2786,9 +2677,7 @@ type InferSettings<T extends SettingsSchema> = {
? boolean
: T[K]['default'] extends string
? string
: T[K]['default'] extends ReadonlyArray<infer U>
? U[]
: T[K]['default'];
: T[K]['default'];
};
type InferMergedSettings<T extends SettingsSchema> = {
@@ -2802,9 +2691,7 @@ type InferMergedSettings<T extends SettingsSchema> = {
? boolean
: T[K]['default'] extends string
? string
: T[K]['default'] extends ReadonlyArray<infer U>
? U[]
: T[K]['default'];
: T[K]['default'];
};
export type Settings = InferSettings<SettingsSchemaType>;
@@ -19,8 +19,9 @@ import {
isWorkspaceTrusted,
resetTrustedFoldersForTesting,
} from './trustedFolders.js';
import { loadEnvironment, type Settings } from './settings.js';
import { loadEnvironment } from './settings.js';
import { createMockSettings } from '../test-utils/settings.js';
import type { Settings } from './settings.js';
// We explicitly do NOT mock 'fs' or 'proper-lockfile' here to ensure
// we are testing the actual behavior on the real file system.
+2 -8
View File
@@ -4,14 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
describe,
it,
expect,
vi,
beforeEach,
type MockInstance,
} from 'vitest';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
runDeferredCommand,
defer,
@@ -21,6 +14,7 @@ import {
import { ExitCodes } from '@google/gemini-cli-core';
import type { ArgumentsCamelCase, CommandModule } from 'yargs';
import { createMockSettings } from './test-utils/settings.js';
import type { MockInstance } from 'vitest';
const { mockRunExitCleanup, mockCoreEvents } = vi.hoisted(() => ({
mockRunExitCleanup: vi.fn(),
+16 -76
View File
@@ -21,19 +21,15 @@ import {
startInteractiveUI,
getNodeMemoryArgs,
} from './gemini.js';
import {
loadCliConfig,
parseArguments,
type CliArgs,
} from './config/config.js';
import { loadCliConfig, parseArguments } from './config/config.js';
import { loadSandboxConfig } from './config/sandboxConfig.js';
import { createMockSandboxConfig } from '@google/gemini-cli-test-utils';
import { terminalCapabilityManager } from './ui/utils/terminalCapabilityManager.js';
import { start_sandbox } from './utils/sandbox.js';
import { validateNonInteractiveAuth } from './validateNonInterActiveAuth.js';
import os from 'node:os';
import v8 from 'node:v8';
import { loadSettings, type LoadedSettings } from './config/settings.js';
import { type CliArgs } from './config/config.js';
import { type LoadedSettings, loadSettings } from './config/settings.js';
import {
createMockConfig,
createMockSettings,
@@ -193,19 +189,12 @@ vi.mock('./ui/utils/terminalCapabilityManager.js', () => ({
vi.mock('./config/config.js', () => ({
loadCliConfig: vi.fn().mockImplementation(async () => createMockConfig()),
parseArguments: vi.fn().mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
}),
parseArguments: vi.fn().mockResolvedValue({}),
isDebugMode: vi.fn(() => false),
}));
vi.mock('read-package-up', () => ({
readPackageUp: vi.fn().mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
packageJson: { name: 'test-pkg', version: 'test-version' },
path: '/fake/path/package.json',
}),
@@ -243,9 +232,6 @@ vi.mock('./utils/relaunch.js', () => ({
vi.mock('./config/sandboxConfig.js', () => ({
loadSandboxConfig: vi.fn().mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
command: 'docker',
image: 'test-image',
}),
@@ -495,7 +481,6 @@ describe('gemini.tsx main function kitty protocol', () => {
yolo: undefined,
approvalMode: undefined,
policy: undefined,
adminPolicy: undefined,
allowedMcpServerNames: undefined,
allowedTools: undefined,
experimentalAcp: undefined,
@@ -551,9 +536,6 @@ describe('gemini.tsx main function kitty protocol', () => {
);
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
promptInteractive: false,
} as any); // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -617,9 +599,6 @@ describe('gemini.tsx main function kitty protocol', () => {
});
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
promptInteractive: false,
} as any); // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -639,17 +618,14 @@ describe('gemini.tsx main function kitty protocol', () => {
const mockConfig = createMockConfig({
isInteractive: () => false,
getQuestion: () => '',
getSandbox: () =>
createMockSandboxConfig({ command: 'docker', image: 'test-image' }),
getSandbox: () => ({ command: 'docker', image: 'test-image' }),
});
vi.mocked(loadCliConfig).mockResolvedValue(mockConfig);
vi.mocked(loadSandboxConfig).mockResolvedValue(
createMockSandboxConfig({
command: 'docker',
image: 'test-image',
}),
);
vi.mocked(loadSandboxConfig).mockResolvedValue({
command: 'docker',
image: 'test-image',
});
process.env['GEMINI_API_KEY'] = 'test-key';
try {
@@ -690,9 +666,6 @@ describe('gemini.tsx main function kitty protocol', () => {
);
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
promptInteractive: false,
} as any); // eslint-disable-line @typescript-eslint/no-explicit-any
vi.mocked(loadCliConfig).mockResolvedValue(
@@ -748,9 +721,6 @@ describe('gemini.tsx main function kitty protocol', () => {
);
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
promptInteractive: false,
resume: 'session-id',
} as any); // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -807,9 +777,6 @@ describe('gemini.tsx main function kitty protocol', () => {
);
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
promptInteractive: false,
resume: 'latest',
} as unknown as CliArgs);
@@ -860,9 +827,6 @@ describe('gemini.tsx main function kitty protocol', () => {
);
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
promptInteractive: false,
} as any); // eslint-disable-line @typescript-eslint/no-explicit-any
vi.mocked(loadCliConfig).mockResolvedValue(
@@ -913,9 +877,6 @@ describe('gemini.tsx main function kitty protocol', () => {
);
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
promptInteractive: false,
} as any); // eslint-disable-line @typescript-eslint/no-explicit-any
vi.mocked(loadCliConfig).mockResolvedValue(
@@ -990,9 +951,6 @@ describe('gemini.tsx main function exit codes', () => {
}),
);
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
promptInteractive: true,
} as unknown as CliArgs);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1009,12 +967,10 @@ describe('gemini.tsx main function exit codes', () => {
it('should exit with 41 for auth failure during sandbox setup', async () => {
vi.stubEnv('SANDBOX', '');
vi.mocked(loadSandboxConfig).mockResolvedValue(
createMockSandboxConfig({
command: 'docker',
image: 'test-image',
}),
);
vi.mocked(loadSandboxConfig).mockResolvedValue({
command: 'docker',
image: 'test-image',
});
vi.mocked(loadCliConfig).mockResolvedValue(
createMockConfig({
refreshAuth: vi.fn().mockRejectedValue(new Error('Auth failed')),
@@ -1054,9 +1010,6 @@ describe('gemini.tsx main function exit codes', () => {
}),
);
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
resume: 'invalid-session',
} as unknown as CliArgs);
@@ -1098,11 +1051,7 @@ describe('gemini.tsx main function exit codes', () => {
merged: { security: { auth: {} }, ui: {} },
}),
);
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
} as unknown as CliArgs);
vi.mocked(parseArguments).mockResolvedValue({} as unknown as CliArgs);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(process.stdin as any).isTTY = true;
@@ -1137,11 +1086,7 @@ describe('gemini.tsx main function exit codes', () => {
merged: { security: { auth: { selectedType: undefined } }, ui: {} },
}),
);
vi.mocked(parseArguments).mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
} as unknown as CliArgs);
vi.mocked(parseArguments).mockResolvedValue({} as unknown as CliArgs);
runNonInteractiveSpy.mockImplementation(() => Promise.resolve());
@@ -1211,12 +1156,7 @@ describe('project hooks loading based on trust', () => {
const configModule = await import('./config/config.js');
loadCliConfig = vi.mocked(configModule.loadCliConfig);
parseArguments = vi.mocked(configModule.parseArguments);
parseArguments.mockResolvedValue({
enabled: true,
allowedPaths: [],
networkAccess: false,
startupMessages: [],
});
parseArguments.mockResolvedValue({ startupMessages: [] });
const settingsModule = await import('./config/settings.js');
loadSettings = vi.mocked(settingsModule.loadSettings);
+219 -44
View File
@@ -4,53 +4,24 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
type StartupWarning,
WarningPriority,
type Config,
type ResumedSessionData,
type OutputPayload,
type ConsoleLogPayload,
type UserFeedbackPayload,
sessionId,
logUserPrompt,
AuthType,
UserPromptEvent,
coreEvents,
CoreEvent,
getOauthClient,
patchStdio,
writeToStdout,
writeToStderr,
shouldEnterAlternateScreen,
startupProfiler,
ExitCodes,
SessionStartSource,
SessionEndReason,
ValidationCancelledError,
ValidationRequiredError,
type AdminControlsSettings,
debugLogger,
} from '@google/gemini-cli-core';
import React from 'react';
import { render } from 'ink';
import { AppContainer } from './ui/AppContainer.js';
import { loadCliConfig, parseArguments } from './config/config.js';
import * as cliConfig from './config/config.js';
import { readStdin } from './utils/readStdin.js';
import { basename } from 'node:path';
import { createHash } from 'node:crypto';
import v8 from 'node:v8';
import os from 'node:os';
import dns from 'node:dns';
import { start_sandbox } from './utils/sandbox.js';
import {
loadSettings,
SettingScope,
type DnsResolutionOrder,
type LoadedSettings,
} from './config/settings.js';
import type { DnsResolutionOrder, LoadedSettings } from './config/settings.js';
import {
loadTrustedFolders,
type TrustedFoldersError,
} from './config/trustedFolders.js';
import { loadSettings, SettingScope } from './config/settings.js';
import { getStartupWarnings } from './utils/startupWarnings.js';
import { getUserStartupWarnings } from './utils/userStartupWarnings.js';
import { ConsolePatcher } from './ui/utils/ConsolePatcher.js';
@@ -62,11 +33,47 @@ import {
runExitCleanup,
registerTelemetryConfig,
setupSignalHandlers,
setupTtyCheck,
} from './utils/cleanup.js';
import {
cleanupToolOutputFiles,
cleanupExpiredSessions,
} from './utils/sessionCleanup.js';
import {
type StartupWarning,
WarningPriority,
type Config,
type ResumedSessionData,
type OutputPayload,
type ConsoleLogPayload,
type UserFeedbackPayload,
sessionId,
logUserPrompt,
AuthType,
getOauthClient,
UserPromptEvent,
debugLogger,
recordSlowRender,
coreEvents,
CoreEvent,
createWorkingStdio,
patchStdio,
writeToStdout,
writeToStderr,
disableMouseEvents,
enableMouseEvents,
disableLineWrapping,
enableLineWrapping,
shouldEnterAlternateScreen,
startupProfiler,
ExitCodes,
SessionStartSource,
SessionEndReason,
getVersion,
ValidationCancelledError,
ValidationRequiredError,
type AdminControlsSettings,
} from '@google/gemini-cli-core';
import {
initializeApp,
type InitializationResult,
@@ -74,9 +81,21 @@ import {
import { validateAuthMethod } from './config/auth.js';
import { runAcpClient } from './acp/acpClient.js';
import { validateNonInteractiveAuth } from './validateNonInterActiveAuth.js';
import { checkForUpdates } from './ui/utils/updateCheck.js';
import { handleAutoUpdate } from './utils/handleAutoUpdate.js';
import { appEvents, AppEvent } from './utils/events.js';
import { SessionError, SessionSelector } from './utils/sessionUtils.js';
import { SettingsContext } from './ui/contexts/SettingsContext.js';
import { MouseProvider } from './ui/contexts/MouseContext.js';
import { StreamingState } from './ui/types.js';
import { computeTerminalTitle } from './utils/windowTitle.js';
import { SessionStatsProvider } from './ui/contexts/SessionContext.js';
import { VimModeProvider } from './ui/contexts/VimModeContext.js';
import { KeyMatchersProvider } from './ui/hooks/useKeyMatchers.js';
import { loadKeyMatchers } from './ui/key/keyMatchers.js';
import { KeypressProvider } from './ui/contexts/KeypressContext.js';
import { useKittyKeyboardProtocol } from './ui/hooks/useKittyKeyboardProtocol.js';
import {
relaunchAppInChildProcess,
relaunchOnExitCode,
@@ -84,13 +103,19 @@ import {
import { loadSandboxConfig } from './config/sandboxConfig.js';
import { deleteSession, listSessions } from './utils/sessions.js';
import { createPolicyUpdater } from './config/policy.js';
import { ScrollProvider } from './ui/contexts/ScrollProvider.js';
import { TerminalProvider } from './ui/contexts/TerminalContext.js';
import { isAlternateBufferEnabled } from './ui/hooks/useAlternateBuffer.js';
import { OverflowProvider } from './ui/contexts/OverflowContext.js';
import { setupTerminalAndTheme } from './utils/terminalTheme.js';
import { profiler } from './ui/components/DebugProfiler.js';
import { runDeferredCommand } from './deferred.js';
import { cleanupBackgroundLogs } from './utils/logCleanup.js';
import { SlashCommandConflictHandler } from './services/SlashCommandConflictHandler.js';
const SLOW_RENDER_MS = 200;
export function validateDnsResolutionOrder(
order: string | undefined,
): DnsResolutionOrder {
@@ -169,16 +194,147 @@ export async function startInteractiveUI(
resumedSessionData: ResumedSessionData | undefined,
initializationResult: InitializationResult,
) {
// Dynamically import the heavy UI module so React/Ink are only parsed when needed
const { startInteractiveUI: doStartUI } = await import('./interactiveCli.js');
await doStartUI(
config,
settings,
startupWarnings,
workspaceRoot,
resumedSessionData,
initializationResult,
// Never enter Ink alternate buffer mode when screen reader mode is enabled
// as there is no benefit of alternate buffer mode when using a screen reader
// and the Ink alternate buffer mode requires line wrapping harmful to
// screen readers.
const useAlternateBuffer = shouldEnterAlternateScreen(
isAlternateBufferEnabled(config),
config.getScreenReader(),
);
const mouseEventsEnabled = useAlternateBuffer;
if (mouseEventsEnabled) {
enableMouseEvents();
registerCleanup(() => {
disableMouseEvents();
});
}
const { matchers, errors } = await loadKeyMatchers();
errors.forEach((error) => {
coreEvents.emitFeedback('warning', error);
});
const version = await getVersion();
setWindowTitle(basename(workspaceRoot), settings);
const consolePatcher = new ConsolePatcher({
onNewMessage: (msg) => {
coreEvents.emitConsoleLog(msg.type, msg.content);
},
debugMode: config.getDebugMode(),
});
consolePatcher.patch();
registerCleanup(consolePatcher.cleanup);
const { stdout: inkStdout, stderr: inkStderr } = createWorkingStdio();
const isShpool = !!process.env['SHPOOL_SESSION_NAME'];
// Create wrapper component to use hooks inside render
const AppWrapper = () => {
useKittyKeyboardProtocol();
return (
<SettingsContext.Provider value={settings}>
<KeyMatchersProvider value={matchers}>
<KeypressProvider
config={config}
debugKeystrokeLogging={
settings.merged.general.debugKeystrokeLogging
}
>
<MouseProvider
mouseEventsEnabled={mouseEventsEnabled}
debugKeystrokeLogging={
settings.merged.general.debugKeystrokeLogging
}
>
<TerminalProvider>
<ScrollProvider>
<OverflowProvider>
<SessionStatsProvider>
<VimModeProvider>
<AppContainer
config={config}
startupWarnings={startupWarnings}
version={version}
resumedSessionData={resumedSessionData}
initializationResult={initializationResult}
/>
</VimModeProvider>
</SessionStatsProvider>
</OverflowProvider>
</ScrollProvider>
</TerminalProvider>
</MouseProvider>
</KeypressProvider>
</KeyMatchersProvider>
</SettingsContext.Provider>
);
};
if (isShpool) {
// Wait a moment for shpool to stabilize terminal size and state.
// shpool is a persistence tool that restores terminal state by replaying it.
// This delay gives shpool time to finish its restoration replay and send
// the actual terminal size (often via an immediate SIGWINCH) before we
// render the first TUI frame. Without this, the first frame may be
// garbled or rendered at an incorrect size, which disabling incremental
// rendering alone cannot fix for the initial frame.
await new Promise((resolve) => setTimeout(resolve, 100));
}
const instance = render(
process.env['DEBUG'] ? (
<React.StrictMode>
<AppWrapper />
</React.StrictMode>
) : (
<AppWrapper />
),
{
stdout: inkStdout,
stderr: inkStderr,
stdin: process.stdin,
exitOnCtrlC: false,
isScreenReaderEnabled: config.getScreenReader(),
onRender: ({ renderTime }: { renderTime: number }) => {
if (renderTime > SLOW_RENDER_MS) {
recordSlowRender(config, renderTime);
}
profiler.reportFrameRendered();
},
patchConsole: false,
alternateBuffer: useAlternateBuffer,
incrementalRendering:
settings.merged.ui.incrementalRendering !== false &&
useAlternateBuffer &&
!isShpool,
},
);
if (useAlternateBuffer) {
disableLineWrapping();
registerCleanup(() => {
enableLineWrapping();
});
}
checkForUpdates(settings)
.then((info) => {
handleAutoUpdate(info, settings, config.getProjectRoot());
})
.catch((err) => {
// Silently ignore update check errors.
if (config.getDebugMode()) {
debugLogger.warn('Update check failed:', err);
}
});
registerCleanup(() => instance.unmount());
registerCleanup(setupTtyCheck());
}
export async function main() {
@@ -685,6 +841,25 @@ export async function main() {
}
}
function setWindowTitle(title: string, settings: LoadedSettings) {
if (!settings.merged.ui.hideWindowTitle) {
// Initial state before React loop starts
const windowTitle = computeTerminalTitle({
streamingState: StreamingState.Idle,
isConfirming: false,
isSilentWorking: false,
folderName: title,
showThoughts: !!settings.merged.ui.showStatusInTitle,
useDynamicTitle: settings.merged.ui.dynamicWindowTitle,
});
writeToStdout(`\x1b]0;${windowTitle}\x07`);
process.on('exit', () => {
writeToStdout(`\x1b]0;\x07`);
});
}
}
export function initializeOutputListenersAndFlush() {
// If there are no listeners for output, make sure we flush so output is not
// lost.
+2 -1
View File
@@ -6,7 +6,8 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { main } from './gemini.js';
import { debugLogger, type Config } from '@google/gemini-cli-core';
import { debugLogger } from '@google/gemini-cli-core';
import { type Config } from '@google/gemini-cli-core';
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
const actual =
-214
View File
@@ -1,214 +0,0 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import React from 'react';
import { render } from 'ink';
import { basename } from 'node:path';
import { AppContainer } from './ui/AppContainer.js';
import { ConsolePatcher } from './ui/utils/ConsolePatcher.js';
import { registerCleanup, setupTtyCheck } from './utils/cleanup.js';
import {
type StartupWarning,
type Config,
type ResumedSessionData,
coreEvents,
createWorkingStdio,
disableMouseEvents,
enableMouseEvents,
disableLineWrapping,
enableLineWrapping,
shouldEnterAlternateScreen,
recordSlowRender,
writeToStdout,
getVersion,
debugLogger,
} from '@google/gemini-cli-core';
import type { InitializationResult } from './core/initializer.js';
import type { LoadedSettings } from './config/settings.js';
import { checkForUpdates } from './ui/utils/updateCheck.js';
import { handleAutoUpdate } from './utils/handleAutoUpdate.js';
import { SettingsContext } from './ui/contexts/SettingsContext.js';
import { MouseProvider } from './ui/contexts/MouseContext.js';
import { StreamingState } from './ui/types.js';
import { computeTerminalTitle } from './utils/windowTitle.js';
import { SessionStatsProvider } from './ui/contexts/SessionContext.js';
import { VimModeProvider } from './ui/contexts/VimModeContext.js';
import { KeyMatchersProvider } from './ui/hooks/useKeyMatchers.js';
import { loadKeyMatchers } from './ui/key/keyMatchers.js';
import { KeypressProvider } from './ui/contexts/KeypressContext.js';
import { useKittyKeyboardProtocol } from './ui/hooks/useKittyKeyboardProtocol.js';
import { ScrollProvider } from './ui/contexts/ScrollProvider.js';
import { TerminalProvider } from './ui/contexts/TerminalContext.js';
import { isAlternateBufferEnabled } from './ui/hooks/useAlternateBuffer.js';
import { OverflowProvider } from './ui/contexts/OverflowContext.js';
import { profiler } from './ui/components/DebugProfiler.js';
const SLOW_RENDER_MS = 200;
export async function startInteractiveUI(
config: Config,
settings: LoadedSettings,
startupWarnings: StartupWarning[],
workspaceRoot: string = process.cwd(),
resumedSessionData: ResumedSessionData | undefined,
initializationResult: InitializationResult,
) {
// Never enter Ink alternate buffer mode when screen reader mode is enabled
// as there is no benefit of alternate buffer mode when using a screen reader
// and the Ink alternate buffer mode requires line wrapping harmful to
// screen readers.
const useAlternateBuffer = shouldEnterAlternateScreen(
isAlternateBufferEnabled(config),
config.getScreenReader(),
);
const mouseEventsEnabled = useAlternateBuffer;
if (mouseEventsEnabled) {
enableMouseEvents();
registerCleanup(() => {
disableMouseEvents();
});
}
const { matchers, errors } = await loadKeyMatchers();
errors.forEach((error) => {
coreEvents.emitFeedback('warning', error);
});
const version = await getVersion();
setWindowTitle(basename(workspaceRoot), settings);
const consolePatcher = new ConsolePatcher({
onNewMessage: (msg) => {
coreEvents.emitConsoleLog(msg.type, msg.content);
},
debugMode: config.getDebugMode(),
});
consolePatcher.patch();
registerCleanup(consolePatcher.cleanup);
const { stdout: inkStdout, stderr: inkStderr } = createWorkingStdio();
const isShpool = !!process.env['SHPOOL_SESSION_NAME'];
// Create wrapper component to use hooks inside render
const AppWrapper = () => {
useKittyKeyboardProtocol();
return (
<SettingsContext.Provider value={settings}>
<KeyMatchersProvider value={matchers}>
<KeypressProvider
config={config}
debugKeystrokeLogging={
settings.merged.general.debugKeystrokeLogging
}
>
<MouseProvider
mouseEventsEnabled={mouseEventsEnabled}
debugKeystrokeLogging={
settings.merged.general.debugKeystrokeLogging
}
>
<TerminalProvider>
<ScrollProvider>
<OverflowProvider>
<SessionStatsProvider>
<VimModeProvider>
<AppContainer
config={config}
startupWarnings={startupWarnings}
version={version}
resumedSessionData={resumedSessionData}
initializationResult={initializationResult}
/>
</VimModeProvider>
</SessionStatsProvider>
</OverflowProvider>
</ScrollProvider>
</TerminalProvider>
</MouseProvider>
</KeypressProvider>
</KeyMatchersProvider>
</SettingsContext.Provider>
);
};
if (isShpool) {
// Wait a moment for shpool to stabilize terminal size and state.
await new Promise((resolve) => setTimeout(resolve, 100));
}
const instance = render(
process.env['DEBUG'] ? (
<React.StrictMode>
<AppWrapper />
</React.StrictMode>
) : (
<AppWrapper />
),
{
stdout: inkStdout,
stderr: inkStderr,
stdin: process.stdin,
exitOnCtrlC: false,
isScreenReaderEnabled: config.getScreenReader(),
onRender: ({ renderTime }: { renderTime: number }) => {
if (renderTime > SLOW_RENDER_MS) {
recordSlowRender(config, renderTime);
}
profiler.reportFrameRendered();
},
patchConsole: false,
alternateBuffer: useAlternateBuffer,
incrementalRendering:
settings.merged.ui.incrementalRendering !== false &&
useAlternateBuffer &&
!isShpool,
},
);
if (useAlternateBuffer) {
disableLineWrapping();
registerCleanup(() => {
enableLineWrapping();
});
}
checkForUpdates(settings)
.then((info) => {
handleAutoUpdate(info, settings, config.getProjectRoot());
})
.catch((err) => {
// Silently ignore update check errors.
if (config.getDebugMode()) {
debugLogger.warn('Update check failed:', err);
}
});
registerCleanup(() => instance.unmount());
registerCleanup(setupTtyCheck());
}
function setWindowTitle(title: string, settings: LoadedSettings) {
if (!settings.merged.ui.hideWindowTitle) {
// Initial state before React loop starts
const windowTitle = computeTerminalTitle({
streamingState: StreamingState.Idle,
isConfirming: false,
isSilentWorking: false,
folderName: title,
showThoughts: !!settings.merged.ui.showStatusInTitle,
useDynamicTitle: settings.merged.ui.dynamicWindowTitle,
});
writeToStdout(`\x1b]0;${windowTitle}\x07`);
process.on('exit', () => {
writeToStdout(`\x1b]0;\x07`);
});
}
}
+1 -1
View File
@@ -263,8 +263,8 @@ export async function runNonInteractive({
onDebugMessage: () => {},
messageId: Date.now(),
signal: abortController.signal,
escapePastedAtSymbols: false,
});
if (error || !processedQuery) {
// An error occurred during @include processing (e.g., file not found).
// The error message is already logged by handleAtCommand.
@@ -52,7 +52,8 @@ vi.mock('../ui/commands/permissionsCommand.js', async () => {
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { BuiltinCommandLoader } from './BuiltinCommandLoader.js';
import { isNightly, type Config } from '@google/gemini-cli-core';
import type { Config } from '@google/gemini-cli-core';
import { isNightly } from '@google/gemini-cli-core';
import { CommandKind } from '../ui/commands/types.js';
import { restoreCommand } from '../ui/commands/restoreCommand.js';
@@ -6,7 +6,8 @@
import * as glob from 'glob';
import * as path from 'node:path';
import { GEMINI_DIR, Storage, type Config } from '@google/gemini-cli-core';
import type { Config } from '@google/gemini-cli-core';
import { GEMINI_DIR, Storage } from '@google/gemini-cli-core';
import mock from 'mock-fs';
import { FileCommandLoader } from './FileCommandLoader.js';
import { assert, vi } from 'vitest';
@@ -9,7 +9,8 @@ import path from 'node:path';
import toml from '@iarna/toml';
import { glob } from 'glob';
import { z } from 'zod';
import { Storage, coreEvents, type Config } from '@google/gemini-cli-core';
import type { Config } from '@google/gemini-cli-core';
import { Storage, coreEvents } from '@google/gemini-cli-core';
import type { ICommandLoader } from './types.js';
import type {
CommandContext,
+7 -10
View File
@@ -4,17 +4,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
getErrorMessage,
getMCPServerPrompts,
type Config,
} from '@google/gemini-cli-core';
import {
CommandKind,
type CommandContext,
type SlashCommand,
type SlashCommandActionReturn,
import type { Config } from '@google/gemini-cli-core';
import { getErrorMessage, getMCPServerPrompts } from '@google/gemini-cli-core';
import type {
CommandContext,
SlashCommand,
SlashCommandActionReturn,
} from '../ui/commands/types.js';
import { CommandKind } from '../ui/commands/types.js';
import type { ICommandLoader } from './types.js';
import type { PromptArgument } from '@modelcontextprotocol/sdk/types.js';
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { CommandKind, type SlashCommand } from '../ui/commands/types.js';
import type { SlashCommand } from '../ui/commands/types.js';
import { CommandKind } from '../ui/commands/types.js';
import type { CommandConflict } from './types.js';
/**
@@ -6,7 +6,8 @@
import { vi } from 'vitest';
import type { CommandContext } from '../ui/commands/types.js';
import { mergeSettings, type LoadedSettings } from '../config/settings.js';
import type { LoadedSettings } from '../config/settings.js';
import { mergeSettings } from '../config/settings.js';
import type { GitService } from '@google/gemini-cli-core';
import type { SessionStatsState } from '../ui/contexts/SessionContext.js';
+2 -5
View File
@@ -6,11 +6,8 @@
import { vi } from 'vitest';
import type { Config } from '@google/gemini-cli-core';
import {
createTestMergedSettings,
type LoadedSettings,
type Settings,
} from '../config/settings.js';
import type { LoadedSettings, Settings } from '../config/settings.js';
import { createTestMergedSettings } from '../config/settings.js';
/**
* Creates a mocked Config object with default values and allows overrides.
+1 -1
View File
@@ -11,10 +11,10 @@ import {
} from 'ink';
import { EventEmitter } from 'node:events';
import { Box } from 'ink';
import type React from 'react';
import { Terminal } from '@xterm/headless';
import { vi } from 'vitest';
import stripAnsi from 'strip-ansi';
import type React from 'react';
import { act, useState } from 'react';
import os from 'node:os';
import path from 'node:path';
+38
View File
@@ -3893,4 +3893,42 @@ describe('AppContainer State Management', () => {
unmount!();
});
});
describe('Keyboard Shortcuts', () => {
it('should toggle constrainHeight when Ctrl+O is pressed', async () => {
let renderResult: {
stdin: { write: (data: string) => boolean };
waitUntilReady: () => Promise<void>;
unmount: () => void;
};
await act(async () => {
renderResult = renderAppContainer() as unknown as typeof renderResult;
});
const { stdin, waitUntilReady, unmount } = renderResult!;
await waitUntilReady();
// Initially constrained
expect(capturedUIState.constrainHeight).toBe(true);
// Press Ctrl+O (\x0f)
await act(async () => {
stdin.write('\x0f');
});
await waitUntilReady();
// Now unconstrained
expect(capturedUIState.constrainHeight).toBe(false);
// Press Ctrl+O again
await act(async () => {
stdin.write('\x0f');
});
await waitUntilReady();
// Constrained again
expect(capturedUIState.constrainHeight).toBe(true);
unmount();
});
});
});
+2 -16
View File
@@ -162,7 +162,6 @@ import {
import { LoginWithGoogleRestartDialog } from './auth/LoginWithGoogleRestartDialog.js';
import { NewAgentsChoice } from './components/NewAgentsNotification.js';
import { isSlashCommand } from './utils/commandUtils.js';
import { parseSlashCommand } from '../utils/commands.js';
import { useTerminalTheme } from './hooks/useTerminalTheme.js';
import { useTimedMessage } from './hooks/useTimedMessage.js';
import { useIsHelpDismissKey } from './utils/shortcutsHelp.js';
@@ -1290,18 +1289,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
...pendingGeminiHistoryItems,
]);
if (isSlash && isAgentRunning) {
const { commandToExecute } = parseSlashCommand(
submittedValue,
slashCommands ?? [],
);
if (commandToExecute?.isSafeConcurrent) {
void handleSlashCommand(submittedValue);
addInput(submittedValue);
return;
}
}
if (config.isModelSteeringEnabled() && isAgentRunning && !isSlash) {
handleHintSubmit(submittedValue);
addInput(submittedValue);
@@ -1345,8 +1332,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
addMessage,
addInput,
submitQuery,
handleSlashCommand,
slashCommands,
isMcpReady,
streamingState,
messageQueue.length,
@@ -1769,7 +1754,8 @@ Logging in with Google... Restarting Gemini CLI to continue.
if (!isAlternateBuffer) {
refreshStatic();
}
return true;
// Return false to allow other components (e.g. ToolConfirmationMessage) to also handle expansion.
return false;
} else if (
(keyMatchers[Command.FOCUS_SHELL_INPUT](key) ||
keyMatchers[Command.UNFOCUS_BACKGROUND_SHELL_LIST](key)) &&
+2 -4
View File
@@ -6,10 +6,8 @@
import type { IdeInfo } from '@google/gemini-cli-core';
import { Box, Text } from 'ink';
import {
RadioButtonSelect,
type RadioSelectItem,
} from './components/shared/RadioButtonSelect.js';
import type { RadioSelectItem } from './components/shared/RadioButtonSelect.js';
import { RadioButtonSelect } from './components/shared/RadioButtonSelect.js';
import { useKeypress } from './hooks/useKeypress.js';
import { theme } from './semantic-colors.js';
+4 -4
View File
@@ -9,11 +9,11 @@ import { useCallback, useState } from 'react';
import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js';
import { RadioButtonSelect } from '../components/shared/RadioButtonSelect.js';
import {
SettingScope,
type LoadableSettingScope,
type LoadedSettings,
import type {
LoadableSettingScope,
LoadedSettings,
} from '../../config/settings.js';
import { SettingScope } from '../../config/settings.js';
import {
AuthType,
clearCachedCredentialFile,
+2 -6
View File
@@ -4,11 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
CommandKind,
type CommandContext,
type SlashCommand,
} from './types.js';
import type { CommandContext, SlashCommand } from './types.js';
import { CommandKind } from './types.js';
import process from 'node:process';
import { MessageType, type HistoryItemAbout } from '../types.js';
import {
@@ -23,7 +20,6 @@ export const aboutCommand: SlashCommand = {
description: 'Show version info',
kind: CommandKind.BUILT_IN,
autoExecute: true,
isSafeConcurrent: true,
action: async (context) => {
const osVersion = process.platform;
let sandboxEnv = 'no sandbox';
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { vi, describe, it, expect, beforeEach, type Mock } from 'vitest';
import type { Mock } from 'vitest';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { clearCommand } from './clearCommand.js';
import { type CommandContext } from './types.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
@@ -21,7 +22,8 @@ vi.mock('@google/gemini-cli-core', async () => {
};
});
import { uiTelemetryService, type GeminiClient } from '@google/gemini-cli-core';
import type { GeminiClient } from '@google/gemini-cli-core';
import { uiTelemetryService } from '@google/gemini-cli-core';
describe('clearCommand', () => {
let mockContext: CommandContext;
+2 -1
View File
@@ -10,7 +10,8 @@ import {
SessionStartSource,
flushTelemetry,
} from '@google/gemini-cli-core';
import { CommandKind, type SlashCommand } from './types.js';
import type { SlashCommand } from './types.js';
import { CommandKind } from './types.js';
import { MessageType } from '../types.js';
import { randomUUID } from 'node:crypto';
@@ -4,8 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { MessageType, type HistoryItemCompression } from '../types.js';
import { CommandKind, type SlashCommand } from './types.js';
import type { HistoryItemCompression } from '../types.js';
import { MessageType } from '../types.js';
import type { SlashCommand } from './types.js';
import { CommandKind } from './types.js';
export const compressCommand: SlashCommand = {
name: 'compress',
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { vi, describe, it, expect, beforeEach, type Mock } from 'vitest';
import type { Mock } from 'vitest';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { copyCommand } from './copyCommand.js';
import { type CommandContext } from './types.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
+2 -5
View File
@@ -6,11 +6,8 @@
import { debugLogger } from '@google/gemini-cli-core';
import { copyToClipboard } from '../utils/commandUtils.js';
import {
CommandKind,
type SlashCommand,
type SlashCommandActionReturn,
} from './types.js';
import type { SlashCommand, SlashCommandActionReturn } from './types.js';
import { CommandKind } from './types.js';
export const copyCommand: SlashCommand = {
name: 'copy',
@@ -4,15 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
vi,
describe,
it,
expect,
beforeEach,
afterEach,
type Mock,
} from 'vitest';
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { Mock } from 'vitest';
import { directoryCommand } from './directoryCommand.js';
import {
expandHomeDir,
@@ -9,21 +9,16 @@ import {
loadTrustedFolders,
} from '../../config/trustedFolders.js';
import { MultiFolderTrustDialog } from '../components/MultiFolderTrustDialog.js';
import {
CommandKind,
type SlashCommand,
type CommandContext,
} from './types.js';
import type { SlashCommand, CommandContext } from './types.js';
import { CommandKind } from './types.js';
import { MessageType, type HistoryItem } from '../types.js';
import {
refreshServerHierarchicalMemory,
type Config,
} from '@google/gemini-cli-core';
import { refreshServerHierarchicalMemory } from '@google/gemini-cli-core';
import {
expandHomeDir,
getDirectorySuggestions,
batchAddDirectories,
} from '../utils/directoryUtils.js';
import type { Config } from '@google/gemini-cli-core';
import * as path from 'node:path';
import * as fs from 'node:fs';
@@ -6,9 +6,10 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { helpCommand } from './helpCommand.js';
import { CommandKind, type CommandContext } from './types.js';
import { type CommandContext } from './types.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
import { MessageType } from '../types.js';
import { CommandKind } from './types.js';
describe('helpCommand', () => {
let mockContext: CommandContext;
+2 -1
View File
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { CommandKind, type SlashCommand } from './types.js';
import type { SlashCommand } from './types.js';
import { CommandKind } from './types.js';
import { MessageType, type HistoryItemHelp } from '../types.js';
export const helpCommand: SlashCommand = {
@@ -7,12 +7,8 @@
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import { hooksCommand } from './hooksCommand.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
import {
HookType,
HookEventName,
ConfigSource,
type HookRegistryEntry,
} from '@google/gemini-cli-core';
import type { HookRegistryEntry } from '@google/gemini-cli-core';
import { HookType, HookEventName, ConfigSource } from '@google/gemini-cli-core';
import type { CommandContext } from './types.js';
import { SettingScope } from '../../config/settings.js';
@@ -4,15 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
vi,
describe,
it,
expect,
beforeEach,
afterEach,
type MockInstance,
} from 'vitest';
import type { MockInstance } from 'vitest';
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import { ideCommand } from './ideCommand.js';
import { type CommandContext } from './types.js';
import { IDE_DEFINITIONS } from '@google/gemini-cli-core';
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { vi, describe, it, expect, beforeEach, type Mock } from 'vitest';
import type { Mock } from 'vitest';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { memoryCommand } from './memoryCommand.js';
import type { SlashCommand, CommandContext } from './types.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
@@ -11,11 +11,8 @@ import {
showMemory,
} from '@google/gemini-cli-core';
import { MessageType } from '../types.js';
import {
CommandKind,
type SlashCommand,
type SlashCommandActionReturn,
} from './types.js';
import type { SlashCommand, SlashCommandActionReturn } from './types.js';
import { CommandKind } from './types.js';
export const memoryCommand: SlashCommand = {
name: 'memory',
@@ -4,11 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
CommandKind,
type OpenDialogActionReturn,
type SlashCommand,
} from './types.js';
import type { OpenDialogActionReturn, SlashCommand } from './types.js';
import { CommandKind } from './types.js';
export const privacyCommand: SlashCommand = {
name: 'privacy',
@@ -4,18 +4,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
CommandKind,
type OpenDialogActionReturn,
type SlashCommand,
} from './types.js';
import type { OpenDialogActionReturn, SlashCommand } from './types.js';
import { CommandKind } from './types.js';
export const settingsCommand: SlashCommand = {
name: 'settings',
description: 'View and edit Gemini CLI settings',
kind: CommandKind.BUILT_IN,
autoExecute: true,
isSafeConcurrent: true,
action: (_context, _args): OpenDialogActionReturn => ({
type: 'dialog',
dialog: 'settings',
@@ -17,7 +17,8 @@ import {
} from './setupGithubCommand.js';
import type { CommandContext } from './types.js';
import * as commandUtils from '../utils/commandUtils.js';
import { debugLogger, type ToolActionReturn } from '@google/gemini-cli-core';
import type { ToolActionReturn } from '@google/gemini-cli-core';
import { debugLogger } from '@google/gemini-cli-core';
vi.mock('child_process');
@@ -17,11 +17,8 @@ import {
getGitHubRepoInfo,
} from '../../utils/gitUtils.js';
import {
CommandKind,
type SlashCommand,
type SlashCommandActionReturn,
} from './types.js';
import type { SlashCommand, SlashCommandActionReturn } from './types.js';
import { CommandKind } from './types.js';
import { getUrlOpenCommand } from '../../ui/utils/commandUtils.js';
import { debugLogger } from '@google/gemini-cli-core';
@@ -123,6 +120,7 @@ async function downloadFiles({
downloads.push(
(async () => {
const endpoint = `${REPO_DOWNLOAD_URL}/refs/tags/${releaseTag}/${SOURCE_DIR}/${fileBasename}`;
// eslint-disable-next-line no-restricted-syntax -- TODO: Migrate to safeFetch for SSRF protection
const response = await fetch(endpoint, {
method: 'GET',
dispatcher: proxy ? new ProxyAgent(proxy) : undefined,
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { CommandKind, type SlashCommand } from './types.js';
import type { SlashCommand } from './types.js';
import { CommandKind } from './types.js';
export const shortcutsCommand: SlashCommand = {
name: 'shortcuts',
@@ -84,7 +84,6 @@ export const statsCommand: SlashCommand = {
description: 'Check session stats. Usage: /stats [session|model|tools]',
kind: CommandKind.BUILT_IN,
autoExecute: false,
isSafeConcurrent: true,
action: async (context: CommandContext) => {
await defaultSessionView(context);
},
@@ -94,7 +93,6 @@ export const statsCommand: SlashCommand = {
description: 'Show session-specific usage statistics',
kind: CommandKind.BUILT_IN,
autoExecute: true,
isSafeConcurrent: true,
action: async (context: CommandContext) => {
await defaultSessionView(context);
},
@@ -104,7 +102,6 @@ export const statsCommand: SlashCommand = {
description: 'Show model-specific usage statistics',
kind: CommandKind.BUILT_IN,
autoExecute: true,
isSafeConcurrent: true,
action: (context: CommandContext) => {
const { selectedAuthType, userEmail, tier } = getUserIdentity(context);
const currentModel = context.services.config?.getModel();
@@ -128,7 +125,6 @@ export const statsCommand: SlashCommand = {
description: 'Show tool-specific usage statistics',
kind: CommandKind.BUILT_IN,
autoExecute: true,
isSafeConcurrent: true,
action: (context: CommandContext) => {
context.ui.addItem({
type: MessageType.TOOL_STATS,
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { CommandKind, type SlashCommand } from './types.js';
import type { SlashCommand } from './types.js';
import { CommandKind } from './types.js';
import { terminalSetup } from '../utils/terminalSetup.js';
import { type MessageActionReturn } from '@google/gemini-cli-core';
+2 -5
View File
@@ -4,11 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
CommandKind,
type OpenDialogActionReturn,
type SlashCommand,
} from './types.js';
import type { OpenDialogActionReturn, SlashCommand } from './types.js';
import { CommandKind } from './types.js';
export const themeCommand: SlashCommand = {
name: 'theme',
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, type vi } from 'vitest';
import type { vi } from 'vitest';
import { describe, it, expect } from 'vitest';
import { toolsCommand } from './toolsCommand.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
import { MessageType } from '../types.js';
-5
View File
@@ -207,11 +207,6 @@ export interface SlashCommand {
*/
autoExecute?: boolean;
/**
* Whether this command can be safely executed while the agent is busy (e.g. streaming a response).
*/
isSafeConcurrent?: boolean;
// Optional metadata for extension commands
extensionName?: string;
extensionId?: string;
@@ -37,7 +37,6 @@ describe('upgradeCommand', () => {
getContentGeneratorConfig: vi.fn().mockReturnValue({
authType: AuthType.LOGIN_WITH_GOOGLE,
}),
getUserTierName: vi.fn().mockReturnValue(undefined),
},
},
} as unknown as CommandContext);
@@ -116,23 +115,4 @@ describe('upgradeCommand', () => {
});
expect(openBrowserSecurely).not.toHaveBeenCalled();
});
it('should return info message for ultra tiers', async () => {
vi.mocked(mockContext.services.config!.getUserTierName).mockReturnValue(
'Advanced Ultra',
);
if (!upgradeCommand.action) {
throw new Error('The upgrade command must have an action.');
}
const result = await upgradeCommand.action(mockContext, '');
expect(result).toEqual({
type: 'message',
messageType: 'info',
content: 'You are already on the highest tier: Advanced Ultra.',
});
expect(openBrowserSecurely).not.toHaveBeenCalled();
});
});
+2 -11
View File
@@ -10,8 +10,8 @@ import {
shouldLaunchBrowser,
UPGRADE_URL_PAGE,
} from '@google/gemini-cli-core';
import { isUltraTier } from '../../utils/tierUtils.js';
import { CommandKind, type SlashCommand } from './types.js';
import type { SlashCommand } from './types.js';
import { CommandKind } from './types.js';
/**
* Command to open the upgrade page for Gemini Code Assist.
@@ -36,15 +36,6 @@ export const upgradeCommand: SlashCommand = {
};
}
const tierName = context.services.config?.getUserTierName();
if (isUltraTier(tierName)) {
return {
type: 'message',
messageType: 'info',
content: `You are already on the highest tier: ${tierName}.`,
};
}
if (!shouldLaunchBrowser()) {
return {
type: 'message',
+2 -2
View File
@@ -4,14 +4,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { CommandKind, type SlashCommand } from './types.js';
import type { SlashCommand } from './types.js';
import { CommandKind } from './types.js';
export const vimCommand: SlashCommand = {
name: 'vim',
description: 'Toggle vim mode on/off',
kind: CommandKind.BUILT_IN,
autoExecute: true,
isSafeConcurrent: true,
action: async (context, _args) => {
const newVimState = await context.ui.toggleVimEnabled();
@@ -8,11 +8,11 @@ import type React from 'react';
import { useState, useEffect, useMemo, useCallback } from 'react';
import { Text } from 'ink';
import { theme } from '../semantic-colors.js';
import {
SettingScope,
type LoadableSettingScope,
type LoadedSettings,
import type {
LoadableSettingScope,
LoadedSettings,
} from '../../config/settings.js';
import { SettingScope } from '../../config/settings.js';
import type { AgentDefinition, AgentOverride } from '@google/gemini-cli-core';
import { getCachedStringWidth } from '../utils/textUtils.js';
import {
@@ -15,12 +15,13 @@ import {
} from 'react';
import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js';
import { checkExhaustive, type Question } from '@google/gemini-cli-core';
import type { Question } from '@google/gemini-cli-core';
import { BaseSelectionList } from './shared/BaseSelectionList.js';
import type { SelectionListItem } from '../hooks/useSelectionList.js';
import { TabHeader, type Tab } from './shared/TabHeader.js';
import { useKeypress, type Key } from '../hooks/useKeypress.js';
import { Command } from '../key/keyMatchers.js';
import { checkExhaustive } from '@google/gemini-cli-core';
import { TextInput } from './shared/TextInput.js';
import { formatCommand } from '../key/keybindingUtils.js';
import {
+1 -1
View File
@@ -5,9 +5,9 @@
*/
import type React from 'react';
import { useMemo } from 'react';
import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js';
import { useMemo } from 'react';
import { ChecklistItem, type ChecklistItemData } from './ChecklistItem.js';
export interface ChecklistProps {
@@ -4,8 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type React from 'react';
import { useRef, useCallback } from 'react';
import type React from 'react';
import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js';
import type { ConsoleMessageItem } from '../types.js';
@@ -87,7 +87,6 @@ export const DialogManager = ({
!!uiState.quota.proQuotaRequest.isModelNotFoundError
}
authType={uiState.quota.proQuotaRequest.authType}
tierName={config?.getUserTierName()}
onChoice={uiActions.handleProQuotaChoice}
/>
);
@@ -7,7 +7,8 @@
import { render } from '../../test-utils/render.js';
import { EditorSettingsDialog } from './EditorSettingsDialog.js';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { SettingScope, type LoadedSettings } from '../../config/settings.js';
import { SettingScope } from '../../config/settings.js';
import type { LoadedSettings } from '../../config/settings.js';
import { KeypressProvider } from '../contexts/KeypressContext.js';
import { act } from 'react';
import { waitFor } from '../../test-utils/async.js';
@@ -13,18 +13,18 @@ import {
type EditorDisplay,
} from '../editors/editorSettingsManager.js';
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
import {
SettingScope,
type LoadableSettingScope,
type LoadedSettings,
import type {
LoadableSettingScope,
LoadedSettings,
} from '../../config/settings.js';
import { SettingScope } from '../../config/settings.js';
import {
type EditorType,
isEditorAvailable,
EDITOR_DISPLAY_NAMES,
coreEvents,
} from '@google/gemini-cli-core';
import { useKeypress } from '../hooks/useKeypress.js';
import { coreEvents } from '@google/gemini-cli-core';
interface EditorDialogProps {
onSelect: (

Some files were not shown because too many files have changed in this diff Show More