diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d40b49bb69..82e9194a02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -175,10 +175,10 @@ jobs: NO_COLOR: true run: | if [[ "${{ matrix.shard }}" == "cli" ]]; then - npm run test:ci --workspace @google/gemini-cli + npm run test:ci --workspace "@google/gemini-cli" else # Explicitly list non-cli packages to ensure they are sharded correctly - npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present -- --coverage.enabled=false + npm run test:ci --workspace "@google/gemini-cli-core" --workspace "@google/gemini-cli-a2a-server" --workspace "gemini-cli-vscode-ide-companion" --workspace "@google/gemini-cli-test-utils" --if-present -- --coverage.enabled=false npm run test:scripts fi @@ -263,10 +263,10 @@ jobs: NO_COLOR: true run: | if [[ "${{ matrix.shard }}" == "cli" ]]; then - npm run test:ci --workspace @google/gemini-cli -- --coverage.enabled=false + npm run test:ci --workspace "@google/gemini-cli" -- --coverage.enabled=false else # Explicitly list non-cli packages to ensure they are sharded correctly - npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present -- --coverage.enabled=false + npm run test:ci --workspace "@google/gemini-cli-core" --workspace "@google/gemini-cli-a2a-server" --workspace "gemini-cli-vscode-ide-companion" --workspace "@google/gemini-cli-test-utils" --if-present -- --coverage.enabled=false npm run test:scripts fi @@ -429,11 +429,14 @@ jobs: NODE_ENV: 'test' run: | if ("${{ matrix.shard }}" -eq "cli") { - npm run test:ci --workspace @google/gemini-cli -- --coverage.enabled=false + npm run test:ci --workspace "@google/gemini-cli" -- --coverage.enabled=false + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } else { # Explicitly list non-cli packages to ensure they are sharded correctly - npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present -- --coverage.enabled=false + npm run test:ci --workspace "@google/gemini-cli-core" --workspace "@google/gemini-cli-a2a-server" --workspace "gemini-cli-vscode-ide-companion" --workspace "@google/gemini-cli-test-utils" --if-present -- --coverage.enabled=false + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } npm run test:scripts + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } shell: 'pwsh' diff --git a/.github/workflows/eval-pr.yml b/.github/workflows/eval-pr.yml index e0f839e667..9da0fc8511 100644 --- a/.github/workflows/eval-pr.yml +++ b/.github/workflows/eval-pr.yml @@ -46,12 +46,6 @@ jobs: node-version-file: '.nvmrc' cache: 'npm' - - name: 'Install dependencies' - run: 'npm ci' - - - name: 'Build project' - run: 'npm run build' - - name: 'Detect Steering Changes' id: 'detect' run: | @@ -60,6 +54,14 @@ jobs: echo "SHOULD_RUN=$SHOULD_RUN" >> "$GITHUB_OUTPUT" echo "STEERING_DETECTED=$STEERING_DETECTED" >> "$GITHUB_OUTPUT" + - name: 'Install dependencies' + if: "steps.detect.outputs.SHOULD_RUN == 'true'" + run: 'npm ci' + + - name: 'Build project' + if: "steps.detect.outputs.SHOULD_RUN == 'true'" + run: 'npm run build' + - name: 'Analyze PR Content (Guidance)' if: "steps.detect.outputs.STEERING_DETECTED == 'true'" id: 'analysis' @@ -94,7 +96,7 @@ jobs: fi - name: 'Post or Update PR Comment' - if: "always() && steps.detect.outputs.STEERING_DETECTED == 'true'" + if: "always() && (steps.detect.outputs.STEERING_DETECTED == 'true' || env.REPORT_FILE != '')" env: GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' run: | @@ -104,17 +106,20 @@ jobs: cat eval_regression_report.md echo "" fi - echo "### 🧠 Model Steering Guidance" - echo "" - echo "This PR modifies files that affect the model's behavior (prompts, tools, or instructions)." - echo "" - if [[ "${{ steps.analysis.outputs.MISSING_EVALS }}" == "true" ]]; then - echo "- ⚠️ **Consider adding Evals:** No behavioral evaluations (\`evals/*.eval.ts\`) were added or updated in this PR. Consider [adding a test case](https://github.com/google-gemini/gemini-cli/blob/main/evals/README.md#creating-an-evaluation) to verify the new behavior and prevent regressions." - fi + if [[ "${{ steps.detect.outputs.STEERING_DETECTED }}" == "true" ]]; then + echo "### 🧠 Model Steering Guidance" + echo "" + echo "This PR modifies files that affect the model's behavior (prompts, tools, or instructions)." + echo "" - if [[ "${{ steps.analysis.outputs.IS_MAINTAINER }}" == "true" ]]; then - echo "- 🚀 **Maintainer Reminder:** Please ensure that these changes do not regress results on benchmark evals before merging." + if [[ "${{ steps.analysis.outputs.MISSING_EVALS }}" == "true" ]]; then + echo "- ⚠️ **Consider adding Evals:** No behavioral evaluations (\`evals/*.eval.ts\`) were added or updated in this PR. Consider [adding a test case](https://github.com/google-gemini/gemini-cli/blob/main/evals/README.md#creating-an-evaluation) to verify the new behavior and prevent regressions." + fi + + if [[ "${{ steps.analysis.outputs.IS_MAINTAINER }}" == "true" ]]; then + echo "- 🚀 **Maintainer Reminder:** Please ensure that these changes do not regress results on benchmark evals before merging." + fi fi echo "" diff --git a/docs/changelogs/index.md b/docs/changelogs/index.md index 84a0daa3b2..ac3a433d0e 100644 --- a/docs/changelogs/index.md +++ b/docs/changelogs/index.md @@ -18,6 +18,31 @@ on GitHub. | [Preview](preview.md) | Experimental features ready for early feedback. | | [Stable](latest.md) | Stable, recommended for general use. | +## Announcements: v0.36.0 - 2026-04-01 + +- **Multi-Registry Architecture and Sandboxing:** Introduced a multi-registry + architecture and implemented native macOS Seatbelt and Windows sandboxing for + enhanced subagent security + ([#22712](https://github.com/google-gemini/gemini-cli/pull/22712), + [#22718](https://github.com/google-gemini/gemini-cli/pull/22718) by @akh64bit, + [#22832](https://github.com/google-gemini/gemini-cli/pull/22832) by @ehedlund, + [#21807](https://github.com/google-gemini/gemini-cli/pull/21807) by + @mattKorwel). +- **Refreshed Composer UX:** Implemented a refreshed user experience for the + Composer layout and improved terminal interaction robustness + ([#21212](https://github.com/google-gemini/gemini-cli/pull/21212), + [#23286](https://github.com/google-gemini/gemini-cli/pull/23286) by + @jwhelangoog). +- **Git Worktree Support:** Added native support for Git worktrees, allowing for + isolated parallel sessions + ([#22973](https://github.com/google-gemini/gemini-cli/pull/22973), + [#23265](https://github.com/google-gemini/gemini-cli/pull/23265) by @jerop). +- **Subagent Context and Feedback:** Enhanced subagents with JIT context + injection and resilient tool rejection with contextual feedback + ([#23032](https://github.com/google-gemini/gemini-cli/pull/23032), + [#22951](https://github.com/google-gemini/gemini-cli/pull/22951) by + @abhipatel12). + ## Announcements: v0.35.0 - 2026-03-24 - **Customizable Keyboard Shortcuts:** Users can now customize their keyboard diff --git a/docs/changelogs/latest.md b/docs/changelogs/latest.md index 3d3cf07f7a..d776a43135 100644 --- a/docs/changelogs/latest.md +++ b/docs/changelogs/latest.md @@ -1,6 +1,6 @@ -# Latest stable release: v0.35.3 +# Latest stable release: v0.36.0 -Released: March 28, 2026 +Released: April 1, 2026 For most users, our latest stable release is the recommended release. Install the latest stable version with: @@ -11,381 +11,372 @@ npm install -g @google/gemini-cli ## Highlights -- **Customizable Keyboard Shortcuts:** Significant improvements to input - flexibility with support for custom keybindings, literal character bindings, - and extended terminal protocol keys. -- **Vim Mode Enhancements:** Further refinement of the Vim modal editing - experience, adding common motions like \`X\`, \`~\`, \`r\`, and \`f/F/t/T\`, - along with yank and paste support. -- **Enhanced Security through Sandboxing:** Introduction of a unified - \`SandboxManager\` and integration of Linux-native sandboxing (bubblewrap and - seccomp) to isolate tool execution and improve system security. -- **JIT Context Discovery:** Improved performance and accuracy by enabling - Just-In-Time context loading for file system tools, ensuring the model has the - most relevant information without overwhelming the context. -- **Subagent & Performance Updates:** Subagents are now enabled by default, - supported by a model-driven parallel tool scheduler and code splitting for - faster startup and more efficient task execution. +- **Multi-Registry Architecture and Tool Isolation:** Introduced a + multi-registry architecture for subagents and implemented strict sandboxing + for macOS (Seatbelt) and Windows to enhance security and isolation. +- **Improved Subagent Coordination:** Enhanced subagents with local execution + capabilities, JIT context injection (upward traversal capped at git root), and + resilient tool rejection with contextual feedback. +- **Enhanced UI and UX:** Implemented a refreshed UX for the Composer layout, + improved terminal fallback warnings, and resolved various UI flickering and + state persistence issues. +- **Git Worktree Support:** Added support for Git worktrees to enable isolated + parallel sessions within the same repository. +- **Plan Mode Improvements:** Plan mode now supports non-interactive execution + and includes hardened sandbox path resolution to prevent hallucinations. ## What's Changed -- fix(patch): cherry-pick 765fb67 to release/v0.35.2-pr-24055 [CONFLICTS] by - @gemini-cli-robot in - [#24063](https://github.com/google-gemini/gemini-cli/pull/24063) -- fix(core): allow disabling environment variable redaction by @galz10 in - [#23927](https://github.com/google-gemini/gemini-cli/pull/23927) -- fix(a2a-server): A2A server should execute ask policies in interactive mode by - @keith.schaab in - [#23831](https://github.com/google-gemini/gemini-cli/pull/23831) -- feat(cli): customizable keyboard shortcuts by @scidomino in - [#21945](https://github.com/google-gemini/gemini-cli/pull/21945) -- feat(core): Thread `AgentLoopContext` through core. by @joshualitt in - [#21944](https://github.com/google-gemini/gemini-cli/pull/21944) -- chore(release): bump version to 0.35.0-nightly.20260311.657f19c1f by - @gemini-cli-robot in - [#21966](https://github.com/google-gemini/gemini-cli/pull/21966) -- refactor(a2a): remove legacy CoreToolScheduler by @adamfweidman in - [#21955](https://github.com/google-gemini/gemini-cli/pull/21955) -- feat(ui): add missing vim mode motions (X, ~, r, f/F/t/T, df/dt and friends) - by @aanari in [#21932](https://github.com/google-gemini/gemini-cli/pull/21932) -- Feat/retry fetch notifications by @aishaneeshah in - [#21813](https://github.com/google-gemini/gemini-cli/pull/21813) -- fix(core): remove OAuth check from handle fallback and clean up stray file by - @sehoon38 in [#21962](https://github.com/google-gemini/gemini-cli/pull/21962) -- feat(cli): support literal character keybindings and extended Kitty protocol - keys by @scidomino in - [#21972](https://github.com/google-gemini/gemini-cli/pull/21972) -- fix(ui): clamp cursor to last char after all NORMAL mode deletes by @aanari in - [#21973](https://github.com/google-gemini/gemini-cli/pull/21973) -- test(core): add missing tests for prompts/utils.ts by @krrishverma1805-web in - [#19941](https://github.com/google-gemini/gemini-cli/pull/19941) -- fix(cli): allow scrolling keys in copy mode (Ctrl+S selection mode) by - @nsalerni in [#19933](https://github.com/google-gemini/gemini-cli/pull/19933) -- docs(cli): add custom keybinding documentation by @scidomino in - [#21980](https://github.com/google-gemini/gemini-cli/pull/21980) -- docs: fix misleading YOLO mode description in defaultApprovalMode by - @Gyanranjan-Priyam in - [#21878](https://github.com/google-gemini/gemini-cli/pull/21878) -- fix: clean up /clear and /resume by @jackwotherspoon in - [#22007](https://github.com/google-gemini/gemini-cli/pull/22007) -- fix(core)#20941: reap orphaned descendant processes on PTY abort by @manavmax - in [#21124](https://github.com/google-gemini/gemini-cli/pull/21124) -- fix(core): update language detection to use LSP 3.18 identifiers by @yunaseoul - in [#21931](https://github.com/google-gemini/gemini-cli/pull/21931) -- feat(cli): support removing keybindings via '-' prefix by @scidomino in - [#22042](https://github.com/google-gemini/gemini-cli/pull/22042) -- feat(policy): add --admin-policy flag for supplemental admin policies by - @galz10 in [#20360](https://github.com/google-gemini/gemini-cli/pull/20360) -- merge duplicate imports packages/cli/src subtask1 by @Nixxx19 in - [#22040](https://github.com/google-gemini/gemini-cli/pull/22040) -- perf(core): parallelize user quota and experiments fetching in refreshAuth by - @sehoon38 in [#21648](https://github.com/google-gemini/gemini-cli/pull/21648) -- Changelog for v0.34.0-preview.0 by @gemini-cli-robot in - [#21965](https://github.com/google-gemini/gemini-cli/pull/21965) -- Changelog for v0.33.0 by @gemini-cli-robot in - [#21967](https://github.com/google-gemini/gemini-cli/pull/21967) -- fix(core): handle EISDIR in robustRealpath on Windows by @sehoon38 in - [#21984](https://github.com/google-gemini/gemini-cli/pull/21984) -- feat(core): include initiationMethod in conversation interaction telemetry by - @yunaseoul in [#22054](https://github.com/google-gemini/gemini-cli/pull/22054) -- feat(ui): add vim yank/paste (y/p/P) with unnamed register by @aanari in - [#22026](https://github.com/google-gemini/gemini-cli/pull/22026) -- fix(core): enable numerical routing for api key users by @sehoon38 in - [#21977](https://github.com/google-gemini/gemini-cli/pull/21977) -- feat(telemetry): implement retry attempt telemetry for network related retries - by @aishaneeshah in - [#22027](https://github.com/google-gemini/gemini-cli/pull/22027) -- fix(policy): remove unnecessary escapeRegex from pattern builders by - @spencer426 in - [#21921](https://github.com/google-gemini/gemini-cli/pull/21921) -- fix(core): preserve dynamic tool descriptions on session resume by @sehoon38 - in [#18835](https://github.com/google-gemini/gemini-cli/pull/18835) -- chore: allow 'gemini-3.1' in sensitive keyword linter by @scidomino in - [#22065](https://github.com/google-gemini/gemini-cli/pull/22065) -- feat(core): support custom base URL via env vars by @junaiddshaukat in - [#21561](https://github.com/google-gemini/gemini-cli/pull/21561) -- merge duplicate imports packages/cli/src subtask2 by @Nixxx19 in - [#22051](https://github.com/google-gemini/gemini-cli/pull/22051) -- fix(core): silently retry API errors up to 3 times before halting session by - @spencer426 in - [#21989](https://github.com/google-gemini/gemini-cli/pull/21989) -- feat(core): simplify subagent success UI and improve early termination display - by @abhipatel12 in - [#21917](https://github.com/google-gemini/gemini-cli/pull/21917) -- merge duplicate imports packages/cli/src subtask3 by @Nixxx19 in - [#22056](https://github.com/google-gemini/gemini-cli/pull/22056) -- fix(hooks): fix BeforeAgent/AfterAgent inconsistencies (#18514) by @krishdef7 - in [#21383](https://github.com/google-gemini/gemini-cli/pull/21383) -- feat(core): implement SandboxManager interface and config schema by @galz10 in - [#21774](https://github.com/google-gemini/gemini-cli/pull/21774) -- docs: document npm deprecation warnings as safe to ignore by @h30s in - [#20692](https://github.com/google-gemini/gemini-cli/pull/20692) -- fix: remove status/need-triage from maintainer-only issues by @SandyTao520 in - [#22044](https://github.com/google-gemini/gemini-cli/pull/22044) -- fix(core): propagate subagent context to policy engine by @NTaylorMullen in - [#22086](https://github.com/google-gemini/gemini-cli/pull/22086) -- fix(cli): resolve skill uninstall failure when skill name is updated by - @NTaylorMullen in - [#22085](https://github.com/google-gemini/gemini-cli/pull/22085) -- docs(plan): clarify interactive plan editing with Ctrl+X by @Adib234 in - [#22076](https://github.com/google-gemini/gemini-cli/pull/22076) -- fix(policy): ensure user policies are loaded when policyPaths is empty by - @NTaylorMullen in - [#22090](https://github.com/google-gemini/gemini-cli/pull/22090) -- Docs: Add documentation for model steering (experimental). by @jkcinouye in - [#21154](https://github.com/google-gemini/gemini-cli/pull/21154) -- Add issue for automated changelogs by @g-samroberts in - [#21912](https://github.com/google-gemini/gemini-cli/pull/21912) -- fix(core): secure argsPattern and revert WEB_FETCH_TOOL_NAME escalation by - @spencer426 in - [#22104](https://github.com/google-gemini/gemini-cli/pull/22104) -- feat(core): differentiate User-Agent for a2a-server and ACP clients by - @bdmorgan in [#22059](https://github.com/google-gemini/gemini-cli/pull/22059) -- refactor(core): extract ExecutionLifecycleService for tool backgrounding by - @adamfweidman in - [#21717](https://github.com/google-gemini/gemini-cli/pull/21717) -- feat: Display pending and confirming tool calls by @sripasg in - [#22106](https://github.com/google-gemini/gemini-cli/pull/22106) -- feat(browser): implement input blocker overlay during automation by - @kunal-10-cloud in - [#21132](https://github.com/google-gemini/gemini-cli/pull/21132) -- fix: register themes on extension load not start by @jackwotherspoon in - [#22148](https://github.com/google-gemini/gemini-cli/pull/22148) -- feat(ui): Do not show Ultra users /upgrade hint (#22154) by @sehoon38 in - [#22156](https://github.com/google-gemini/gemini-cli/pull/22156) -- chore: remove unnecessary log for themes by @jackwotherspoon in - [#22165](https://github.com/google-gemini/gemini-cli/pull/22165) -- fix(core): resolve MCP tool FQN validation, schema export, and wildcards in - subagents by @abhipatel12 in - [#22069](https://github.com/google-gemini/gemini-cli/pull/22069) -- fix(cli): validate --model argument at startup by @JaisalJain in - [#21393](https://github.com/google-gemini/gemini-cli/pull/21393) -- fix(core): handle policy ALLOW for exit_plan_mode by @backnotprop in - [#21802](https://github.com/google-gemini/gemini-cli/pull/21802) -- feat(telemetry): add Clearcut instrumentation for AI credits billing events by - @gsquared94 in - [#22153](https://github.com/google-gemini/gemini-cli/pull/22153) -- feat(core): add google credentials provider for remote agents by @adamfweidman - in [#21024](https://github.com/google-gemini/gemini-cli/pull/21024) -- test(cli): add integration test for node deprecation warnings by @Nixxx19 in - [#20215](https://github.com/google-gemini/gemini-cli/pull/20215) -- feat(cli): allow safe tools to execute concurrently while agent is busy by - @spencer426 in - [#21988](https://github.com/google-gemini/gemini-cli/pull/21988) -- feat(core): implement model-driven parallel tool scheduler by @abhipatel12 in - [#21933](https://github.com/google-gemini/gemini-cli/pull/21933) -- update vulnerable deps by @scidomino in - [#22180](https://github.com/google-gemini/gemini-cli/pull/22180) -- fix(core): fix startup stats to use int values for timestamps and durations by - @yunaseoul in [#22201](https://github.com/google-gemini/gemini-cli/pull/22201) -- fix(core): prevent duplicate tool schemas for instantiated tools by - @abhipatel12 in - [#22204](https://github.com/google-gemini/gemini-cli/pull/22204) -- fix(core): add proxy routing support for remote A2A subagents by @adamfweidman - in [#22199](https://github.com/google-gemini/gemini-cli/pull/22199) -- fix(core/ide): add Antigravity CLI fallbacks by @apfine in - [#22030](https://github.com/google-gemini/gemini-cli/pull/22030) -- fix(browser): fix duplicate function declaration error in browser agent by - @gsquared94 in - [#22207](https://github.com/google-gemini/gemini-cli/pull/22207) -- feat(core): implement Stage 1 improvements for webfetch tool by @aishaneeshah - in [#21313](https://github.com/google-gemini/gemini-cli/pull/21313) -- Changelog for v0.34.0-preview.1 by @gemini-cli-robot in - [#22194](https://github.com/google-gemini/gemini-cli/pull/22194) -- perf(cli): enable code splitting and deferred UI loading by @sehoon38 in - [#22117](https://github.com/google-gemini/gemini-cli/pull/22117) -- fix: remove unused img.png from project root by @SandyTao520 in - [#22222](https://github.com/google-gemini/gemini-cli/pull/22222) -- docs(local model routing): add docs on how to use Gemma for local model - routing by @douglas-reid in - [#21365](https://github.com/google-gemini/gemini-cli/pull/21365) -- feat(a2a): enable native gRPC support and protocol routing by @alisa-alisa in - [#21403](https://github.com/google-gemini/gemini-cli/pull/21403) -- fix(cli): escape @ symbols on paste to prevent unintended file expansion by - @krishdef7 in [#21239](https://github.com/google-gemini/gemini-cli/pull/21239) -- feat(core): add trajectoryId to ConversationOffered telemetry by @yunaseoul in - [#22214](https://github.com/google-gemini/gemini-cli/pull/22214) -- docs: clarify that tools.core is an allowlist for ALL built-in tools by - @hobostay in [#18813](https://github.com/google-gemini/gemini-cli/pull/18813) -- docs(plan): document hooks with plan mode by @ruomengz in - [#22197](https://github.com/google-gemini/gemini-cli/pull/22197) -- Changelog for v0.33.1 by @gemini-cli-robot in - [#22235](https://github.com/google-gemini/gemini-cli/pull/22235) -- build(ci): fix false positive evals trigger on merge commits by @gundermanc in - [#22237](https://github.com/google-gemini/gemini-cli/pull/22237) -- fix(core): explicitly pass messageBus to policy engine for MCP tool saves by - @abhipatel12 in - [#22255](https://github.com/google-gemini/gemini-cli/pull/22255) -- feat(core): Fully migrate packages/core to AgentLoopContext. by @joshualitt in - [#22115](https://github.com/google-gemini/gemini-cli/pull/22115) -- feat(core): increase sub-agent turn and time limits by @bdmorgan in - [#22196](https://github.com/google-gemini/gemini-cli/pull/22196) -- feat(core): instrument file system tools for JIT context discovery by +- Changelog for v0.33.2 by @gemini-cli-robot in + [#22730](https://github.com/google-gemini/gemini-cli/pull/22730) +- feat(core): multi-registry architecture and tool filtering for subagents by + @akh64bit in [#22712](https://github.com/google-gemini/gemini-cli/pull/22712) +- Changelog for v0.34.0-preview.4 by @gemini-cli-robot in + [#22752](https://github.com/google-gemini/gemini-cli/pull/22752) +- fix(devtools): use theme-aware text colors for console warnings and errors by @SandyTao520 in - [#22082](https://github.com/google-gemini/gemini-cli/pull/22082) -- refactor(ui): extract pure session browser utilities by @abhipatel12 in - [#22256](https://github.com/google-gemini/gemini-cli/pull/22256) -- fix(plan): Fix AskUser evals by @Adib234 in - [#22074](https://github.com/google-gemini/gemini-cli/pull/22074) -- fix(settings): prevent j/k navigation keys from intercepting edit buffer input - by @student-ankitpandit in - [#21865](https://github.com/google-gemini/gemini-cli/pull/21865) -- feat(skills): improve async-pr-review workflow and logging by @mattKorwel in - [#21790](https://github.com/google-gemini/gemini-cli/pull/21790) -- refactor(cli): consolidate getErrorMessage utility to core by @scidomino in - [#22190](https://github.com/google-gemini/gemini-cli/pull/22190) -- fix(core): show descriptive error messages when saving settings fails by - @afarber in [#18095](https://github.com/google-gemini/gemini-cli/pull/18095) -- docs(core): add authentication guide for remote subagents by @adamfweidman in - [#22178](https://github.com/google-gemini/gemini-cli/pull/22178) -- docs: overhaul subagents documentation and add /agents command by @abhipatel12 - in [#22345](https://github.com/google-gemini/gemini-cli/pull/22345) -- refactor(ui): extract SessionBrowser static ui components by @abhipatel12 in - [#22348](https://github.com/google-gemini/gemini-cli/pull/22348) -- test: add Object.create context regression test and tool confirmation - integration test by @gsquared94 in - [#22356](https://github.com/google-gemini/gemini-cli/pull/22356) -- feat(tracker): return TodoList display for tracker tools by @anj-s in - [#22060](https://github.com/google-gemini/gemini-cli/pull/22060) -- feat(agent): add allowed domain restrictions for browser agent by + [#22181](https://github.com/google-gemini/gemini-cli/pull/22181) +- Add support for dynamic model Resolution to ModelConfigService by @kevinjwang1 + in [#22578](https://github.com/google-gemini/gemini-cli/pull/22578) +- chore(release): bump version to 0.36.0-nightly.20260317.2f90b4653 by + @gemini-cli-robot in + [#22858](https://github.com/google-gemini/gemini-cli/pull/22858) +- fix(cli): use active sessionId in useLogger and improve resume robustness by + @mattKorwel in + [#22606](https://github.com/google-gemini/gemini-cli/pull/22606) +- fix(cli): expand tilde in policy paths from settings.json by @abhipatel12 in + [#22772](https://github.com/google-gemini/gemini-cli/pull/22772) +- fix(core): add actionable warnings for terminal fallbacks (#14426) by + @spencer426 in + [#22211](https://github.com/google-gemini/gemini-cli/pull/22211) +- feat(tracker): integrate task tracker protocol into core system prompt by + @anj-s in [#22442](https://github.com/google-gemini/gemini-cli/pull/22442) +- chore: add posttest build hooks and fix missing dependencies by @NTaylorMullen + in [#22865](https://github.com/google-gemini/gemini-cli/pull/22865) +- feat(a2a): add agent acknowledgment command and enhance registry discovery by + @alisa-alisa in + [#22389](https://github.com/google-gemini/gemini-cli/pull/22389) +- fix(cli): automatically add all VSCode workspace folders to Gemini context by + @sakshisemalti in + [#21380](https://github.com/google-gemini/gemini-cli/pull/21380) +- feat: add 'blocked' status to tasks and todos by @anj-s in + [#22735](https://github.com/google-gemini/gemini-cli/pull/22735) +- refactor(cli): remove extra newlines in ShellToolMessage.tsx by @NTaylorMullen + in [#22868](https://github.com/google-gemini/gemini-cli/pull/22868) +- fix(cli): lazily load settings in onModelChange to prevent stale closure data + loss by @KumarADITHYA123 in + [#20403](https://github.com/google-gemini/gemini-cli/pull/20403) +- feat(core): subagent local execution and tool isolation by @akh64bit in + [#22718](https://github.com/google-gemini/gemini-cli/pull/22718) +- fix(cli): resolve subagent grouping and UI state persistence by @abhipatel12 + in [#22252](https://github.com/google-gemini/gemini-cli/pull/22252) +- refactor(ui): extract SessionBrowser search and navigation components by + @abhipatel12 in + [#22377](https://github.com/google-gemini/gemini-cli/pull/22377) +- fix: updates Docker image reference for GitHub MCP server by @jhhornn in + [#22938](https://github.com/google-gemini/gemini-cli/pull/22938) +- refactor(cli): group subagent trajectory deletion and use native filesystem + testing by @abhipatel12 in + [#22890](https://github.com/google-gemini/gemini-cli/pull/22890) +- refactor(cli): simplify keypress and mouse providers and update tests by + @scidomino in [#22853](https://github.com/google-gemini/gemini-cli/pull/22853) +- Changelog for v0.34.0 by @gemini-cli-robot in + [#22860](https://github.com/google-gemini/gemini-cli/pull/22860) +- test(cli): simplify createMockSettings calls by @scidomino in + [#22952](https://github.com/google-gemini/gemini-cli/pull/22952) +- feat(ui): format multi-line banner warnings with a bold title by @keithguerin + in [#22955](https://github.com/google-gemini/gemini-cli/pull/22955) +- Docs: Remove references to stale Gemini CLI file structure info by + @g-samroberts in + [#22976](https://github.com/google-gemini/gemini-cli/pull/22976) +- feat(ui): remove write todo list tool from UI tips by @aniruddhaadak80 in + [#22281](https://github.com/google-gemini/gemini-cli/pull/22281) +- Fix issue where subagent thoughts are appended. by @gundermanc in + [#22975](https://github.com/google-gemini/gemini-cli/pull/22975) +- Feat/browser privacy consent by @kunal-10-cloud in + [#21119](https://github.com/google-gemini/gemini-cli/pull/21119) +- fix(core): explicitly map execution context in LocalAgentExecutor by @akh64bit + in [#22949](https://github.com/google-gemini/gemini-cli/pull/22949) +- feat(plan): support plan mode in non-interactive mode by @ruomengz in + [#22670](https://github.com/google-gemini/gemini-cli/pull/22670) +- feat(core): implement strict macOS sandboxing using Seatbelt allowlist by + @ehedlund in [#22832](https://github.com/google-gemini/gemini-cli/pull/22832) +- docs: add additional notes by @abhipatel12 in + [#23008](https://github.com/google-gemini/gemini-cli/pull/23008) +- fix(cli): resolve duplicate footer on tool cancel via ESC (#21743) by + @ruomengz in [#21781](https://github.com/google-gemini/gemini-cli/pull/21781) +- Changelog for v0.35.0-preview.1 by @gemini-cli-robot in + [#23012](https://github.com/google-gemini/gemini-cli/pull/23012) +- fix(ui): fix flickering on small terminal heights by @devr0306 in + [#21416](https://github.com/google-gemini/gemini-cli/pull/21416) +- fix(acp): provide more meta in tool_call_update by @Mervap in + [#22663](https://github.com/google-gemini/gemini-cli/pull/22663) +- docs: add FAQ entry for checking Gemini CLI version by @surajsahani in + [#21271](https://github.com/google-gemini/gemini-cli/pull/21271) +- feat(core): resilient subagent tool rejection with contextual feedback by + @abhipatel12 in + [#22951](https://github.com/google-gemini/gemini-cli/pull/22951) +- fix(cli): correctly handle auto-update for standalone binaries by @bdmorgan in + [#23038](https://github.com/google-gemini/gemini-cli/pull/23038) +- feat(core): add content-utils by @adamfweidman in + [#22984](https://github.com/google-gemini/gemini-cli/pull/22984) +- fix: circumvent genai sdk requirement for api key when using gateway auth via + ACP by @sripasg in + [#23042](https://github.com/google-gemini/gemini-cli/pull/23042) +- fix(core): don't persist browser consent sentinel in non-interactive mode by + @jasonmatthewsuhari in + [#23073](https://github.com/google-gemini/gemini-cli/pull/23073) +- fix(core): narrow browser agent description to prevent stealing URL tasks from + web_fetch by @gsquared94 in + [#23086](https://github.com/google-gemini/gemini-cli/pull/23086) +- feat(cli): Partial threading of AgentLoopContext. by @joshualitt in + [#22978](https://github.com/google-gemini/gemini-cli/pull/22978) +- fix(browser-agent): enable "Allow all server tools" session policy by @cynthialong0-0 in - [#21775](https://github.com/google-gemini/gemini-cli/pull/21775) -- chore/release: bump version to 0.35.0-nightly.20260313.bb060d7a9 by - @gemini-cli-robot in - [#22251](https://github.com/google-gemini/gemini-cli/pull/22251) -- Move keychain fallback to keychain service by @chrstnb in - [#22332](https://github.com/google-gemini/gemini-cli/pull/22332) -- feat(core): integrate SandboxManager to sandbox all process-spawning tools by - @galz10 in [#22231](https://github.com/google-gemini/gemini-cli/pull/22231) -- fix(cli): support CJK input and full Unicode scalar values in terminal - protocols by @scidomino in - [#22353](https://github.com/google-gemini/gemini-cli/pull/22353) -- Promote stable tests. by @gundermanc in - [#22253](https://github.com/google-gemini/gemini-cli/pull/22253) -- feat(tracker): add tracker policy by @anj-s in - [#22379](https://github.com/google-gemini/gemini-cli/pull/22379) -- feat(security): add disableAlwaysAllow setting to disable auto-approvals by - @galz10 in [#21941](https://github.com/google-gemini/gemini-cli/pull/21941) -- Revert "fix(cli): validate --model argument at startup" by @sehoon38 in - [#22378](https://github.com/google-gemini/gemini-cli/pull/22378) -- fix(mcp): handle equivalent root resource URLs in OAuth validation by @galz10 - in [#20231](https://github.com/google-gemini/gemini-cli/pull/20231) -- fix(core): use session-specific temp directory for task tracker by @anj-s in - [#22382](https://github.com/google-gemini/gemini-cli/pull/22382) -- Fix issue where config was undefined. by @gundermanc in - [#22397](https://github.com/google-gemini/gemini-cli/pull/22397) -- fix(core): deduplicate project memory when JIT context is enabled by + [#22343](https://github.com/google-gemini/gemini-cli/pull/22343) +- refactor(cli): integrate real config loading into async test utils by + @scidomino in [#23040](https://github.com/google-gemini/gemini-cli/pull/23040) +- feat(core): inject memory and JIT context into subagents by @abhipatel12 in + [#23032](https://github.com/google-gemini/gemini-cli/pull/23032) +- Fix logging and virtual list. by @jacob314 in + [#23080](https://github.com/google-gemini/gemini-cli/pull/23080) +- feat(core): cap JIT context upward traversal at git root by @SandyTao520 in + [#23074](https://github.com/google-gemini/gemini-cli/pull/23074) +- Docs: Minor style updates from initial docs audit. by @g-samroberts in + [#22872](https://github.com/google-gemini/gemini-cli/pull/22872) +- feat(core): add experimental memory manager agent to replace save_memory tool + by @SandyTao520 in + [#22726](https://github.com/google-gemini/gemini-cli/pull/22726) +- Changelog for v0.35.0-preview.2 by @gemini-cli-robot in + [#23142](https://github.com/google-gemini/gemini-cli/pull/23142) +- Update website issue template for label and title by @g-samroberts in + [#23036](https://github.com/google-gemini/gemini-cli/pull/23036) +- fix: upgrade ACP SDK from 0.12 to 0.16.1 by @sripasg in + [#23132](https://github.com/google-gemini/gemini-cli/pull/23132) +- Update callouts to work on github. by @g-samroberts in + [#22245](https://github.com/google-gemini/gemini-cli/pull/22245) +- feat: ACP: Add token usage metadata to the `send` method's return value by + @sripasg in [#23148](https://github.com/google-gemini/gemini-cli/pull/23148) +- fix(plan): clarify that plan mode policies are combined with normal mode by + @ruomengz in [#23158](https://github.com/google-gemini/gemini-cli/pull/23158) +- Add ModelChain support to ModelConfigService and make ModelDialog dynamic by + @kevinjwang1 in + [#22914](https://github.com/google-gemini/gemini-cli/pull/22914) +- Ensure that copied extensions are writable in the user's local directory by + @kevinjwang1 in + [#23016](https://github.com/google-gemini/gemini-cli/pull/23016) +- feat(core): implement native Windows sandboxing by @mattKorwel in + [#21807](https://github.com/google-gemini/gemini-cli/pull/21807) +- feat(core): add support for admin-forced MCP server installations by + @gsquared94 in + [#23163](https://github.com/google-gemini/gemini-cli/pull/23163) +- chore(lint): ignore .gemini directory and recursive node_modules by + @mattKorwel in + [#23211](https://github.com/google-gemini/gemini-cli/pull/23211) +- feat(cli): conditionally exclude ask_user tool in ACP mode by @nmcnamara-eng + in [#23045](https://github.com/google-gemini/gemini-cli/pull/23045) +- feat(core): introduce AgentSession and rename stream events to agent events by + @mbleigh in [#23159](https://github.com/google-gemini/gemini-cli/pull/23159) +- feat(worktree): add Git worktree support for isolated parallel sessions by + @jerop in [#22973](https://github.com/google-gemini/gemini-cli/pull/22973) +- Add support for linking in the extension registry by @kevinjwang1 in + [#23153](https://github.com/google-gemini/gemini-cli/pull/23153) +- feat(extensions): add --skip-settings flag to install command by @Ratish1 in + [#17212](https://github.com/google-gemini/gemini-cli/pull/17212) +- feat(telemetry): track if session is running in a Git worktree by @jerop in + [#23265](https://github.com/google-gemini/gemini-cli/pull/23265) +- refactor(core): use absolute paths in GEMINI.md context markers by @SandyTao520 in - [#22234](https://github.com/google-gemini/gemini-cli/pull/22234) -- feat(prompts): implement Topic-Action-Summary model for verbosity reduction by - @Abhijit-2592 in - [#21503](https://github.com/google-gemini/gemini-cli/pull/21503) -- fix(core): fix manual deletion of subagent histories by @abhipatel12 in - [#22407](https://github.com/google-gemini/gemini-cli/pull/22407) -- Add registry var by @kevinjwang1 in - [#22224](https://github.com/google-gemini/gemini-cli/pull/22224) -- Add ModelDefinitions to ModelConfigService by @kevinjwang1 in - [#22302](https://github.com/google-gemini/gemini-cli/pull/22302) -- fix(cli): improve command conflict handling for skills by @NTaylorMullen in - [#21942](https://github.com/google-gemini/gemini-cli/pull/21942) -- fix(core): merge user settings with extension-provided MCP servers by + [#23135](https://github.com/google-gemini/gemini-cli/pull/23135) +- fix(core): add sanitization to sub agent thoughts and centralize utilities by + @devr0306 in [#22828](https://github.com/google-gemini/gemini-cli/pull/22828) +- feat(core): refine User-Agent for VS Code traffic (unified format) by + @sehoon38 in [#23256](https://github.com/google-gemini/gemini-cli/pull/23256) +- Fix schema for ModelChains by @kevinjwang1 in + [#23284](https://github.com/google-gemini/gemini-cli/pull/23284) +- test(cli): refactor tests for async render utilities by @scidomino in + [#23252](https://github.com/google-gemini/gemini-cli/pull/23252) +- feat(core): add security prompt for browser agent by @cynthialong0-0 in + [#23241](https://github.com/google-gemini/gemini-cli/pull/23241) +- refactor(ide): replace dynamic undici import with static fetch import by + @cocosheng-g in + [#23268](https://github.com/google-gemini/gemini-cli/pull/23268) +- test(cli): address unresolved feedback from PR #23252 by @scidomino in + [#23303](https://github.com/google-gemini/gemini-cli/pull/23303) +- feat(browser): add sensitive action controls and read-only noise reduction by + @cynthialong0-0 in + [#22867](https://github.com/google-gemini/gemini-cli/pull/22867) +- Disabling failing test while investigating by @alisa-alisa in + [#23311](https://github.com/google-gemini/gemini-cli/pull/23311) +- fix broken extension link in hooks guide by @Indrapal-70 in + [#21728](https://github.com/google-gemini/gemini-cli/pull/21728) +- fix(core): fix agent description indentation by @abhipatel12 in + [#23315](https://github.com/google-gemini/gemini-cli/pull/23315) +- Wrap the text under TOML rule for easier readability in policy-engine.md… by + @CogitationOps in + [#23076](https://github.com/google-gemini/gemini-cli/pull/23076) +- fix(extensions): revert broken extension removal behavior by @ehedlund in + [#23317](https://github.com/google-gemini/gemini-cli/pull/23317) +- feat(core): set up onboarding telemetry by @yunaseoul in + [#23118](https://github.com/google-gemini/gemini-cli/pull/23118) +- Retry evals on API error. by @gundermanc in + [#23322](https://github.com/google-gemini/gemini-cli/pull/23322) +- fix(evals): remove tool restrictions and add compile-time guards by + @SandyTao520 in + [#23312](https://github.com/google-gemini/gemini-cli/pull/23312) +- fix(hooks): support 'ask' decision for BeforeTool hooks by @gundermanc in + [#21146](https://github.com/google-gemini/gemini-cli/pull/21146) +- feat(browser): add warning message for session mode 'existing' by + @cynthialong0-0 in + [#23288](https://github.com/google-gemini/gemini-cli/pull/23288) +- chore(lint): enforce zero warnings and cleanup syntax restrictions by + @alisa-alisa in + [#22902](https://github.com/google-gemini/gemini-cli/pull/22902) +- fix(cli): add Esc instruction to HooksDialog footer by @abhipatel12 in + [#23258](https://github.com/google-gemini/gemini-cli/pull/23258) +- Disallow and suppress misused spread operator. by @gundermanc in + [#23294](https://github.com/google-gemini/gemini-cli/pull/23294) +- fix(core): refine CliHelpAgent description for better delegation by @abhipatel12 in - [#22484](https://github.com/google-gemini/gemini-cli/pull/22484) -- fix(core): skip discovery for incomplete MCP configs and resolve merge race - condition by @abhipatel12 in - [#22494](https://github.com/google-gemini/gemini-cli/pull/22494) -- fix(automation): harden stale PR closer permissions and maintainer detection - by @bdmorgan in - [#22558](https://github.com/google-gemini/gemini-cli/pull/22558) -- fix(automation): evaluate staleness before checking protected labels by - @bdmorgan in [#22561](https://github.com/google-gemini/gemini-cli/pull/22561) -- feat(agent): replace the runtime npx for browser agent chrome devtool mcp with - pre-built bundle by @cynthialong0-0 in - [#22213](https://github.com/google-gemini/gemini-cli/pull/22213) -- perf: optimize TrackerService dependency checks by @anj-s in - [#22384](https://github.com/google-gemini/gemini-cli/pull/22384) -- docs(policy): remove trailing space from commandPrefix examples by @kawasin73 - in [#22264](https://github.com/google-gemini/gemini-cli/pull/22264) -- fix(a2a-server): resolve unsafe assignment lint errors by @ehedlund in - [#22661](https://github.com/google-gemini/gemini-cli/pull/22661) -- fix: Adjust ToolGroupMessage filtering to hide Confirming and show Canceled - tool calls. by @sripasg in - [#22230](https://github.com/google-gemini/gemini-cli/pull/22230) -- Disallow Object.create() and reflect. by @gundermanc in - [#22408](https://github.com/google-gemini/gemini-cli/pull/22408) -- Guard pro model usage by @sehoon38 in - [#22665](https://github.com/google-gemini/gemini-cli/pull/22665) -- refactor(core): Creates AgentSession abstraction for consolidated agent - interface. by @mbleigh in - [#22270](https://github.com/google-gemini/gemini-cli/pull/22270) -- docs(changelog): remove internal commands from release notes by + [#23310](https://github.com/google-gemini/gemini-cli/pull/23310) +- fix(core): enable global session and persistent approval for web_fetch by + @NTaylorMullen in + [#23295](https://github.com/google-gemini/gemini-cli/pull/23295) +- fix(plan): add state transition override to prevent plan mode freeze by + @Adib234 in [#23020](https://github.com/google-gemini/gemini-cli/pull/23020) +- fix(cli): record skill activation tool calls in chat history by @NTaylorMullen + in [#23203](https://github.com/google-gemini/gemini-cli/pull/23203) +- fix(core): ensure subagent tool updates apply configuration overrides + immediately by @abhipatel12 in + [#23161](https://github.com/google-gemini/gemini-cli/pull/23161) +- fix(cli): resolve flicker at boundaries of list in BaseSelectionList by @jackwotherspoon in - [#22529](https://github.com/google-gemini/gemini-cli/pull/22529) -- feat: enable subagents by @abhipatel12 in - [#22386](https://github.com/google-gemini/gemini-cli/pull/22386) -- feat(extensions): implement cryptographic integrity verification for extension - updates by @ehedlund in - [#21772](https://github.com/google-gemini/gemini-cli/pull/21772) -- feat(tracker): polish UI sorting and formatting by @anj-s in - [#22437](https://github.com/google-gemini/gemini-cli/pull/22437) -- Changelog for v0.34.0-preview.2 by @gemini-cli-robot in - [#22220](https://github.com/google-gemini/gemini-cli/pull/22220) -- fix(core): fix three JIT context bugs in read_file, read_many_files, and - memoryDiscovery by @SandyTao520 in - [#22679](https://github.com/google-gemini/gemini-cli/pull/22679) -- refactor(core): introduce InjectionService with source-aware injection and - backend-native background completions by @adamfweidman in - [#22544](https://github.com/google-gemini/gemini-cli/pull/22544) -- Linux sandbox bubblewrap by @DavidAPierce in - [#22680](https://github.com/google-gemini/gemini-cli/pull/22680) -- feat(core): increase thought signature retry resilience by @bdmorgan in - [#22202](https://github.com/google-gemini/gemini-cli/pull/22202) -- feat(core): implement Stage 2 security and consistency improvements for - web_fetch by @aishaneeshah in - [#22217](https://github.com/google-gemini/gemini-cli/pull/22217) -- refactor(core): replace positional execute params with ExecuteOptions bag by + [#23298](https://github.com/google-gemini/gemini-cli/pull/23298) +- test(cli): force generic terminal in tests to fix snapshot failures by + @abhipatel12 in + [#23499](https://github.com/google-gemini/gemini-cli/pull/23499) +- Evals: PR Guidance adding workflow by @alisa-alisa in + [#23164](https://github.com/google-gemini/gemini-cli/pull/23164) +- feat(core): refactor SandboxManager to a stateless architecture and introduce + explicit Deny interface by @ehedlund in + [#23141](https://github.com/google-gemini/gemini-cli/pull/23141) +- feat(core): add event-translator and update agent types by @adamfweidman in + [#22985](https://github.com/google-gemini/gemini-cli/pull/22985) +- perf(cli): parallelize and background startup cleanup tasks by @sehoon38 in + [#23545](https://github.com/google-gemini/gemini-cli/pull/23545) +- fix: "allow always" for commands with paths by @scidomino in + [#23558](https://github.com/google-gemini/gemini-cli/pull/23558) +- fix(cli): prevent terminal escape sequences from leaking on exit by + @mattKorwel in + [#22682](https://github.com/google-gemini/gemini-cli/pull/22682) +- feat(cli): implement full "GEMINI CLI" logo for logged-out state by + @keithguerin in + [#22412](https://github.com/google-gemini/gemini-cli/pull/22412) +- fix(plan): reserve minimum height for selection list in AskUserDialog by + @ruomengz in [#23280](https://github.com/google-gemini/gemini-cli/pull/23280) +- fix(core): harden AgentSession replay semantics by @adamfweidman in + [#23548](https://github.com/google-gemini/gemini-cli/pull/23548) +- test(core): migrate hook tests to scheduler by @abhipatel12 in + [#23496](https://github.com/google-gemini/gemini-cli/pull/23496) +- chore(config): disable agents by default by @abhipatel12 in + [#23546](https://github.com/google-gemini/gemini-cli/pull/23546) +- fix(ui): make tool confirmations take up entire terminal height by @devr0306 + in [#22366](https://github.com/google-gemini/gemini-cli/pull/22366) +- fix(core): prevent redundant remote agent loading on model switch by @adamfweidman in - [#22674](https://github.com/google-gemini/gemini-cli/pull/22674) -- feat(config): enable JIT context loading by default by @SandyTao520 in - [#22736](https://github.com/google-gemini/gemini-cli/pull/22736) -- fix(config): ensure discoveryMaxDirs is passed to global config during - initialization by @kevin-ramdass in - [#22744](https://github.com/google-gemini/gemini-cli/pull/22744) -- fix(plan): allowlist get_internal_docs in Plan Mode by @Adib234 in - [#22668](https://github.com/google-gemini/gemini-cli/pull/22668) -- Changelog for v0.34.0-preview.3 by @gemini-cli-robot in - [#22393](https://github.com/google-gemini/gemini-cli/pull/22393) -- feat(core): add foundation for subagent tool isolation by @akh64bit in - [#22708](https://github.com/google-gemini/gemini-cli/pull/22708) -- fix(core): handle surrogate pairs in truncateString by @sehoon38 in - [#22754](https://github.com/google-gemini/gemini-cli/pull/22754) -- fix(cli): override j/k navigation in settings dialog to fix search input - conflict by @sehoon38 in - [#22800](https://github.com/google-gemini/gemini-cli/pull/22800) -- feat(plan): add 'All the above' option to multi-select AskUser questions by - @Adib234 in [#22365](https://github.com/google-gemini/gemini-cli/pull/22365) -- docs: distribute package-specific GEMINI.md context to each package by + [#23576](https://github.com/google-gemini/gemini-cli/pull/23576) +- refactor(core): update production type imports from coreToolScheduler by + @abhipatel12 in + [#23498](https://github.com/google-gemini/gemini-cli/pull/23498) +- feat(cli): always prefix extension skills with colon separator by + @NTaylorMullen in + [#23566](https://github.com/google-gemini/gemini-cli/pull/23566) +- fix(core): properly support allowRedirect in policy engine by @scidomino in + [#23579](https://github.com/google-gemini/gemini-cli/pull/23579) +- fix(cli): prevent subcommand shadowing and skip auth for commands by + @mattKorwel in + [#23177](https://github.com/google-gemini/gemini-cli/pull/23177) +- fix(test): move flaky tests to non-blocking suite by @mattKorwel in + [#23259](https://github.com/google-gemini/gemini-cli/pull/23259) +- Changelog for v0.35.0-preview.3 by @gemini-cli-robot in + [#23574](https://github.com/google-gemini/gemini-cli/pull/23574) +- feat(skills): add behavioral-evals skill with fixing and promoting guides by + @abhipatel12 in + [#23349](https://github.com/google-gemini/gemini-cli/pull/23349) +- refactor(core): delete obsolete coreToolScheduler by @abhipatel12 in + [#23502](https://github.com/google-gemini/gemini-cli/pull/23502) +- Changelog for v0.35.0-preview.4 by @gemini-cli-robot in + [#23581](https://github.com/google-gemini/gemini-cli/pull/23581) +- feat(core): add LegacyAgentSession by @adamfweidman in + [#22986](https://github.com/google-gemini/gemini-cli/pull/22986) +- feat(test-utils): add TestMcpServerBuilder and support in TestRig by + @abhipatel12 in + [#23491](https://github.com/google-gemini/gemini-cli/pull/23491) +- fix(core)!: Force policy config to specify toolName by @kschaab in + [#23330](https://github.com/google-gemini/gemini-cli/pull/23330) +- eval(save_memory): add multi-turn interactive evals for memoryManager by @SandyTao520 in - [#22734](https://github.com/google-gemini/gemini-cli/pull/22734) -- fix(cli): clean up stale pasted placeholder metadata after word/line deletions - by @Jomak-x in - [#20375](https://github.com/google-gemini/gemini-cli/pull/20375) -- refactor(core): align JIT memory placement with tiered context model by - @SandyTao520 in - [#22766](https://github.com/google-gemini/gemini-cli/pull/22766) -- Linux sandbox seccomp by @DavidAPierce in - [#22815](https://github.com/google-gemini/gemini-cli/pull/22815) -- fix(patch): cherry-pick 4e5dfd0 to release/v0.35.0-preview.1-pr-23074 to patch - version v0.35.0-preview.1 and create version 0.35.0-preview.2 by + [#23572](https://github.com/google-gemini/gemini-cli/pull/23572) +- fix(telemetry): patch memory leak and enforce logPrompts privacy by + @spencer426 in + [#23281](https://github.com/google-gemini/gemini-cli/pull/23281) +- perf(cli): background IDE client to speed up initialization by @sehoon38 in + [#23603](https://github.com/google-gemini/gemini-cli/pull/23603) +- fix(cli): prevent Ctrl+D exit when input buffer is not empty by @wtanaka in + [#23306](https://github.com/google-gemini/gemini-cli/pull/23306) +- fix: ACP: separate conversational text from execute tool command title by + @sripasg in [#23179](https://github.com/google-gemini/gemini-cli/pull/23179) +- feat(evals): add behavioral evaluations for subagent routing by @Samee24 in + [#23272](https://github.com/google-gemini/gemini-cli/pull/23272) +- refactor(cli,core): foundational layout, identity management, and type safety + by @jwhelangoog in + [#23286](https://github.com/google-gemini/gemini-cli/pull/23286) +- fix(core): accurately reflect subagent tool failure in UI by @abhipatel12 in + [#23187](https://github.com/google-gemini/gemini-cli/pull/23187) +- Changelog for v0.35.0-preview.5 by @gemini-cli-robot in + [#23606](https://github.com/google-gemini/gemini-cli/pull/23606) +- feat(ui): implement refreshed UX for Composer layout by @jwhelangoog in + [#21212](https://github.com/google-gemini/gemini-cli/pull/21212) +- fix: API key input dialog user interaction when selected Gemini API Key by + @kartikangiras in + [#21057](https://github.com/google-gemini/gemini-cli/pull/21057) +- docs: update `/mcp refresh` to `/mcp reload` by @adamfweidman in + [#23631](https://github.com/google-gemini/gemini-cli/pull/23631) +- Implementation of sandbox "Write-Protected" Governance Files by @DavidAPierce + in [#23139](https://github.com/google-gemini/gemini-cli/pull/23139) +- feat(sandbox): dynamic macOS sandbox expansion and worktree support by @galz10 + in [#23301](https://github.com/google-gemini/gemini-cli/pull/23301) +- fix(acp): Pass the cwd to `AcpFileSystemService` to avoid looping failures in + asking for perms to write plan md file by @sripasg in + [#23612](https://github.com/google-gemini/gemini-cli/pull/23612) +- fix(plan): sandbox path resolution in Plan Mode to prevent hallucinations by + @Adib234 in [#22737](https://github.com/google-gemini/gemini-cli/pull/22737) +- feat(ui): allow immediate user input during startup by @sehoon38 in + [#23661](https://github.com/google-gemini/gemini-cli/pull/23661) +- refactor(sandbox): reorganize Windows sandbox files by @galz10 in + [#23645](https://github.com/google-gemini/gemini-cli/pull/23645) +- fix(core): improve remote agent streaming UI and UX by @adamfweidman in + [#23633](https://github.com/google-gemini/gemini-cli/pull/23633) +- perf(cli): optimize --version startup time by @sehoon38 in + [#23671](https://github.com/google-gemini/gemini-cli/pull/23671) +- refactor(core): stop gemini CLI from producing unsafe casts by @gundermanc in + [#23611](https://github.com/google-gemini/gemini-cli/pull/23611) +- use enableAutoUpdate in test rig by @scidomino in + [#23681](https://github.com/google-gemini/gemini-cli/pull/23681) +- feat(core): change user-facing auth type from oauth2 to oauth by @adamfweidman + in [#23639](https://github.com/google-gemini/gemini-cli/pull/23639) +- chore(deps): fix npm audit vulnerabilities by @scidomino in + [#23679](https://github.com/google-gemini/gemini-cli/pull/23679) +- test(evals): fix overlapping act() deadlock in app-test-helper by @Adib234 in + [#23666](https://github.com/google-gemini/gemini-cli/pull/23666) +- fix(patch): cherry-pick 055ff92 to release/v0.36.0-preview.0-pr-23672 to patch + version v0.36.0-preview.0 and create version 0.36.0-preview.1 by @gemini-cli-robot in - [#23134](https://github.com/google-gemini/gemini-cli/pull/23134) -- fix(patch): cherry-pick daf3691 to release/v0.35.0-preview.2-pr-23558 to patch - version v0.35.0-preview.2 and create version 0.35.0-preview.3 by + [#23723](https://github.com/google-gemini/gemini-cli/pull/23723) +- fix(patch): cherry-pick 765fb67 to release/v0.36.0-preview.5-pr-24055 to patch + version v0.36.0-preview.5 and create version 0.36.0-preview.6 by @gemini-cli-robot in - [#23565](https://github.com/google-gemini/gemini-cli/pull/23565) -- fix(patch): cherry-pick b2d6dc4 to release/v0.35.0-preview.4-pr-23546 - [CONFLICTS] by @gemini-cli-robot in - [#23585](https://github.com/google-gemini/gemini-cli/pull/23585) + [#24061](https://github.com/google-gemini/gemini-cli/pull/24061) **Full Changelog**: -https://github.com/google-gemini/gemini-cli/compare/v0.34.0...v0.35.3 +https://github.com/google-gemini/gemini-cli/compare/v0.35.3...v0.36.0 diff --git a/docs/changelogs/preview.md b/docs/changelogs/preview.md index 63246807d6..5bb8d5b575 100644 --- a/docs/changelogs/preview.md +++ b/docs/changelogs/preview.md @@ -1,6 +1,6 @@ -# Preview release: v0.37.0-preview.0 +# Preview release: v0.37.0-preview.1 -Released: April 01, 2026 +Released: April 02, 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). @@ -33,6 +33,10 @@ npm install -g @google/gemini-cli@preview ## What's Changed +- fix(patch): cherry-pick 64c928f to release/v0.37.0-preview.0-pr-23257 to patch + version v0.37.0-preview.0 and create version 0.37.0-preview.1 by + @gemini-cli-robot in + [#24561](https://github.com/google-gemini/gemini-cli/pull/24561) - feat(evals): centralize test agents into test-utils for reuse by @Samee24 in [#23616](https://github.com/google-gemini/gemini-cli/pull/23616) - revert: chore(config): disable agents by default by @abhipatel12 in @@ -415,4 +419,4 @@ npm install -g @google/gemini-cli@preview [#23275](https://github.com/google-gemini/gemini-cli/pull/23275) **Full Changelog**: -https://github.com/google-gemini/gemini-cli/compare/v0.36.0-preview.8...v0.37.0-preview.0 +https://github.com/google-gemini/gemini-cli/compare/v0.36.0-preview.8...v0.37.0-preview.1 diff --git a/docs/cli/sandbox.md b/docs/cli/sandbox.md index e27587abf0..f81b561e0a 100644 --- a/docs/cli/sandbox.md +++ b/docs/cli/sandbox.md @@ -136,6 +136,58 @@ gemini -p "build the snap" absolute path — the path must be writable inside the container. - Used with tools like Snapcraft or Rockcraft that require a full system. +## Tool sandboxing + +Tool-level sandboxing provides granular isolation for individual tool executions +(like `shell_exec` and `write_file`) instead of sandboxing the entire Gemini CLI +process. + +This approach offers better integration with your local environment for non-tool +tasks (like UI rendering and configuration loading) while still providing +security for tool-driven operations. + +### How to turn off tool sandboxing + +If you experience issues with tool sandboxing or prefer full-process isolation, +you can disable it by setting `security.toolSandboxing` to `false` in your +`settings.json` file. + +```json +{ + "security": { + "toolSandboxing": false + } +} +``` + + +> [!NOTE] +> Changing the `security.toolSandboxing` setting requires a restart of Gemini +> CLI to take effect. + +## Sandbox expansion + +Sandbox expansion is a dynamic permission system that lets Gemini CLI request +additional permissions for a command when needed. + +When a sandboxed command fails due to permission restrictions (like restricted +file paths or network access), or when a command is proactively identified as +requiring extra permissions (like `npm install`), Gemini CLI will present you +with a "Sandbox Expansion Request." + +### How sandbox expansion works + +1. **Detection**: Gemini CLI detects a sandbox denial or proactively identifies + a command that requires extra permissions. +2. **Request**: A modal dialog is shown, explaining which additional + permissions (e.g., specific directories or network access) are required. +3. **Approval**: If you approve the expansion, the command is executed with the + extended permissions for that specific run. + +This mechanism ensures you don't have to manually re-run commands with more +permissive sandbox settings, while still maintaining control over what the AI +can access. + ## Quickstart ```bash diff --git a/docs/cli/settings.md b/docs/cli/settings.md index b75f53141c..4a6b9a77b7 100644 --- a/docs/cli/settings.md +++ b/docs/cli/settings.md @@ -60,7 +60,7 @@ they appear in the UI. | 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` | -| Compact Tool Output | `ui.compactToolOutput` | Display tool outputs (like directory listings and file reads) in a compact, structured format. | `false` | +| Compact Tool Output | `ui.compactToolOutput` | Display tool outputs (like directory listings and file reads) in a compact, structured format. | `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` | | Hide CWD | `ui.footer.hideCWD` | Hide the current working directory in the footer. | `false` | @@ -74,6 +74,8 @@ they appear in the UI. | Show Model Info In Chat | `ui.showModelInfoInChat` | Show the model name in the chat for each model turn. | `false` | | Show User Identity | `ui.showUserIdentity` | Show the signed-in user's identity (e.g. email) in the UI. | `true` | | Use Alternate Screen Buffer | `ui.useAlternateBuffer` | Use an alternate screen buffer for the UI, preserving shell history. | `false` | +| Render Process | `ui.renderProcess` | Enable Ink render process for the UI. | `true` | +| Terminal Buffer | `ui.terminalBuffer` | Use the new terminal buffer architecture for rendering. | `true` | | Use Background Color | `ui.useBackgroundColor` | Whether to use background colors in the UI. | `true` | | Incremental Rendering | `ui.incrementalRendering` | Enable incremental rendering for the UI. This option will reduce flickering but may cause rendering artifacts. Only supported when useAlternateBuffer is enabled. | `true` | | Show Spinner | `ui.showSpinner` | Show the spinner during operations. | `true` | @@ -129,7 +131,7 @@ they appear in the UI. | Sandbox Allowed Paths | `tools.sandboxAllowedPaths` | List of additional paths that the sandbox is allowed to access. | `[]` | | Sandbox Network Access | `tools.sandboxNetworkAccess` | Whether the sandbox is allowed to access the network. | `false` | | Enable Interactive Shell | `tools.shell.enableInteractiveShell` | Use node-pty for an interactive shell experience. Fallback to child_process still applies. | `true` | -| Show Color | `tools.shell.showColor` | Show color in shell output. | `false` | +| Show Color | `tools.shell.showColor` | Show color in shell output. | `true` | | Use Ripgrep | `tools.useRipgrep` | Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance. | `true` | | Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Maximum characters to show when truncating large tool outputs. Set to 0 or negative to disable truncation. | `40000` | | Disable LLM Correction | `tools.disableLLMCorrection` | Disable LLM-based error correction for edit tools. When enabled, tools will fail immediately if exact string matches are not found, instead of attempting to self-correct. | `true` | @@ -138,7 +140,7 @@ they appear in the UI. | UI Label | Setting | Description | Default | | ------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -| Tool Sandboxing | `security.toolSandboxing` | Experimental tool-level sandboxing (implementation in progress). | `false` | +| Tool Sandboxing | `security.toolSandboxing` | Tool-level sandboxing. Isolates individual tools instead of the entire CLI process. | `false` | | Disable YOLO Mode | `security.disableYoloMode` | Disable YOLO mode, even if enabled by a flag. | `false` | | Disable Always Allow | `security.disableAlwaysAllow` | Disable "Always allow" options in tool confirmation dialogs. | `false` | | Allow Permanent Tool Approval | `security.enablePermanentToolApproval` | Enable the "Allow for all future sessions" option in tool confirmation dialogs. | `false` | diff --git a/docs/get-started/authentication.md b/docs/get-started/authentication.md index 6d8758b958..31f2fff540 100644 --- a/docs/get-started/authentication.md +++ b/docs/get-started/authentication.md @@ -398,8 +398,8 @@ on this page. ## Running in headless mode -[Headless mode](../cli/headless) will use your existing authentication method, -if an existing authentication credential is cached. +[Headless mode](../cli/headless.md) will use your existing authentication +method, if an existing authentication credential is cached. If you have not already signed in with an authentication credential, you must configure authentication using environment variables: diff --git a/docs/get-started/installation.md b/docs/get-started/installation.md index e56d98d889..15922a6b8e 100644 --- a/docs/get-started/installation.md +++ b/docs/get-started/installation.md @@ -122,6 +122,13 @@ code. # From the root of the repository npm run start ``` +- **Production mode (React optimizations):** This method runs the CLI with React + production mode enabled, which is useful for testing performance without + development overhead. + ```bash + # From the root of the repository + npm run start:prod + ``` - **Production-like mode (linked package):** This method simulates a global installation by linking your local package. It's useful for testing a local build in a production workflow. diff --git a/docs/hooks/index.md b/docs/hooks/index.md index 71fdec268f..f2c786361c 100644 --- a/docs/hooks/index.md +++ b/docs/hooks/index.md @@ -22,11 +22,11 @@ With hooks, you can: ### Getting started -- **[Writing hooks guide](../hooks/writing-hooks)**: A tutorial on creating your - first hook with comprehensive examples. -- **[Best practices](../hooks/best-practices)**: Guidelines on security, +- **[Writing hooks guide](../hooks/writing-hooks.md)**: A tutorial on creating + your first hook with comprehensive examples. +- **[Best practices](../hooks/best-practices.md)**: Guidelines on security, performance, and debugging. -- **[Hooks reference](../hooks/reference)**: The definitive technical +- **[Hooks reference](../hooks/reference.md)**: The definitive technical specification of I/O schemas and exit codes. ## Core concepts @@ -154,8 +154,8 @@ Gemini CLI **fingerprints** project hooks. If a hook's name or command changes (e.g., via `git pull`), it is treated as a **new, untrusted hook** and you will be warned before it executes. -See [Security Considerations](../hooks/best-practices#using-hooks-securely) for -a detailed threat model. +See [Security Considerations](../hooks/best-practices.md#using-hooks-securely) +for a detailed threat model. ## Managing hooks diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index a972883ce0..5c9a3e7044 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -267,7 +267,7 @@ their corresponding top-level category object in your `settings.json` file. - **`ui.compactToolOutput`** (boolean): - **Description:** Display tool outputs (like directory listings and file reads) in a compact, structured format. - - **Default:** `false` + - **Default:** `true` - **`ui.hideBanner`** (boolean): - **Description:** Hide the application banner @@ -339,6 +339,16 @@ their corresponding top-level category object in your `settings.json` file. - **Default:** `false` - **Requires restart:** Yes +- **`ui.renderProcess`** (boolean): + - **Description:** Enable Ink render process for the UI. + - **Default:** `true` + - **Requires restart:** Yes + +- **`ui.terminalBuffer`** (boolean): + - **Description:** Use the new terminal buffer architecture for rendering. + - **Default:** `true` + - **Requires restart:** Yes + - **`ui.useBackgroundColor`** (boolean): - **Description:** Whether to use background colors in the UI. - **Default:** `true` @@ -1394,7 +1404,7 @@ their corresponding top-level category object in your `settings.json` file. - **`tools.shell.showColor`** (boolean): - **Description:** Show color in shell output. - - **Default:** `false` + - **Default:** `true` - **`tools.shell.inactivityTimeout`** (number): - **Description:** The maximum time in seconds allowed without output from the @@ -1482,9 +1492,10 @@ their corresponding top-level category object in your `settings.json` file. #### `security` - **`security.toolSandboxing`** (boolean): - - **Description:** Experimental tool-level sandboxing (implementation in - progress). + - **Description:** Tool-level sandboxing. Isolates individual tools instead of + the entire CLI process. - **Default:** `false` + - **Requires restart:** Yes - **`security.disableYoloMode`** (boolean): - **Description:** Disable YOLO mode, even if enabled by a flag. diff --git a/docs/reference/keyboard-shortcuts.md b/docs/reference/keyboard-shortcuts.md index e87c8682df..68b3d884fe 100644 --- a/docs/reference/keyboard-shortcuts.md +++ b/docs/reference/keyboard-shortcuts.md @@ -102,7 +102,8 @@ available combinations. | `app.showFullTodos` | Toggle the full TODO list. | `Ctrl+T` | | `app.showIdeContextDetail` | Show IDE context details. | `Ctrl+G` | | `app.toggleMarkdown` | Toggle Markdown rendering. | `Alt+M` | -| `app.toggleCopyMode` | Toggle copy mode when in alternate buffer mode. | `Ctrl+S` | +| `app.toggleCopyMode` | Toggle copy mode when in alternate buffer mode. | `F9` | +| `app.toggleMouseMode` | Toggle mouse mode (scrolling and clicking). | `Ctrl+S` | | `app.toggleYolo` | Toggle YOLO (auto-approval) mode for tool calls. | `Ctrl+Y` | | `app.cycleApprovalMode` | Cycle through approval modes: default (prompt), auto_edit (auto-approve edits), and plan (read-only). Plan mode is skipped when the agent is busy. | `Shift+Tab` | | `app.showMoreLines` | Expand and collapse blocks of content when not in alternate buffer mode. | `Ctrl+O` | @@ -126,6 +127,9 @@ available combinations. | `background.unfocus` | Move focus from background shell to Gemini. | `Shift+Tab` | | `background.unfocusList` | Move focus from background shell list to Gemini. | `Tab` | | `background.unfocusWarning` | Show warning when trying to move focus away from background shell. | `Tab` | +| `app.dumpFrame` | Dump the current frame as a snapshot. | `F8` | +| `app.startRecording` | Start recording the session. | `F6` | +| `app.stopRecording` | Stop recording the session. | `F7` | #### Extension Controls diff --git a/docs/release-confidence.md b/docs/release-confidence.md index c46a702820..44dca1b2f3 100644 --- a/docs/release-confidence.md +++ b/docs/release-confidence.md @@ -22,12 +22,6 @@ nightly) or the release branch (for preview/stable). - **Platforms:** Tests must pass on **Linux and macOS**. - -> [!NOTE] -> Windows tests currently run with `continue-on-error: true`. While a -> failure here doesn't block the release technically, it should be -> investigated. - - **Checks:** - **Linting:** No linting errors (ESLint, Prettier, etc.). - **Typechecking:** No TypeScript errors. diff --git a/docs/releases.md b/docs/releases.md index 23fb9fcf90..c6ff1a523a 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -1,5 +1,9 @@ # Gemini CLI releases + +> [!IMPORTANT] +> **Coordinate with the Release Manager:** The release manager is responsible for coordinating patches and releases. Please update them before performing any of the release actions described in this document. + ## `dev` vs `prod` environment Our release flows support both `dev` and `prod` environments. diff --git a/esbuild.config.js b/esbuild.config.js index 63d5d9f00a..ee1f722f4b 100644 --- a/esbuild.config.js +++ b/esbuild.config.js @@ -94,6 +94,10 @@ const cliConfig = { 'process.env.GEMINI_SANDBOX_IMAGE_DEFAULT': JSON.stringify( pkg.config?.sandboxImageUri, ), + 'process.env.NODE_ENV': JSON.stringify( + process.env.NODE_ENV || 'production', + ), + 'process.env.DEV': JSON.stringify(process.env.DEV || 'false'), }, plugins: createWasmPlugins(), alias: { @@ -114,6 +118,10 @@ const a2aServerConfig = { __filename: '__chunk_filename', __dirname: '__chunk_dirname', 'process.env.CLI_VERSION': JSON.stringify(pkg.version), + 'process.env.NODE_ENV': JSON.stringify( + process.env.NODE_ENV || 'production', + ), + 'process.env.DEV': JSON.stringify(process.env.DEV || 'false'), }, plugins: createWasmPlugins(), alias: commonAliases, diff --git a/evals/update_topic.eval.ts b/evals/update_topic.eval.ts index 1836e7f61b..ce895d5ad7 100644 --- a/evals/update_topic.eval.ts +++ b/evals/update_topic.eval.ts @@ -5,6 +5,8 @@ */ import { describe, expect } from 'vitest'; +import fs from 'node:fs'; +import path from 'node:path'; import { evalTest } from './test-helper.js'; describe('update_topic_behavior', () => { @@ -113,4 +115,104 @@ describe('update_topic_behavior', () => { } }, }); + + evalTest('USUALLY_PASSES', { + name: 'update_topic should NOT be used for informational coding tasks (Obvious)', + approvalMode: 'default', + prompt: + 'Explain the difference between Map and Object in JavaScript and provide a performance-focused code snippet for each.', + files: { + '.gemini/settings.json': JSON.stringify({ + experimental: { + topicUpdateNarration: true, + }, + }), + }, + assert: async (rig) => { + const toolLogs = rig.readToolLogs(); + const topicCalls = toolLogs.filter( + (l) => l.toolRequest.name === UPDATE_TOPIC_TOOL_NAME, + ); + + expect( + topicCalls.length, + `Expected 0 update_topic calls for an informational task, but found ${topicCalls.length}`, + ).toBe(0); + }, + }); + + evalTest('USUALLY_PASSES', { + name: 'update_topic should NOT be used for surgical symbol searches (Grey Area)', + approvalMode: 'default', + prompt: + "Find the file where the 'UPDATE_TOPIC_TOOL_NAME' constant is defined.", + files: { + 'packages/core/src/tools/tool-names.ts': + "export const UPDATE_TOPIC_TOOL_NAME = 'update_topic';", + '.gemini/settings.json': JSON.stringify({ + experimental: { + topicUpdateNarration: true, + }, + }), + }, + assert: async (rig) => { + const toolLogs = rig.readToolLogs(); + const topicCalls = toolLogs.filter( + (l) => l.toolRequest.name === UPDATE_TOPIC_TOOL_NAME, + ); + + expect( + topicCalls.length, + `Expected 0 update_topic calls for a surgical symbol search, but found ${topicCalls.length}`, + ).toBe(0); + }, + }); + + evalTest('USUALLY_PASSES', { + name: 'update_topic should be used for medium complexity multi-step tasks', + prompt: + 'Refactor the `users-api` project. Move the routing logic from src/app.ts into a new file src/routes.ts, and update app.ts to use the new routes file.', + files: { + 'package.json': JSON.stringify( + { + name: 'users-api', + version: '1.0.0', + }, + null, + 2, + ), + 'src/app.ts': ` +import express from 'express'; +const app = express(); + +app.get('/users', (req, res) => { + res.json([{id: 1, name: 'Alice'}]); +}); + +app.post('/users', (req, res) => { + res.status(201).send(); +}); + +export default app; + `, + '.gemini/settings.json': JSON.stringify({ + experimental: { + topicUpdateNarration: true, + }, + }), + }, + assert: async (rig) => { + const toolLogs = rig.readToolLogs(); + const topicCalls = toolLogs.filter( + (l) => l.toolRequest.name === UPDATE_TOPIC_TOOL_NAME, + ); + + // This is a multi-step task (read, create new file, edit old file). + // It should clear the bar and use update_topic at least at the start and end. + expect(topicCalls.length).toBeGreaterThanOrEqual(2); + + // Verify it actually did the refactoring to ensure it didn't just fail immediately + expect(fs.existsSync(path.join(rig.testDir, 'src/routes.ts'))).toBe(true); + }, + }); }); diff --git a/package.json b/package.json index 0212208bda..e24f6a20b5 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ }, "scripts": { "start": "cross-env NODE_ENV=development node scripts/start.js", + "start:prod": "cross-env NODE_ENV=production node scripts/start.js", "start:a2a-server": "CODER_AGENT_PORT=41242 npm run start --workspace @google/gemini-cli-a2a-server", "debug": "cross-env DEBUG=1 node --inspect-brk scripts/start.js", "deflake": "node scripts/deflake.js", diff --git a/packages/cli/src/acp/commandHandler.test.ts b/packages/cli/src/acp/commandHandler.test.ts index 8e04f014f3..23bf907ec3 100644 --- a/packages/cli/src/acp/commandHandler.test.ts +++ b/packages/cli/src/acp/commandHandler.test.ts @@ -26,5 +26,8 @@ describe('CommandHandler', () => { const init = parse('/init'); expect(init.commandToExecute?.name).toBe('init'); + + const about = parse('/about'); + expect(about.commandToExecute?.name).toBe('about'); }); }); diff --git a/packages/cli/src/acp/commandHandler.ts b/packages/cli/src/acp/commandHandler.ts index 836cdf7736..4ed846188e 100644 --- a/packages/cli/src/acp/commandHandler.ts +++ b/packages/cli/src/acp/commandHandler.ts @@ -10,6 +10,7 @@ import { MemoryCommand } from './commands/memory.js'; import { ExtensionsCommand } from './commands/extensions.js'; import { InitCommand } from './commands/init.js'; import { RestoreCommand } from './commands/restore.js'; +import { AboutCommand } from './commands/about.js'; export class CommandHandler { private registry: CommandRegistry; @@ -24,6 +25,7 @@ export class CommandHandler { registry.register(new ExtensionsCommand()); registry.register(new InitCommand()); registry.register(new RestoreCommand()); + registry.register(new AboutCommand()); return registry; } diff --git a/packages/cli/src/acp/commands/about.ts b/packages/cli/src/acp/commands/about.ts new file mode 100644 index 0000000000..06349e88d7 --- /dev/null +++ b/packages/cli/src/acp/commands/about.ts @@ -0,0 +1,74 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + IdeClient, + UserAccountManager, + getVersion, +} from '@google/gemini-cli-core'; +import type { + Command, + CommandContext, + CommandExecutionResponse, +} from './types.js'; +import process from 'node:process'; + +export class AboutCommand implements Command { + readonly name = 'about'; + readonly description = 'Show version and environment info'; + + async execute( + context: CommandContext, + _args: string[] = [], + ): Promise { + const osVersion = process.platform; + let sandboxEnv = 'no sandbox'; + if (process.env['SANDBOX'] && process.env['SANDBOX'] !== 'sandbox-exec') { + sandboxEnv = process.env['SANDBOX']; + } else if (process.env['SANDBOX'] === 'sandbox-exec') { + sandboxEnv = `sandbox-exec (${ + process.env['SEATBELT_PROFILE'] || 'unknown' + })`; + } + const modelVersion = context.agentContext.config.getModel() || 'Unknown'; + const cliVersion = await getVersion(); + const selectedAuthType = + context.settings.merged?.security?.auth?.selectedType ?? ''; + const gcpProject = process.env['GOOGLE_CLOUD_PROJECT'] || ''; + const ideClient = await getIdeClientName(context); + + const userAccountManager = new UserAccountManager(); + const cachedAccount = userAccountManager.getCachedGoogleAccount(); + const userEmail = cachedAccount ?? 'Unknown'; + + const tier = context.agentContext.config.getUserTierName() || 'Unknown'; + + const info = [ + `- Version: ${cliVersion}`, + `- OS: ${osVersion}`, + `- Sandbox: ${sandboxEnv}`, + `- Model: ${modelVersion}`, + `- Auth Type: ${selectedAuthType}`, + `- GCP Project: ${gcpProject}`, + `- IDE Client: ${ideClient}`, + `- User Email: ${userEmail}`, + `- Tier: ${tier}`, + ].join('\n'); + + return { + name: this.name, + data: `Gemini CLI Info:\n${info}`, + }; + } +} + +async function getIdeClientName(context: CommandContext) { + if (!context.agentContext.config.getIdeMode()) { + return ''; + } + const ideClient = await IdeClient.getInstance(); + return ideClient?.getDetectedIdeDisplayName() ?? ''; +} diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 37f1291475..c1ac3e57dd 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -1001,6 +1001,8 @@ export async function loadCliConfig( trustedFolder, useBackgroundColor: settings.ui?.useBackgroundColor, useAlternateBuffer: settings.ui?.useAlternateBuffer, + useTerminalBuffer: settings.ui?.terminalBuffer, + useRenderProcess: settings.ui?.renderProcess, useRipgrep: settings.tools?.useRipgrep, enableInteractiveShell: settings.tools?.shell?.enableInteractiveShell, shellBackgroundCompletionBehavior: settings.tools?.shell diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index 9b62c9d93f..9343be6b02 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -576,7 +576,7 @@ const SETTINGS_SCHEMA = { label: 'Compact Tool Output', category: 'UI', requiresRestart: false, - default: false, + default: true, description: 'Display tool outputs (like directory listings and file reads) in a compact, structured format.', showInDialog: true, @@ -743,6 +743,24 @@ const SETTINGS_SCHEMA = { 'Use an alternate screen buffer for the UI, preserving shell history.', showInDialog: true, }, + renderProcess: { + type: 'boolean', + label: 'Render Process', + category: 'UI', + requiresRestart: true, + default: true, + description: 'Enable Ink render process for the UI.', + showInDialog: true, + }, + terminalBuffer: { + type: 'boolean', + label: 'Terminal Buffer', + category: 'UI', + requiresRestart: true, + default: true, + description: 'Use the new terminal buffer architecture for rendering.', + showInDialog: true, + }, useBackgroundColor: { type: 'boolean', label: 'Use Background Color', @@ -1509,7 +1527,7 @@ const SETTINGS_SCHEMA = { label: 'Show Color', category: 'Tools', requiresRestart: false, - default: false, + default: true, description: 'Show color in shell output.', showInDialog: true, }, @@ -1693,10 +1711,10 @@ const SETTINGS_SCHEMA = { type: 'boolean', label: 'Tool Sandboxing', category: 'Security', - requiresRestart: false, + requiresRestart: true, default: false, description: - 'Experimental tool-level sandboxing (implementation in progress).', + 'Tool-level sandboxing. Isolates individual tools instead of the entire CLI process.', showInDialog: true, }, disableYoloMode: { diff --git a/packages/cli/src/gemini_cleanup.test.tsx b/packages/cli/src/gemini_cleanup.test.tsx index b2fa2139fd..4bbc7e7648 100644 --- a/packages/cli/src/gemini_cleanup.test.tsx +++ b/packages/cli/src/gemini_cleanup.test.tsx @@ -327,6 +327,7 @@ describe('gemini.tsx main function cleanup', () => { refreshAuth: vi.fn(), getRemoteAdminSettings: vi.fn(() => undefined), getUseAlternateBuffer: vi.fn(() => false), + getUseTerminalBuffer: vi.fn(() => false), ...overrides, } as unknown as Config; } diff --git a/packages/cli/src/integration-tests/modelSteering.test.tsx b/packages/cli/src/integration-tests/modelSteering.test.tsx index bada268329..80640045a0 100644 --- a/packages/cli/src/integration-tests/modelSteering.test.tsx +++ b/packages/cli/src/integration-tests/modelSteering.test.tsx @@ -67,7 +67,7 @@ describe('Model Steering Integration', () => { // Then it should proceed with the next action await rig.waitForOutput( - /Since you want me to focus on .txt files,[\s\S]*I will read file1.txt/, + /Since you want me to focus on \.txt[\s\S]*files,[\s\S]*I will read file1\.txt/, ); await rig.waitForOutput('ReadFile'); diff --git a/packages/cli/src/interactiveCli.tsx b/packages/cli/src/interactiveCli.tsx index 2e0cd25619..418f58b193 100644 --- a/packages/cli/src/interactiveCli.tsx +++ b/packages/cli/src/interactiveCli.tsx @@ -43,7 +43,6 @@ 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'; import { initializeConsoleStore } from './ui/hooks/useConsoleMessages.js'; @@ -64,7 +63,7 @@ export async function startInteractiveUI( // and the Ink alternate buffer mode requires line wrapping harmful to // screen readers. const useAlternateBuffer = shouldEnterAlternateScreen( - isAlternateBufferEnabled(config), + config.getUseAlternateBuffer(), config.getScreenReader(), ); const mouseEventsEnabled = useAlternateBuffer; @@ -133,7 +132,6 @@ export async function startInteractiveUI( // Wait a moment for shpool to stabilize terminal size and state. await new Promise((resolve) => setTimeout(resolve, 100)); } - const instance = render( process.env['DEBUG'] ? ( @@ -154,8 +152,12 @@ export async function startInteractiveUI( } profiler.reportFrameRendered(); }, + standardReactLayoutTiming: + useAlternateBuffer || config.getUseTerminalBuffer(), patchConsole: false, alternateBuffer: useAlternateBuffer, + renderProcess: config.getUseRenderProcess(), + terminalBuffer: config.getUseTerminalBuffer(), incrementalRendering: settings.merged.ui.incrementalRendering !== false && useAlternateBuffer && diff --git a/packages/cli/src/test-utils/mockConfig.ts b/packages/cli/src/test-utils/mockConfig.ts index 57ddd83141..7be8463382 100644 --- a/packages/cli/src/test-utils/mockConfig.ts +++ b/packages/cli/src/test-utils/mockConfig.ts @@ -176,6 +176,8 @@ export const createMockConfig = (overrides: Partial = {}): Config => getHasAccessToPreviewModel: vi.fn().mockReturnValue(false), validatePathAccess: vi.fn().mockReturnValue(null), getUseAlternateBuffer: vi.fn().mockReturnValue(false), + getUseTerminalBuffer: vi.fn().mockReturnValue(false), + getUseRenderProcess: vi.fn().mockReturnValue(false), ...overrides, }) as unknown as Config; diff --git a/packages/cli/src/test-utils/render.tsx b/packages/cli/src/test-utils/render.tsx index 817921e83a..c9982103d3 100644 --- a/packages/cli/src/test-utils/render.tsx +++ b/packages/cli/src/test-utils/render.tsx @@ -223,7 +223,7 @@ class XtermStdout extends EventEmitter { this.once('render', resolve), ); const timeoutPromise = new Promise((resolve) => - setTimeout(resolve, 50), + setTimeout(resolve, 1000), ); await Promise.race([renderPromise, timeoutPromise]); } @@ -254,7 +254,12 @@ class XtermStdout extends EventEmitter { const isMatch = () => { if (expectedFrame === '...') { - return currentFrame !== ''; + // '...' is our fallback when output isn't in metrics, meaning Ink rendered *something* + // but we don't know what it is. If terminal has content, we consider it a match. + // However, if the component rendered null, both would be empty, but our fallback + // made expectedFrame '...'. In that case, we can't easily know if it's ready, + // but we can assume if there are no pending writes, it's ready. + return currentFrame !== '' || this.pendingWrites === 0; } // If Ink expects nothing (no new static content and no dynamic output), diff --git a/packages/cli/src/ui/AppContainer.test.tsx b/packages/cli/src/ui/AppContainer.test.tsx index 0e436cc645..21bd931d8f 100644 --- a/packages/cli/src/ui/AppContainer.test.tsx +++ b/packages/cli/src/ui/AppContainer.test.tsx @@ -346,6 +346,7 @@ describe('AppContainer State Management', () => { // Initialize mock stdout for terminal title tests mocks.mockStdout.write.mockClear(); + (disableMouseEvents as import('vitest').Mock).mockClear(); capturedUIState = null!; @@ -470,6 +471,7 @@ describe('AppContainer State Management', () => { // Mock Config mockConfig = makeFakeConfig(); + vi.spyOn(mockConfig, 'getUseRenderProcess').mockReturnValue(false); // Mock config's getTargetDir to return consistent workspace directory vi.spyOn(mockConfig, 'getTargetDir').mockReturnValue('/test/workspace'); @@ -1356,6 +1358,7 @@ describe('AppContainer State Management', () => { beforeEach(() => { // Reset mock stdout for each test mocks.mockStdout.write.mockClear(); + (disableMouseEvents as import('vitest').Mock).mockClear(); }); it('verifies useStdout is mocked', async () => { @@ -2459,7 +2462,7 @@ describe('AppContainer State Management', () => { }); }); - describe('Copy Mode (CTRL+S)', () => { + describe('Copy Mode (F9)', () => { let rerender: () => void; let unmount: () => void; let stdin: Awaited>['stdin']; @@ -2468,6 +2471,8 @@ describe('AppContainer State Management', () => { isAlternateMode = false, childHandler?: Mock, ) => { + vi.spyOn(mockConfig, 'getUseTerminalBuffer').mockReturnValue(false); + vi.spyOn(mockConfig, 'getUseAlternateBuffer').mockReturnValue( isAlternateMode, ); @@ -2512,6 +2517,8 @@ describe('AppContainer State Management', () => { beforeEach(() => { mocks.mockStdout.write.mockClear(); + (disableMouseEvents as import('vitest').Mock).mockClear(); + vi.useFakeTimers(); }); @@ -2532,12 +2539,13 @@ describe('AppContainer State Management', () => { modeName: 'Alternate Buffer Mode', }, ])('$modeName', ({ isAlternateMode, shouldEnable }) => { - it(`should ${shouldEnable ? 'toggle' : 'NOT toggle'} mouse off when Ctrl+S is pressed`, async () => { + it(`should ${shouldEnable ? 'toggle' : 'NOT toggle'} mouse off when F9 is pressed`, async () => { await setupCopyModeTest(isAlternateMode); mocks.mockStdout.write.mockClear(); // Clear initial enable call + (disableMouseEvents as import('vitest').Mock).mockClear(); act(() => { - stdin.write('\x13'); // Ctrl+S + stdin.write('\x1b[20~'); // F9 }); rerender(); @@ -2550,13 +2558,13 @@ describe('AppContainer State Management', () => { }); if (shouldEnable) { - it('should toggle mouse back on when Ctrl+S is pressed again', async () => { + it('should toggle mouse back on when F9 is pressed again', async () => { await setupCopyModeTest(isAlternateMode); (writeToStdout as Mock).mockClear(); // Turn it on (disable mouse) act(() => { - stdin.write('\x13'); // Ctrl+S + stdin.write('\x1b[20~'); // F9 }); rerender(); expect(disableMouseEvents).toHaveBeenCalled(); @@ -2576,7 +2584,7 @@ describe('AppContainer State Management', () => { // Enter copy mode act(() => { - stdin.write('\x13'); // Ctrl+S + stdin.write('\x1b[20~'); // F9 }); rerender(); @@ -2656,7 +2664,7 @@ describe('AppContainer State Management', () => { // 2. Enter copy mode act(() => { - stdin.write('\x13'); // Ctrl+S + stdin.write('\x1b[20~'); // F9 }); rerender(); @@ -3093,6 +3101,7 @@ describe('AppContainer State Management', () => { // Clear previous calls mocks.mockStdout.write.mockClear(); + (disableMouseEvents as import('vitest').Mock).mockClear(); const { unmount } = await act(async () => renderAppContainer()); @@ -3135,16 +3144,13 @@ describe('AppContainer State Management', () => { // Reset mock stdout to clear any initial writes mocks.mockStdout.write.mockClear(); + (disableMouseEvents as import('vitest').Mock).mockClear(); // Submit await act(async () => capturedUIActions.handleFinalSubmit('test prompt')); // Should be reset expect(capturedUIState.constrainHeight).toBe(true); - // Should refresh static (which clears terminal in non-alternate buffer) - expect(mocks.mockStdout.write).toHaveBeenCalledWith( - ansiEscapes.clearTerminal, - ); unmount(); }); @@ -3154,6 +3160,8 @@ describe('AppContainer State Management', () => { ); vi.mocked(checkPermissions).mockResolvedValue([]); + vi.spyOn(mockConfig, 'getUseTerminalBuffer').mockReturnValue(false); + vi.spyOn(mockConfig, 'getUseAlternateBuffer').mockReturnValue(true); const { unmount } = await act(async () => @@ -3170,6 +3178,7 @@ describe('AppContainer State Management', () => { // Reset mock stdout mocks.mockStdout.write.mockClear(); + (disableMouseEvents as import('vitest').Mock).mockClear(); // Submit await act(async () => capturedUIActions.handleFinalSubmit('test prompt')); @@ -3403,6 +3412,8 @@ describe('AppContainer State Management', () => { ui: { useAlternateBuffer: true }, }); + vi.spyOn(mockConfig, 'getUseTerminalBuffer').mockReturnValue(false); + vi.spyOn(mockConfig, 'getUseAlternateBuffer').mockReturnValue(true); const { unmount } = await act(async () => diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index a955dfae6c..f12d39ea9e 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -11,6 +11,7 @@ import { useEffect, useRef, useLayoutEffect, + useContext, } from 'react'; import { type DOMElement, @@ -19,6 +20,7 @@ import { useStdout, useStdin, type AppProps, + AppContext as InkAppContext, } from 'ink'; import { App } from './App.js'; import { AppContext } from './contexts/AppContext.js'; @@ -38,6 +40,8 @@ import { import { checkPermissions } from './hooks/atCommandProcessor.js'; import { MessageType, StreamingState } from './types.js'; import { ToolActionsProvider } from './contexts/ToolActionsContext.js'; +import { MouseProvider } from './contexts/MouseContext.js'; +import { ScrollProvider } from './contexts/ScrollProvider.js'; import { type StartupWarning, type EditorType, @@ -210,12 +214,30 @@ export const AppContainer = (props: AppContainerProps) => { const { reset } = useOverflowActions()!; const notificationsEnabled = isNotificationsEnabled(settings); + const { setOptions, dumpCurrentFrame, startRecording, stopRecording } = + useContext(InkAppContext); + const recordingFilenameRef = useRef(null); const historyManager = useHistory({ chatRecordingService: config.getGeminiClient()?.getChatRecordingService(), }); useMemoryMonitor(historyManager); const isAlternateBuffer = config.getUseAlternateBuffer(); + const [mouseMode, setMouseMode] = useState(() => + config.getUseAlternateBuffer(), + ); + + useEffect(() => { + setOptions({ + stickyHeadersInBackbuffer: mouseMode, + }); + if (mouseMode) { + enableMouseEvents(); + } else { + disableMouseEvents(); + } + }, [mouseMode, setOptions]); + const [corgiMode, setCorgiMode] = useState(false); const [forceRerenderKey, setForceRerenderKey] = useState(0); const [debugMessage, setDebugMessage] = useState(''); @@ -621,11 +643,11 @@ export const AppContainer = (props: AppContainerProps) => { }); const refreshStatic = useCallback(() => { - if (!isAlternateBuffer) { + if (!isAlternateBuffer && !config.getUseTerminalBuffer()) { stdout.write(ansiEscapes.clearTerminal); + setHistoryRemountKey((prev) => prev + 1); } - setHistoryRemountKey((prev) => prev + 1); - }, [setHistoryRemountKey, isAlternateBuffer, stdout]); + }, [setHistoryRemountKey, isAlternateBuffer, stdout, config]); const shouldUseAlternateScreen = shouldEnterAlternateScreen( isAlternateBuffer, @@ -1433,6 +1455,14 @@ Logging in with Google... Restarting Gemini CLI to continue. !proQuotaRequest; const observerRef = useRef(null); + + useEffect( + () => () => { + observerRef.current?.disconnect(); + }, + [], + ); + const [controlsHeight, setControlsHeight] = useState(0); const [lastNonCopyControlsHeight, setLastNonCopyControlsHeight] = useState(0); @@ -1731,6 +1761,14 @@ Logging in with Google... Restarting Gemini CLI to continue. setShortcutsHelpVisible(false); } + if (keyMatchers[Command.TOGGLE_MOUSE_MODE](key)) { + setMouseMode((prev) => !prev); + if (mouseMode && !isAlternateBuffer) { + appEvents.emit(AppEvent.ScrollToBottom); + } + return true; + } + if (isAlternateBuffer && keyMatchers[Command.TOGGLE_COPY_MODE](key)) { setCopyModeEnabled(true); disableMouseEvents(); @@ -1753,6 +1791,32 @@ Logging in with Google... Restarting Gemini CLI to continue. return true; } else if (keyMatchers[Command.SUSPEND_APP](key)) { handleSuspend(); + } else if (keyMatchers[Command.DUMP_FRAME](key)) { + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const filename = `snapshot-${timestamp}.json`; + if (dumpCurrentFrame) { + dumpCurrentFrame(filename); + debugLogger.log(`Dumped frame to: ${filename}`); + } + return true; + } else if (keyMatchers[Command.START_RECORDING](key)) { + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const filename = `recording-${timestamp}.json`; + if (startRecording) { + startRecording(filename); + recordingFilenameRef.current = filename; + debugLogger.log(`Started recording to: ${filename}`); + } + return true; + } else if (keyMatchers[Command.STOP_RECORDING](key)) { + if (stopRecording) { + stopRecording(); + debugLogger.log( + `Stopped recording, saved to: ${recordingFilenameRef.current ?? 'unknown'}`, + ); + recordingFilenameRef.current = null; + } + return true; } else if ( keyMatchers[Command.TOGGLE_COPY_MODE](key) && !isAlternateBuffer @@ -1939,6 +2003,10 @@ Logging in with Google... Restarting Gemini CLI to continue. historyManager.history, pendingHistoryItems, toggleAllExpansion, + dumpCurrentFrame, + startRecording, + stopRecording, + mouseMode, ], ); @@ -1958,7 +2026,9 @@ Logging in with Google... Restarting Gemini CLI to continue. } setCopyModeEnabled(false); - enableMouseEvents(); + if (mouseMode) { + enableMouseEvents(); + } return true; }, { @@ -2275,6 +2345,7 @@ Logging in with Google... Restarting Gemini CLI to continue. editorError, isEditorDialogOpen, showPrivacyNotice, + mouseMode, corgiMode, debugMessage, quittingMessages, @@ -2401,6 +2472,7 @@ Logging in with Google... Restarting Gemini CLI to continue. editorError, isEditorDialogOpen, showPrivacyNotice, + mouseMode, corgiMode, debugMessage, quittingMessages, @@ -2701,7 +2773,11 @@ Logging in with Google... Restarting Gemini CLI to continue. toggleAllExpansion={toggleAllExpansion} > - + + + + + diff --git a/packages/cli/src/ui/__snapshots__/App.test.tsx.snap b/packages/cli/src/ui/__snapshots__/App.test.tsx.snap index f145eadfff..94b1f9b1a4 100644 --- a/packages/cli/src/ui/__snapshots__/App.test.tsx.snap +++ b/packages/cli/src/ui/__snapshots__/App.test.tsx.snap @@ -55,12 +55,6 @@ Footer Gemini CLI v1.2.3 - -Tips for getting started: -1. Create GEMINI.md files to customize your interactions -2. /help for more information -3. Ask coding questions, edit code or run commands -4. Be specific for the best results Composer " `; @@ -130,13 +124,14 @@ HistoryItemDisplay │ │ │ ? ls list directory │ │ │ -│ ls │ -│ Allow execution of: 'ls'? │ +│ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │ +│ │ ls │ │ +│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │ +│ Allow execution of [ls]? │ │ │ │ ● 1. Allow once │ │ 2. Allow for this session │ │ 3. No, suggest changes (esc) │ -│ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ @@ -144,7 +139,6 @@ HistoryItemDisplay - Notifications Composer diff --git a/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame-Full-Terminal-Tool-Confirmation-Snapshot-renders-tool-confirmation-box-in-the-frame-of-the-entire-terminal.snap.svg b/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame-Full-Terminal-Tool-Confirmation-Snapshot-renders-tool-confirmation-box-in-the-frame-of-the-entire-terminal.snap.svg index b83d79928c..7565185d93 100644 --- a/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame-Full-Terminal-Tool-Confirmation-Snapshot-renders-tool-confirmation-box-in-the-frame-of-the-entire-terminal.snap.svg +++ b/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame-Full-Terminal-Tool-Confirmation-Snapshot-renders-tool-confirmation-box-in-the-frame-of-the-entire-terminal.snap.svg @@ -12,253 +12,283 @@ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ - ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ - - Action Required - - - - - ? - Edit - packages/.../InputPrompt.tsx: return kittyProtocolSupporte... => return kittyProto - - - - - - ... first 44 lines hidden (Ctrl+O to show) ... - - - 45 - const - line45 - = - true - ; - - - 46 - const - line46 - = - true - ; - - - 47 - const - line47 - = - true - ; - - - - 48 - const - line48 - = - true - ; - - - - 49 - const - line49 - = - true - ; - - - - 50 - const - line50 - = - true - ; - - - - 51 - const - line51 - = - true - ; - - - - 52 - const - line52 - = - true - ; - - - - 53 - const - line53 - = - true - ; - - - - 54 - const - line54 - = - true - ; - - - - 55 - const - line55 - = - true - ; - - - - 56 - const - line56 - = - true - ; - - - - 57 - const - line57 - = - true - ; - - - - 58 - const - line58 - = - true - ; - - - - 59 - const - line59 - = - true - ; - - - - 60 - const - line60 - = - true - ; - - - - - 61 - - - - + ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ + + ? Edit + + + ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ + + + + ... first 42 lines hidden (Ctrl+O to show) ... + + + + + 43 + const + line43 + = + true + ; + + + + + 44 + const + line44 + = + true + ; + + + + + 45 + const + line45 + = + true + ; + + + + + 46 + const + line46 + = + true + ; + + + + + 47 + const + line47 + = + true + ; + + │▄ + + + 48 + const + line48 + = + true + ; + + │█ + + + 49 + const + line49 + = + true + ; + + │█ + + + 50 + const + line50 + = + true + ; + + │█ + + + 51 + const + line51 + = + true + ; + + │█ + + + 52 + const + line52 + = + true + ; + + │█ + + + 53 + const + line53 + = + true + ; + + │█ + + + 54 + const + line54 + = + true + ; + + │█ + + + 55 + const + line55 + = + true + ; + + │█ + + + 56 + const + line56 + = + true + ; + + │█ + + + 57 + const + line57 + = + true + ; + + │█ + + + 58 + const + line58 + = + true + ; + + │█ + + + 59 + const + line59 + = + true + ; + + │█ + + + 60 + const + line60 + = + true + ; + + │█ + + + + 61 - - return - - kittyProtocolSupporte...; - - - - - 61 - - - + + - + + + + return + + kittyProtocolSupporte...; + + │█ + + + + 61 - - return - - kittyProtocolSupporte...; - - - - 62 - buffer: TextBuffer; - - - - 63 - onSubmit - : ( - value - : - string - ) => - void - ; - - - - Apply this change? - - - - - - - - - - - 1. - - - Allow once - - - - - 2. - Allow for this session - - - - 3. - Allow for this file in all future sessions - - - - 4. - Modify with external editor - - - - 5. - No, suggest changes (esc) - - - - - - ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ - + + + + + + return + + kittyProtocolSupporte...; + + │█ + + + 62 + buffer: TextBuffer; + + │█ + + + 63 + onSubmit + : ( + value + : + string + ) => + void + ; + + │█ + + ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ + │█ + + Apply this change? + │█ + + │█ + + + + + + 1. + + + Allow once + + │█ + + 2. + Allow for this session + │█ + + 3. + Allow for this file in all future sessions + ~/.gemini/policies/auto-saved.toml + │█ + + 4. + Modify with external editor + │█ + + 5. + No, suggest changes (esc) + │█ + ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯█ \ No newline at end of file diff --git a/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame.test.tsx.snap b/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame.test.tsx.snap index 6841182785..d9cc9f7ce3 100644 --- a/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame.test.tsx.snap +++ b/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame.test.tsx.snap @@ -5,39 +5,39 @@ exports[`Full Terminal Tool Confirmation Snapshot > renders tool confirmation bo ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ Action Required │ -│ │ -│ ? Edit packages/.../InputPrompt.tsx: return kittyProtocolSupporte... => return kittyProto… │ -│ │ -│ ... first 44 lines hidden (Ctrl+O to show) ... │ -│ 45 const line45 = true; │ -│ 46 const line46 = true; │ -│ 47 const line47 = true; │▄ -│ 48 const line48 = true; │█ -│ 49 const line49 = true; │█ -│ 50 const line50 = true; │█ -│ 51 const line51 = true; │█ -│ 52 const line52 = true; │█ -│ 53 const line53 = true; │█ -│ 54 const line54 = true; │█ -│ 55 const line55 = true; │█ -│ 56 const line56 = true; │█ -│ 57 const line57 = true; │█ -│ 58 const line58 = true; │█ -│ 59 const line59 = true; │█ -│ 60 const line60 = true; │█ -│ 61 - return kittyProtocolSupporte...; │█ -│ 61 + return kittyProtocolSupporte...; │█ -│ 62 buffer: TextBuffer; │█ -│ 63 onSubmit: (value: string) => void; │█ +│ ? Edit │ +│ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │ +│ │ ... first 42 lines hidden (Ctrl+O to show) ... │ │ +│ │ 43 const line43 = true; │ │ +│ │ 44 const line44 = true; │ │ +│ │ 45 const line45 = true; │ │ +│ │ 46 const line46 = true; │ │ +│ │ 47 const line47 = true; │ │▄ +│ │ 48 const line48 = true; │ │█ +│ │ 49 const line49 = true; │ │█ +│ │ 50 const line50 = true; │ │█ +│ │ 51 const line51 = true; │ │█ +│ │ 52 const line52 = true; │ │█ +│ │ 53 const line53 = true; │ │█ +│ │ 54 const line54 = true; │ │█ +│ │ 55 const line55 = true; │ │█ +│ │ 56 const line56 = true; │ │█ +│ │ 57 const line57 = true; │ │█ +│ │ 58 const line58 = true; │ │█ +│ │ 59 const line59 = true; │ │█ +│ │ 60 const line60 = true; │ │█ +│ │ 61 - return kittyProtocolSupporte...; │ │█ +│ │ 61 + return kittyProtocolSupporte...; │ │█ +│ │ 62 buffer: TextBuffer; │ │█ +│ │ 63 onSubmit: (value: string) => void; │ │█ +│ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ │█ │ Apply this change? │█ │ │█ │ ● 1. Allow once │█ │ 2. Allow for this session │█ -│ 3. Allow for this file in all future sessions │█ +│ 3. Allow for this file in all future sessions ~/.gemini/policies/auto-saved.toml │█ │ 4. Modify with external editor │█ │ 5. No, suggest changes (esc) │█ -│ │█ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯█ " `; diff --git a/packages/cli/src/ui/components/AnsiOutput.test.tsx b/packages/cli/src/ui/components/AnsiOutput.test.tsx index 6331c149a8..04d6ccb0d9 100644 --- a/packages/cli/src/ui/components/AnsiOutput.test.tsx +++ b/packages/cli/src/ui/components/AnsiOutput.test.tsx @@ -16,6 +16,7 @@ const createAnsiToken = (overrides: Partial): AnsiToken => ({ underline: false, dim: false, inverse: false, + isUninitialized: false, fg: '#ffffff', bg: '#000000', ...overrides, diff --git a/packages/cli/src/ui/components/Composer.tsx b/packages/cli/src/ui/components/Composer.tsx index 66b54a70f3..4a1647d11b 100644 --- a/packages/cli/src/ui/components/Composer.tsx +++ b/packages/cli/src/ui/components/Composer.tsx @@ -26,6 +26,7 @@ import { OverflowProvider } from '../contexts/OverflowContext.js'; import { ConfigInitDisplay } from './ConfigInitDisplay.js'; import { TodoTray } from './messages/Todo.js'; import { useComposerStatus } from '../hooks/useComposerStatus.js'; +import { appEvents, AppEvent } from '../../utils/events.js'; export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { const uiState = useUIState(); @@ -55,6 +56,12 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { const { setShortcutsHelpVisible } = uiActions; + useEffect(() => { + if (hasPendingActionRequired) { + appEvents.emit(AppEvent.ScrollToBottom); + } + }, [hasPendingActionRequired]); + useEffect(() => { if (uiState.shortcutsHelpVisible && !isPassiveShortcutsHelpState) { setShortcutsHelpVisible(false); diff --git a/packages/cli/src/ui/components/ExitPlanModeDialog.test.tsx b/packages/cli/src/ui/components/ExitPlanModeDialog.test.tsx index d6fc23dd70..523f15516c 100644 --- a/packages/cli/src/ui/components/ExitPlanModeDialog.test.tsx +++ b/packages/cli/src/ui/components/ExitPlanModeDialog.test.tsx @@ -166,6 +166,7 @@ Implement a comprehensive authentication system with multiple providers. writeTextFile: vi.fn(), }), getUseAlternateBuffer: () => useAlternateBuffer, + getUseTerminalBuffer: () => false, } as unknown as import('@google/gemini-cli-core').Config, settings: createMockSettings({ ui: { useAlternateBuffer } }), }, @@ -466,6 +467,7 @@ Implement a comprehensive authentication system with multiple providers. writeTextFile: vi.fn(), }), getUseAlternateBuffer: () => useAlternateBuffer ?? true, + getUseTerminalBuffer: () => false, } as unknown as import('@google/gemini-cli-core').Config, settings: createMockSettings({ ui: { useAlternateBuffer: useAlternateBuffer ?? true }, diff --git a/packages/cli/src/ui/components/FolderTrustDialog.test.tsx b/packages/cli/src/ui/components/FolderTrustDialog.test.tsx index de6e8096ec..02977c68c0 100644 --- a/packages/cli/src/ui/components/FolderTrustDialog.test.tsx +++ b/packages/cli/src/ui/components/FolderTrustDialog.test.tsx @@ -18,7 +18,7 @@ vi.mock('../../utils/processUtils.js', () => ({ })); const mockedExit = vi.hoisted(() => vi.fn()); -const mockedCwd = vi.hoisted(() => vi.fn()); +const mockedCwd = vi.hoisted(() => vi.fn().mockReturnValue('/mock/cwd')); const mockedRows = vi.hoisted(() => ({ current: 24 })); vi.mock('node:process', async () => { @@ -85,7 +85,7 @@ describe('FolderTrustDialog', () => { ); expect(lastFrame()).toContain('This folder contains:'); - expect(lastFrame()).toContain('hidden'); + expect(lastFrame()).not.toContain('cmd9'); unmount(); }); @@ -116,7 +116,7 @@ describe('FolderTrustDialog', () => { // With maxHeight=4, the intro text (4 lines) will take most of the space. // The discovery results will likely be hidden. - expect(lastFrame()).toContain('hidden'); + expect(lastFrame()).not.toContain('cmd1'); unmount(); }); @@ -145,7 +145,7 @@ describe('FolderTrustDialog', () => { }, ); - expect(lastFrame()).toContain('hidden'); + expect(lastFrame()).not.toContain('cmd1'); unmount(); }); @@ -178,10 +178,11 @@ describe('FolderTrustDialog', () => { // Initial state: truncated await waitFor(() => { expect(lastFrame()).toContain('Do you trust the files in this folder?'); - expect(lastFrame()).toContain('Press Ctrl+O'); - expect(lastFrame()).toContain('hidden'); + expect(lastFrame()).not.toContain('cmd9'); }); + unmount(); + // We can't easily simulate global Ctrl+O toggle in this unit test // because it's handled in AppContainer. // But we can re-render with constrainHeight: false. @@ -195,7 +196,7 @@ describe('FolderTrustDialog', () => { width: 80, config: makeFakeConfig({ useAlternateBuffer: false }), settings: createMockSettings({ ui: { useAlternateBuffer: false } }), - uiState: { constrainHeight: false, terminalHeight: 24 }, + uiState: { constrainHeight: false, terminalHeight: 50 }, }, ); @@ -205,7 +206,6 @@ describe('FolderTrustDialog', () => { expect(lastFrameExpanded()).toContain('- cmd4'); }); - unmount(); unmountExpanded(); }); diff --git a/packages/cli/src/ui/components/Footer.test.tsx b/packages/cli/src/ui/components/Footer.test.tsx index e21db7940b..8c62434e61 100644 --- a/packages/cli/src/ui/components/Footer.test.tsx +++ b/packages/cli/src/ui/components/Footer.test.tsx @@ -81,6 +81,7 @@ const mockConfigPlain = { isTrustedFolder: () => true, getExtensionRegistryURI: () => undefined, getContentGeneratorConfig: () => ({ authType: undefined }), + getSandboxEnabled: () => false, }; const mockConfig = mockConfigPlain as unknown as Config; @@ -364,7 +365,7 @@ describe('