mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-03 13:41:05 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b4301d498 |
@@ -2,8 +2,7 @@
|
||||
"experimental": {
|
||||
"plan": true,
|
||||
"extensionReloading": true,
|
||||
"modelSteering": true,
|
||||
"memoryManager": true
|
||||
"modelSteering": true
|
||||
},
|
||||
"general": {
|
||||
"devtools": true
|
||||
|
||||
@@ -71,44 +71,12 @@ accessible.
|
||||
tables).
|
||||
- **Media:** Use lowercase hyphenated filenames. Provide descriptive alt text
|
||||
for all images.
|
||||
- **Details section:** Use the `<details>` tag to create a collapsible section.
|
||||
This is useful for supplementary or data-heavy information that isn't critical
|
||||
to the main flow.
|
||||
|
||||
Example:
|
||||
|
||||
<details>
|
||||
<summary>Title</summary>
|
||||
|
||||
- First entry
|
||||
- Second entry
|
||||
|
||||
</details>
|
||||
|
||||
- **Callouts**: Use GitHub-flavored markdown alerts to highlight important
|
||||
information. To ensure the formatting is preserved by `npm run format`, place
|
||||
an empty line, then the `<!-- prettier-ignore -->` comment directly before
|
||||
the callout block. The callout type (`[!TYPE]`) should be on the first line,
|
||||
followed by a newline, and then the content, with each subsequent line of
|
||||
content starting with `>`. Available types are `NOTE`, `TIP`, `IMPORTANT`,
|
||||
`WARNING`, and `CAUTION`.
|
||||
|
||||
Example:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an example of a multi-line note that will be preserved
|
||||
> by Prettier.
|
||||
|
||||
### Structure
|
||||
- **BLUF:** Start with an introduction explaining what to expect.
|
||||
- **Experimental features:** If a feature is clearly noted as experimental,
|
||||
add the following note immediately after the introductory paragraph:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development.
|
||||
|
||||
add the following note immediately after the introductory paragraph:
|
||||
`> **Note:** This is a preview feature currently under active development.`
|
||||
- **Headings:** Use hierarchical headings to support the user journey.
|
||||
- **Procedures:**
|
||||
- Introduce lists of steps with a complete sentence.
|
||||
@@ -117,7 +85,8 @@ accessible.
|
||||
- Put conditions before instructions (e.g., "On the Settings page, click...").
|
||||
- Provide clear context for where the action takes place.
|
||||
- Indicate optional steps clearly (e.g., "Optional: ...").
|
||||
- **Elements:** Use bullet lists, tables, details, and callouts.
|
||||
- **Elements:** Use bullet lists, tables, notes (`> **Note:**`), and warnings
|
||||
(`> **Warning:**`).
|
||||
- **Avoid using a table of contents:** If a table of contents is present, remove
|
||||
it.
|
||||
- **Next steps:** Conclude with a "Next steps" section if applicable.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
packages/core/src/services/scripts/*.exe
|
||||
@@ -1,9 +1,7 @@
|
||||
name: 'Website issue'
|
||||
description: 'Report an issue with the Gemini CLI Website and Gemini CLI Extensions Gallery'
|
||||
title: 'GeminiCLI.com Feedback: [ISSUE]'
|
||||
labels:
|
||||
- 'area/extensions'
|
||||
- 'area/documentation'
|
||||
body:
|
||||
- type: 'markdown'
|
||||
attributes:
|
||||
|
||||
@@ -352,6 +352,21 @@ npm run lint
|
||||
- **Imports:** Pay special attention to import paths. The project uses ESLint to
|
||||
enforce restrictions on relative imports between packages.
|
||||
|
||||
### Project structure
|
||||
|
||||
- `packages/`: Contains the individual sub-packages of the project.
|
||||
- `a2a-server`: A2A server implementation for the Gemini CLI. (Experimental)
|
||||
- `cli/`: The command-line interface.
|
||||
- `core/`: The core backend logic for the Gemini CLI.
|
||||
- `test-utils` Utilities for creating and cleaning temporary file systems for
|
||||
testing.
|
||||
- `vscode-ide-companion/`: The Gemini CLI Companion extension pairs with
|
||||
Gemini CLI.
|
||||
- `docs/`: Contains all project documentation.
|
||||
- `scripts/`: Utility scripts for building, testing, and development tasks.
|
||||
|
||||
For more detailed architecture, see `docs/architecture.md`.
|
||||
|
||||
### Debugging
|
||||
|
||||
#### VS Code
|
||||
|
||||
@@ -314,6 +314,7 @@ gemini
|
||||
|
||||
- [**Headless Mode (Scripting)**](./docs/cli/headless.md) - Use Gemini CLI in
|
||||
automated workflows.
|
||||
- [**Architecture Overview**](./docs/architecture.md) - How Gemini CLI works.
|
||||
- [**IDE Integration**](./docs/ide-integration/index.md) - VS Code companion.
|
||||
- [**Sandboxing & Security**](./docs/cli/sandbox.md) - Safe execution
|
||||
environments.
|
||||
|
||||
@@ -106,67 +106,6 @@ organization.
|
||||
ensures users maintain final control over which permitted servers are actually
|
||||
active in their environment.
|
||||
|
||||
#### Required MCP Servers (preview)
|
||||
|
||||
**Default**: empty
|
||||
|
||||
Allows administrators to define MCP servers that are **always injected** into
|
||||
the user's environment. Unlike the allowlist (which filters user-configured
|
||||
servers), required servers are automatically added regardless of the user's
|
||||
local configuration.
|
||||
|
||||
**Required Servers Format:**
|
||||
|
||||
```json
|
||||
{
|
||||
"requiredMcpServers": {
|
||||
"corp-compliance-tool": {
|
||||
"url": "https://mcp.corp/compliance",
|
||||
"type": "http",
|
||||
"trust": true,
|
||||
"description": "Corporate compliance tool"
|
||||
},
|
||||
"internal-registry": {
|
||||
"url": "https://registry.corp/mcp",
|
||||
"type": "sse",
|
||||
"authProviderType": "google_credentials",
|
||||
"oauth": {
|
||||
"scopes": ["https://www.googleapis.com/auth/scope"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Supported Fields:**
|
||||
|
||||
- `url`: (Required) The full URL of the MCP server endpoint.
|
||||
- `type`: (Required) The connection type (`sse` or `http`).
|
||||
- `trust`: (Optional) If set to `true`, tool execution will not require user
|
||||
approval. Defaults to `true` for required servers.
|
||||
- `description`: (Optional) Human-readable description of the server.
|
||||
- `authProviderType`: (Optional) Authentication provider (`dynamic_discovery`,
|
||||
`google_credentials`, or `service_account_impersonation`).
|
||||
- `oauth`: (Optional) OAuth configuration including `scopes`, `clientId`, and
|
||||
`clientSecret`.
|
||||
- `targetAudience`: (Optional) OAuth target audience for service-to-service
|
||||
auth.
|
||||
- `targetServiceAccount`: (Optional) Service account email to impersonate.
|
||||
- `headers`: (Optional) Additional HTTP headers to send with requests.
|
||||
- `includeTools` / `excludeTools`: (Optional) Tool filtering lists.
|
||||
- `timeout`: (Optional) Timeout in milliseconds for MCP requests.
|
||||
|
||||
**Client Enforcement Logic:**
|
||||
|
||||
- Required servers are injected **after** allowlist filtering, so they are
|
||||
always available even if the allowlist is active.
|
||||
- If a required server has the **same name** as a locally configured server, the
|
||||
admin configuration **completely overrides** the local one.
|
||||
- Required servers only support remote transports (`sse`, `http`). Local
|
||||
execution fields (`command`, `args`, `env`, `cwd`) are not supported.
|
||||
- Required servers can coexist with allowlisted servers — both features work
|
||||
independently.
|
||||
|
||||
### Unmanaged Capabilities
|
||||
|
||||
**Enabled/Disabled** | Default: disabled
|
||||
|
||||
@@ -18,17 +18,6 @@ on GitHub.
|
||||
| [Preview](preview.md) | Experimental features ready for early feedback. |
|
||||
| [Stable](latest.md) | Stable, recommended for general use. |
|
||||
|
||||
## Announcements: v0.34.0 - 2026-03-17
|
||||
|
||||
- **Plan Mode Enabled by Default:** Plan Mode is now enabled by default to help
|
||||
you break down complex tasks and execute them systematically
|
||||
([#21713](https://github.com/google-gemini/gemini-cli/pull/21713) by @jerop).
|
||||
- **Sandboxing Enhancements:** We've added native gVisor (runsc) and
|
||||
experimental LXC container sandboxing support for safer execution environments
|
||||
([#21062](https://github.com/google-gemini/gemini-cli/pull/21062) by
|
||||
@Zheyuan-Lin, [#20735](https://github.com/google-gemini/gemini-cli/pull/20735)
|
||||
by @h30s).
|
||||
|
||||
## Announcements: v0.33.0 - 2026-03-11
|
||||
|
||||
- **Agent Architecture Enhancements:** Introduced HTTP authentication for A2A
|
||||
|
||||
+212
-459
@@ -1,6 +1,6 @@
|
||||
# Latest stable release: v0.34.0
|
||||
# Latest stable release: v0.33.2
|
||||
|
||||
Released: March 17, 2026
|
||||
Released: March 16, 2026
|
||||
|
||||
For most users, our latest stable release is the recommended release. Install
|
||||
the latest stable version with:
|
||||
@@ -11,474 +11,227 @@ npm install -g @google/gemini-cli
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Plan Mode Enabled by Default**: The comprehensive planning capability is now
|
||||
enabled by default, allowing for better structured task management and
|
||||
execution.
|
||||
- **Enhanced Sandboxing Capabilities**: Added support for native gVisor (runsc)
|
||||
sandboxing as well as experimental LXC container sandboxing to provide more
|
||||
robust and isolated execution environments.
|
||||
- **Improved Loop Detection & Recovery**: Implemented iterative loop detection
|
||||
and model feedback mechanisms to prevent the CLI from getting stuck in
|
||||
repetitive actions.
|
||||
- **Customizable UI Elements**: You can now configure a custom footer using the
|
||||
new `/footer` command, and enjoy standardized semantic focus colors for better
|
||||
history visibility.
|
||||
- **Extensive Subagent Updates**: Refinements across the tracker visualization
|
||||
tools, background process logging, and broader fallback support for models in
|
||||
tool execution scenarios.
|
||||
- **Agent Architecture Enhancements:** Introduced HTTP authentication support
|
||||
for A2A remote agents, authenticated A2A agent card discovery, and directly
|
||||
indicated auth-required states.
|
||||
- **Plan Mode Updates:** Expanded Plan Mode capabilities with built-in research
|
||||
subagents, annotation support for feedback during iteration, and a new `copy`
|
||||
subcommand.
|
||||
- **CLI UX Improvements:** Redesigned the header to be compact with an ASCII
|
||||
icon, inverted the context window display to show usage, and allowed sub-agent
|
||||
confirmation requests in the UI while preventing background flicker.
|
||||
- **ACP & MCP Integrations:** Implemented slash command handling in ACP for
|
||||
`/memory`, `/init`, `/extensions`, and `/restore`, added an MCPOAuthProvider,
|
||||
and introduced a `set models` interface for ACP.
|
||||
- **Admin & Core Stability:** Enabled a 30-day default retention for chat
|
||||
history, added tool name validation in TOML policy files, and improved tool
|
||||
parameter extraction.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- feat(cli): add chat resume footer on session quit by @lordshashank in
|
||||
[#20667](https://github.com/google-gemini/gemini-cli/pull/20667)
|
||||
- Support bold and other styles in svg snapshots by @jacob314 in
|
||||
[#20937](https://github.com/google-gemini/gemini-cli/pull/20937)
|
||||
- fix(core): increase A2A agent timeout to 30 minutes by @adamfweidman in
|
||||
[#21028](https://github.com/google-gemini/gemini-cli/pull/21028)
|
||||
- Cleanup old branches. by @jacob314 in
|
||||
[#19354](https://github.com/google-gemini/gemini-cli/pull/19354)
|
||||
- chore(release): bump version to 0.34.0-nightly.20260303.34f0c1538 by
|
||||
- fix(patch): cherry-pick 48130eb to release/v0.33.1-pr-22665 [CONFLICTS] by
|
||||
@gemini-cli-robot in
|
||||
[#21034](https://github.com/google-gemini/gemini-cli/pull/21034)
|
||||
- feat(ui): standardize semantic focus colors and enhance history visibility by
|
||||
@keithguerin in
|
||||
[#20745](https://github.com/google-gemini/gemini-cli/pull/20745)
|
||||
- fix: merge duplicate imports in packages/core (3/4) by @Nixxx19 in
|
||||
[#20928](https://github.com/google-gemini/gemini-cli/pull/20928)
|
||||
- Add extra safety checks for proto pollution by @jacob314 in
|
||||
[#20396](https://github.com/google-gemini/gemini-cli/pull/20396)
|
||||
- feat(core): Add tracker CRUD tools & visualization by @anj-s in
|
||||
[#19489](https://github.com/google-gemini/gemini-cli/pull/19489)
|
||||
- Revert "fix(ui): persist expansion in AskUser dialog when navigating options"
|
||||
by @jacob314 in
|
||||
[#21042](https://github.com/google-gemini/gemini-cli/pull/21042)
|
||||
- Changelog for v0.33.0-preview.0 by @gemini-cli-robot in
|
||||
[#21030](https://github.com/google-gemini/gemini-cli/pull/21030)
|
||||
- fix: model persistence for all scenarios by @sripasg in
|
||||
[#21051](https://github.com/google-gemini/gemini-cli/pull/21051)
|
||||
- chore/release: bump version to 0.34.0-nightly.20260304.28af4e127 by
|
||||
[#22720](https://github.com/google-gemini/gemini-cli/pull/22720)
|
||||
- fix(patch): cherry-pick 8432bce to release/v0.33.0-pr-22069 to patch version
|
||||
v0.33.0 and create version 0.33.1 by @gemini-cli-robot in
|
||||
[#22206](https://github.com/google-gemini/gemini-cli/pull/22206)
|
||||
- Docs: Update model docs to remove Preview Features. by @jkcinouye in
|
||||
[#20084](https://github.com/google-gemini/gemini-cli/pull/20084)
|
||||
- docs: fix typo in installation documentation by @AdityaSharma-Git3207 in
|
||||
[#20153](https://github.com/google-gemini/gemini-cli/pull/20153)
|
||||
- docs: add Windows PowerShell equivalents for environments and scripting by
|
||||
@scidomino in [#20333](https://github.com/google-gemini/gemini-cli/pull/20333)
|
||||
- fix(core): parse raw ASCII buffer strings in Gaxios errors by @sehoon38 in
|
||||
[#20626](https://github.com/google-gemini/gemini-cli/pull/20626)
|
||||
- chore(release): bump version to 0.33.0-nightly.20260227.ba149afa0 by @galz10
|
||||
in [#20637](https://github.com/google-gemini/gemini-cli/pull/20637)
|
||||
- fix(github): use robot PAT for automated PRs to pass CLA check by @galz10 in
|
||||
[#20641](https://github.com/google-gemini/gemini-cli/pull/20641)
|
||||
- chore/release: bump version to 0.33.0-nightly.20260228.1ca5c05d0 by
|
||||
@gemini-cli-robot in
|
||||
[#21054](https://github.com/google-gemini/gemini-cli/pull/21054)
|
||||
- Consistently guard restarts against concurrent auto updates by @scidomino in
|
||||
[#21016](https://github.com/google-gemini/gemini-cli/pull/21016)
|
||||
- Defensive coding to reduce the risk of Maximum update depth errors by
|
||||
@jacob314 in [#20940](https://github.com/google-gemini/gemini-cli/pull/20940)
|
||||
- fix(cli): Polish shell autocomplete rendering to be a little more shell native
|
||||
feeling. by @jacob314 in
|
||||
[#20931](https://github.com/google-gemini/gemini-cli/pull/20931)
|
||||
- Docs: Update plan mode docs by @jkcinouye in
|
||||
[#19682](https://github.com/google-gemini/gemini-cli/pull/19682)
|
||||
- fix(mcp): Notifications/tools/list_changed support not working by @jacob314 in
|
||||
[#21050](https://github.com/google-gemini/gemini-cli/pull/21050)
|
||||
- fix(cli): register extension lifecycle events in DebugProfiler by
|
||||
@fayerman-source in
|
||||
[#20101](https://github.com/google-gemini/gemini-cli/pull/20101)
|
||||
- chore(dev): update vscode settings for typescriptreact by @rohit-4321 in
|
||||
[#19907](https://github.com/google-gemini/gemini-cli/pull/19907)
|
||||
- fix(cli): enable multi-arch docker builds for sandbox by @ru-aish in
|
||||
[#19821](https://github.com/google-gemini/gemini-cli/pull/19821)
|
||||
- Changelog for v0.32.0 by @gemini-cli-robot in
|
||||
[#21033](https://github.com/google-gemini/gemini-cli/pull/21033)
|
||||
- Changelog for v0.33.0-preview.1 by @gemini-cli-robot in
|
||||
[#21058](https://github.com/google-gemini/gemini-cli/pull/21058)
|
||||
- feat(core): improve @scripts/copy_files.js autocomplete to prioritize
|
||||
filenames by @sehoon38 in
|
||||
[#21064](https://github.com/google-gemini/gemini-cli/pull/21064)
|
||||
- feat(sandbox): add experimental LXC container sandbox support by @h30s in
|
||||
[#20735](https://github.com/google-gemini/gemini-cli/pull/20735)
|
||||
- feat(evals): add overall pass rate row to eval nightly summary table by
|
||||
@gundermanc in
|
||||
[#20905](https://github.com/google-gemini/gemini-cli/pull/20905)
|
||||
- feat(telemetry): include language in telemetry and fix accepted lines
|
||||
computation by @gundermanc in
|
||||
[#21126](https://github.com/google-gemini/gemini-cli/pull/21126)
|
||||
- Changelog for v0.32.1 by @gemini-cli-robot in
|
||||
[#21055](https://github.com/google-gemini/gemini-cli/pull/21055)
|
||||
- feat(core): add robustness tests, logging, and metrics for CodeAssistServer
|
||||
SSE parsing by @yunaseoul in
|
||||
[#21013](https://github.com/google-gemini/gemini-cli/pull/21013)
|
||||
- feat: add issue assignee workflow by @kartikangiras in
|
||||
[#21003](https://github.com/google-gemini/gemini-cli/pull/21003)
|
||||
- fix: improve error message when OAuth succeeds but project ID is required by
|
||||
@Nixxx19 in [#21070](https://github.com/google-gemini/gemini-cli/pull/21070)
|
||||
- feat(loop-reduction): implement iterative loop detection and model feedback by
|
||||
[#20644](https://github.com/google-gemini/gemini-cli/pull/20644)
|
||||
- Changelog for v0.31.0 by @gemini-cli-robot in
|
||||
[#20634](https://github.com/google-gemini/gemini-cli/pull/20634)
|
||||
- fix: use full paths for ACP diff payloads by @JagjeevanAK in
|
||||
[#19539](https://github.com/google-gemini/gemini-cli/pull/19539)
|
||||
- Changelog for v0.32.0-preview.0 by @gemini-cli-robot in
|
||||
[#20627](https://github.com/google-gemini/gemini-cli/pull/20627)
|
||||
- fix: acp/zed race condition between MCP initialisation and prompt by
|
||||
@kartikangiras in
|
||||
[#20205](https://github.com/google-gemini/gemini-cli/pull/20205)
|
||||
- fix(cli): reset themeManager between tests to ensure isolation by
|
||||
@NTaylorMullen in
|
||||
[#20598](https://github.com/google-gemini/gemini-cli/pull/20598)
|
||||
- refactor(core): Extract tool parameter names as constants by @SandyTao520 in
|
||||
[#20460](https://github.com/google-gemini/gemini-cli/pull/20460)
|
||||
- fix(cli): resolve autoThemeSwitching when background hasn't changed but theme
|
||||
mismatches by @sehoon38 in
|
||||
[#20706](https://github.com/google-gemini/gemini-cli/pull/20706)
|
||||
- feat(skills): add github-issue-creator skill by @sehoon38 in
|
||||
[#20709](https://github.com/google-gemini/gemini-cli/pull/20709)
|
||||
- fix(cli): allow sub-agent confirmation requests in UI while preventing
|
||||
background flicker by @abhipatel12 in
|
||||
[#20722](https://github.com/google-gemini/gemini-cli/pull/20722)
|
||||
- Merge User and Agent Card Descriptions #20849 by @adamfweidman in
|
||||
[#20850](https://github.com/google-gemini/gemini-cli/pull/20850)
|
||||
- fix(core): reduce LLM-based loop detection false positives by @SandyTao520 in
|
||||
[#20701](https://github.com/google-gemini/gemini-cli/pull/20701)
|
||||
- fix(plan): deflake plan mode integration tests by @Adib234 in
|
||||
[#20477](https://github.com/google-gemini/gemini-cli/pull/20477)
|
||||
- Add /unassign support by @scidomino in
|
||||
[#20864](https://github.com/google-gemini/gemini-cli/pull/20864)
|
||||
- feat(core): implement HTTP authentication support for A2A remote agents by
|
||||
@SandyTao520 in
|
||||
[#20510](https://github.com/google-gemini/gemini-cli/pull/20510)
|
||||
- feat(core): centralize read_file limits and update gemini-3 description by
|
||||
@aishaneeshah in
|
||||
[#20763](https://github.com/google-gemini/gemini-cli/pull/20763)
|
||||
- chore(github): require prompt approvers for agent prompt files by @gundermanc
|
||||
in [#20896](https://github.com/google-gemini/gemini-cli/pull/20896)
|
||||
- Docs: Create tools reference by @jkcinouye in
|
||||
[#19470](https://github.com/google-gemini/gemini-cli/pull/19470)
|
||||
- fix(core, a2a-server): prevent hang during OAuth in non-interactive sessions
|
||||
by @spencer426 in
|
||||
[#21045](https://github.com/google-gemini/gemini-cli/pull/21045)
|
||||
- chore(cli): enable deprecated settings removal by default by @yashodipmore in
|
||||
[#20682](https://github.com/google-gemini/gemini-cli/pull/20682)
|
||||
- feat(core): Disable fast ack helper for hints. by @joshualitt in
|
||||
[#21011](https://github.com/google-gemini/gemini-cli/pull/21011)
|
||||
- fix(ui): suppress redundant failure note when tool error note is shown by
|
||||
@NTaylorMullen in
|
||||
[#21078](https://github.com/google-gemini/gemini-cli/pull/21078)
|
||||
- docs: document planning workflows with Conductor example by @jerop in
|
||||
[#21166](https://github.com/google-gemini/gemini-cli/pull/21166)
|
||||
- feat(release): ship esbuild bundle in npm package by @genneth in
|
||||
[#19171](https://github.com/google-gemini/gemini-cli/pull/19171)
|
||||
- fix(extensions): preserve symlinks in extension source path while enforcing
|
||||
folder trust by @galz10 in
|
||||
[#20867](https://github.com/google-gemini/gemini-cli/pull/20867)
|
||||
- fix(cli): defer tool exclusions to policy engine in non-interactive mode by
|
||||
@EricRahm in [#20639](https://github.com/google-gemini/gemini-cli/pull/20639)
|
||||
- fix(ui): removed double padding on rendered content by @devr0306 in
|
||||
[#21029](https://github.com/google-gemini/gemini-cli/pull/21029)
|
||||
- fix(core): truncate excessively long lines in grep search output by
|
||||
@gundermanc in
|
||||
[#21147](https://github.com/google-gemini/gemini-cli/pull/21147)
|
||||
- feat: add custom footer configuration via `/footer` by @jackwotherspoon in
|
||||
[#19001](https://github.com/google-gemini/gemini-cli/pull/19001)
|
||||
- perf(core): fix OOM crash in long-running sessions by @WizardsForgeGames in
|
||||
[#19608](https://github.com/google-gemini/gemini-cli/pull/19608)
|
||||
- refactor(cli): categorize built-in themes into dark/ and light/ directories by
|
||||
@JayadityaGit in
|
||||
[#18634](https://github.com/google-gemini/gemini-cli/pull/18634)
|
||||
- fix(core): explicitly allow codebase_investigator and cli_help in read-only
|
||||
mode by @Adib234 in
|
||||
[#21157](https://github.com/google-gemini/gemini-cli/pull/21157)
|
||||
- test: add browser agent integration tests by @kunal-10-cloud in
|
||||
[#21151](https://github.com/google-gemini/gemini-cli/pull/21151)
|
||||
- fix(cli): fix enabling kitty codes on Windows Terminal by @scidomino in
|
||||
[#21136](https://github.com/google-gemini/gemini-cli/pull/21136)
|
||||
- refactor(core): extract shared OAuth flow primitives from MCPOAuthProvider by
|
||||
@SandyTao520 in
|
||||
[#20895](https://github.com/google-gemini/gemini-cli/pull/20895)
|
||||
- fix(ui): add partial output to cancelled shell UI by @devr0306 in
|
||||
[#21178](https://github.com/google-gemini/gemini-cli/pull/21178)
|
||||
- fix(cli): replace hardcoded keybinding strings with dynamic formatters by
|
||||
@scidomino in [#21159](https://github.com/google-gemini/gemini-cli/pull/21159)
|
||||
- DOCS: Update quota and pricing page by @g-samroberts in
|
||||
[#21194](https://github.com/google-gemini/gemini-cli/pull/21194)
|
||||
- feat(telemetry): implement Clearcut logging for startup statistics by
|
||||
@yunaseoul in [#21172](https://github.com/google-gemini/gemini-cli/pull/21172)
|
||||
- feat(triage): add area/documentation to issue triage by @g-samroberts in
|
||||
[#21222](https://github.com/google-gemini/gemini-cli/pull/21222)
|
||||
- Fix so shell calls are formatted by @jacob314 in
|
||||
[#21237](https://github.com/google-gemini/gemini-cli/pull/21237)
|
||||
- feat(cli): add native gVisor (runsc) sandboxing support by @Zheyuan-Lin in
|
||||
[#21062](https://github.com/google-gemini/gemini-cli/pull/21062)
|
||||
- docs: use absolute paths for internal links in plan-mode.md by @jerop in
|
||||
[#21299](https://github.com/google-gemini/gemini-cli/pull/21299)
|
||||
- fix(core): prevent unhandled AbortError crash during stream loop detection by
|
||||
@7hokerz in [#21123](https://github.com/google-gemini/gemini-cli/pull/21123)
|
||||
- fix:reorder env var redaction checks to scan values first by @kartikangiras in
|
||||
[#21059](https://github.com/google-gemini/gemini-cli/pull/21059)
|
||||
- fix(acp): rename --experimental-acp to --acp & remove Zed-specific refrences
|
||||
[#20619](https://github.com/google-gemini/gemini-cli/pull/20619)
|
||||
- Do not block CI on evals by @gundermanc in
|
||||
[#20870](https://github.com/google-gemini/gemini-cli/pull/20870)
|
||||
- document node limitation for shift+tab by @scidomino in
|
||||
[#20877](https://github.com/google-gemini/gemini-cli/pull/20877)
|
||||
- Add install as an option when extension is selected. by @DavidAPierce in
|
||||
[#20358](https://github.com/google-gemini/gemini-cli/pull/20358)
|
||||
- Update CODEOWNERS for README.md reviewers by @g-samroberts in
|
||||
[#20860](https://github.com/google-gemini/gemini-cli/pull/20860)
|
||||
- feat(core): truncate large MCP tool output by @SandyTao520 in
|
||||
[#19365](https://github.com/google-gemini/gemini-cli/pull/19365)
|
||||
- Subagent activity UX. by @gundermanc in
|
||||
[#17570](https://github.com/google-gemini/gemini-cli/pull/17570)
|
||||
- style(cli) : Dialog pattern for /hooks Command by @AbdulTawabJuly in
|
||||
[#17930](https://github.com/google-gemini/gemini-cli/pull/17930)
|
||||
- feat: redesign header to be compact with ASCII icon by @keithguerin in
|
||||
[#18713](https://github.com/google-gemini/gemini-cli/pull/18713)
|
||||
- fix(core): ensure subagents use qualified MCP tool names by @abhipatel12 in
|
||||
[#20801](https://github.com/google-gemini/gemini-cli/pull/20801)
|
||||
- feat(core): support authenticated A2A agent card discovery by @SandyTao520 in
|
||||
[#20622](https://github.com/google-gemini/gemini-cli/pull/20622)
|
||||
- refactor(cli): fully remove React anti patterns, improve type safety and fix
|
||||
UX oversights in SettingsDialog.tsx by @psinha40898 in
|
||||
[#18963](https://github.com/google-gemini/gemini-cli/pull/18963)
|
||||
- Adding MCPOAuthProvider implementing the MCPSDK OAuthClientProvider by
|
||||
@Nayana-Parameswarappa in
|
||||
[#20121](https://github.com/google-gemini/gemini-cli/pull/20121)
|
||||
- feat(core): add tool name validation in TOML policy files by @allenhutchison
|
||||
in [#19281](https://github.com/google-gemini/gemini-cli/pull/19281)
|
||||
- docs: fix broken markdown links in main README.md by @Hamdanbinhashim in
|
||||
[#20300](https://github.com/google-gemini/gemini-cli/pull/20300)
|
||||
- refactor(core): replace manual syncPlanModeTools with declarative policy rules
|
||||
by @jerop in [#20596](https://github.com/google-gemini/gemini-cli/pull/20596)
|
||||
- fix(core): increase default headers timeout to 5 minutes by @gundermanc in
|
||||
[#20890](https://github.com/google-gemini/gemini-cli/pull/20890)
|
||||
- feat(admin): enable 30 day default retention for chat history & remove warning
|
||||
by @skeshive in
|
||||
[#21171](https://github.com/google-gemini/gemini-cli/pull/21171)
|
||||
- feat(core): fallback to 2.5 models with no access for toolcalls by @sehoon38
|
||||
in [#21283](https://github.com/google-gemini/gemini-cli/pull/21283)
|
||||
- test(core): improve testing for API request/response parsing by @sehoon38 in
|
||||
[#21227](https://github.com/google-gemini/gemini-cli/pull/21227)
|
||||
- docs(links): update docs-writer skill and fix broken link by @g-samroberts in
|
||||
[#21314](https://github.com/google-gemini/gemini-cli/pull/21314)
|
||||
- Fix code colorizer ansi escape bug. by @jacob314 in
|
||||
[#21321](https://github.com/google-gemini/gemini-cli/pull/21321)
|
||||
- remove wildcard behavior on keybindings by @scidomino in
|
||||
[#21315](https://github.com/google-gemini/gemini-cli/pull/21315)
|
||||
- feat(acp): Add support for AI Gateway auth by @skeshive in
|
||||
[#21305](https://github.com/google-gemini/gemini-cli/pull/21305)
|
||||
- fix(theme): improve theme color contrast for macOS Terminal.app by @clocky in
|
||||
[#21175](https://github.com/google-gemini/gemini-cli/pull/21175)
|
||||
- feat (core): Implement tracker related SI changes by @anj-s in
|
||||
[#19964](https://github.com/google-gemini/gemini-cli/pull/19964)
|
||||
- Changelog for v0.33.0-preview.2 by @gemini-cli-robot in
|
||||
[#21333](https://github.com/google-gemini/gemini-cli/pull/21333)
|
||||
- Changelog for v0.33.0-preview.3 by @gemini-cli-robot in
|
||||
[#21347](https://github.com/google-gemini/gemini-cli/pull/21347)
|
||||
- docs: format release times as HH:MM UTC by @pavan-sh in
|
||||
[#20726](https://github.com/google-gemini/gemini-cli/pull/20726)
|
||||
- fix(cli): implement --all flag for extensions uninstall by @sehoon38 in
|
||||
[#21319](https://github.com/google-gemini/gemini-cli/pull/21319)
|
||||
- docs: fix incorrect relative links to command reference by @kanywst in
|
||||
[#20964](https://github.com/google-gemini/gemini-cli/pull/20964)
|
||||
- documentiong ensures ripgrep by @Jatin24062005 in
|
||||
[#21298](https://github.com/google-gemini/gemini-cli/pull/21298)
|
||||
- fix(core): handle AbortError thrown during processTurn by @MumuTW in
|
||||
[#21296](https://github.com/google-gemini/gemini-cli/pull/21296)
|
||||
- docs(cli): clarify ! command output visibility in shell commands tutorial by
|
||||
@MohammedADev in
|
||||
[#21041](https://github.com/google-gemini/gemini-cli/pull/21041)
|
||||
- fix: logic for task tracker strategy and remove tracker tools by @anj-s in
|
||||
[#21355](https://github.com/google-gemini/gemini-cli/pull/21355)
|
||||
- fix(partUtils): display media type and size for inline data parts by @Aboudjem
|
||||
in [#21358](https://github.com/google-gemini/gemini-cli/pull/21358)
|
||||
- Fix(accessibility): add screen reader support to RewindViewer by @Famous077 in
|
||||
[#20750](https://github.com/google-gemini/gemini-cli/pull/20750)
|
||||
- fix(hooks): propagate stopHookActive in AfterAgent retry path (#20426) by
|
||||
@Aarchi-07 in [#20439](https://github.com/google-gemini/gemini-cli/pull/20439)
|
||||
- fix(core): deduplicate GEMINI.md files by device/inode on case-insensitive
|
||||
filesystems (#19904) by @Nixxx19 in
|
||||
[#19915](https://github.com/google-gemini/gemini-cli/pull/19915)
|
||||
- feat(core): add concurrency safety guidance for subagent delegation (#17753)
|
||||
by @abhipatel12 in
|
||||
[#21278](https://github.com/google-gemini/gemini-cli/pull/21278)
|
||||
- feat(ui): dynamically generate all keybinding hints by @scidomino in
|
||||
[#21346](https://github.com/google-gemini/gemini-cli/pull/21346)
|
||||
- feat(core): implement unified KeychainService and migrate token storage by
|
||||
@ehedlund in [#21344](https://github.com/google-gemini/gemini-cli/pull/21344)
|
||||
- fix(cli): gracefully handle --resume when no sessions exist by @SandyTao520 in
|
||||
[#21429](https://github.com/google-gemini/gemini-cli/pull/21429)
|
||||
- fix(plan): keep approved plan during chat compression by @ruomengz in
|
||||
[#21284](https://github.com/google-gemini/gemini-cli/pull/21284)
|
||||
- feat(core): implement generic CacheService and optimize setupUser by @sehoon38
|
||||
in [#21374](https://github.com/google-gemini/gemini-cli/pull/21374)
|
||||
- Update quota and pricing documentation with subscription tiers by @srithreepo
|
||||
in [#21351](https://github.com/google-gemini/gemini-cli/pull/21351)
|
||||
- fix(core): append correct OTLP paths for HTTP exporters by
|
||||
@sebastien-prudhomme in
|
||||
[#16836](https://github.com/google-gemini/gemini-cli/pull/16836)
|
||||
- Changelog for v0.33.0-preview.4 by @gemini-cli-robot in
|
||||
[#21354](https://github.com/google-gemini/gemini-cli/pull/21354)
|
||||
- feat(cli): implement dot-prefixing for slash command conflicts by @ehedlund in
|
||||
[#20979](https://github.com/google-gemini/gemini-cli/pull/20979)
|
||||
- refactor(core): standardize MCP tool naming to mcp\_ FQN format by
|
||||
@abhipatel12 in
|
||||
[#21425](https://github.com/google-gemini/gemini-cli/pull/21425)
|
||||
- feat(cli): hide gemma settings from display and mark as experimental by
|
||||
@abhipatel12 in
|
||||
[#21471](https://github.com/google-gemini/gemini-cli/pull/21471)
|
||||
- feat(skills): refine string-reviewer guidelines and description by @clocky in
|
||||
[#20368](https://github.com/google-gemini/gemini-cli/pull/20368)
|
||||
- fix(core): whitelist TERM and COLORTERM in environment sanitization by
|
||||
@deadsmash07 in
|
||||
[#20514](https://github.com/google-gemini/gemini-cli/pull/20514)
|
||||
- fix(billing): fix overage strategy lifecycle and settings integration by
|
||||
@gsquared94 in
|
||||
[#21236](https://github.com/google-gemini/gemini-cli/pull/21236)
|
||||
- fix: expand paste placeholders in TextInput on submit by @Jefftree in
|
||||
[#19946](https://github.com/google-gemini/gemini-cli/pull/19946)
|
||||
- fix(core): add in-memory cache to ChatRecordingService to prevent OOM by
|
||||
@SandyTao520 in
|
||||
[#21502](https://github.com/google-gemini/gemini-cli/pull/21502)
|
||||
- feat(cli): overhaul thinking UI by @keithguerin in
|
||||
[#18725](https://github.com/google-gemini/gemini-cli/pull/18725)
|
||||
- fix(ui): unify Ctrl+O expansion hint experience across buffer modes by
|
||||
@jwhelangoog in
|
||||
[#21474](https://github.com/google-gemini/gemini-cli/pull/21474)
|
||||
- fix(cli): correct shell height reporting by @jacob314 in
|
||||
[#21492](https://github.com/google-gemini/gemini-cli/pull/21492)
|
||||
- Make test suite pass when the GEMINI_SYSTEM_MD env variable or
|
||||
GEMINI_WRITE_SYSTEM_MD variable happens to be set locally/ by @jacob314 in
|
||||
[#21480](https://github.com/google-gemini/gemini-cli/pull/21480)
|
||||
- Disallow underspecified types by @gundermanc in
|
||||
[#21485](https://github.com/google-gemini/gemini-cli/pull/21485)
|
||||
- refactor(cli): standardize on 'reload' verb for all components by @keithguerin
|
||||
in [#20654](https://github.com/google-gemini/gemini-cli/pull/20654)
|
||||
- feat(cli): Invert quota language to 'percent used' by @keithguerin in
|
||||
[#20100](https://github.com/google-gemini/gemini-cli/pull/20100)
|
||||
- Docs: Add documentation for notifications (experimental)(macOS) by @jkcinouye
|
||||
in [#21163](https://github.com/google-gemini/gemini-cli/pull/21163)
|
||||
- Code review comments as a pr by @jacob314 in
|
||||
[#21209](https://github.com/google-gemini/gemini-cli/pull/21209)
|
||||
- feat(cli): unify /chat and /resume command UX by @LyalinDotCom in
|
||||
[#20256](https://github.com/google-gemini/gemini-cli/pull/20256)
|
||||
- docs: fix typo 'allowslisted' -> 'allowlisted' in mcp-server.md by
|
||||
@Gyanranjan-Priyam in
|
||||
[#21665](https://github.com/google-gemini/gemini-cli/pull/21665)
|
||||
- fix(core): display actual graph output in tracker_visualize tool by @anj-s in
|
||||
[#21455](https://github.com/google-gemini/gemini-cli/pull/21455)
|
||||
- fix(core): sanitize SSE-corrupted JSON and domain strings in error
|
||||
classification by @gsquared94 in
|
||||
[#21702](https://github.com/google-gemini/gemini-cli/pull/21702)
|
||||
- Docs: Make documentation links relative by @diodesign in
|
||||
[#21490](https://github.com/google-gemini/gemini-cli/pull/21490)
|
||||
- feat(cli): expose /tools desc as explicit subcommand for discoverability by
|
||||
@aworki in [#21241](https://github.com/google-gemini/gemini-cli/pull/21241)
|
||||
- feat(cli): add /compact alias for /compress command by @jackwotherspoon in
|
||||
[#21711](https://github.com/google-gemini/gemini-cli/pull/21711)
|
||||
- feat(plan): enable Plan Mode by default by @jerop in
|
||||
[#21713](https://github.com/google-gemini/gemini-cli/pull/21713)
|
||||
- feat(core): Introduce `AgentLoopContext`. by @joshualitt in
|
||||
[#21198](https://github.com/google-gemini/gemini-cli/pull/21198)
|
||||
- fix(core): resolve symlinks for non-existent paths during validation by
|
||||
@Adib234 in [#21487](https://github.com/google-gemini/gemini-cli/pull/21487)
|
||||
- docs: document tool exclusion from memory via deny policy by @Abhijit-2592 in
|
||||
[#21428](https://github.com/google-gemini/gemini-cli/pull/21428)
|
||||
- perf(core): cache loadApiKey to reduce redundant keychain access by @sehoon38
|
||||
in [#21520](https://github.com/google-gemini/gemini-cli/pull/21520)
|
||||
- feat(cli): implement /upgrade command by @sehoon38 in
|
||||
[#21511](https://github.com/google-gemini/gemini-cli/pull/21511)
|
||||
- Feat/browser agent progress emission by @kunal-10-cloud in
|
||||
[#21218](https://github.com/google-gemini/gemini-cli/pull/21218)
|
||||
- fix(settings): display objects as JSON instead of [object Object] by
|
||||
@Zheyuan-Lin in
|
||||
[#21458](https://github.com/google-gemini/gemini-cli/pull/21458)
|
||||
- Unmarshall update by @DavidAPierce in
|
||||
[#21721](https://github.com/google-gemini/gemini-cli/pull/21721)
|
||||
- Update mcp's list function to check for disablement. by @DavidAPierce in
|
||||
[#21148](https://github.com/google-gemini/gemini-cli/pull/21148)
|
||||
- robustness(core): static checks to validate history is immutable by @jacob314
|
||||
in [#21228](https://github.com/google-gemini/gemini-cli/pull/21228)
|
||||
- refactor(cli): better react patterns for BaseSettingsDialog by @psinha40898 in
|
||||
[#21206](https://github.com/google-gemini/gemini-cli/pull/21206)
|
||||
- feat(security): implement robust IP validation and safeFetch foundation by
|
||||
@alisa-alisa in
|
||||
[#21401](https://github.com/google-gemini/gemini-cli/pull/21401)
|
||||
- feat(core): improve subagent result display by @joshualitt in
|
||||
[#20378](https://github.com/google-gemini/gemini-cli/pull/20378)
|
||||
- docs: fix broken markdown syntax and anchor links in /tools by @campox747 in
|
||||
[#20902](https://github.com/google-gemini/gemini-cli/pull/20902)
|
||||
- feat(policy): support subagent-specific policies in TOML by @akh64bit in
|
||||
[#21431](https://github.com/google-gemini/gemini-cli/pull/21431)
|
||||
- Add script to speed up reviewing PRs adding a worktree. by @jacob314 in
|
||||
[#21748](https://github.com/google-gemini/gemini-cli/pull/21748)
|
||||
- fix(core): prevent infinite recursion in symlink resolution by @Adib234 in
|
||||
[#21750](https://github.com/google-gemini/gemini-cli/pull/21750)
|
||||
- fix(docs): fix headless mode docs by @ame2en in
|
||||
[#21287](https://github.com/google-gemini/gemini-cli/pull/21287)
|
||||
- feat/redesign header compact by @jacob314 in
|
||||
[#20922](https://github.com/google-gemini/gemini-cli/pull/20922)
|
||||
- refactor: migrate to useKeyMatchers hook by @scidomino in
|
||||
[#21753](https://github.com/google-gemini/gemini-cli/pull/21753)
|
||||
- perf(cli): cache loadSettings to reduce redundant disk I/O at startup by
|
||||
@sehoon38 in [#21521](https://github.com/google-gemini/gemini-cli/pull/21521)
|
||||
- fix(core): resolve Windows line ending and path separation bugs across CLI by
|
||||
@muhammadusman586 in
|
||||
[#21068](https://github.com/google-gemini/gemini-cli/pull/21068)
|
||||
- docs: fix heading formatting in commands.md and phrasing in tools-api.md by
|
||||
@campox747 in [#20679](https://github.com/google-gemini/gemini-cli/pull/20679)
|
||||
- refactor(ui): unify keybinding infrastructure and support string
|
||||
initialization by @scidomino in
|
||||
[#21776](https://github.com/google-gemini/gemini-cli/pull/21776)
|
||||
- Add support for updating extension sources and names by @chrstnb in
|
||||
[#21715](https://github.com/google-gemini/gemini-cli/pull/21715)
|
||||
- fix(core): handle GUI editor non-zero exit codes gracefully by @reyyanxahmed
|
||||
in [#20376](https://github.com/google-gemini/gemini-cli/pull/20376)
|
||||
- fix(core): destroy PTY on kill() and exception to prevent fd leak by @nbardy
|
||||
in [#21693](https://github.com/google-gemini/gemini-cli/pull/21693)
|
||||
- fix(docs): update theme screenshots and add missing themes by @ashmod in
|
||||
[#20689](https://github.com/google-gemini/gemini-cli/pull/20689)
|
||||
- refactor(cli): rename 'return' key to 'enter' internally by @scidomino in
|
||||
[#21796](https://github.com/google-gemini/gemini-cli/pull/21796)
|
||||
- build(release): restrict npm bundling to non-stable tags by @sehoon38 in
|
||||
[#21821](https://github.com/google-gemini/gemini-cli/pull/21821)
|
||||
- fix(core): override toolRegistry property for sub-agent schedulers by
|
||||
@gsquared94 in
|
||||
[#21766](https://github.com/google-gemini/gemini-cli/pull/21766)
|
||||
- fix(cli): make footer items equally spaced by @jacob314 in
|
||||
[#21843](https://github.com/google-gemini/gemini-cli/pull/21843)
|
||||
- docs: clarify global policy rules application in plan mode by @jerop in
|
||||
[#21864](https://github.com/google-gemini/gemini-cli/pull/21864)
|
||||
- fix(core): ensure correct flash model steering in plan mode implementation
|
||||
phase by @jerop in
|
||||
[#21871](https://github.com/google-gemini/gemini-cli/pull/21871)
|
||||
- fix(core): update @a2a-js/sdk to 0.3.11 by @adamfweidman in
|
||||
[#21875](https://github.com/google-gemini/gemini-cli/pull/21875)
|
||||
- refactor(core): improve API response error logging when retry by @yunaseoul in
|
||||
[#21784](https://github.com/google-gemini/gemini-cli/pull/21784)
|
||||
- fix(ui): handle headless execution in credits and upgrade dialogs by
|
||||
@gsquared94 in
|
||||
[#21850](https://github.com/google-gemini/gemini-cli/pull/21850)
|
||||
- fix(core): treat retryable errors with >5 min delay as terminal quota errors
|
||||
by @gsquared94 in
|
||||
[#21881](https://github.com/google-gemini/gemini-cli/pull/21881)
|
||||
- feat(telemetry): add specific PR, issue, and custom tracking IDs for GitHub
|
||||
Actions by @cocosheng-g in
|
||||
[#21129](https://github.com/google-gemini/gemini-cli/pull/21129)
|
||||
- feat(core): add OAuth2 Authorization Code auth provider for A2A agents by
|
||||
@SandyTao520 in
|
||||
[#21496](https://github.com/google-gemini/gemini-cli/pull/21496)
|
||||
- feat(cli): give visibility to /tools list command in the TUI and follow the
|
||||
subcommand pattern of other commands by @JayadityaGit in
|
||||
[#21213](https://github.com/google-gemini/gemini-cli/pull/21213)
|
||||
- Handle dirty worktrees better and warn about running scripts/review.sh on
|
||||
untrusted code. by @jacob314 in
|
||||
[#21791](https://github.com/google-gemini/gemini-cli/pull/21791)
|
||||
- feat(policy): support auto-add to policy by default and scoped persistence by
|
||||
@spencer426 in
|
||||
[#20361](https://github.com/google-gemini/gemini-cli/pull/20361)
|
||||
- fix(core): handle AbortError when ESC cancels tool execution by @PrasannaPal21
|
||||
in [#20863](https://github.com/google-gemini/gemini-cli/pull/20863)
|
||||
- fix(release): Improve Patch Release Workflow Comments: Clearer Approval
|
||||
Guidance by @jerop in
|
||||
[#21894](https://github.com/google-gemini/gemini-cli/pull/21894)
|
||||
- docs: clarify telemetry setup and comprehensive data map by @jerop in
|
||||
[#21879](https://github.com/google-gemini/gemini-cli/pull/21879)
|
||||
- feat(core): add per-model token usage to stream-json output by @yongruilin in
|
||||
[#21839](https://github.com/google-gemini/gemini-cli/pull/21839)
|
||||
- docs: remove experimental badge from plan mode in sidebar by @jerop in
|
||||
[#21906](https://github.com/google-gemini/gemini-cli/pull/21906)
|
||||
- fix(cli): prevent race condition in loop detection retry by @skyvanguard in
|
||||
[#17916](https://github.com/google-gemini/gemini-cli/pull/17916)
|
||||
- Add behavioral evals for tracker by @anj-s in
|
||||
[#20069](https://github.com/google-gemini/gemini-cli/pull/20069)
|
||||
- fix(auth): update terminology to 'sign in' and 'sign out' by @clocky in
|
||||
[#20892](https://github.com/google-gemini/gemini-cli/pull/20892)
|
||||
- docs(mcp): standardize mcp tool fqn documentation by @abhipatel12 in
|
||||
[#21664](https://github.com/google-gemini/gemini-cli/pull/21664)
|
||||
- fix(ui): prevent empty tool-group border stubs after filtering by @Aaxhirrr in
|
||||
[#21852](https://github.com/google-gemini/gemini-cli/pull/21852)
|
||||
- make command names consistent by @scidomino in
|
||||
[#21907](https://github.com/google-gemini/gemini-cli/pull/21907)
|
||||
- refactor: remove agent_card_requires_auth config flag by @adamfweidman in
|
||||
[#21914](https://github.com/google-gemini/gemini-cli/pull/21914)
|
||||
- feat(a2a): implement standardized normalization and streaming reassembly by
|
||||
@alisa-alisa in
|
||||
[#21402](https://github.com/google-gemini/gemini-cli/pull/21402)
|
||||
- feat(cli): enable skill activation via slash commands by @NTaylorMullen in
|
||||
[#21758](https://github.com/google-gemini/gemini-cli/pull/21758)
|
||||
- docs(cli): mention per-model token usage in stream-json result event by
|
||||
@yongruilin in
|
||||
[#21908](https://github.com/google-gemini/gemini-cli/pull/21908)
|
||||
- fix(plan): prevent plan truncation in approval dialog by supporting
|
||||
unconstrained heights by @Adib234 in
|
||||
[#21037](https://github.com/google-gemini/gemini-cli/pull/21037)
|
||||
- feat(a2a): switch from callback-based to event-driven tool scheduler by
|
||||
@cocosheng-g in
|
||||
[#21467](https://github.com/google-gemini/gemini-cli/pull/21467)
|
||||
- feat(voice): implement speech-friendly response formatter by @ayush31010 in
|
||||
[#20989](https://github.com/google-gemini/gemini-cli/pull/20989)
|
||||
- feat: add pulsating blue border automation overlay to browser agent by
|
||||
@kunal-10-cloud in
|
||||
[#21173](https://github.com/google-gemini/gemini-cli/pull/21173)
|
||||
- Add extensionRegistryURI setting to change where the registry is read from by
|
||||
@kevinjwang1 in
|
||||
[#20463](https://github.com/google-gemini/gemini-cli/pull/20463)
|
||||
- fix: patch gaxios v7 Array.toString() stream corruption by @gsquared94 in
|
||||
[#21884](https://github.com/google-gemini/gemini-cli/pull/21884)
|
||||
- fix: prevent hangs in non-interactive mode and improve agent guidance by
|
||||
@cocosheng-g in
|
||||
[#20893](https://github.com/google-gemini/gemini-cli/pull/20893)
|
||||
- Add ExtensionDetails dialog and support install by @chrstnb in
|
||||
[#20845](https://github.com/google-gemini/gemini-cli/pull/20845)
|
||||
- chore/release: bump version to 0.34.0-nightly.20260310.4653b126f by
|
||||
[#20853](https://github.com/google-gemini/gemini-cli/pull/20853)
|
||||
- feat(plan): support annotating plans with feedback for iteration by @Adib234
|
||||
in [#20876](https://github.com/google-gemini/gemini-cli/pull/20876)
|
||||
- Add some dos and don'ts to behavioral evals README. by @gundermanc in
|
||||
[#20629](https://github.com/google-gemini/gemini-cli/pull/20629)
|
||||
- fix(core): skip telemetry logging for AbortError exceptions by @yunaseoul in
|
||||
[#19477](https://github.com/google-gemini/gemini-cli/pull/19477)
|
||||
- fix(core): restrict "System: Please continue" invalid stream retry to Gemini 2
|
||||
models by @SandyTao520 in
|
||||
[#20897](https://github.com/google-gemini/gemini-cli/pull/20897)
|
||||
- ci(evals): only run evals in CI if prompts or tools changed by @gundermanc in
|
||||
[#20898](https://github.com/google-gemini/gemini-cli/pull/20898)
|
||||
- Build binary by @aswinashok44 in
|
||||
[#18933](https://github.com/google-gemini/gemini-cli/pull/18933)
|
||||
- Code review fixes as a pr by @jacob314 in
|
||||
[#20612](https://github.com/google-gemini/gemini-cli/pull/20612)
|
||||
- fix(ci): handle empty APP_ID in stale PR closer by @bdmorgan in
|
||||
[#20919](https://github.com/google-gemini/gemini-cli/pull/20919)
|
||||
- feat(cli): invert context window display to show usage by @keithguerin in
|
||||
[#20071](https://github.com/google-gemini/gemini-cli/pull/20071)
|
||||
- fix(plan): clean up session directories and plans on deletion by @jerop in
|
||||
[#20914](https://github.com/google-gemini/gemini-cli/pull/20914)
|
||||
- fix(core): enforce optionality for API response fields in code_assist by
|
||||
@sehoon38 in [#20714](https://github.com/google-gemini/gemini-cli/pull/20714)
|
||||
- feat(extensions): add support for plan directory in extension manifest by
|
||||
@mahimashanware in
|
||||
[#20354](https://github.com/google-gemini/gemini-cli/pull/20354)
|
||||
- feat(plan): enable built-in research subagents in plan mode by @Adib234 in
|
||||
[#20972](https://github.com/google-gemini/gemini-cli/pull/20972)
|
||||
- feat(agents): directly indicate auth required state by @adamfweidman in
|
||||
[#20986](https://github.com/google-gemini/gemini-cli/pull/20986)
|
||||
- fix(cli): wait for background auto-update before relaunching by @scidomino in
|
||||
[#20904](https://github.com/google-gemini/gemini-cli/pull/20904)
|
||||
- fix: pre-load @scripts/copy_files.js references from external editor prompts
|
||||
by @kartikangiras in
|
||||
[#20963](https://github.com/google-gemini/gemini-cli/pull/20963)
|
||||
- feat(evals): add behavioral evals for ask_user tool by @Adib234 in
|
||||
[#20620](https://github.com/google-gemini/gemini-cli/pull/20620)
|
||||
- refactor common settings logic for skills,agents by @ishaanxgupta in
|
||||
[#17490](https://github.com/google-gemini/gemini-cli/pull/17490)
|
||||
- Update docs-writer skill with new resource by @g-samroberts in
|
||||
[#20917](https://github.com/google-gemini/gemini-cli/pull/20917)
|
||||
- fix(cli): pin clipboardy to ~5.2.x by @scidomino in
|
||||
[#21009](https://github.com/google-gemini/gemini-cli/pull/21009)
|
||||
- feat: Implement slash command handling in ACP for
|
||||
`/memory`,`/init`,`/extensions` and `/restore` by @sripasg in
|
||||
[#20528](https://github.com/google-gemini/gemini-cli/pull/20528)
|
||||
- Docs/add hooks reference by @AadithyaAle in
|
||||
[#20961](https://github.com/google-gemini/gemini-cli/pull/20961)
|
||||
- feat(plan): add copy subcommand to plan (#20491) by @ruomengz in
|
||||
[#20988](https://github.com/google-gemini/gemini-cli/pull/20988)
|
||||
- fix(core): sanitize and length-check MCP tool qualified names by @abhipatel12
|
||||
in [#20987](https://github.com/google-gemini/gemini-cli/pull/20987)
|
||||
- Format the quota/limit style guide. by @g-samroberts in
|
||||
[#21017](https://github.com/google-gemini/gemini-cli/pull/21017)
|
||||
- fix(core): send shell output to model on cancel by @devr0306 in
|
||||
[#20501](https://github.com/google-gemini/gemini-cli/pull/20501)
|
||||
- remove hardcoded tiername when missing tier by @sehoon38 in
|
||||
[#21022](https://github.com/google-gemini/gemini-cli/pull/21022)
|
||||
- feat(acp): add set models interface by @skeshive in
|
||||
[#20991](https://github.com/google-gemini/gemini-cli/pull/20991)
|
||||
- fix(patch): cherry-pick 0659ad1 to release/v0.33.0-preview.0-pr-21042 to patch
|
||||
version v0.33.0-preview.0 and create version 0.33.0-preview.1 by
|
||||
@gemini-cli-robot in
|
||||
[#21816](https://github.com/google-gemini/gemini-cli/pull/21816)
|
||||
- Changelog for v0.33.0-preview.13 by @gemini-cli-robot in
|
||||
[#21927](https://github.com/google-gemini/gemini-cli/pull/21927)
|
||||
- fix(cli): stabilize prompt layout to prevent jumping when typing by
|
||||
@NTaylorMullen in
|
||||
[#21081](https://github.com/google-gemini/gemini-cli/pull/21081)
|
||||
- fix: preserve prompt text when cancelling streaming by @Nixxx19 in
|
||||
[#21103](https://github.com/google-gemini/gemini-cli/pull/21103)
|
||||
- fix: robust UX for remote agent errors by @Shyam-Raghuwanshi in
|
||||
[#20307](https://github.com/google-gemini/gemini-cli/pull/20307)
|
||||
- feat: implement background process logging and cleanup by @galz10 in
|
||||
[#21189](https://github.com/google-gemini/gemini-cli/pull/21189)
|
||||
- Changelog for v0.33.0-preview.14 by @gemini-cli-robot in
|
||||
[#21938](https://github.com/google-gemini/gemini-cli/pull/21938)
|
||||
- fix(patch): cherry-pick 45faf4d to release/v0.34.0-preview.0-pr-22148
|
||||
[#21047](https://github.com/google-gemini/gemini-cli/pull/21047)
|
||||
- fix(patch): cherry-pick 173376b to release/v0.33.0-preview.1-pr-21157 to patch
|
||||
version v0.33.0-preview.1 and create version 0.33.0-preview.2 by
|
||||
@gemini-cli-robot in
|
||||
[#21300](https://github.com/google-gemini/gemini-cli/pull/21300)
|
||||
- fix(patch): cherry-pick 0135b03 to release/v0.33.0-preview.2-pr-21171
|
||||
[CONFLICTS] by @gemini-cli-robot in
|
||||
[#22174](https://github.com/google-gemini/gemini-cli/pull/22174)
|
||||
- fix(patch): cherry-pick 8432bce to release/v0.34.0-preview.1-pr-22069 to patch
|
||||
version v0.34.0-preview.1 and create version 0.34.0-preview.2 by
|
||||
[#21336](https://github.com/google-gemini/gemini-cli/pull/21336)
|
||||
- fix(patch): cherry-pick 7ec477d to release/v0.33.0-preview.3-pr-21305 to patch
|
||||
version v0.33.0-preview.3 and create version 0.33.0-preview.4 by
|
||||
@gemini-cli-robot in
|
||||
[#22205](https://github.com/google-gemini/gemini-cli/pull/22205)
|
||||
- fix(patch): cherry-pick 24adacd to release/v0.34.0-preview.2-pr-22332 to patch
|
||||
version v0.34.0-preview.2 and create version 0.34.0-preview.3 by
|
||||
[#21349](https://github.com/google-gemini/gemini-cli/pull/21349)
|
||||
- fix(patch): cherry-pick 931e668 to release/v0.33.0-preview.4-pr-21425
|
||||
[CONFLICTS] by @gemini-cli-robot in
|
||||
[#21478](https://github.com/google-gemini/gemini-cli/pull/21478)
|
||||
- fix(patch): cherry-pick 7837194 to release/v0.33.0-preview.5-pr-21487 to patch
|
||||
version v0.33.0-preview.5 and create version 0.33.0-preview.6 by
|
||||
@gemini-cli-robot in
|
||||
[#22391](https://github.com/google-gemini/gemini-cli/pull/22391)
|
||||
- fix(patch): cherry-pick 48130eb to release/v0.34.0-preview.3-pr-22665 to patch
|
||||
version v0.34.0-preview.3 and create version 0.34.0-preview.4 by
|
||||
[#21720](https://github.com/google-gemini/gemini-cli/pull/21720)
|
||||
- fix(patch): cherry-pick 4f4431e to release/v0.33.0-preview.7-pr-21750 to patch
|
||||
version v0.33.0-preview.7 and create version 0.33.0-preview.8 by
|
||||
@gemini-cli-robot in
|
||||
[#22719](https://github.com/google-gemini/gemini-cli/pull/22719)
|
||||
[#21782](https://github.com/google-gemini/gemini-cli/pull/21782)
|
||||
- fix(patch): cherry-pick 9a74271 to release/v0.33.0-preview.8-pr-21236
|
||||
[CONFLICTS] by @gemini-cli-robot in
|
||||
[#21788](https://github.com/google-gemini/gemini-cli/pull/21788)
|
||||
- fix(patch): cherry-pick 936f624 to release/v0.33.0-preview.9-pr-21702 to patch
|
||||
version v0.33.0-preview.9 and create version 0.33.0-preview.10 by
|
||||
@gemini-cli-robot in
|
||||
[#21800](https://github.com/google-gemini/gemini-cli/pull/21800)
|
||||
- fix(patch): cherry-pick 35ee2a8 to release/v0.33.0-preview.10-pr-21713 by
|
||||
@gemini-cli-robot in
|
||||
[#21859](https://github.com/google-gemini/gemini-cli/pull/21859)
|
||||
- fix(patch): cherry-pick 5dd2dab to release/v0.33.0-preview.11-pr-21871 by
|
||||
@gemini-cli-robot in
|
||||
[#21876](https://github.com/google-gemini/gemini-cli/pull/21876)
|
||||
- fix(patch): cherry-pick e5615f4 to release/v0.33.0-preview.12-pr-21037 to
|
||||
patch version v0.33.0-preview.12 and create version 0.33.0-preview.13 by
|
||||
@gemini-cli-robot in
|
||||
[#21922](https://github.com/google-gemini/gemini-cli/pull/21922)
|
||||
- fix(patch): cherry-pick 1b69637 to release/v0.33.0-preview.13-pr-21467
|
||||
[CONFLICTS] by @gemini-cli-robot in
|
||||
[#21930](https://github.com/google-gemini/gemini-cli/pull/21930)
|
||||
- fix(patch): cherry-pick 3ff68a9 to release/v0.33.0-preview.14-pr-21884
|
||||
[CONFLICTS] by @gemini-cli-robot in
|
||||
[#21952](https://github.com/google-gemini/gemini-cli/pull/21952)
|
||||
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.33.2...v0.34.0
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.32.1...v0.33.2
|
||||
|
||||
+459
-356
@@ -1,6 +1,6 @@
|
||||
# Preview release: v0.35.0-preview.2
|
||||
# Preview release: v0.34.0-preview.4
|
||||
|
||||
Released: March 19, 2026
|
||||
Released: March 16, 2026
|
||||
|
||||
Our preview release includes the latest, new, and experimental features. This
|
||||
release may not be as stable as our [latest weekly release](latest.md).
|
||||
@@ -13,368 +13,471 @@ npm install -g @google/gemini-cli@preview
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Subagents & Architecture Enhancements**: Enabled subagents and laid the
|
||||
foundation for subagent tool isolation. Added proxy routing support for remote
|
||||
A2A subagents and integrated `SandboxManager` to sandbox all process-spawning
|
||||
tools.
|
||||
- **CLI & UI Improvements**: Introduced customizable keyboard shortcuts and
|
||||
support for literal character keybindings. Added missing vim mode motions and
|
||||
CJK input support. Enabled code splitting and deferred UI loading for improved
|
||||
performance.
|
||||
- **Context & Tools Optimization**: JIT context loading is now enabled by
|
||||
default with deduplication for project memory. Introduced a model-driven
|
||||
parallel tool scheduler and allowed safe tools to execute concurrently.
|
||||
- **Security & Extensions**: Implemented cryptographic integrity verification
|
||||
for extension updates and added a `disableAlwaysAllow` setting to prevent
|
||||
auto-approvals for enhanced security.
|
||||
- **Plan Mode & Web Fetch Updates**: Added an 'All the above' option for
|
||||
multi-select AskUser questions in Plan Mode. Rolled out Stage 1 and Stage 2
|
||||
security and consistency improvements for the `web_fetch` tool.
|
||||
- **Plan Mode Enabled by Default:** Plan Mode is now enabled out-of-the-box,
|
||||
providing a structured planning workflow and keeping approved plans during
|
||||
chat compression.
|
||||
- **Sandboxing Enhancements:** Added experimental LXC container sandbox support
|
||||
and native gVisor (`runsc`) sandboxing for improved security and isolation.
|
||||
- **Tracker Visualization and Tools:** Introduced CRUD tools and visualization
|
||||
for trackers, along with task tracker strategy improvements.
|
||||
- **Browser Agent Improvements:** Enhanced the browser agent with progress
|
||||
emission, a new automation overlay, and additional integration tests.
|
||||
- **CLI and UI Updates:** Standardized semantic focus colors, polished shell
|
||||
autocomplete rendering, unified keybinding infrastructure, and added custom
|
||||
footer configuration options.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- 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
|
||||
- fix(patch): cherry-pick 48130eb to release/v0.34.0-preview.3-pr-22665 to patch
|
||||
version v0.34.0-preview.3 and create version 0.34.0-preview.4 by
|
||||
@gemini-cli-robot in
|
||||
[#23134](https://github.com/google-gemini/gemini-cli/pull/23134)
|
||||
- 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
|
||||
[#22719](https://github.com/google-gemini/gemini-cli/pull/22719)
|
||||
- fix(patch): cherry-pick 24adacd to release/v0.34.0-preview.2-pr-22332 to patch
|
||||
version v0.34.0-preview.2 and create version 0.34.0-preview.3 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 handleFallback 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
|
||||
[#22391](https://github.com/google-gemini/gemini-cli/pull/22391)
|
||||
- fix(patch): cherry-pick 8432bce to release/v0.34.0-preview.1-pr-22069 to patch
|
||||
version v0.34.0-preview.1 and create version 0.34.0-preview.2 by
|
||||
@gemini-cli-robot in
|
||||
[#22205](https://github.com/google-gemini/gemini-cli/pull/22205)
|
||||
- fix(patch): cherry-pick 45faf4d to release/v0.34.0-preview.0-pr-22148
|
||||
[CONFLICTS] by @gemini-cli-robot in
|
||||
[#22174](https://github.com/google-gemini/gemini-cli/pull/22174)
|
||||
- feat(cli): add chat resume footer on session quit by @lordshashank in
|
||||
[#20667](https://github.com/google-gemini/gemini-cli/pull/20667)
|
||||
- Support bold and other styles in svg snapshots by @jacob314 in
|
||||
[#20937](https://github.com/google-gemini/gemini-cli/pull/20937)
|
||||
- fix(core): increase A2A agent timeout to 30 minutes by @adamfweidman in
|
||||
[#21028](https://github.com/google-gemini/gemini-cli/pull/21028)
|
||||
- Cleanup old branches. by @jacob314 in
|
||||
[#19354](https://github.com/google-gemini/gemini-cli/pull/19354)
|
||||
- chore(release): bump version to 0.34.0-nightly.20260303.34f0c1538 by
|
||||
@gemini-cli-robot in
|
||||
[#21034](https://github.com/google-gemini/gemini-cli/pull/21034)
|
||||
- feat(ui): standardize semantic focus colors and enhance history visibility by
|
||||
@keithguerin in
|
||||
[#20745](https://github.com/google-gemini/gemini-cli/pull/20745)
|
||||
- fix: merge duplicate imports in packages/core (3/4) by @Nixxx19 in
|
||||
[#20928](https://github.com/google-gemini/gemini-cli/pull/20928)
|
||||
- Add extra safety checks for proto pollution by @jacob314 in
|
||||
[#20396](https://github.com/google-gemini/gemini-cli/pull/20396)
|
||||
- feat(core): Add tracker CRUD tools & visualization by @anj-s in
|
||||
[#19489](https://github.com/google-gemini/gemini-cli/pull/19489)
|
||||
- Revert "fix(ui): persist expansion in AskUser dialog when navigating options"
|
||||
by @jacob314 in
|
||||
[#21042](https://github.com/google-gemini/gemini-cli/pull/21042)
|
||||
- Changelog for v0.33.0-preview.0 by @gemini-cli-robot in
|
||||
[#21030](https://github.com/google-gemini/gemini-cli/pull/21030)
|
||||
- fix: model persistence for all scenarios by @sripasg in
|
||||
[#21051](https://github.com/google-gemini/gemini-cli/pull/21051)
|
||||
- chore/release: bump version to 0.34.0-nightly.20260304.28af4e127 by
|
||||
@gemini-cli-robot in
|
||||
[#21054](https://github.com/google-gemini/gemini-cli/pull/21054)
|
||||
- Consistently guard restarts against concurrent auto updates by @scidomino in
|
||||
[#21016](https://github.com/google-gemini/gemini-cli/pull/21016)
|
||||
- Defensive coding to reduce the risk of Maximum update depth errors by
|
||||
@jacob314 in [#20940](https://github.com/google-gemini/gemini-cli/pull/20940)
|
||||
- fix(cli): Polish shell autocomplete rendering to be a little more shell native
|
||||
feeling. by @jacob314 in
|
||||
[#20931](https://github.com/google-gemini/gemini-cli/pull/20931)
|
||||
- Docs: Update plan mode docs by @jkcinouye in
|
||||
[#19682](https://github.com/google-gemini/gemini-cli/pull/19682)
|
||||
- fix(mcp): Notifications/tools/list_changed support not working by @jacob314 in
|
||||
[#21050](https://github.com/google-gemini/gemini-cli/pull/21050)
|
||||
- fix(cli): register extension lifecycle events in DebugProfiler by
|
||||
@fayerman-source in
|
||||
[#20101](https://github.com/google-gemini/gemini-cli/pull/20101)
|
||||
- chore(dev): update vscode settings for typescriptreact by @rohit-4321 in
|
||||
[#19907](https://github.com/google-gemini/gemini-cli/pull/19907)
|
||||
- fix(cli): enable multi-arch docker builds for sandbox by @ru-aish in
|
||||
[#19821](https://github.com/google-gemini/gemini-cli/pull/19821)
|
||||
- Changelog for v0.32.0 by @gemini-cli-robot in
|
||||
[#21033](https://github.com/google-gemini/gemini-cli/pull/21033)
|
||||
- Changelog for v0.33.0-preview.1 by @gemini-cli-robot in
|
||||
[#21058](https://github.com/google-gemini/gemini-cli/pull/21058)
|
||||
- feat(core): improve @scripts/copy_files.js autocomplete to prioritize
|
||||
filenames by @sehoon38 in
|
||||
[#21064](https://github.com/google-gemini/gemini-cli/pull/21064)
|
||||
- feat(sandbox): add experimental LXC container sandbox support by @h30s in
|
||||
[#20735](https://github.com/google-gemini/gemini-cli/pull/20735)
|
||||
- feat(evals): add overall pass rate row to eval nightly summary table by
|
||||
@gundermanc in
|
||||
[#20905](https://github.com/google-gemini/gemini-cli/pull/20905)
|
||||
- feat(telemetry): include language in telemetry and fix accepted lines
|
||||
computation by @gundermanc in
|
||||
[#21126](https://github.com/google-gemini/gemini-cli/pull/21126)
|
||||
- Changelog for v0.32.1 by @gemini-cli-robot in
|
||||
[#21055](https://github.com/google-gemini/gemini-cli/pull/21055)
|
||||
- feat(core): add robustness tests, logging, and metrics for CodeAssistServer
|
||||
SSE parsing by @yunaseoul in
|
||||
[#21013](https://github.com/google-gemini/gemini-cli/pull/21013)
|
||||
- feat: add issue assignee workflow by @kartikangiras in
|
||||
[#21003](https://github.com/google-gemini/gemini-cli/pull/21003)
|
||||
- fix: improve error message when OAuth succeeds but project ID is required by
|
||||
@Nixxx19 in [#21070](https://github.com/google-gemini/gemini-cli/pull/21070)
|
||||
- feat(loop-reduction): implement iterative loop detection and model feedback by
|
||||
@aishaneeshah in
|
||||
[#20763](https://github.com/google-gemini/gemini-cli/pull/20763)
|
||||
- chore(github): require prompt approvers for agent prompt files by @gundermanc
|
||||
in [#20896](https://github.com/google-gemini/gemini-cli/pull/20896)
|
||||
- Docs: Create tools reference by @jkcinouye in
|
||||
[#19470](https://github.com/google-gemini/gemini-cli/pull/19470)
|
||||
- fix(core, a2a-server): prevent hang during OAuth in non-interactive sessions
|
||||
by @spencer426 in
|
||||
[#21045](https://github.com/google-gemini/gemini-cli/pull/21045)
|
||||
- chore(cli): enable deprecated settings removal by default by @yashodipmore in
|
||||
[#20682](https://github.com/google-gemini/gemini-cli/pull/20682)
|
||||
- feat(core): Disable fast ack helper for hints. by @joshualitt in
|
||||
[#21011](https://github.com/google-gemini/gemini-cli/pull/21011)
|
||||
- fix(ui): suppress redundant failure note when tool error note is shown by
|
||||
@NTaylorMullen in
|
||||
[#21078](https://github.com/google-gemini/gemini-cli/pull/21078)
|
||||
- docs: document planning workflows with Conductor example by @jerop in
|
||||
[#21166](https://github.com/google-gemini/gemini-cli/pull/21166)
|
||||
- feat(release): ship esbuild bundle in npm package by @genneth in
|
||||
[#19171](https://github.com/google-gemini/gemini-cli/pull/19171)
|
||||
- fix(extensions): preserve symlinks in extension source path while enforcing
|
||||
folder trust by @galz10 in
|
||||
[#20867](https://github.com/google-gemini/gemini-cli/pull/20867)
|
||||
- fix(cli): defer tool exclusions to policy engine in non-interactive mode by
|
||||
@EricRahm in [#20639](https://github.com/google-gemini/gemini-cli/pull/20639)
|
||||
- fix(ui): removed double padding on rendered content by @devr0306 in
|
||||
[#21029](https://github.com/google-gemini/gemini-cli/pull/21029)
|
||||
- fix(core): truncate excessively long lines in grep search output by
|
||||
@gundermanc in
|
||||
[#21147](https://github.com/google-gemini/gemini-cli/pull/21147)
|
||||
- feat: add custom footer configuration via `/footer` by @jackwotherspoon in
|
||||
[#19001](https://github.com/google-gemini/gemini-cli/pull/19001)
|
||||
- perf(core): fix OOM crash in long-running sessions by @WizardsForgeGames in
|
||||
[#19608](https://github.com/google-gemini/gemini-cli/pull/19608)
|
||||
- refactor(cli): categorize built-in themes into dark/ and light/ directories by
|
||||
@JayadityaGit in
|
||||
[#18634](https://github.com/google-gemini/gemini-cli/pull/18634)
|
||||
- fix(core): explicitly allow codebase_investigator and cli_help in read-only
|
||||
mode by @Adib234 in
|
||||
[#21157](https://github.com/google-gemini/gemini-cli/pull/21157)
|
||||
- test: add browser agent integration tests by @kunal-10-cloud in
|
||||
[#21151](https://github.com/google-gemini/gemini-cli/pull/21151)
|
||||
- fix(cli): fix enabling kitty codes on Windows Terminal by @scidomino in
|
||||
[#21136](https://github.com/google-gemini/gemini-cli/pull/21136)
|
||||
- refactor(core): extract shared OAuth flow primitives from MCPOAuthProvider by
|
||||
@SandyTao520 in
|
||||
[#20895](https://github.com/google-gemini/gemini-cli/pull/20895)
|
||||
- fix(ui): add partial output to cancelled shell UI by @devr0306 in
|
||||
[#21178](https://github.com/google-gemini/gemini-cli/pull/21178)
|
||||
- fix(cli): replace hardcoded keybinding strings with dynamic formatters by
|
||||
@scidomino in [#21159](https://github.com/google-gemini/gemini-cli/pull/21159)
|
||||
- DOCS: Update quota and pricing page by @g-samroberts in
|
||||
[#21194](https://github.com/google-gemini/gemini-cli/pull/21194)
|
||||
- feat(telemetry): implement Clearcut logging for startup statistics by
|
||||
@yunaseoul in [#21172](https://github.com/google-gemini/gemini-cli/pull/21172)
|
||||
- feat(triage): add area/documentation to issue triage by @g-samroberts in
|
||||
[#21222](https://github.com/google-gemini/gemini-cli/pull/21222)
|
||||
- Fix so shell calls are formatted by @jacob314 in
|
||||
[#21237](https://github.com/google-gemini/gemini-cli/pull/21237)
|
||||
- feat(cli): add native gVisor (runsc) sandboxing support by @Zheyuan-Lin in
|
||||
[#21062](https://github.com/google-gemini/gemini-cli/pull/21062)
|
||||
- docs: use absolute paths for internal links in plan-mode.md by @jerop in
|
||||
[#21299](https://github.com/google-gemini/gemini-cli/pull/21299)
|
||||
- fix(core): prevent unhandled AbortError crash during stream loop detection by
|
||||
@7hokerz in [#21123](https://github.com/google-gemini/gemini-cli/pull/21123)
|
||||
- fix:reorder env var redaction checks to scan values first by @kartikangiras in
|
||||
[#21059](https://github.com/google-gemini/gemini-cli/pull/21059)
|
||||
- fix(acp): rename --experimental-acp to --acp & remove Zed-specific refrences
|
||||
by @skeshive in
|
||||
[#21171](https://github.com/google-gemini/gemini-cli/pull/21171)
|
||||
- feat(core): fallback to 2.5 models with no access for toolcalls by @sehoon38
|
||||
in [#21283](https://github.com/google-gemini/gemini-cli/pull/21283)
|
||||
- test(core): improve testing for API request/response parsing by @sehoon38 in
|
||||
[#21227](https://github.com/google-gemini/gemini-cli/pull/21227)
|
||||
- docs(links): update docs-writer skill and fix broken link by @g-samroberts in
|
||||
[#21314](https://github.com/google-gemini/gemini-cli/pull/21314)
|
||||
- Fix code colorizer ansi escape bug. by @jacob314 in
|
||||
[#21321](https://github.com/google-gemini/gemini-cli/pull/21321)
|
||||
- remove wildcard behavior on keybindings by @scidomino in
|
||||
[#21315](https://github.com/google-gemini/gemini-cli/pull/21315)
|
||||
- feat(acp): Add support for AI Gateway auth by @skeshive in
|
||||
[#21305](https://github.com/google-gemini/gemini-cli/pull/21305)
|
||||
- fix(theme): improve theme color contrast for macOS Terminal.app by @clocky in
|
||||
[#21175](https://github.com/google-gemini/gemini-cli/pull/21175)
|
||||
- feat (core): Implement tracker related SI changes by @anj-s in
|
||||
[#19964](https://github.com/google-gemini/gemini-cli/pull/19964)
|
||||
- Changelog for v0.33.0-preview.2 by @gemini-cli-robot in
|
||||
[#21333](https://github.com/google-gemini/gemini-cli/pull/21333)
|
||||
- Changelog for v0.33.0-preview.3 by @gemini-cli-robot in
|
||||
[#21347](https://github.com/google-gemini/gemini-cli/pull/21347)
|
||||
- docs: format release times as HH:MM UTC by @pavan-sh in
|
||||
[#20726](https://github.com/google-gemini/gemini-cli/pull/20726)
|
||||
- fix(cli): implement --all flag for extensions uninstall by @sehoon38 in
|
||||
[#21319](https://github.com/google-gemini/gemini-cli/pull/21319)
|
||||
- docs: fix incorrect relative links to command reference by @kanywst in
|
||||
[#20964](https://github.com/google-gemini/gemini-cli/pull/20964)
|
||||
- documentiong ensures ripgrep by @Jatin24062005 in
|
||||
[#21298](https://github.com/google-gemini/gemini-cli/pull/21298)
|
||||
- fix(core): handle AbortError thrown during processTurn by @MumuTW in
|
||||
[#21296](https://github.com/google-gemini/gemini-cli/pull/21296)
|
||||
- docs(cli): clarify ! command output visibility in shell commands tutorial by
|
||||
@MohammedADev in
|
||||
[#21041](https://github.com/google-gemini/gemini-cli/pull/21041)
|
||||
- fix: logic for task tracker strategy and remove tracker tools by @anj-s in
|
||||
[#21355](https://github.com/google-gemini/gemini-cli/pull/21355)
|
||||
- fix(partUtils): display media type and size for inline data parts by @Aboudjem
|
||||
in [#21358](https://github.com/google-gemini/gemini-cli/pull/21358)
|
||||
- Fix(accessibility): add screen reader support to RewindViewer by @Famous077 in
|
||||
[#20750](https://github.com/google-gemini/gemini-cli/pull/20750)
|
||||
- fix(hooks): propagate stopHookActive in AfterAgent retry path (#20426) by
|
||||
@Aarchi-07 in [#20439](https://github.com/google-gemini/gemini-cli/pull/20439)
|
||||
- fix(core): deduplicate GEMINI.md files by device/inode on case-insensitive
|
||||
filesystems (#19904) by @Nixxx19 in
|
||||
[#19915](https://github.com/google-gemini/gemini-cli/pull/19915)
|
||||
- feat(core): add concurrency safety guidance for subagent delegation (#17753)
|
||||
by @abhipatel12 in
|
||||
[#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
|
||||
[#21278](https://github.com/google-gemini/gemini-cli/pull/21278)
|
||||
- feat(ui): dynamically generate all keybinding hints by @scidomino in
|
||||
[#21346](https://github.com/google-gemini/gemini-cli/pull/21346)
|
||||
- feat(core): implement unified KeychainService and migrate token storage by
|
||||
@ehedlund in [#21344](https://github.com/google-gemini/gemini-cli/pull/21344)
|
||||
- fix(cli): gracefully handle --resume when no sessions exist by @SandyTao520 in
|
||||
[#21429](https://github.com/google-gemini/gemini-cli/pull/21429)
|
||||
- fix(plan): keep approved plan during chat compression by @ruomengz in
|
||||
[#21284](https://github.com/google-gemini/gemini-cli/pull/21284)
|
||||
- feat(core): implement generic CacheService and optimize setupUser by @sehoon38
|
||||
in [#21374](https://github.com/google-gemini/gemini-cli/pull/21374)
|
||||
- Update quota and pricing documentation with subscription tiers by @srithreepo
|
||||
in [#21351](https://github.com/google-gemini/gemini-cli/pull/21351)
|
||||
- fix(core): append correct OTLP paths for HTTP exporters by
|
||||
@sebastien-prudhomme in
|
||||
[#16836](https://github.com/google-gemini/gemini-cli/pull/16836)
|
||||
- Changelog for v0.33.0-preview.4 by @gemini-cli-robot in
|
||||
[#21354](https://github.com/google-gemini/gemini-cli/pull/21354)
|
||||
- feat(cli): implement dot-prefixing for slash command conflicts by @ehedlund in
|
||||
[#20979](https://github.com/google-gemini/gemini-cli/pull/20979)
|
||||
- refactor(core): standardize MCP tool naming to mcp\_ FQN format by
|
||||
@abhipatel12 in
|
||||
[#21425](https://github.com/google-gemini/gemini-cli/pull/21425)
|
||||
- feat(cli): hide gemma settings from display and mark as experimental by
|
||||
@abhipatel12 in
|
||||
[#21471](https://github.com/google-gemini/gemini-cli/pull/21471)
|
||||
- feat(skills): refine string-reviewer guidelines and description by @clocky in
|
||||
[#20368](https://github.com/google-gemini/gemini-cli/pull/20368)
|
||||
- fix(core): whitelist TERM and COLORTERM in environment sanitization by
|
||||
@deadsmash07 in
|
||||
[#20514](https://github.com/google-gemini/gemini-cli/pull/20514)
|
||||
- fix(billing): fix overage strategy lifecycle and settings integration by
|
||||
@gsquared94 in
|
||||
[#21236](https://github.com/google-gemini/gemini-cli/pull/21236)
|
||||
- fix: expand paste placeholders in TextInput on submit by @Jefftree in
|
||||
[#19946](https://github.com/google-gemini/gemini-cli/pull/19946)
|
||||
- fix(core): add in-memory cache to ChatRecordingService to prevent OOM by
|
||||
@SandyTao520 in
|
||||
[#21502](https://github.com/google-gemini/gemini-cli/pull/21502)
|
||||
- feat(cli): overhaul thinking UI by @keithguerin in
|
||||
[#18725](https://github.com/google-gemini/gemini-cli/pull/18725)
|
||||
- fix(ui): unify Ctrl+O expansion hint experience across buffer modes by
|
||||
@jwhelangoog in
|
||||
[#21474](https://github.com/google-gemini/gemini-cli/pull/21474)
|
||||
- fix(cli): correct shell height reporting by @jacob314 in
|
||||
[#21492](https://github.com/google-gemini/gemini-cli/pull/21492)
|
||||
- Make test suite pass when the GEMINI_SYSTEM_MD env variable or
|
||||
GEMINI_WRITE_SYSTEM_MD variable happens to be set locally/ by @jacob314 in
|
||||
[#21480](https://github.com/google-gemini/gemini-cli/pull/21480)
|
||||
- Disallow underspecified types by @gundermanc in
|
||||
[#21485](https://github.com/google-gemini/gemini-cli/pull/21485)
|
||||
- refactor(cli): standardize on 'reload' verb for all components by @keithguerin
|
||||
in [#20654](https://github.com/google-gemini/gemini-cli/pull/20654)
|
||||
- feat(cli): Invert quota language to 'percent used' by @keithguerin in
|
||||
[#20100](https://github.com/google-gemini/gemini-cli/pull/20100)
|
||||
- Docs: Add documentation for notifications (experimental)(macOS) by @jkcinouye
|
||||
in [#21163](https://github.com/google-gemini/gemini-cli/pull/21163)
|
||||
- Code review comments as a pr by @jacob314 in
|
||||
[#21209](https://github.com/google-gemini/gemini-cli/pull/21209)
|
||||
- feat(cli): unify /chat and /resume command UX by @LyalinDotCom in
|
||||
[#20256](https://github.com/google-gemini/gemini-cli/pull/20256)
|
||||
- docs: fix typo 'allowslisted' -> 'allowlisted' in mcp-server.md by
|
||||
@Gyanranjan-Priyam in
|
||||
[#21665](https://github.com/google-gemini/gemini-cli/pull/21665)
|
||||
- fix(core): display actual graph output in tracker_visualize tool by @anj-s in
|
||||
[#21455](https://github.com/google-gemini/gemini-cli/pull/21455)
|
||||
- fix(core): sanitize SSE-corrupted JSON and domain strings in error
|
||||
classification by @gsquared94 in
|
||||
[#21702](https://github.com/google-gemini/gemini-cli/pull/21702)
|
||||
- Docs: Make documentation links relative by @diodesign in
|
||||
[#21490](https://github.com/google-gemini/gemini-cli/pull/21490)
|
||||
- feat(cli): expose /tools desc as explicit subcommand for discoverability by
|
||||
@aworki in [#21241](https://github.com/google-gemini/gemini-cli/pull/21241)
|
||||
- feat(cli): add /compact alias for /compress command by @jackwotherspoon in
|
||||
[#21711](https://github.com/google-gemini/gemini-cli/pull/21711)
|
||||
- feat(plan): enable Plan Mode by default by @jerop in
|
||||
[#21713](https://github.com/google-gemini/gemini-cli/pull/21713)
|
||||
- feat(core): Introduce `AgentLoopContext`. by @joshualitt in
|
||||
[#21198](https://github.com/google-gemini/gemini-cli/pull/21198)
|
||||
- fix(core): resolve symlinks for non-existent paths during validation by
|
||||
@Adib234 in [#21487](https://github.com/google-gemini/gemini-cli/pull/21487)
|
||||
- docs: document tool exclusion from memory via deny policy by @Abhijit-2592 in
|
||||
[#21428](https://github.com/google-gemini/gemini-cli/pull/21428)
|
||||
- perf(core): cache loadApiKey to reduce redundant keychain access by @sehoon38
|
||||
in [#21520](https://github.com/google-gemini/gemini-cli/pull/21520)
|
||||
- feat(cli): implement /upgrade command by @sehoon38 in
|
||||
[#21511](https://github.com/google-gemini/gemini-cli/pull/21511)
|
||||
- Feat/browser agent progress emission by @kunal-10-cloud in
|
||||
[#21218](https://github.com/google-gemini/gemini-cli/pull/21218)
|
||||
- fix(settings): display objects as JSON instead of [object Object] by
|
||||
@Zheyuan-Lin in
|
||||
[#21458](https://github.com/google-gemini/gemini-cli/pull/21458)
|
||||
- Unmarshall update by @DavidAPierce in
|
||||
[#21721](https://github.com/google-gemini/gemini-cli/pull/21721)
|
||||
- Update mcp's list function to check for disablement. by @DavidAPierce in
|
||||
[#21148](https://github.com/google-gemini/gemini-cli/pull/21148)
|
||||
- robustness(core): static checks to validate history is immutable by @jacob314
|
||||
in [#21228](https://github.com/google-gemini/gemini-cli/pull/21228)
|
||||
- refactor(cli): better react patterns for BaseSettingsDialog by @psinha40898 in
|
||||
[#21206](https://github.com/google-gemini/gemini-cli/pull/21206)
|
||||
- feat(security): implement robust IP validation and safeFetch foundation by
|
||||
@alisa-alisa in
|
||||
[#21401](https://github.com/google-gemini/gemini-cli/pull/21401)
|
||||
- feat(core): improve subagent result display by @joshualitt in
|
||||
[#20378](https://github.com/google-gemini/gemini-cli/pull/20378)
|
||||
- docs: fix broken markdown syntax and anchor links in /tools by @campox747 in
|
||||
[#20902](https://github.com/google-gemini/gemini-cli/pull/20902)
|
||||
- feat(policy): support subagent-specific policies in TOML by @akh64bit in
|
||||
[#21431](https://github.com/google-gemini/gemini-cli/pull/21431)
|
||||
- Add script to speed up reviewing PRs adding a worktree. by @jacob314 in
|
||||
[#21748](https://github.com/google-gemini/gemini-cli/pull/21748)
|
||||
- fix(core): prevent infinite recursion in symlink resolution by @Adib234 in
|
||||
[#21750](https://github.com/google-gemini/gemini-cli/pull/21750)
|
||||
- fix(docs): fix headless mode docs by @ame2en in
|
||||
[#21287](https://github.com/google-gemini/gemini-cli/pull/21287)
|
||||
- feat/redesign header compact by @jacob314 in
|
||||
[#20922](https://github.com/google-gemini/gemini-cli/pull/20922)
|
||||
- refactor: migrate to useKeyMatchers hook by @scidomino in
|
||||
[#21753](https://github.com/google-gemini/gemini-cli/pull/21753)
|
||||
- perf(cli): cache loadSettings to reduce redundant disk I/O at startup by
|
||||
@sehoon38 in [#21521](https://github.com/google-gemini/gemini-cli/pull/21521)
|
||||
- fix(core): resolve Windows line ending and path separation bugs across CLI by
|
||||
@muhammadusman586 in
|
||||
[#21068](https://github.com/google-gemini/gemini-cli/pull/21068)
|
||||
- docs: fix heading formatting in commands.md and phrasing in tools-api.md by
|
||||
@campox747 in [#20679](https://github.com/google-gemini/gemini-cli/pull/20679)
|
||||
- refactor(ui): unify keybinding infrastructure and support string
|
||||
initialization by @scidomino in
|
||||
[#21776](https://github.com/google-gemini/gemini-cli/pull/21776)
|
||||
- Add support for updating extension sources and names by @chrstnb in
|
||||
[#21715](https://github.com/google-gemini/gemini-cli/pull/21715)
|
||||
- fix(core): handle GUI editor non-zero exit codes gracefully by @reyyanxahmed
|
||||
in [#20376](https://github.com/google-gemini/gemini-cli/pull/20376)
|
||||
- fix(core): destroy PTY on kill() and exception to prevent fd leak by @nbardy
|
||||
in [#21693](https://github.com/google-gemini/gemini-cli/pull/21693)
|
||||
- fix(docs): update theme screenshots and add missing themes by @ashmod in
|
||||
[#20689](https://github.com/google-gemini/gemini-cli/pull/20689)
|
||||
- refactor(cli): rename 'return' key to 'enter' internally by @scidomino in
|
||||
[#21796](https://github.com/google-gemini/gemini-cli/pull/21796)
|
||||
- build(release): restrict npm bundling to non-stable tags by @sehoon38 in
|
||||
[#21821](https://github.com/google-gemini/gemini-cli/pull/21821)
|
||||
- fix(core): override toolRegistry property for sub-agent schedulers by
|
||||
@gsquared94 in
|
||||
[#21766](https://github.com/google-gemini/gemini-cli/pull/21766)
|
||||
- fix(cli): make footer items equally spaced by @jacob314 in
|
||||
[#21843](https://github.com/google-gemini/gemini-cli/pull/21843)
|
||||
- docs: clarify global policy rules application in plan mode by @jerop in
|
||||
[#21864](https://github.com/google-gemini/gemini-cli/pull/21864)
|
||||
- fix(core): ensure correct flash model steering in plan mode implementation
|
||||
phase by @jerop in
|
||||
[#21871](https://github.com/google-gemini/gemini-cli/pull/21871)
|
||||
- fix(core): update @a2a-js/sdk to 0.3.11 by @adamfweidman in
|
||||
[#21875](https://github.com/google-gemini/gemini-cli/pull/21875)
|
||||
- refactor(core): improve API response error logging when retry by @yunaseoul in
|
||||
[#21784](https://github.com/google-gemini/gemini-cli/pull/21784)
|
||||
- fix(ui): handle headless execution in credits and upgrade dialogs by
|
||||
@gsquared94 in
|
||||
[#21850](https://github.com/google-gemini/gemini-cli/pull/21850)
|
||||
- fix(core): treat retryable errors with >5 min delay as terminal quota errors
|
||||
by @gsquared94 in
|
||||
[#21881](https://github.com/google-gemini/gemini-cli/pull/21881)
|
||||
- feat(telemetry): add specific PR, issue, and custom tracking IDs for GitHub
|
||||
Actions by @cocosheng-g in
|
||||
[#21129](https://github.com/google-gemini/gemini-cli/pull/21129)
|
||||
- feat(core): add OAuth2 Authorization Code auth provider for A2A agents by
|
||||
@SandyTao520 in
|
||||
[#21496](https://github.com/google-gemini/gemini-cli/pull/21496)
|
||||
- feat(cli): give visibility to /tools list command in the TUI and follow the
|
||||
subcommand pattern of other commands by @JayadityaGit in
|
||||
[#21213](https://github.com/google-gemini/gemini-cli/pull/21213)
|
||||
- Handle dirty worktrees better and warn about running scripts/review.sh on
|
||||
untrusted code. by @jacob314 in
|
||||
[#21791](https://github.com/google-gemini/gemini-cli/pull/21791)
|
||||
- feat(policy): support auto-add to policy by default and scoped persistence by
|
||||
@spencer426 in
|
||||
[#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
|
||||
[#20361](https://github.com/google-gemini/gemini-cli/pull/20361)
|
||||
- fix(core): handle AbortError when ESC cancels tool execution by @PrasannaPal21
|
||||
in [#20863](https://github.com/google-gemini/gemini-cli/pull/20863)
|
||||
- fix(release): Improve Patch Release Workflow Comments: Clearer Approval
|
||||
Guidance by @jerop in
|
||||
[#21894](https://github.com/google-gemini/gemini-cli/pull/21894)
|
||||
- docs: clarify telemetry setup and comprehensive data map by @jerop in
|
||||
[#21879](https://github.com/google-gemini/gemini-cli/pull/21879)
|
||||
- feat(core): add per-model token usage to stream-json output by @yongruilin in
|
||||
[#21839](https://github.com/google-gemini/gemini-cli/pull/21839)
|
||||
- docs: remove experimental badge from plan mode in sidebar by @jerop in
|
||||
[#21906](https://github.com/google-gemini/gemini-cli/pull/21906)
|
||||
- fix(cli): prevent race condition in loop detection retry by @skyvanguard in
|
||||
[#17916](https://github.com/google-gemini/gemini-cli/pull/17916)
|
||||
- Add behavioral evals for tracker by @anj-s in
|
||||
[#20069](https://github.com/google-gemini/gemini-cli/pull/20069)
|
||||
- fix(auth): update terminology to 'sign in' and 'sign out' by @clocky in
|
||||
[#20892](https://github.com/google-gemini/gemini-cli/pull/20892)
|
||||
- docs(mcp): standardize mcp tool fqn documentation by @abhipatel12 in
|
||||
[#21664](https://github.com/google-gemini/gemini-cli/pull/21664)
|
||||
- fix(ui): prevent empty tool-group border stubs after filtering by @Aaxhirrr in
|
||||
[#21852](https://github.com/google-gemini/gemini-cli/pull/21852)
|
||||
- make command names consistent by @scidomino in
|
||||
[#21907](https://github.com/google-gemini/gemini-cli/pull/21907)
|
||||
- refactor: remove agent_card_requires_auth config flag by @adamfweidman in
|
||||
[#21914](https://github.com/google-gemini/gemini-cli/pull/21914)
|
||||
- feat(a2a): implement standardized normalization and streaming reassembly by
|
||||
@alisa-alisa in
|
||||
[#21402](https://github.com/google-gemini/gemini-cli/pull/21402)
|
||||
- feat(cli): enable skill activation via slash commands by @NTaylorMullen in
|
||||
[#21758](https://github.com/google-gemini/gemini-cli/pull/21758)
|
||||
- docs(cli): mention per-model token usage in stream-json result event by
|
||||
@yongruilin in
|
||||
[#21908](https://github.com/google-gemini/gemini-cli/pull/21908)
|
||||
- fix(plan): prevent plan truncation in approval dialog by supporting
|
||||
unconstrained heights by @Adib234 in
|
||||
[#21037](https://github.com/google-gemini/gemini-cli/pull/21037)
|
||||
- feat(a2a): switch from callback-based to event-driven tool scheduler by
|
||||
@cocosheng-g in
|
||||
[#21467](https://github.com/google-gemini/gemini-cli/pull/21467)
|
||||
- feat(voice): implement speech-friendly response formatter by @Solventerritory
|
||||
in [#20989](https://github.com/google-gemini/gemini-cli/pull/20989)
|
||||
- feat: add pulsating blue border automation overlay to browser agent by
|
||||
@kunal-10-cloud in
|
||||
[#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
|
||||
@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
|
||||
@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
|
||||
[#21173](https://github.com/google-gemini/gemini-cli/pull/21173)
|
||||
- Add extensionRegistryURI setting to change where the registry is read from by
|
||||
@kevinjwang1 in
|
||||
[#20463](https://github.com/google-gemini/gemini-cli/pull/20463)
|
||||
- fix: patch gaxios v7 Array.toString() stream corruption by @gsquared94 in
|
||||
[#21884](https://github.com/google-gemini/gemini-cli/pull/21884)
|
||||
- fix: prevent hangs in non-interactive mode and improve agent guidance by
|
||||
@cocosheng-g in
|
||||
[#20893](https://github.com/google-gemini/gemini-cli/pull/20893)
|
||||
- Add ExtensionDetails dialog and support install by @chrstnb in
|
||||
[#20845](https://github.com/google-gemini/gemini-cli/pull/20845)
|
||||
- chore/release: bump version to 0.34.0-nightly.20260310.4653b126f by
|
||||
@gemini-cli-robot in
|
||||
[#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
|
||||
@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
|
||||
@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
|
||||
@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
|
||||
@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
|
||||
@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)
|
||||
[#21816](https://github.com/google-gemini/gemini-cli/pull/21816)
|
||||
- Changelog for v0.33.0-preview.13 by @gemini-cli-robot in
|
||||
[#21927](https://github.com/google-gemini/gemini-cli/pull/21927)
|
||||
- fix(cli): stabilize prompt layout to prevent jumping when typing by
|
||||
@NTaylorMullen in
|
||||
[#21081](https://github.com/google-gemini/gemini-cli/pull/21081)
|
||||
- fix: preserve prompt text when cancelling streaming by @Nixxx19 in
|
||||
[#21103](https://github.com/google-gemini/gemini-cli/pull/21103)
|
||||
- fix: robust UX for remote agent errors by @Shyam-Raghuwanshi in
|
||||
[#20307](https://github.com/google-gemini/gemini-cli/pull/20307)
|
||||
- feat: implement background process logging and cleanup by @galz10 in
|
||||
[#21189](https://github.com/google-gemini/gemini-cli/pull/21189)
|
||||
- Changelog for v0.33.0-preview.14 by @gemini-cli-robot in
|
||||
[#21938](https://github.com/google-gemini/gemini-cli/pull/21938)
|
||||
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.34.0-preview.4...v0.35.0-preview.2
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.33.0-preview.15...v0.34.0-preview.4
|
||||
|
||||
@@ -39,9 +39,7 @@ file in your project's temporary directory, typically located at
|
||||
The Checkpointing feature is disabled by default. To enable it, you need to edit
|
||||
your `settings.json` file.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!CAUTION]
|
||||
> The `--checkpointing` command-line flag was removed in version
|
||||
> **Note:** The `--checkpointing` command-line flag was removed in version
|
||||
> 0.11.0. Checkpointing can now only be enabled through the `settings.json`
|
||||
> configuration file.
|
||||
|
||||
|
||||
@@ -30,9 +30,7 @@ separator (`/` or `\`) being converted to a colon (`:`).
|
||||
- A file at `<project>/.gemini/commands/git/commit.toml` becomes the namespaced
|
||||
command `/git:commit`.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> After creating or modifying `.toml` command files, run
|
||||
> [!TIP] After creating or modifying `.toml` command files, run
|
||||
> `/commands reload` to pick up your changes without restarting the CLI.
|
||||
|
||||
## TOML file format (v1)
|
||||
@@ -179,10 +177,10 @@ ensure that only intended commands can be run.
|
||||
automatically shell-escaped (see
|
||||
[Context-Aware Injection](#1-context-aware-injection-with-args) above).
|
||||
3. **Robust parsing:** The parser correctly handles complex shell commands that
|
||||
include nested braces, such as JSON payloads. The content inside `!{...}`
|
||||
must have balanced braces (`{` and `}`). If you need to execute a command
|
||||
containing unbalanced braces, consider wrapping it in an external script
|
||||
file and calling the script within the `!{...}` block.
|
||||
include nested braces, such as JSON payloads. **Note:** The content inside
|
||||
`!{...}` must have balanced braces (`{` and `}`). If you need to execute a
|
||||
command containing unbalanced braces, consider wrapping it in an external
|
||||
script file and calling the script within the `!{...}` block.
|
||||
4. **Security check and confirmation:** The CLI performs a security check on
|
||||
the final, resolved command (after arguments are escaped and substituted). A
|
||||
dialog will appear showing the exact command(s) to be executed.
|
||||
|
||||
+9
-15
@@ -5,11 +5,9 @@ and managing Gemini CLI in an enterprise environment. By leveraging system-level
|
||||
settings, administrators can enforce security policies, manage tool access, and
|
||||
ensure a consistent experience for all users.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> The patterns described in this document are intended to help
|
||||
> administrators create a more controlled and secure environment for using
|
||||
> Gemini CLI. However, they should not be considered a foolproof security
|
||||
> **A note on security:** The patterns described in this document are intended
|
||||
> to help administrators create a more controlled and secure environment for
|
||||
> using Gemini CLI. However, they should not be considered a foolproof security
|
||||
> boundary. A determined user with sufficient privileges on their local machine
|
||||
> may still be able to circumvent these configurations. These measures are
|
||||
> designed to prevent accidental misuse and enforce corporate policy in a
|
||||
@@ -282,12 +280,10 @@ environment to a blocklist.
|
||||
}
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Blocklisting with `excludeTools` is less secure than
|
||||
> allowlisting with `coreTools`, as it relies on blocking known-bad commands,
|
||||
> and clever users may find ways to bypass simple string-based blocks.
|
||||
> **Allowlisting is the recommended approach.**
|
||||
**Security note:** Blocklisting with `excludeTools` is less secure than
|
||||
allowlisting with `coreTools`, as it relies on blocking known-bad commands, and
|
||||
clever users may find ways to bypass simple string-based blocks. **Allowlisting
|
||||
is the recommended approach.**
|
||||
|
||||
### Disabling YOLO mode
|
||||
|
||||
@@ -498,10 +494,8 @@ other events. For more information, see the
|
||||
}
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Ensure that `logPrompts` is set to `false` in an enterprise setting to
|
||||
> avoid collecting potentially sensitive information from user prompts.
|
||||
**Note:** Ensure that `logPrompts` is set to `false` in an enterprise setting to
|
||||
avoid collecting potentially sensitive information from user prompts.
|
||||
|
||||
## Authentication
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ Model steering lets you provide real-time guidance and feedback to Gemini CLI
|
||||
while it is actively executing a task. This lets you correct course, add missing
|
||||
context, or skip unnecessary steps without having to stop and restart the agent.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development and
|
||||
> may need to be enabled under `/settings`.
|
||||
> **Note:** This is a preview feature under active development. Preview features
|
||||
> may only be available in the **Preview** channel or may need to be enabled
|
||||
> under `/settings`.
|
||||
|
||||
Model steering is particularly useful during complex [Plan Mode](./plan-mode.md)
|
||||
workflows or long-running subagent executions where you want to ensure the agent
|
||||
|
||||
+1
-3
@@ -5,9 +5,7 @@ used by Gemini CLI, giving you more control over your results. Use **Pro**
|
||||
models for complex tasks and reasoning, **Flash** models for high speed results,
|
||||
or the (recommended) **Auto** setting to choose the best model for your tasks.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> The `/model` command (and the `--model` flag) does not override the
|
||||
> **Note:** The `/model` command (and the `--model` flag) does not override the
|
||||
> model used by sub-agents. Consequently, even when using the `/model` flag you
|
||||
> may see other models used in your model usage reports.
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ Gemini CLI can send system notifications to alert you when a session completes
|
||||
or when it needs your attention, such as when it's waiting for you to approve a
|
||||
tool call.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development and
|
||||
> may need to be enabled under `/settings`.
|
||||
> **Note:** This is a preview feature currently under active development.
|
||||
> Preview features may be available on the **Preview** channel or may need to be
|
||||
> enabled under `/settings`.
|
||||
|
||||
Notifications are particularly useful when running long-running tasks or using
|
||||
[Plan Mode](./plan-mode.md), letting you switch to other windows while Gemini
|
||||
|
||||
+8
-28
@@ -35,17 +35,19 @@ To launch Gemini CLI in Plan Mode once:
|
||||
To start Plan Mode while using Gemini CLI:
|
||||
|
||||
- **Keyboard shortcut:** Press `Shift+Tab` to cycle through approval modes
|
||||
(`Default` -> `Auto-Edit` -> `Plan`). Plan Mode is automatically removed from
|
||||
the rotation when Gemini CLI is actively processing or showing confirmation
|
||||
dialogs.
|
||||
(`Default` -> `Auto-Edit` -> `Plan`).
|
||||
|
||||
> **Note:** Plan Mode is automatically removed from the rotation when Gemini
|
||||
> CLI is actively processing or showing confirmation dialogs.
|
||||
|
||||
- **Command:** Type `/plan` in the input box.
|
||||
|
||||
- **Natural Language:** Ask Gemini CLI to "start a plan for...". Gemini CLI
|
||||
calls the
|
||||
[`enter_plan_mode`](../tools/planning.md#1-enter_plan_mode-enterplanmode) tool
|
||||
to switch modes. This tool is not available when Gemini CLI is in
|
||||
[YOLO mode](../reference/configuration.md#command-line-arguments).
|
||||
to switch modes.
|
||||
> **Note:** This tool is not available when Gemini CLI is in
|
||||
> [YOLO mode](../reference/configuration.md#command-line-arguments).
|
||||
|
||||
## How to use Plan Mode
|
||||
|
||||
@@ -405,9 +407,7 @@ To build a custom planning workflow, you can use:
|
||||
[custom plan directories](#custom-plan-directory-and-policies) and
|
||||
[custom policies](#custom-policies).
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> Use [Conductor] as a reference when building your own custom
|
||||
> **Note:** Use [Conductor] as a reference when building your own custom
|
||||
> planning workflow.
|
||||
|
||||
By using Plan Mode as its execution environment, your custom methodology can
|
||||
@@ -460,26 +460,6 @@ Manual deletion also removes all associated artifacts:
|
||||
If you use a [custom plans directory](#custom-plan-directory-and-policies),
|
||||
those files are not automatically deleted and must be managed manually.
|
||||
|
||||
## Non-interactive execution
|
||||
|
||||
When running Gemini CLI in non-interactive environments (such as headless
|
||||
scripts or CI/CD pipelines), Plan Mode optimizes for automated workflows:
|
||||
|
||||
- **Automatic transitions:** The policy engine automatically approves the
|
||||
`enter_plan_mode` and `exit_plan_mode` tools without prompting for user
|
||||
confirmation.
|
||||
- **Automated implementation:** When exiting Plan Mode to execute the plan,
|
||||
Gemini CLI automatically switches to
|
||||
[YOLO mode](../reference/policy-engine.md#approval-modes) instead of the
|
||||
standard Default mode. This allows the CLI to execute the implementation steps
|
||||
automatically without hanging on interactive tool approvals.
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
gemini --approval-mode plan -p "Analyze telemetry and suggest improvements"
|
||||
```
|
||||
|
||||
[`plan.toml`]:
|
||||
https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/policy/policies/plan.toml
|
||||
[Conductor]: https://github.com/gemini-cli-extensions/conductor
|
||||
|
||||
+4
-24
@@ -50,25 +50,7 @@ Cross-platform sandboxing with complete process isolation.
|
||||
**Note**: Requires building the sandbox image locally or using a published image
|
||||
from your organization's registry.
|
||||
|
||||
### 3. Windows Native Sandbox (Windows only)
|
||||
|
||||
... **Troubleshooting and Side Effects:**
|
||||
|
||||
The Windows Native sandbox uses the `icacls` command to set a "Low Mandatory
|
||||
Level" on files and directories it needs to write to.
|
||||
|
||||
- **Persistence**: These integrity level changes are persistent on the
|
||||
filesystem. Even after the sandbox session ends, files created or modified by
|
||||
the sandbox will retain their "Low" integrity level.
|
||||
- **Manual Reset**: If you need to reset the integrity level of a file or
|
||||
directory, you can use:
|
||||
```powershell
|
||||
icacls "C:\path\to\dir" /setintegritylevel Medium
|
||||
```
|
||||
- **System Folders**: The sandbox manager automatically skips setting integrity
|
||||
levels on system folders (like `C:\Windows`) for safety.
|
||||
|
||||
### 4. gVisor / runsc (Linux only)
|
||||
### 3. gVisor / runsc (Linux only)
|
||||
|
||||
Strongest isolation available: runs containers inside a user-space kernel via
|
||||
[gVisor](https://github.com/google/gvisor). gVisor intercepts all container
|
||||
@@ -271,11 +253,9 @@ $env:SANDBOX_SET_UID_GID="false" # Disable UID/GID mapping
|
||||
DEBUG=1 gemini -s -p "debug command"
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> If you have `DEBUG=true` in a project's `.env` file, it won't affect
|
||||
> gemini-cli due to automatic exclusion. Use `.gemini/.env` files for
|
||||
> gemini-cli specific debug settings.
|
||||
**Note:** If you have `DEBUG=true` in a project's `.env` file, it won't affect
|
||||
gemini-cli due to automatic exclusion. Use `.gemini/.env` files for gemini-cli
|
||||
specific debug settings.
|
||||
|
||||
### Inspect sandbox
|
||||
|
||||
|
||||
@@ -11,9 +11,7 @@ locations:
|
||||
- **User settings**: `~/.gemini/settings.json`
|
||||
- **Workspace settings**: `your-project/.gemini/settings.json`
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!IMPORTANT]
|
||||
> Workspace settings override user settings.
|
||||
Note: Workspace settings override user settings.
|
||||
|
||||
## Settings reference
|
||||
|
||||
@@ -117,8 +115,6 @@ they appear in the UI.
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| -------------------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| 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` |
|
||||
| Use Ripgrep | `tools.useRipgrep` | Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance. | `true` |
|
||||
@@ -156,7 +152,6 @@ they appear in the UI.
|
||||
| Plan | `experimental.plan` | Enable Plan Mode. | `true` |
|
||||
| Model Steering | `experimental.modelSteering` | Enable model steering (user hints) to guide the model during tool execution. | `false` |
|
||||
| Direct Web Fetch | `experimental.directWebFetch` | Enable web fetch behavior that bypasses LLM summarization. | `false` |
|
||||
| Memory Manager Agent | `experimental.memoryManager` | Replace the built-in save_memory tool with a memory manager subagent that supports adding, removing, de-duplicating, and organizing memories. | `false` |
|
||||
| Topic & Update Narration | `experimental.topicUpdateNarration` | Enable the experimental Topic & Update communication model for reduced chattiness and structured progress reporting. | `false` |
|
||||
|
||||
### Skills
|
||||
|
||||
+2
-4
@@ -63,10 +63,8 @@ Use the `/skills` slash command to view and manage available expertise:
|
||||
- `/skills enable <name>`: Re-enables a disabled skill.
|
||||
- `/skills reload`: Refreshes the list of discovered skills from all tiers.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> `/skills disable` and `/skills enable` default to the `user` scope. Use
|
||||
> `--scope workspace` to manage workspace-specific settings.
|
||||
_Note: `/skills disable` and `/skills enable` default to the `user` scope. Use
|
||||
`--scope workspace` to manage workspace-specific settings._
|
||||
|
||||
### From the Terminal
|
||||
|
||||
|
||||
@@ -14,9 +14,7 @@ core instructions will apply unless you include them yourself.
|
||||
This feature is intended for advanced users who need to enforce strict,
|
||||
project-specific behavior or create a customized persona.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> You can export the current default system prompt to a file first, review
|
||||
> Tip: You can export the current default system prompt to a file first, review
|
||||
> it, and then selectively modify or replace it (see
|
||||
> [“Export the default prompt”](#export-the-default-prompt-recommended)).
|
||||
|
||||
|
||||
@@ -125,11 +125,9 @@ You must complete several setup steps before enabling Google Cloud telemetry.
|
||||
}
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This setting requires **Direct export** (in-process exporters)
|
||||
> and cannot be used when `useCollector` is `true`. If both are enabled,
|
||||
> telemetry will be disabled.
|
||||
> **Note:** This setting requires **Direct export** (in-process exporters)
|
||||
> and cannot be used when `useCollector` is `true`. If both are enabled,
|
||||
> telemetry will be disabled.
|
||||
|
||||
3. Ensure your account or service account has these IAM roles:
|
||||
- Cloud Trace Agent
|
||||
|
||||
+8
-12
@@ -36,11 +36,9 @@ using the `/theme` command within Gemini CLI:
|
||||
preview or highlight as you select.
|
||||
4. Confirm your selection to apply the theme.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> If a theme is defined in your `settings.json` file (either by name or
|
||||
> by a file path), you must remove the `"theme"` setting from the file before
|
||||
> you can change the theme using the `/theme` command.
|
||||
**Note:** If a theme is defined in your `settings.json` file (either by name or
|
||||
by a file path), you must remove the `"theme"` setting from the file before you
|
||||
can change the theme using the `/theme` command.
|
||||
|
||||
### Theme persistence
|
||||
|
||||
@@ -181,13 +179,11 @@ custom theme defined in `settings.json`.
|
||||
}
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> For your safety, Gemini CLI will only load theme files that
|
||||
> are located within your home directory. If you attempt to load a theme from
|
||||
> outside your home directory, a warning will be displayed and the theme will
|
||||
> not be loaded. This is to prevent loading potentially malicious theme files
|
||||
> from untrusted sources.
|
||||
**Security note:** For your safety, Gemini CLI will only load theme files that
|
||||
are located within your home directory. If you attempt to load a theme from
|
||||
outside your home directory, a warning will be displayed and the theme will not
|
||||
be loaded. This is to prevent loading potentially malicious theme files from
|
||||
untrusted sources.
|
||||
|
||||
### Example custom theme
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ create files, and control what Gemini CLI can see.
|
||||
## Prerequisites
|
||||
|
||||
- Gemini CLI installed and authenticated.
|
||||
- A project directory to work with (for example, a git repository).
|
||||
- A project directory to work with (e.g., a git repository).
|
||||
|
||||
## Providing context by reading files
|
||||
## How to give the agent context (Reading files)
|
||||
|
||||
Gemini CLI will generally try to read relevant files, sometimes prompting you
|
||||
for access (depending on your settings). To ensure that Gemini CLI uses a file,
|
||||
@@ -58,13 +58,11 @@ You know there's a `UserProfile` component, but you don't know where it lives.
|
||||
```
|
||||
|
||||
Gemini uses the `glob` or `list_directory` tools to search your project
|
||||
structure. It will return the specific path (for example,
|
||||
structure. It will return the specific path (e.g.,
|
||||
`src/components/UserProfile.tsx`), which you can then use with `@` in your next
|
||||
turn.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> You can also ask for lists of files, like "Show me all the TypeScript
|
||||
> **Tip:** You can also ask for lists of files, like "Show me all the TypeScript
|
||||
> configuration files in the root directory."
|
||||
|
||||
## How to modify code
|
||||
@@ -113,8 +111,8 @@ or, better yet, run your project's tests.
|
||||
`Run the tests for the UserProfile component.`
|
||||
```
|
||||
|
||||
Gemini CLI uses the `run_shell_command` tool to execute your test runner (for
|
||||
example, `npm test` or `jest`). This ensures the changes didn't break existing
|
||||
Gemini CLI uses the `run_shell_command` tool to execute your test runner (e.g.,
|
||||
`npm test` or `jest`). This ensures the changes didn't break existing
|
||||
functionality.
|
||||
|
||||
## Advanced: Controlling what Gemini sees
|
||||
|
||||
@@ -52,7 +52,7 @@ You tell Gemini about new servers by editing your `settings.json`.
|
||||
"--rm",
|
||||
"-e",
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN",
|
||||
"ghcr.io/github/github-mcp-server:latest"
|
||||
"ghcr.io/modelcontextprotocol/servers/github:latest"
|
||||
],
|
||||
"env": {
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PERSONAL_ACCESS_TOKEN}"
|
||||
@@ -62,10 +62,8 @@ You tell Gemini about new servers by editing your `settings.json`.
|
||||
}
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> The `command` is `docker`, and the rest are arguments passed to it. We
|
||||
> map the local environment variable into the container so your secret isn't
|
||||
> **Note:** The `command` is `docker`, and the rest are arguments passed to it.
|
||||
> We map the local environment variable into the container so your secret isn't
|
||||
> hardcoded in the config file.
|
||||
|
||||
## How to verify the connection
|
||||
|
||||
@@ -11,8 +11,8 @@ persistent facts, and inspect the active context.
|
||||
|
||||
## Why manage context?
|
||||
|
||||
Gemini CLI is powerful but general. It doesn't know your preferred testing
|
||||
framework, your indentation style, or your preference against `any` in
|
||||
Out of the box, Gemini CLI is smart but generic. It doesn't know your preferred
|
||||
testing framework, your indentation style, or that you hate using `any` in
|
||||
TypeScript. Context management solves this by giving the agent persistent
|
||||
memory.
|
||||
|
||||
@@ -109,11 +109,11 @@ immediately. Force a reload with:
|
||||
|
||||
## Best practices
|
||||
|
||||
- **Keep it focused:** Avoid adding excessive content to `GEMINI.md`. Keep
|
||||
instructions actionable and relevant to code generation.
|
||||
- **Keep it focused:** Don't dump your entire internal wiki into `GEMINI.md`.
|
||||
Keep instructions actionable and relevant to code generation.
|
||||
- **Use negative constraints:** Explicitly telling the agent what _not_ to do
|
||||
(for example, "Do not use class components") is often more effective than
|
||||
vague positive instructions.
|
||||
(e.g., "Do not use class components") is often more effective than vague
|
||||
positive instructions.
|
||||
- **Review often:** Periodically check your `GEMINI.md` files to remove outdated
|
||||
rules.
|
||||
|
||||
|
||||
@@ -5,10 +5,9 @@ structured environment with model steering's real-time feedback, you can guide
|
||||
Gemini CLI through the research and design phases to ensure the final
|
||||
implementation plan is exactly what you need.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development and
|
||||
> may need to be enabled under `/settings`.
|
||||
> **Note:** This is a preview feature under active development. Preview features
|
||||
> may only be available in the **Preview** channel or may need to be enabled
|
||||
> under `/settings`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ automate complex workflows, and manage background processes safely.
|
||||
## Prerequisites
|
||||
|
||||
- Gemini CLI installed and authenticated.
|
||||
- Basic familiarity with your system's shell (Bash, Zsh, PowerShell, and so on).
|
||||
- Basic familiarity with your system's shell (Bash, Zsh, PowerShell, etc.).
|
||||
|
||||
## How to run commands directly (`!`)
|
||||
|
||||
@@ -49,7 +49,7 @@ You want to run tests and fix any failures.
|
||||
6. Gemini uses `replace` to fix the bug.
|
||||
7. Gemini runs `npm test` again to verify the fix.
|
||||
|
||||
This loop lets Gemini work autonomously.
|
||||
This loop turns Gemini into an autonomous engineer.
|
||||
|
||||
## How to manage background processes
|
||||
|
||||
@@ -75,7 +75,7 @@ confirmation prompts) by streaming the output to you. However, for highly
|
||||
interactive tools (like `vim` or `top`), it's often better to run them yourself
|
||||
in a separate terminal window or use the `!` prefix.
|
||||
|
||||
## Safety features
|
||||
## Safety first
|
||||
|
||||
Giving an AI access to your shell is powerful but risky. Gemini CLI includes
|
||||
several safety layers.
|
||||
|
||||
@@ -10,9 +10,7 @@ agents in the following repositories:
|
||||
- [ADK Samples (Python)](https://github.com/google/adk-samples/tree/main/python)
|
||||
- [ADK Python Contributing Samples](https://github.com/google/adk-python/tree/main/contributing/samples)
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Remote subagents are currently an experimental feature.
|
||||
> **Note: Remote subagents are currently an experimental feature.**
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -84,8 +82,7 @@ Markdown file.
|
||||
---
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE] Mixed local and remote agents, or multiple local agents, are not
|
||||
> **Note:** Mixed local and remote agents, or multiple local agents, are not
|
||||
> supported in a single file; the list format is currently remote-only.
|
||||
|
||||
## Authentication
|
||||
@@ -365,7 +362,5 @@ Users can manage subagents using the following commands within the Gemini CLI:
|
||||
- `/agents enable <agent_name>`: Enables a specific subagent.
|
||||
- `/agents disable <agent_name>`: Disables a specific subagent.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> You can use the `@cli_help` agent within Gemini CLI for assistance
|
||||
> **Tip:** You can use the `@cli_help` agent within Gemini CLI for assistance
|
||||
> with configuring subagents.
|
||||
|
||||
+13
-21
@@ -5,18 +5,16 @@ session. They are designed to handle specific, complex tasks—like deep codebas
|
||||
analysis, documentation lookup, or domain-specific reasoning—without cluttering
|
||||
the main agent's context or toolset.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Subagents are currently an experimental feature.
|
||||
>
|
||||
To use custom subagents, you must ensure they are enabled in your
|
||||
`settings.json` (enabled by default):
|
||||
|
||||
```json
|
||||
{
|
||||
"experimental": { "enableAgents": true }
|
||||
}
|
||||
```
|
||||
> **Note: Subagents are currently an experimental feature.**
|
||||
>
|
||||
> To use custom subagents, you must ensure they are enabled in your
|
||||
> `settings.json` (enabled by default):
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "experimental": { "enableAgents": true }
|
||||
> }
|
||||
> ```
|
||||
|
||||
## What are subagents?
|
||||
|
||||
@@ -116,9 +114,7 @@ Gemini CLI comes with the following built-in subagents:
|
||||
the pricing table from this page," "Click the login button and enter my
|
||||
credentials."
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is a preview feature currently under active development.
|
||||
> **Note:** This is a preview feature currently under active development.
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
@@ -221,9 +217,7 @@ captures a screenshot and sends it to the vision model for analysis. The model
|
||||
returns coordinates and element descriptions that the browser agent uses with
|
||||
the `click_at` tool for precise, coordinate-based interactions.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> The visual agent requires API key or Vertex AI authentication. It is
|
||||
> **Note:** The visual agent requires API key or Vertex AI authentication. It is
|
||||
> not available when using "Sign in with Google".
|
||||
|
||||
## Creating custom subagents
|
||||
@@ -411,9 +405,7 @@ that your subagent was called with a specific prompt and the given description.
|
||||
Gemini CLI can also delegate tasks to remote subagents using the Agent-to-Agent
|
||||
(A2A) protocol.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Remote subagents are currently an experimental feature.
|
||||
> **Note: Remote subagents are currently an experimental feature.**
|
||||
|
||||
See the [Remote Subagents documentation](remote-agents) for detailed
|
||||
configuration, authentication, and usage instructions.
|
||||
|
||||
@@ -234,9 +234,7 @@ skill definitions in a `skills/` directory. For example,
|
||||
|
||||
### Sub-agents
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Sub-agents are a preview feature currently under active development.
|
||||
> **Note:** Sub-agents are a preview feature currently under active development.
|
||||
|
||||
Provide [sub-agents](../core/subagents.md) that users can delegate tasks to. Add
|
||||
agent definition files (`.md`) to an `agents/` directory in your extension root.
|
||||
@@ -255,9 +253,7 @@ Rules contributed by extensions run in their own tier (tier 2), alongside
|
||||
workspace-defined policies. This tier has higher priority than the default rules
|
||||
but lower priority than user or admin policies.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> For security, Gemini CLI ignores any `allow` decisions or `yolo`
|
||||
> **Warning:** For security, Gemini CLI ignores any `allow` decisions or `yolo`
|
||||
> mode configurations in extension policies. This ensures that an extension
|
||||
> cannot automatically approve tool calls or bypass security measures without
|
||||
> your confirmation.
|
||||
|
||||
@@ -4,9 +4,7 @@ To use Gemini CLI, you'll need to authenticate with Google. This guide helps you
|
||||
quickly find the best way to sign in based on your account type and how you're
|
||||
using the CLI.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> Looking for a high-level comparison of all available subscriptions?
|
||||
> **Note:** Looking for a high-level comparison of all available subscriptions?
|
||||
> To compare features and find the right quota for your needs, see our
|
||||
> [Plans page](https://geminicli.com/plans/).
|
||||
|
||||
@@ -42,11 +40,11 @@ Select the authentication method that matches your situation in the table below:
|
||||
|
||||
If you run Gemini CLI on your local machine, the simplest authentication method
|
||||
is logging in with your Google account. This method requires a web browser on a
|
||||
machine that can communicate with the terminal running Gemini CLI (for example,
|
||||
your local machine).
|
||||
machine that can communicate with the terminal running Gemini CLI (e.g., your
|
||||
local machine).
|
||||
|
||||
If you are a **Google AI Pro** or **Google AI Ultra** subscriber, use the Google
|
||||
account associated with your subscription.
|
||||
> **Important:** If you are a **Google AI Pro** or **Google AI Ultra**
|
||||
> subscriber, use the Google account associated with your subscription.
|
||||
|
||||
To authenticate and use Gemini CLI:
|
||||
|
||||
@@ -109,9 +107,7 @@ To authenticate and use Gemini CLI with a Gemini API key:
|
||||
|
||||
4. Select **Use Gemini API key**.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Treat API keys, especially for services like Gemini, as sensitive
|
||||
> **Warning:** Treat API keys, especially for services like Gemini, as sensitive
|
||||
> credentials. Protect them to prevent unauthorized access and potential misuse
|
||||
> of the service under your account.
|
||||
|
||||
@@ -134,7 +130,7 @@ For example:
|
||||
**macOS/Linux**
|
||||
|
||||
```bash
|
||||
# Replace with your project ID and desired location (for example, us-central1)
|
||||
# Replace with your project ID and desired location (e.g., us-central1)
|
||||
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"
|
||||
```
|
||||
@@ -142,7 +138,7 @@ export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"
|
||||
**Windows (PowerShell)**
|
||||
|
||||
```powershell
|
||||
# Replace with your project ID and desired location (for example, us-central1)
|
||||
# Replace with your project ID and desired location (e.g., us-central1)
|
||||
$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
$env:GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"
|
||||
```
|
||||
@@ -154,20 +150,20 @@ To make any Vertex AI environment variable settings persistent, see
|
||||
|
||||
Consider this authentication method if you have Google Cloud CLI installed.
|
||||
|
||||
If you have previously set `GOOGLE_API_KEY` or `GEMINI_API_KEY`, you must unset
|
||||
them to use ADC.
|
||||
|
||||
**macOS/Linux**
|
||||
|
||||
```bash
|
||||
unset GOOGLE_API_KEY GEMINI_API_KEY
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**
|
||||
|
||||
```powershell
|
||||
Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
```
|
||||
> **Note:** If you have previously set `GOOGLE_API_KEY` or `GEMINI_API_KEY`, you
|
||||
> must unset them to use ADC:
|
||||
>
|
||||
> **macOS/Linux**
|
||||
>
|
||||
> ```bash
|
||||
> unset GOOGLE_API_KEY GEMINI_API_KEY
|
||||
> ```
|
||||
>
|
||||
> **Windows (PowerShell)**
|
||||
>
|
||||
> ```powershell
|
||||
> Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
> ```
|
||||
|
||||
1. Verify you have a Google Cloud project and Vertex AI API is enabled.
|
||||
|
||||
@@ -192,20 +188,20 @@ Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
Consider this method of authentication in non-interactive environments, CI/CD
|
||||
pipelines, or if your organization restricts user-based ADC or API key creation.
|
||||
|
||||
If you have previously set `GOOGLE_API_KEY` or `GEMINI_API_KEY`, you must unset
|
||||
them:
|
||||
|
||||
**macOS/Linux**
|
||||
|
||||
```bash
|
||||
unset GOOGLE_API_KEY GEMINI_API_KEY
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**
|
||||
|
||||
```powershell
|
||||
Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
```
|
||||
> **Note:** If you have previously set `GOOGLE_API_KEY` or `GEMINI_API_KEY`, you
|
||||
> must unset them:
|
||||
>
|
||||
> **macOS/Linux**
|
||||
>
|
||||
> ```bash
|
||||
> unset GOOGLE_API_KEY GEMINI_API_KEY
|
||||
> ```
|
||||
>
|
||||
> **Windows (PowerShell)**
|
||||
>
|
||||
> ```powershell
|
||||
> Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
> ```
|
||||
|
||||
1. [Create a service account and key](https://cloud.google.com/iam/docs/keys-create-delete)
|
||||
and download the provided JSON file. Assign the "Vertex AI User" role to the
|
||||
@@ -237,11 +233,8 @@ Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
```
|
||||
|
||||
5. Select **Vertex AI**.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Protect your service account key file as it gives access to
|
||||
> your resources.
|
||||
> **Warning:** Protect your service account key file as it gives access to
|
||||
> your resources.
|
||||
|
||||
#### C. Vertex AI - Google Cloud API key
|
||||
|
||||
@@ -264,9 +257,10 @@ Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
$env:GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
|
||||
```
|
||||
|
||||
If you see errors like `"API keys are not supported by this API..."`, your
|
||||
organization might restrict API key usage for this service. Try the other
|
||||
Vertex AI authentication methods instead.
|
||||
> **Note:** If you see errors like
|
||||
> `"API keys are not supported by this API..."`, your organization might
|
||||
> restrict API key usage for this service. Try the other Vertex AI
|
||||
> authentication methods instead.
|
||||
|
||||
3. [Configure your Google Cloud Project](#set-gcp).
|
||||
|
||||
@@ -280,9 +274,7 @@ Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
|
||||
## Set your Google Cloud project <a id="set-gcp"></a>
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!IMPORTANT]
|
||||
> Most individual Google accounts (free and paid) don't require a
|
||||
> **Important:** Most individual Google accounts (free and paid) don't require a
|
||||
> Google Cloud project for authentication.
|
||||
|
||||
When you sign in using your Google account, you may need to configure a Google
|
||||
@@ -333,31 +325,29 @@ persist them with the following methods:
|
||||
1. **Add your environment variables to your shell configuration file:** Append
|
||||
the environment variable commands to your shell's startup file.
|
||||
|
||||
**macOS/Linux** (for example, `~/.bashrc`, `~/.zshrc`, or `~/.profile`):
|
||||
**macOS/Linux** (e.g., `~/.bashrc`, `~/.zshrc`, or `~/.profile`):
|
||||
|
||||
```bash
|
||||
echo 'export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
**Windows (PowerShell)** (for example, `$PROFILE`):
|
||||
**Windows (PowerShell)** (e.g., `$PROFILE`):
|
||||
|
||||
```powershell
|
||||
Add-Content -Path $PROFILE -Value '$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"'
|
||||
. $PROFILE
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Be aware that when you export API keys or service account
|
||||
> paths in your shell configuration file, any process launched from that
|
||||
> shell can read them.
|
||||
> **Warning:** Be aware that when you export API keys or service account
|
||||
> paths in your shell configuration file, any process launched from that
|
||||
> shell can read them.
|
||||
|
||||
2. **Use a `.env` file:** Create a `.gemini/.env` file in your project
|
||||
directory or home directory. Gemini CLI automatically loads variables from
|
||||
the first `.env` file it finds, searching up from the current directory,
|
||||
then in your home directory's `.gemini/.env` (for example, `~/.gemini/.env`
|
||||
or `%USERPROFILE%\.gemini\.env`).
|
||||
then in your home directory's `.gemini/.env` (e.g., `~/.gemini/.env` or
|
||||
`%USERPROFILE%\.gemini\.env`).
|
||||
|
||||
Example for user-wide settings:
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ Gemini CLI helps you automate common engineering tasks by combining AI reasoning
|
||||
with local system tools. This document provides examples of how to use the CLI
|
||||
for file management, code analysis, and data transformation.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> These examples demonstrate potential capabilities. Your actual
|
||||
> **Note:** These examples demonstrate potential capabilities. Your actual
|
||||
> results can vary based on the model used and your project environment.
|
||||
|
||||
## Rename your photographs based on content
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
Gemini 3 Pro and Gemini 3 Flash are available on Gemini CLI for all users!
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Gemini 3.1 Pro Preview is rolling out. To determine whether you have
|
||||
> **Note:** Gemini 3.1 Pro Preview is rolling out. To determine whether you have
|
||||
> access to Gemini 3.1, use the `/model` command and select **Manual**. If you
|
||||
> have access, you will see `gemini-3.1-pro-preview`.
|
||||
>
|
||||
@@ -27,7 +25,7 @@ Get started by upgrading Gemini CLI to the latest version:
|
||||
npm install -g @google/gemini-cli@latest
|
||||
```
|
||||
|
||||
If your version is 0.21.1 or later:
|
||||
After you’ve confirmed your version is 0.21.1 or later:
|
||||
|
||||
1. Run `/model`.
|
||||
2. Select **Auto (Gemini 3)**.
|
||||
@@ -41,9 +39,7 @@ When you encounter that limit, you’ll be given the option to switch to Gemini
|
||||
2.5 Pro, upgrade for higher limits, or stop. You’ll also be told when your usage
|
||||
limit resets and Gemini 3 Pro can be used again.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> Looking to upgrade for higher limits? To compare subscription
|
||||
> **Note:** Looking to upgrade for higher limits? To compare subscription
|
||||
> options and find the right quota for your needs, see our
|
||||
> [Plans page](https://geminicli.com/plans/).
|
||||
|
||||
@@ -56,9 +52,7 @@ There may be times when the Gemini 3 Pro model is overloaded. When that happens,
|
||||
Gemini CLI will ask you to decide whether you want to keep trying Gemini 3 Pro
|
||||
or fallback to Gemini 2.5 Pro.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> The **Keep trying** option uses exponential backoff, in which Gemini
|
||||
> **Note:** The **Keep trying** option uses exponential backoff, in which Gemini
|
||||
> CLI waits longer between each retry, when the system is busy. If the retry
|
||||
> doesn't happen immediately, please wait a few minutes for the request to
|
||||
> process.
|
||||
@@ -115,7 +109,7 @@ then:
|
||||
|
||||
Restart Gemini CLI and you should have access to Gemini 3.
|
||||
|
||||
## Next steps
|
||||
## Need help?
|
||||
|
||||
If you need help, we recommend searching for an existing
|
||||
[GitHub issue](https://github.com/google-gemini/gemini-cli/issues). If you
|
||||
|
||||
+1
-3
@@ -143,9 +143,7 @@ Hooks are executed with a sanitized environment.
|
||||
|
||||
## Security and risks
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Hooks execute arbitrary code with your user privileges. By
|
||||
> **Warning: Hooks execute arbitrary code with your user privileges.** By
|
||||
> configuring hooks, you are allowing scripts to run shell commands on your
|
||||
> machine.
|
||||
|
||||
|
||||
@@ -132,11 +132,9 @@ to the CLI whenever the user's context changes.
|
||||
}
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> The `openFiles` list should only include files that exist on disk.
|
||||
> Virtual files (e.g., unsaved files without a path, editor settings pages)
|
||||
> **MUST** be excluded.
|
||||
**Note:** The `openFiles` list should only include files that exist on disk.
|
||||
Virtual files (e.g., unsaved files without a path, editor settings pages)
|
||||
**MUST** be excluded.
|
||||
|
||||
### How the CLI uses this context
|
||||
|
||||
|
||||
@@ -66,11 +66,9 @@ You can also install the extension directly from a marketplace.
|
||||
Follow your editor's instructions for installing extensions from this
|
||||
registry.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> The "Gemini CLI Companion" extension may appear towards the bottom of
|
||||
> search results. If you don't see it immediately, try scrolling down or
|
||||
> sorting by "Newly Published".
|
||||
> NOTE: The "Gemini CLI Companion" extension may appear towards the bottom of
|
||||
> search results. If you don't see it immediately, try scrolling down or sorting
|
||||
> by "Newly Published".
|
||||
>
|
||||
> After manually installing the extension, you must run `/ide enable` in the CLI
|
||||
> to activate the integration.
|
||||
@@ -105,9 +103,7 @@ IDE, run:
|
||||
If connected, this command will show the IDE it's connected to and a list of
|
||||
recently opened files it is aware of.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> The file list is limited to 10 recently accessed files within your
|
||||
> [!NOTE] The file list is limited to 10 recently accessed files within your
|
||||
> workspace and only includes local files on disk.)
|
||||
|
||||
### Working with diffs
|
||||
|
||||
@@ -14,9 +14,7 @@ feature), while the PR is the "how" (the implementation). This separation helps
|
||||
us track work, prioritize features, and maintain clear historical context. Our
|
||||
automation is built around this principle.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Issues tagged as "🔒Maintainers only" are reserved for project
|
||||
> **Note:** Issues tagged as "🔒Maintainers only" are reserved for project
|
||||
> maintainers. We will not accept pull requests related to these issues.
|
||||
|
||||
---
|
||||
|
||||
@@ -79,9 +79,7 @@ You can view traces in the Jaeger UI for local development.
|
||||
You can use an OpenTelemetry collector to forward telemetry data to Google Cloud
|
||||
Trace for custom processing or routing.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Ensure you complete the
|
||||
> **Warning:** Ensure you complete the
|
||||
> [Google Cloud telemetry prerequisites](./cli/telemetry.md#prerequisites)
|
||||
> (Project ID, authentication, IAM roles, and APIs) before using this method.
|
||||
|
||||
|
||||
@@ -60,8 +60,8 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
- `list` (selecting this opens the auto-saved session browser)
|
||||
- `-- checkpoints --`
|
||||
- `list`, `save`, `resume`, `delete`, `share` (manual tagged checkpoints)
|
||||
- Unique prefixes (for example `/cha` or `/resu`) resolve to the same grouped
|
||||
menu.
|
||||
- **Note:** Unique prefixes (for example `/cha` or `/resum`) resolve to the
|
||||
same grouped menu.
|
||||
- **Sub-commands:**
|
||||
- **`debug`**
|
||||
- **Description:** Export the most recent API request as a JSON payload.
|
||||
|
||||
@@ -25,9 +25,7 @@ overridden by higher numbers):
|
||||
Gemini CLI uses JSON settings files for persistent configuration. There are four
|
||||
locations for these files:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> JSON-aware editors can use autocomplete and validation by pointing to
|
||||
> **Tip:** JSON-aware editors can use autocomplete and validation by pointing to
|
||||
> the generated schema at `schemas/settings.schema.json` in this repository.
|
||||
> When working outside the repo, reference the hosted schema at
|
||||
> `https://raw.githubusercontent.com/google-gemini/gemini-cli/main/schemas/settings.schema.json`.
|
||||
@@ -68,9 +66,9 @@ an environment variable `MY_API_TOKEN`, you could use it in `settings.json` like
|
||||
this: `"apiKey": "$MY_API_TOKEN"`. Additionally, each extension can have its own
|
||||
`.env` file in its directory, which will be loaded automatically.
|
||||
|
||||
**Note for Enterprise Users:** For guidance on deploying and managing Gemini CLI
|
||||
in a corporate environment, please see the
|
||||
[Enterprise Configuration](../cli/enterprise.md) documentation.
|
||||
> **Note for Enterprise Users:** For guidance on deploying and managing Gemini
|
||||
> CLI in a corporate environment, please see the
|
||||
> [Enterprise Configuration](../cli/enterprise.md) documentation.
|
||||
|
||||
### The `.gemini` directory in your project
|
||||
|
||||
@@ -686,16 +684,6 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"gemini-3.1-flash-lite-preview": {
|
||||
"tier": "flash-lite",
|
||||
"family": "gemini-3",
|
||||
"isPreview": true,
|
||||
"isVisible": true,
|
||||
"features": {
|
||||
"thinking": false,
|
||||
"multimodalToolUse": true
|
||||
}
|
||||
},
|
||||
"gemini-3.1-pro-preview": {
|
||||
"tier": "pro",
|
||||
"family": "gemini-3",
|
||||
@@ -807,7 +795,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"tier": "auto",
|
||||
"isPreview": true,
|
||||
"isVisible": true,
|
||||
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-3-pro, gemini-3-flash",
|
||||
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash",
|
||||
"features": {
|
||||
"thinking": true,
|
||||
"multimodalToolUse": false
|
||||
@@ -836,39 +824,6 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"gemini-3.1-pro-preview": {
|
||||
"default": "gemini-3.1-pro-preview",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gemini-3.1-pro-preview-customtools": {
|
||||
"default": "gemini-3.1-pro-preview-customtools",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gemini-3-flash-preview": {
|
||||
"default": "gemini-3-flash-preview",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-flash"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gemini-3-pro-preview": {
|
||||
"default": "gemini-3-pro-preview",
|
||||
"contexts": [
|
||||
@@ -1040,132 +995,6 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`modelConfigs.modelChains`** (object):
|
||||
- **Description:** Availability policy chains defining fallback behavior for
|
||||
models.
|
||||
- **Default:**
|
||||
|
||||
```json
|
||||
{
|
||||
"preview": [
|
||||
{
|
||||
"model": "gemini-3-pro-preview",
|
||||
"actions": {
|
||||
"terminal": "prompt",
|
||||
"transient": "prompt",
|
||||
"not_found": "prompt",
|
||||
"unknown": "prompt"
|
||||
},
|
||||
"stateTransitions": {
|
||||
"terminal": "terminal",
|
||||
"transient": "terminal",
|
||||
"not_found": "terminal",
|
||||
"unknown": "terminal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gemini-3-flash-preview",
|
||||
"isLastResort": true,
|
||||
"actions": {
|
||||
"terminal": "prompt",
|
||||
"transient": "prompt",
|
||||
"not_found": "prompt",
|
||||
"unknown": "prompt"
|
||||
},
|
||||
"stateTransitions": {
|
||||
"terminal": "terminal",
|
||||
"transient": "terminal",
|
||||
"not_found": "terminal",
|
||||
"unknown": "terminal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default": [
|
||||
{
|
||||
"model": "gemini-2.5-pro",
|
||||
"actions": {
|
||||
"terminal": "prompt",
|
||||
"transient": "prompt",
|
||||
"not_found": "prompt",
|
||||
"unknown": "prompt"
|
||||
},
|
||||
"stateTransitions": {
|
||||
"terminal": "terminal",
|
||||
"transient": "terminal",
|
||||
"not_found": "terminal",
|
||||
"unknown": "terminal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gemini-2.5-flash",
|
||||
"isLastResort": true,
|
||||
"actions": {
|
||||
"terminal": "prompt",
|
||||
"transient": "prompt",
|
||||
"not_found": "prompt",
|
||||
"unknown": "prompt"
|
||||
},
|
||||
"stateTransitions": {
|
||||
"terminal": "terminal",
|
||||
"transient": "terminal",
|
||||
"not_found": "terminal",
|
||||
"unknown": "terminal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"lite": [
|
||||
{
|
||||
"model": "gemini-2.5-flash-lite",
|
||||
"actions": {
|
||||
"terminal": "silent",
|
||||
"transient": "silent",
|
||||
"not_found": "silent",
|
||||
"unknown": "silent"
|
||||
},
|
||||
"stateTransitions": {
|
||||
"terminal": "terminal",
|
||||
"transient": "terminal",
|
||||
"not_found": "terminal",
|
||||
"unknown": "terminal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gemini-2.5-flash",
|
||||
"actions": {
|
||||
"terminal": "silent",
|
||||
"transient": "silent",
|
||||
"not_found": "silent",
|
||||
"unknown": "silent"
|
||||
},
|
||||
"stateTransitions": {
|
||||
"terminal": "terminal",
|
||||
"transient": "terminal",
|
||||
"not_found": "terminal",
|
||||
"unknown": "terminal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "gemini-2.5-pro",
|
||||
"isLastResort": true,
|
||||
"actions": {
|
||||
"terminal": "silent",
|
||||
"transient": "silent",
|
||||
"not_found": "silent",
|
||||
"unknown": "silent"
|
||||
},
|
||||
"stateTransitions": {
|
||||
"terminal": "terminal",
|
||||
"transient": "terminal",
|
||||
"not_found": "terminal",
|
||||
"unknown": "terminal"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **Requires restart:** Yes
|
||||
|
||||
#### `agents`
|
||||
|
||||
- **`agents.overrides`** (object):
|
||||
@@ -1276,21 +1105,10 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Description:** Legacy full-process sandbox execution environment. Set to a
|
||||
boolean to enable or disable the sandbox, provide a string path to a sandbox
|
||||
profile, or specify an explicit sandbox command (e.g., "docker", "podman",
|
||||
"lxc", "windows-native").
|
||||
"lxc").
|
||||
- **Default:** `undefined`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`tools.sandboxAllowedPaths`** (array):
|
||||
- **Description:** List of additional paths that the sandbox is allowed to
|
||||
access.
|
||||
- **Default:** `[]`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`tools.sandboxNetworkAccess`** (boolean):
|
||||
- **Description:** Whether the sandbox is allowed to access the network.
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`tools.shell.enableInteractiveShell`** (boolean):
|
||||
- **Description:** Use node-pty for an interactive shell experience. Fallback
|
||||
to child_process still applies.
|
||||
@@ -1613,13 +1431,6 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `"gemma3-1b-gpu-custom"`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.memoryManager`** (boolean):
|
||||
- **Description:** Replace the built-in save_memory tool with a memory manager
|
||||
subagent that supports adding, removing, de-duplicating, and organizing
|
||||
memories.
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.topicUpdateNarration`** (boolean):
|
||||
- **Description:** Enable the experimental Topic & Update communication model
|
||||
for reduced chattiness and structured progress reporting.
|
||||
@@ -1728,11 +1539,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `true`
|
||||
|
||||
- **`admin.mcp.config`** (object):
|
||||
- **Description:** Admin-configured MCP servers (allowlist).
|
||||
- **Default:** `{}`
|
||||
|
||||
- **`admin.mcp.requiredConfig`** (object):
|
||||
- **Description:** Admin-required MCP servers that are always injected.
|
||||
- **Description:** Admin-configured MCP servers.
|
||||
- **Default:** `{}`
|
||||
|
||||
- **`admin.skills.enabled`** (boolean):
|
||||
@@ -1752,9 +1559,7 @@ for compatibility. At least one of `command`, `url`, or `httpUrl` must be
|
||||
provided. If multiple are specified, the order of precedence is `httpUrl`, then
|
||||
`url`, then `command`.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Avoid using underscores (`_`) in your server aliases (e.g., use
|
||||
> **Warning:** Avoid using underscores (`_`) in your server aliases (e.g., use
|
||||
> `my-server` instead of `my_server`). The underlying policy engine parses Fully
|
||||
> Qualified Names (`mcp_server_tool`) using the first underscore after the
|
||||
> `mcp_` prefix. An underscore in your server alias will cause the parser to
|
||||
|
||||
@@ -90,17 +90,6 @@ If `argsPattern` is specified, the tool's arguments are converted to a stable
|
||||
JSON string, which is then tested against the provided regular expression. If
|
||||
the arguments don't match the pattern, the rule does not apply.
|
||||
|
||||
#### Execution environment
|
||||
|
||||
If `interactive` is specified, the rule will only apply if the CLI's execution
|
||||
environment matches the specified boolean value:
|
||||
|
||||
- `true`: The rule applies only in interactive mode.
|
||||
- `false`: The rule applies only in non-interactive (headless) mode.
|
||||
|
||||
If omitted, the rule applies to both interactive and non-interactive
|
||||
environments.
|
||||
|
||||
### Decisions
|
||||
|
||||
There are three possible decisions a rule can enforce:
|
||||
@@ -113,9 +102,7 @@ There are three possible decisions a rule can enforce:
|
||||
- `ask_user`: The user is prompted to approve or deny the tool call. (In
|
||||
non-interactive mode, this is treated as `deny`.)
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> The `deny` decision is the recommended way to exclude tools. The
|
||||
> **Note:** The `deny` decision is the recommended way to exclude tools. The
|
||||
> legacy `tools.exclude` setting in `settings.json` is deprecated in favor of
|
||||
> policy rules with a `deny` decision.
|
||||
|
||||
@@ -241,17 +228,15 @@ directory are **ignored**.
|
||||
- **Linux / macOS:** Must be owned by `root` (UID 0) and NOT writable by group
|
||||
or others (e.g., `chmod 755`).
|
||||
- **Windows:** Must be in `C:\ProgramData`. Standard users (`Users`, `Everyone`)
|
||||
must NOT have `Write`, `Modify`, or `Full Control` permissions. If you see a
|
||||
security warning, use the folder properties to remove write permissions for
|
||||
non-admin groups. You may need to "Disable inheritance" in Advanced Security
|
||||
Settings.
|
||||
must NOT have `Write`, `Modify`, or `Full Control` permissions. _Tip: If you
|
||||
see a security warning, use the folder properties to remove write permissions
|
||||
for non-admin groups. You may need to "Disable inheritance" in Advanced
|
||||
Security Settings._
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Supplemental admin policies (provided via `--admin-policy` or
|
||||
> `adminPolicyPaths` settings) are **NOT** subject to these strict ownership
|
||||
> checks, as they are explicitly provided by the user or administrator in their
|
||||
> current execution context.
|
||||
**Note:** Supplemental admin policies (provided via `--admin-policy` or
|
||||
`adminPolicyPaths` settings) are **NOT** subject to these strict ownership
|
||||
checks, as they are explicitly provided by the user or administrator in their
|
||||
current execution context.
|
||||
|
||||
### TOML rule schema
|
||||
|
||||
@@ -301,10 +286,6 @@ deny_message = "Deletion is permanent"
|
||||
|
||||
# (Optional) An array of approval modes where this rule is active.
|
||||
modes = ["autoEdit"]
|
||||
|
||||
# (Optional) A boolean to restrict the rule to interactive (true) or non-interactive (false) environments.
|
||||
# If omitted, the rule applies to both.
|
||||
interactive = true
|
||||
```
|
||||
|
||||
### Using arrays (lists)
|
||||
@@ -352,9 +333,7 @@ using the `mcpName` field. **This is the recommended approach** for defining MCP
|
||||
policies, as it is much more robust than manually writing Fully Qualified Names
|
||||
(FQNs) or string wildcards.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Do not use underscores (`_`) in your MCP server names (e.g., use
|
||||
> **Warning:** Do not use underscores (`_`) in your MCP server names (e.g., use
|
||||
> `my-server` rather than `my_server`). The policy parser splits Fully Qualified
|
||||
> Names (`mcp_server_tool`) on the _first_ underscore following the `mcp_`
|
||||
> prefix. If your server name contains an underscore, the parser will
|
||||
@@ -381,8 +360,6 @@ priority = 200
|
||||
Specify only the `mcpName` to apply a rule to every tool provided by that
|
||||
server.
|
||||
|
||||
**Note:** This applies to all decision types (`allow`, `deny`, `ask_user`).
|
||||
|
||||
```toml
|
||||
# Denies all tools from the `untrusted-server` MCP
|
||||
[[rule]]
|
||||
|
||||
@@ -95,9 +95,7 @@ For developers, the tool system is designed to be extensible and robust. The
|
||||
You can extend Gemini CLI with custom tools by configuring
|
||||
`tools.discoveryCommand` in your settings or by connecting to MCP servers.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> For a deep dive into the internal Tool API and how to implement your
|
||||
> **Note:** For a deep dive into the internal Tool API and how to implement your
|
||||
> own tools in the codebase, see the `packages/core/src/tools/` directory in
|
||||
> GitHub.
|
||||
|
||||
|
||||
@@ -21,13 +21,9 @@ All workflows in `.github/workflows/ci.yml` must pass on the `main` branch (for
|
||||
nightly) or the release branch (for preview/stable).
|
||||
|
||||
- **Platforms:** Tests must pass on **Linux and macOS**.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Windows tests currently run with `continue-on-error: true`. While a
|
||||
> failure here doesn't block the release technically, it should be
|
||||
> investigated.
|
||||
|
||||
- _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.
|
||||
|
||||
+7
-11
@@ -234,12 +234,10 @@ This workflow will automatically:
|
||||
Review the automatically created pull request(s) to ensure the cherry-pick was
|
||||
successful and the changes are correct. Once approved, merge the pull request.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> The `release/*` branches are protected by branch protection
|
||||
> rules. A pull request to one of these branches requires at least one review from
|
||||
> a code owner before it can be merged. This ensures that no unauthorized code is
|
||||
> released.
|
||||
**Security note:** The `release/*` branches are protected by branch protection
|
||||
rules. A pull request to one of these branches requires at least one review from
|
||||
a code owner before it can be merged. This ensures that no unauthorized code is
|
||||
released.
|
||||
|
||||
#### 2.5. Adding multiple commits to a hotfix (advanced)
|
||||
|
||||
@@ -526,11 +524,9 @@ Notifications use
|
||||
[GitHub for Google Chat](https://workspace.google.com/marketplace/app/github_for_google_chat/536184076190).
|
||||
To modify the notifications, use `/github-settings` within the chat space.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> The following instructions describe a fragile workaround that depends on the
|
||||
> internal structure of the chat application's UI. It is likely to break with
|
||||
> future updates.
|
||||
> [!WARNING] The following instructions describe a fragile workaround that
|
||||
> depends on the internal structure of the chat application's UI. It is likely
|
||||
> to break with future updates.
|
||||
|
||||
The list of available labels is not currently populated correctly. If you want
|
||||
to add a label that does not appear alphabetically in the first 30 labels in the
|
||||
|
||||
@@ -58,19 +58,6 @@ your total token usage using the `/stats` command in Gemini CLI.
|
||||
|
||||
## Installation and updates
|
||||
|
||||
### How do I check which version of Gemini CLI I'm currently running?
|
||||
|
||||
You can check your current Gemini CLI version using one of these methods:
|
||||
|
||||
- Run `gemini --version` or `gemini -v` from your terminal
|
||||
- Check the globally installed version using your package manager:
|
||||
- npm: `npm list -g @google/gemini-cli`
|
||||
- pnpm: `pnpm list -g @google/gemini-cli`
|
||||
- yarn: `yarn global list @google/gemini-cli`
|
||||
- bun: `bun pm ls -g @google/gemini-cli`
|
||||
- homebrew: `brew list --versions gemini-cli`
|
||||
- Inside an active Gemini CLI session, use the `/about` command
|
||||
|
||||
### How do I update Gemini CLI to the latest version?
|
||||
|
||||
If you installed it globally via `npm`, update it using the command
|
||||
|
||||
@@ -16,10 +16,8 @@ account.
|
||||
Your Gemini CLI Usage Statistics are handled in accordance with Google's Privacy
|
||||
Policy.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> See [quotas and pricing](quota-and-pricing.md) for the quota and
|
||||
> pricing details that apply to your usage of the Gemini CLI.
|
||||
**Note:** See [quotas and pricing](quota-and-pricing.md) for the quota and
|
||||
pricing details that apply to your usage of the Gemini CLI.
|
||||
|
||||
## Supported authentication methods
|
||||
|
||||
|
||||
@@ -187,7 +187,5 @@ guide_, consider searching the Gemini CLI
|
||||
If you can't find an issue similar to yours, consider creating a new GitHub
|
||||
Issue with a detailed description. Pull requests are also welcome!
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Issues tagged as "🔒Maintainers only" are reserved for project
|
||||
> **Note:** Issues tagged as "🔒Maintainers only" are reserved for project
|
||||
> maintainers. We will not accept pull requests related to these issues.
|
||||
|
||||
@@ -176,8 +176,8 @@ Each server configuration supports the following properties:
|
||||
enabled by default.
|
||||
- **`excludeTools`** (string[]): List of tool names to exclude from this MCP
|
||||
server. Tools listed here will not be available to the model, even if they are
|
||||
exposed by the server. `excludeTools` takes precedence over `includeTools`. If
|
||||
a tool is in both lists, it will be excluded.
|
||||
exposed by the server. **Note:** `excludeTools` takes precedence over
|
||||
`includeTools` - if a tool is in both lists, it will be excluded.
|
||||
- **`targetAudience`** (string): The OAuth Client ID allowlisted on the
|
||||
IAP-protected application you are trying to access. Used with
|
||||
`authProviderType: 'service_account_impersonation'`.
|
||||
@@ -238,9 +238,7 @@ This follows the security principle that if a variable is explicitly configured
|
||||
by the user for a specific server, it constitutes informed consent to share that
|
||||
specific data with that server.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Even when explicitly defined, you should avoid hardcoding secrets.
|
||||
> **Note:** Even when explicitly defined, you should avoid hardcoding secrets.
|
||||
> Instead, use environment variable expansion (e.g., `"MY_KEY": "$MY_KEY"`) to
|
||||
> securely pull the value from your host environment at runtime.
|
||||
|
||||
@@ -285,12 +283,10 @@ When connecting to an OAuth-enabled server:
|
||||
|
||||
#### Browser redirect requirements
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!IMPORTANT]
|
||||
> OAuth authentication requires that your local machine can:
|
||||
>
|
||||
> - Open a web browser for authentication
|
||||
> - Receive redirects on `http://localhost:7777/oauth/callback`
|
||||
**Important:** OAuth authentication requires that your local machine can:
|
||||
|
||||
- Open a web browser for authentication
|
||||
- Receive redirects on `http://localhost:7777/oauth/callback`
|
||||
|
||||
This feature will not work in:
|
||||
|
||||
@@ -581,9 +577,7 @@ every discovered MCP tool is assigned a strict namespace.
|
||||
[Special syntax for MCP tools](../reference/policy-engine.md#special-syntax-for-mcp-tools)
|
||||
in the Policy Engine documentation.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Do not use underscores (`_`) in your MCP server names (e.g., use
|
||||
> **Warning:** Do not use underscores (`_`) in your MCP server names (e.g., use
|
||||
> `my-server` rather than `my_server`). The policy parser splits Fully Qualified
|
||||
> Names (`mcp_server_tool`) on the _first_ underscore following the `mcp_`
|
||||
> prefix. If your server name contains an underscore, the parser will
|
||||
@@ -1122,9 +1116,7 @@ command has no flags.
|
||||
gemini mcp list
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> For security, `stdio` MCP servers (those using the
|
||||
> **Note on Trust:** For security, `stdio` MCP servers (those using the
|
||||
> `command` property) are only tested and displayed as "Connected" if the
|
||||
> current folder is trusted. If the folder is untrusted, they will show as
|
||||
> "Disconnected". Use `gemini trust` to trust the current folder.
|
||||
|
||||
@@ -11,9 +11,7 @@ by the agent when you ask it to "start a plan" using natural language. In this
|
||||
mode, the agent is restricted to read-only tools to allow for safe exploration
|
||||
and planning.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This tool is not available when the CLI is in YOLO mode.
|
||||
> **Note:** This tool is not available when the CLI is in YOLO mode.
|
||||
|
||||
- **Tool name:** `enter_plan_mode`
|
||||
- **Display name:** Enter Plan Mode
|
||||
|
||||
+4
-4
@@ -57,8 +57,8 @@ implementation, which does not support interactive commands.
|
||||
### Showing color in output
|
||||
|
||||
To show color in the shell output, you need to set the `tools.shell.showColor`
|
||||
setting to `true`. This setting only applies when
|
||||
`tools.shell.enableInteractiveShell` is enabled.
|
||||
setting to `true`. **Note: This setting only applies when
|
||||
`tools.shell.enableInteractiveShell` is enabled.**
|
||||
|
||||
**Example `settings.json`:**
|
||||
|
||||
@@ -75,8 +75,8 @@ setting to `true`. This setting only applies when
|
||||
### Setting the pager
|
||||
|
||||
You can set a custom pager for the shell output by setting the
|
||||
`tools.shell.pager` setting. The default pager is `cat`. This setting only
|
||||
applies when `tools.shell.enableInteractiveShell` is enabled.
|
||||
`tools.shell.pager` setting. The default pager is `cat`. **Note: This setting
|
||||
only applies when `tools.shell.enableInteractiveShell` is enabled.**
|
||||
|
||||
**Example `settings.json`:**
|
||||
|
||||
|
||||
+1
-2
@@ -13,8 +13,7 @@ updates to the CLI interface.
|
||||
- `todos` (array of objects, required): The complete list of tasks. Each object
|
||||
includes:
|
||||
- `description` (string): Technical description of the task.
|
||||
- `status` (enum): `pending`, `in_progress`, `completed`, `cancelled`, or
|
||||
`blocked`.
|
||||
- `status` (enum): `pending`, `in_progress`, `completed`, or `cancelled`.
|
||||
|
||||
## Technical behavior
|
||||
|
||||
|
||||
+1
-6
@@ -319,12 +319,7 @@ export default tseslint.config(
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'./scripts/**/*.js',
|
||||
'packages/*/scripts/**/*.js',
|
||||
'esbuild.config.js',
|
||||
'packages/core/scripts/**/*.{js,mjs}',
|
||||
],
|
||||
files: ['./scripts/**/*.js', 'esbuild.config.js', 'packages/core/scripts/**/*.{js,mjs}'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
|
||||
+26
-91
@@ -18,18 +18,6 @@ describe('plan_mode', () => {
|
||||
experimental: { plan: true },
|
||||
};
|
||||
|
||||
const getWriteTargets = (logs: any[]) =>
|
||||
logs
|
||||
.filter((log) => ['write_file', 'replace'].includes(log.toolRequest.name))
|
||||
.map((log) => {
|
||||
try {
|
||||
return JSON.parse(log.toolRequest.args).file_path as string;
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: 'should refuse file modification when in plan mode',
|
||||
approvalMode: ApprovalMode.PLAN,
|
||||
@@ -44,23 +32,27 @@ describe('plan_mode', () => {
|
||||
await rig.waitForTelemetryReady();
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
const exitPlanIndex = toolLogs.findIndex(
|
||||
(log) => log.toolRequest.name === 'exit_plan_mode',
|
||||
);
|
||||
|
||||
const writeTargetsBeforeExitPlan = getWriteTargets(
|
||||
toolLogs.slice(0, exitPlanIndex !== -1 ? exitPlanIndex : undefined),
|
||||
);
|
||||
const writeTargets = toolLogs
|
||||
.filter((log) =>
|
||||
['write_file', 'replace'].includes(log.toolRequest.name),
|
||||
)
|
||||
.map((log) => {
|
||||
try {
|
||||
return JSON.parse(log.toolRequest.args).file_path;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(
|
||||
writeTargetsBeforeExitPlan,
|
||||
writeTargets,
|
||||
'Should not attempt to modify README.md in plan mode',
|
||||
).not.toContain('README.md');
|
||||
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: [/plan mode|read-only|cannot modify|refuse|exiting/i],
|
||||
testName: `${TEST_PREFIX}should refuse file modification in plan mode`,
|
||||
testName: `${TEST_PREFIX}should refuse file modification`,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -77,20 +69,24 @@ describe('plan_mode', () => {
|
||||
await rig.waitForTelemetryReady();
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
const exitPlanIndex = toolLogs.findIndex(
|
||||
(log) => log.toolRequest.name === 'exit_plan_mode',
|
||||
);
|
||||
|
||||
const writeTargetsBeforeExit = getWriteTargets(
|
||||
toolLogs.slice(0, exitPlanIndex !== -1 ? exitPlanIndex : undefined),
|
||||
);
|
||||
const writeTargets = toolLogs
|
||||
.filter((log) =>
|
||||
['write_file', 'replace'].includes(log.toolRequest.name),
|
||||
)
|
||||
.map((log) => {
|
||||
try {
|
||||
return JSON.parse(log.toolRequest.args).file_path;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// It should NOT write to the docs folder or any other repo path
|
||||
const hasRepoWriteBeforeExit = writeTargetsBeforeExit.some(
|
||||
const hasRepoWrite = writeTargets.some(
|
||||
(path) => path && !path.includes('/plans/'),
|
||||
);
|
||||
expect(
|
||||
hasRepoWriteBeforeExit,
|
||||
hasRepoWrite,
|
||||
'Should not attempt to create files in the repository while in plan mode',
|
||||
).toBe(false);
|
||||
|
||||
@@ -170,65 +166,4 @@ describe('plan_mode', () => {
|
||||
assertModelHasOutput(result);
|
||||
},
|
||||
});
|
||||
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should create a plan in plan mode and implement it for a refactoring task',
|
||||
params: {
|
||||
settings,
|
||||
},
|
||||
files: {
|
||||
'src/mathUtils.ts':
|
||||
'export const sum = (a: number, b: number) => a + b;\nexport const multiply = (a: number, b: number) => a * b;',
|
||||
'src/main.ts':
|
||||
'import { sum } from "./mathUtils";\nconsole.log(sum(1, 2));',
|
||||
},
|
||||
prompt:
|
||||
'I want to refactor our math utilities. Move the `sum` function from `src/mathUtils.ts` to a new file `src/basicMath.ts` and update `src/main.ts` to use the new file. Please create a detailed implementation plan first, then execute it.',
|
||||
assert: async (rig, result) => {
|
||||
const enterPlanCalled = await rig.waitForToolCall('enter_plan_mode');
|
||||
expect(
|
||||
enterPlanCalled,
|
||||
'Expected enter_plan_mode tool to be called',
|
||||
).toBe(true);
|
||||
|
||||
const exitPlanCalled = await rig.waitForToolCall('exit_plan_mode');
|
||||
expect(exitPlanCalled, 'Expected exit_plan_mode tool to be called').toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
await rig.waitForTelemetryReady();
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
// Check if plan was written
|
||||
const planWrite = toolLogs.find(
|
||||
(log) =>
|
||||
log.toolRequest.name === 'write_file' &&
|
||||
log.toolRequest.args.includes('/plans/'),
|
||||
);
|
||||
expect(
|
||||
planWrite,
|
||||
'Expected a plan file to be written in the plans directory',
|
||||
).toBeDefined();
|
||||
|
||||
// Check for implementation files
|
||||
const newFileWrite = toolLogs.find(
|
||||
(log) =>
|
||||
log.toolRequest.name === 'write_file' &&
|
||||
log.toolRequest.args.includes('src/basicMath.ts'),
|
||||
);
|
||||
expect(
|
||||
newFileWrite,
|
||||
'Expected src/basicMath.ts to be created',
|
||||
).toBeDefined();
|
||||
|
||||
const mainUpdate = toolLogs.find(
|
||||
(log) =>
|
||||
['write_file', 'replace'].includes(log.toolRequest.name) &&
|
||||
log.toolRequest.args.includes('src/main.ts'),
|
||||
);
|
||||
expect(mainUpdate, 'Expected src/main.ts to be updated').toBeDefined();
|
||||
|
||||
assertModelHasOutput(result);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I'll help you with that."},{"functionCall":{"name":"browser_agent","args":{"task":"Open https://example.com and check if there is a heading"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":50,"totalTokenCount":150}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"new_page","args":{"url":"https://example.com"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":50,"totalTokenCount":150}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"take_snapshot","args":{}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":50,"totalTokenCount":150}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"complete_task","args":{"success":true,"summary":"SUCCESS_POLICY_TEST_COMPLETED"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":50,"totalTokenCount":150}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Task completed successfully. The page has the heading \"Example Domain\"."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":200,"candidatesTokenCount":50,"totalTokenCount":250}}]}
|
||||
@@ -1,178 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { TestRig, poll } from './test-helper.js';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'node:fs';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const chromeAvailable = (() => {
|
||||
try {
|
||||
if (process.platform === 'darwin') {
|
||||
execSync(
|
||||
'test -d "/Applications/Google Chrome.app" || test -d "/Applications/Chromium.app"',
|
||||
{
|
||||
stdio: 'ignore',
|
||||
},
|
||||
);
|
||||
} else if (process.platform === 'linux') {
|
||||
execSync(
|
||||
'which google-chrome || which chromium-browser || which chromium',
|
||||
{ stdio: 'ignore' },
|
||||
);
|
||||
} else if (process.platform === 'win32') {
|
||||
const chromePaths = [
|
||||
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
||||
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
|
||||
`${process.env['LOCALAPPDATA'] ?? ''}\\Google\\Chrome\\Application\\chrome.exe`,
|
||||
];
|
||||
const found = chromePaths.some((p) => existsSync(p));
|
||||
if (!found) {
|
||||
execSync('where chrome || where chromium', { stdio: 'ignore' });
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
describe.skipIf(!chromeAvailable)('browser-policy', () => {
|
||||
let rig: TestRig;
|
||||
|
||||
beforeEach(() => {
|
||||
rig = new TestRig();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await rig.cleanup();
|
||||
});
|
||||
|
||||
it('should skip confirmation when "Allow all server tools for this session" is chosen', async () => {
|
||||
rig.setup('browser-policy-skip-confirmation', {
|
||||
fakeResponsesPath: join(__dirname, 'browser-policy.responses'),
|
||||
settings: {
|
||||
agents: {
|
||||
overrides: {
|
||||
browser_agent: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
browser: {
|
||||
headless: true,
|
||||
sessionMode: 'isolated',
|
||||
allowedDomains: ['example.com'],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Manually trust the folder to avoid the dialog and enable option 3
|
||||
const geminiDir = join(rig.homeDir!, '.gemini');
|
||||
mkdirSync(geminiDir, { recursive: true });
|
||||
|
||||
// Write to trustedFolders.json
|
||||
const trustedFoldersPath = join(geminiDir, 'trustedFolders.json');
|
||||
const trustedFolders = {
|
||||
[rig.testDir!]: 'TRUST_FOLDER',
|
||||
};
|
||||
writeFileSync(trustedFoldersPath, JSON.stringify(trustedFolders, null, 2));
|
||||
|
||||
// Force confirmation for browser agent.
|
||||
// NOTE: We don't force confirm browser tools here because "Allow all server tools"
|
||||
// adds a rule with ALWAYS_ALLOW_PRIORITY (3.9x) which would be overshadowed by
|
||||
// a rule in the user tier (4.x) like the one from this TOML.
|
||||
// By removing the explicit mcp rule, the first MCP tool will still prompt
|
||||
// due to default approvalMode = 'default', and then "Allow all" will correctly
|
||||
// bypass subsequent tools.
|
||||
const policyFile = join(rig.testDir!, 'force-confirm.toml');
|
||||
writeFileSync(
|
||||
policyFile,
|
||||
`
|
||||
[[rule]]
|
||||
name = "Force confirm browser_agent"
|
||||
toolName = "browser_agent"
|
||||
decision = "ask_user"
|
||||
priority = 200
|
||||
`,
|
||||
);
|
||||
|
||||
// Update settings.json in both project and home directories to point to the policy file
|
||||
for (const baseDir of [rig.testDir!, rig.homeDir!]) {
|
||||
const settingsPath = join(baseDir, '.gemini', 'settings.json');
|
||||
if (existsSync(settingsPath)) {
|
||||
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
||||
settings.policyPaths = [policyFile];
|
||||
// Ensure folder trust is enabled
|
||||
settings.security = settings.security || {};
|
||||
settings.security.folderTrust = settings.security.folderTrust || {};
|
||||
settings.security.folderTrust.enabled = true;
|
||||
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
const run = await rig.runInteractive({
|
||||
approvalMode: 'default',
|
||||
env: {
|
||||
GEMINI_CLI_INTEGRATION_TEST: 'true',
|
||||
},
|
||||
});
|
||||
|
||||
await run.sendKeys(
|
||||
'Open https://example.com and check if there is a heading\r',
|
||||
);
|
||||
await run.sendKeys('\r');
|
||||
|
||||
// Handle confirmations.
|
||||
// 1. Initial browser_agent delegation (likely only 3 options, so use option 1: Allow once)
|
||||
await poll(
|
||||
() => stripAnsi(run.output).toLowerCase().includes('action required'),
|
||||
60000,
|
||||
1000,
|
||||
);
|
||||
await run.sendKeys('1\r');
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
|
||||
// Handle privacy notice
|
||||
await poll(
|
||||
() => stripAnsi(run.output).toLowerCase().includes('privacy notice'),
|
||||
5000,
|
||||
100,
|
||||
);
|
||||
await run.sendKeys('1\r');
|
||||
await new Promise((r) => setTimeout(r, 5000));
|
||||
|
||||
// new_page (MCP tool, should have 4 options, use option 3: Allow all server tools)
|
||||
await poll(
|
||||
() => {
|
||||
const stripped = stripAnsi(run.output).toLowerCase();
|
||||
return (
|
||||
stripped.includes('new_page') &&
|
||||
stripped.includes('allow all server tools for this session')
|
||||
);
|
||||
},
|
||||
60000,
|
||||
1000,
|
||||
);
|
||||
|
||||
// Select "Allow all server tools for this session" (option 3)
|
||||
await run.sendKeys('3\r');
|
||||
await new Promise((r) => setTimeout(r, 30000));
|
||||
|
||||
const output = stripAnsi(run.output).toLowerCase();
|
||||
|
||||
expect(output).toContain('browser_agent');
|
||||
expect(output).toContain('completed successfully');
|
||||
});
|
||||
});
|
||||
Generated
+2798
-148
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -87,7 +87,7 @@
|
||||
"LICENSE"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.16.1",
|
||||
"@agentclientprotocol/sdk": "^0.12.0",
|
||||
"@octokit/rest": "^22.0.0",
|
||||
"@types/marked": "^5.0.2",
|
||||
"@types/mime-types": "^3.0.1",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.36.0-nightly.20260317.2f90b4653"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.16.1",
|
||||
"@agentclientprotocol/sdk": "^0.12.0",
|
||||
"@google/gemini-cli-core": "file:../core",
|
||||
"@google/genai": "1.30.0",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
|
||||
@@ -177,9 +177,6 @@ describe('GeminiAgent', () => {
|
||||
getHasAccessToPreviewModel: vi.fn().mockReturnValue(false),
|
||||
getCheckpointingEnabled: vi.fn().mockReturnValue(false),
|
||||
getDisableAlwaysAllow: vi.fn().mockReturnValue(false),
|
||||
get config() {
|
||||
return this;
|
||||
},
|
||||
} as unknown as Mocked<Awaited<ReturnType<typeof loadCliConfig>>>;
|
||||
mockSettings = {
|
||||
merged: {
|
||||
@@ -551,7 +548,7 @@ describe('GeminiAgent', () => {
|
||||
});
|
||||
|
||||
expect(session.prompt).toHaveBeenCalled();
|
||||
expect(result).toMatchObject({ stopReason: 'end_turn' });
|
||||
expect(result).toEqual({ stopReason: 'end_turn' });
|
||||
});
|
||||
|
||||
it('should delegate setMode to session', async () => {
|
||||
@@ -659,12 +656,6 @@ describe('Session', () => {
|
||||
getGitService: vi.fn().mockResolvedValue({} as GitService),
|
||||
waitForMcpInit: vi.fn(),
|
||||
getDisableAlwaysAllow: vi.fn().mockReturnValue(false),
|
||||
get config() {
|
||||
return this;
|
||||
},
|
||||
get toolRegistry() {
|
||||
return mockToolRegistry;
|
||||
},
|
||||
} as unknown as Mocked<Config>;
|
||||
mockConnection = {
|
||||
sessionUpdate: vi.fn(),
|
||||
@@ -750,7 +741,7 @@ describe('Session', () => {
|
||||
content: { type: 'text', text: 'Hello' },
|
||||
},
|
||||
});
|
||||
expect(result).toMatchObject({ stopReason: 'end_turn' });
|
||||
expect(result).toEqual({ stopReason: 'end_turn' });
|
||||
});
|
||||
|
||||
it('should handle /memory command', async () => {
|
||||
@@ -767,7 +758,7 @@ describe('Session', () => {
|
||||
prompt: [{ type: 'text', text: '/memory view' }],
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ stopReason: 'end_turn' });
|
||||
expect(result).toEqual({ stopReason: 'end_turn' });
|
||||
expect(handleCommandSpy).toHaveBeenCalledWith(
|
||||
'/memory view',
|
||||
expect.any(Object),
|
||||
@@ -789,7 +780,7 @@ describe('Session', () => {
|
||||
prompt: [{ type: 'text', text: '/extensions list' }],
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ stopReason: 'end_turn' });
|
||||
expect(result).toEqual({ stopReason: 'end_turn' });
|
||||
expect(handleCommandSpy).toHaveBeenCalledWith(
|
||||
'/extensions list',
|
||||
expect.any(Object),
|
||||
@@ -811,7 +802,7 @@ describe('Session', () => {
|
||||
prompt: [{ type: 'text', text: '/extensions explore' }],
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ stopReason: 'end_turn' });
|
||||
expect(result).toEqual({ stopReason: 'end_turn' });
|
||||
expect(handleCommandSpy).toHaveBeenCalledWith(
|
||||
'/extensions explore',
|
||||
expect.any(Object),
|
||||
@@ -833,7 +824,7 @@ describe('Session', () => {
|
||||
prompt: [{ type: 'text', text: '/restore' }],
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ stopReason: 'end_turn' });
|
||||
expect(result).toEqual({ stopReason: 'end_turn' });
|
||||
expect(handleCommandSpy).toHaveBeenCalledWith(
|
||||
'/restore',
|
||||
expect.any(Object),
|
||||
@@ -855,7 +846,7 @@ describe('Session', () => {
|
||||
prompt: [{ type: 'text', text: '/init' }],
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ stopReason: 'end_turn' });
|
||||
expect(result).toEqual({ stopReason: 'end_turn' });
|
||||
expect(handleCommandSpy).toHaveBeenCalledWith('/init', expect.any(Object));
|
||||
expect(mockChat.sendMessageStream).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -903,13 +894,10 @@ describe('Session', () => {
|
||||
update: expect.objectContaining({
|
||||
sessionUpdate: 'tool_call_update',
|
||||
status: 'completed',
|
||||
title: 'Test Tool',
|
||||
locations: [],
|
||||
kind: 'read',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(result).toMatchObject({ stopReason: 'end_turn' });
|
||||
expect(result).toEqual({ stopReason: 'end_turn' });
|
||||
});
|
||||
|
||||
it('should handle tool call permission request', async () => {
|
||||
@@ -1318,18 +1306,6 @@ describe('Session', () => {
|
||||
expect(path.resolve).toHaveBeenCalled();
|
||||
expect(fs.stat).toHaveBeenCalled();
|
||||
|
||||
expect(mockConnection.sessionUpdate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
update: expect.objectContaining({
|
||||
sessionUpdate: 'tool_call_update',
|
||||
status: 'completed',
|
||||
title: 'Read files',
|
||||
locations: [],
|
||||
kind: 'read',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify ReadManyFilesTool was used (implicitly by checking if sendMessageStream was called with resolved content)
|
||||
// Since we mocked ReadManyFilesTool to return specific content, we can check the args passed to sendMessageStream
|
||||
expect(mockChat.sendMessageStream).toHaveBeenCalledWith(
|
||||
@@ -1345,65 +1321,6 @@ describe('Session', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle @path resolution error', async () => {
|
||||
(path.resolve as unknown as Mock).mockReturnValue('/tmp/error.txt');
|
||||
(fs.stat as unknown as Mock).mockResolvedValue({
|
||||
isDirectory: () => false,
|
||||
});
|
||||
(isWithinRoot as unknown as Mock).mockReturnValue(true);
|
||||
|
||||
const MockReadManyFilesTool = ReadManyFilesTool as unknown as Mock;
|
||||
MockReadManyFilesTool.mockImplementationOnce(() => ({
|
||||
name: 'read_many_files',
|
||||
kind: 'read',
|
||||
build: vi.fn().mockReturnValue({
|
||||
getDescription: () => 'Read files',
|
||||
toolLocations: () => [],
|
||||
execute: vi.fn().mockRejectedValue(new Error('File read failed')),
|
||||
}),
|
||||
}));
|
||||
|
||||
const stream = createMockStream([
|
||||
{
|
||||
type: StreamEventType.CHUNK,
|
||||
value: { candidates: [] },
|
||||
},
|
||||
]);
|
||||
mockChat.sendMessageStream.mockResolvedValue(stream);
|
||||
|
||||
await expect(
|
||||
session.prompt({
|
||||
sessionId: 'session-1',
|
||||
prompt: [
|
||||
{ type: 'text', text: 'Read' },
|
||||
{
|
||||
type: 'resource_link',
|
||||
uri: 'file://error.txt',
|
||||
mimeType: 'text/plain',
|
||||
name: 'error.txt',
|
||||
},
|
||||
],
|
||||
}),
|
||||
).rejects.toThrow('File read failed');
|
||||
|
||||
expect(mockConnection.sessionUpdate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
update: expect.objectContaining({
|
||||
sessionUpdate: 'tool_call_update',
|
||||
status: 'failed',
|
||||
content: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
content: expect.objectContaining({
|
||||
text: expect.stringMatching(/File read failed/),
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
kind: 'read',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle cancellation during prompt', async () => {
|
||||
let streamController: ReadableStreamDefaultController<unknown>;
|
||||
const stream = new ReadableStream({
|
||||
@@ -1517,7 +1434,6 @@ describe('Session', () => {
|
||||
content: expect.objectContaining({ text: 'Tool failed' }),
|
||||
}),
|
||||
]),
|
||||
kind: 'read',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -47,7 +47,6 @@ import {
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
getDisplayString,
|
||||
type AgentLoopContext,
|
||||
} from '@google/gemini-cli-core';
|
||||
import * as acp from '@agentclientprotocol/sdk';
|
||||
import { AcpFileSystemService } from './fileSystemService.js';
|
||||
@@ -105,7 +104,7 @@ export class GeminiAgent {
|
||||
private customHeaders: Record<string, string> | undefined;
|
||||
|
||||
constructor(
|
||||
private context: AgentLoopContext,
|
||||
private config: Config,
|
||||
private settings: LoadedSettings,
|
||||
private argv: CliArgs,
|
||||
private connection: acp.AgentSideConnection,
|
||||
@@ -149,7 +148,7 @@ export class GeminiAgent {
|
||||
},
|
||||
];
|
||||
|
||||
await this.context.config.initialize();
|
||||
await this.config.initialize();
|
||||
const version = await getVersion();
|
||||
return {
|
||||
protocolVersion: acp.PROTOCOL_VERSION,
|
||||
@@ -221,7 +220,7 @@ export class GeminiAgent {
|
||||
this.baseUrl = baseUrl;
|
||||
this.customHeaders = headers;
|
||||
|
||||
await this.context.config.refreshAuth(
|
||||
await this.config.refreshAuth(
|
||||
method,
|
||||
apiKey ?? this.apiKey,
|
||||
baseUrl,
|
||||
@@ -538,7 +537,7 @@ export class Session {
|
||||
constructor(
|
||||
private readonly id: string,
|
||||
private readonly chat: GeminiChat,
|
||||
private readonly context: AgentLoopContext,
|
||||
private readonly config: Config,
|
||||
private readonly connection: acp.AgentSideConnection,
|
||||
private readonly settings: LoadedSettings,
|
||||
) {}
|
||||
@@ -553,15 +552,13 @@ export class Session {
|
||||
}
|
||||
|
||||
setMode(modeId: acp.SessionModeId): acp.SetSessionModeResponse {
|
||||
const availableModes = buildAvailableModes(
|
||||
this.context.config.isPlanEnabled(),
|
||||
);
|
||||
const availableModes = buildAvailableModes(this.config.isPlanEnabled());
|
||||
const mode = availableModes.find((m) => m.id === modeId);
|
||||
if (!mode) {
|
||||
throw new Error(`Invalid or unavailable mode: ${modeId}`);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
this.context.config.setApprovalMode(mode.id as ApprovalMode);
|
||||
this.config.setApprovalMode(mode.id as ApprovalMode);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -582,7 +579,7 @@ export class Session {
|
||||
}
|
||||
|
||||
setModel(modelId: acp.ModelId): acp.SetSessionModelResponse {
|
||||
this.context.config.setModel(modelId);
|
||||
this.config.setModel(modelId);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -637,7 +634,7 @@ export class Session {
|
||||
}
|
||||
}
|
||||
|
||||
const tool = this.context.toolRegistry.getTool(toolCall.name);
|
||||
const tool = this.config.getToolRegistry().getTool(toolCall.name);
|
||||
|
||||
await this.sendUpdate({
|
||||
sessionUpdate: 'tool_call',
|
||||
@@ -661,7 +658,7 @@ export class Session {
|
||||
const pendingSend = new AbortController();
|
||||
this.pendingPrompt = pendingSend;
|
||||
|
||||
await this.context.config.waitForMcpInit();
|
||||
await this.config.waitForMcpInit();
|
||||
|
||||
const promptId = Math.random().toString(16).slice(2);
|
||||
const chat = this.chat;
|
||||
@@ -699,22 +696,10 @@ export class Session {
|
||||
// It uses `parts` argument but effectively ignores it in current implementation
|
||||
const handled = await this.handleCommand(commandText, parts);
|
||||
if (handled) {
|
||||
return {
|
||||
stopReason: 'end_turn',
|
||||
_meta: {
|
||||
quota: {
|
||||
token_count: { input_tokens: 0, output_tokens: 0 },
|
||||
model_usage: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
return { stopReason: 'end_turn' };
|
||||
}
|
||||
}
|
||||
|
||||
let totalInputTokens = 0;
|
||||
let totalOutputTokens = 0;
|
||||
const modelUsageMap = new Map<string, { input: number; output: number }>();
|
||||
|
||||
let nextMessage: Content | null = { role: 'user', parts };
|
||||
|
||||
while (nextMessage !== null) {
|
||||
@@ -727,8 +712,8 @@ export class Session {
|
||||
|
||||
try {
|
||||
const model = resolveModel(
|
||||
this.context.config.getModel(),
|
||||
(await this.context.config.getGemini31Launched?.()) ?? false,
|
||||
this.config.getModel(),
|
||||
(await this.config.getGemini31Launched?.()) ?? false,
|
||||
);
|
||||
const responseStream = await chat.sendMessageStream(
|
||||
{ model },
|
||||
@@ -739,25 +724,11 @@ export class Session {
|
||||
);
|
||||
nextMessage = null;
|
||||
|
||||
let turnInputTokens = 0;
|
||||
let turnOutputTokens = 0;
|
||||
let turnModelId = model;
|
||||
|
||||
for await (const resp of responseStream) {
|
||||
if (pendingSend.signal.aborted) {
|
||||
return { stopReason: CoreToolCallStatus.Cancelled };
|
||||
}
|
||||
|
||||
if (resp.type === StreamEventType.CHUNK && resp.value.usageMetadata) {
|
||||
turnInputTokens =
|
||||
resp.value.usageMetadata.promptTokenCount ?? turnInputTokens;
|
||||
turnOutputTokens =
|
||||
resp.value.usageMetadata.candidatesTokenCount ?? turnOutputTokens;
|
||||
if (resp.value.modelVersion) {
|
||||
turnModelId = resp.value.modelVersion;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
resp.type === StreamEventType.CHUNK &&
|
||||
resp.value.candidates &&
|
||||
@@ -789,19 +760,6 @@ export class Session {
|
||||
}
|
||||
}
|
||||
|
||||
totalInputTokens += turnInputTokens;
|
||||
totalOutputTokens += turnOutputTokens;
|
||||
|
||||
if (turnInputTokens > 0 || turnOutputTokens > 0) {
|
||||
const existing = modelUsageMap.get(turnModelId) ?? {
|
||||
input: 0,
|
||||
output: 0,
|
||||
};
|
||||
existing.input += turnInputTokens;
|
||||
existing.output += turnOutputTokens;
|
||||
modelUsageMap.set(turnModelId, existing);
|
||||
}
|
||||
|
||||
if (pendingSend.signal.aborted) {
|
||||
return { stopReason: CoreToolCallStatus.Cancelled };
|
||||
}
|
||||
@@ -838,28 +796,7 @@ export class Session {
|
||||
}
|
||||
}
|
||||
|
||||
const modelUsageArray = Array.from(modelUsageMap.entries()).map(
|
||||
([modelName, counts]) => ({
|
||||
model: modelName,
|
||||
token_count: {
|
||||
input_tokens: counts.input,
|
||||
output_tokens: counts.output,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
stopReason: 'end_turn',
|
||||
_meta: {
|
||||
quota: {
|
||||
token_count: {
|
||||
input_tokens: totalInputTokens,
|
||||
output_tokens: totalOutputTokens,
|
||||
},
|
||||
model_usage: modelUsageArray,
|
||||
},
|
||||
},
|
||||
};
|
||||
return { stopReason: 'end_turn' };
|
||||
}
|
||||
|
||||
private async handleCommand(
|
||||
@@ -867,9 +804,9 @@ export class Session {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
parts: Part[],
|
||||
): Promise<boolean> {
|
||||
const gitService = await this.context.config.getGitService();
|
||||
const gitService = await this.config.getGitService();
|
||||
const commandContext = {
|
||||
agentContext: this.context,
|
||||
config: this.config,
|
||||
settings: this.settings,
|
||||
git: gitService,
|
||||
sendMessage: async (text: string) => {
|
||||
@@ -905,7 +842,7 @@ export class Session {
|
||||
const errorResponse = (error: Error) => {
|
||||
const durationMs = Date.now() - startTime;
|
||||
logToolCall(
|
||||
this.context.config,
|
||||
this.config,
|
||||
new ToolCallEvent(
|
||||
undefined,
|
||||
fc.name ?? '',
|
||||
@@ -935,7 +872,7 @@ export class Session {
|
||||
return errorResponse(new Error('Missing function name'));
|
||||
}
|
||||
|
||||
const toolRegistry = this.context.toolRegistry;
|
||||
const toolRegistry = this.config.getToolRegistry();
|
||||
const tool = toolRegistry.getTool(fc.name);
|
||||
|
||||
if (!tool) {
|
||||
@@ -971,10 +908,7 @@ export class Session {
|
||||
|
||||
const params: acp.RequestPermissionRequest = {
|
||||
sessionId: this.id,
|
||||
options: toPermissionOptions(
|
||||
confirmationDetails,
|
||||
this.context.config,
|
||||
),
|
||||
options: toPermissionOptions(confirmationDetails, this.config),
|
||||
toolCall: {
|
||||
toolCallId: callId,
|
||||
status: 'pending',
|
||||
@@ -1032,15 +966,12 @@ export class Session {
|
||||
sessionUpdate: 'tool_call_update',
|
||||
toolCallId: callId,
|
||||
status: 'completed',
|
||||
title: invocation.getDescription(),
|
||||
content: content ? [content] : [],
|
||||
locations: invocation.toolLocations(),
|
||||
kind: toAcpToolKind(tool.kind),
|
||||
});
|
||||
|
||||
const durationMs = Date.now() - startTime;
|
||||
logToolCall(
|
||||
this.context.config,
|
||||
this.config,
|
||||
new ToolCallEvent(
|
||||
undefined,
|
||||
fc.name ?? '',
|
||||
@@ -1054,7 +985,7 @@ export class Session {
|
||||
),
|
||||
);
|
||||
|
||||
this.chat.recordCompletedToolCalls(this.context.config.getActiveModel(), [
|
||||
this.chat.recordCompletedToolCalls(this.config.getActiveModel(), [
|
||||
{
|
||||
status: CoreToolCallStatus.Success,
|
||||
request: {
|
||||
@@ -1072,8 +1003,8 @@ export class Session {
|
||||
fc.name,
|
||||
callId,
|
||||
toolResult.llmContent,
|
||||
this.context.config.getActiveModel(),
|
||||
this.context.config,
|
||||
this.config.getActiveModel(),
|
||||
this.config,
|
||||
),
|
||||
resultDisplay: toolResult.returnDisplay,
|
||||
error: undefined,
|
||||
@@ -1086,8 +1017,8 @@ export class Session {
|
||||
fc.name,
|
||||
callId,
|
||||
toolResult.llmContent,
|
||||
this.context.config.getActiveModel(),
|
||||
this.context.config,
|
||||
this.config.getActiveModel(),
|
||||
this.config,
|
||||
);
|
||||
} catch (e) {
|
||||
const error = e instanceof Error ? e : new Error(String(e));
|
||||
@@ -1099,10 +1030,9 @@ export class Session {
|
||||
content: [
|
||||
{ type: 'content', content: { type: 'text', text: error.message } },
|
||||
],
|
||||
kind: toAcpToolKind(tool.kind),
|
||||
});
|
||||
|
||||
this.chat.recordCompletedToolCalls(this.context.config.getActiveModel(), [
|
||||
this.chat.recordCompletedToolCalls(this.config.getActiveModel(), [
|
||||
{
|
||||
status: CoreToolCallStatus.Error,
|
||||
request: {
|
||||
@@ -1188,18 +1118,18 @@ export class Session {
|
||||
const atPathToResolvedSpecMap = new Map<string, string>();
|
||||
|
||||
// Get centralized file discovery service
|
||||
const fileDiscovery = this.context.config.getFileService();
|
||||
const fileDiscovery = this.config.getFileService();
|
||||
const fileFilteringOptions: FilterFilesOptions =
|
||||
this.context.config.getFileFilteringOptions();
|
||||
this.config.getFileFilteringOptions();
|
||||
|
||||
const pathSpecsToRead: string[] = [];
|
||||
const contentLabelsForDisplay: string[] = [];
|
||||
const ignoredPaths: string[] = [];
|
||||
|
||||
const toolRegistry = this.context.toolRegistry;
|
||||
const toolRegistry = this.config.getToolRegistry();
|
||||
const readManyFilesTool = new ReadManyFilesTool(
|
||||
this.context.config,
|
||||
this.context.messageBus,
|
||||
this.config,
|
||||
this.config.getMessageBus(),
|
||||
);
|
||||
const globTool = toolRegistry.getTool('glob');
|
||||
|
||||
@@ -1218,11 +1148,8 @@ export class Session {
|
||||
let currentPathSpec = pathName;
|
||||
let resolvedSuccessfully = false;
|
||||
try {
|
||||
const absolutePath = path.resolve(
|
||||
this.context.config.getTargetDir(),
|
||||
pathName,
|
||||
);
|
||||
if (isWithinRoot(absolutePath, this.context.config.getTargetDir())) {
|
||||
const absolutePath = path.resolve(this.config.getTargetDir(), pathName);
|
||||
if (isWithinRoot(absolutePath, this.config.getTargetDir())) {
|
||||
const stats = await fs.stat(absolutePath);
|
||||
if (stats.isDirectory()) {
|
||||
currentPathSpec = pathName.endsWith('/')
|
||||
@@ -1242,7 +1169,7 @@ export class Session {
|
||||
}
|
||||
} catch (error) {
|
||||
if (isNodeError(error) && error.code === 'ENOENT') {
|
||||
if (this.context.config.getEnableRecursiveFileSearch() && globTool) {
|
||||
if (this.config.getEnableRecursiveFileSearch() && globTool) {
|
||||
this.debug(
|
||||
`Path ${pathName} not found directly, attempting glob search.`,
|
||||
);
|
||||
@@ -1250,7 +1177,7 @@ export class Session {
|
||||
const globResult = await globTool.buildAndExecute(
|
||||
{
|
||||
pattern: `**/*${pathName}*`,
|
||||
path: this.context.config.getTargetDir(),
|
||||
path: this.config.getTargetDir(),
|
||||
},
|
||||
abortSignal,
|
||||
);
|
||||
@@ -1264,7 +1191,7 @@ export class Session {
|
||||
if (lines.length > 1 && lines[1]) {
|
||||
const firstMatchAbsolute = lines[1].trim();
|
||||
currentPathSpec = path.relative(
|
||||
this.context.config.getTargetDir(),
|
||||
this.config.getTargetDir(),
|
||||
firstMatchAbsolute,
|
||||
);
|
||||
this.debug(
|
||||
@@ -1397,10 +1324,7 @@ export class Session {
|
||||
sessionUpdate: 'tool_call_update',
|
||||
toolCallId: callId,
|
||||
status: 'completed',
|
||||
title: invocation.getDescription(),
|
||||
content: content ? [content] : [],
|
||||
locations: invocation.toolLocations(),
|
||||
kind: toAcpToolKind(readManyFilesTool.kind),
|
||||
});
|
||||
if (Array.isArray(result.llmContent)) {
|
||||
const fileContentRegex = /^--- (.*?) ---\n\n([\s\S]*?)\n\n$/;
|
||||
@@ -1444,7 +1368,6 @@ export class Session {
|
||||
},
|
||||
},
|
||||
],
|
||||
kind: toAcpToolKind(readManyFilesTool.kind),
|
||||
});
|
||||
|
||||
throw error;
|
||||
@@ -1479,7 +1402,7 @@ export class Session {
|
||||
}
|
||||
|
||||
debug(msg: string) {
|
||||
if (this.context.config.getDebugMode()) {
|
||||
if (this.config.getDebugMode()) {
|
||||
debugLogger.warn(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,9 +97,6 @@ describe('GeminiAgent Session Resume', () => {
|
||||
getHasAccessToPreviewModel: vi.fn().mockReturnValue(false),
|
||||
getGemini31LaunchedSync: vi.fn().mockReturnValue(false),
|
||||
getCheckpointingEnabled: vi.fn().mockReturnValue(false),
|
||||
get config() {
|
||||
return this;
|
||||
},
|
||||
} as unknown as Mocked<Config>;
|
||||
mockSettings = {
|
||||
merged: {
|
||||
@@ -161,10 +158,9 @@ describe('GeminiAgent Session Resume', () => {
|
||||
],
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(mockConfig as any).toolRegistry = {
|
||||
mockConfig.getToolRegistry = vi.fn().mockReturnValue({
|
||||
getTool: vi.fn().mockReturnValue({ kind: 'read' }),
|
||||
};
|
||||
});
|
||||
|
||||
(SessionSelector as unknown as Mock).mockImplementation(() => ({
|
||||
resolveSession: vi.fn().mockResolvedValue({
|
||||
|
||||
@@ -53,7 +53,7 @@ export class ListExtensionsCommand implements Command {
|
||||
context: CommandContext,
|
||||
_: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const extensions = listExtensions(context.agentContext.config);
|
||||
const extensions = listExtensions(context.config);
|
||||
const data = extensions.length ? extensions : 'No extensions installed.';
|
||||
|
||||
return { name: this.name, data };
|
||||
@@ -134,7 +134,7 @@ export class EnableExtensionCommand implements Command {
|
||||
args: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const enableContext = getEnableDisableContext(
|
||||
context.agentContext.config,
|
||||
context.config,
|
||||
args,
|
||||
'enable',
|
||||
);
|
||||
@@ -156,8 +156,7 @@ export class EnableExtensionCommand implements Command {
|
||||
|
||||
if (extension?.mcpServers) {
|
||||
const mcpEnablementManager = McpServerEnablementManager.getInstance();
|
||||
const mcpClientManager =
|
||||
context.agentContext.config.getMcpClientManager();
|
||||
const mcpClientManager = context.config.getMcpClientManager();
|
||||
const enabledServers = await mcpEnablementManager.autoEnableServers(
|
||||
Object.keys(extension.mcpServers),
|
||||
);
|
||||
@@ -192,7 +191,7 @@ export class DisableExtensionCommand implements Command {
|
||||
args: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const enableContext = getEnableDisableContext(
|
||||
context.agentContext.config,
|
||||
context.config,
|
||||
args,
|
||||
'disable',
|
||||
);
|
||||
@@ -224,7 +223,7 @@ export class InstallExtensionCommand implements Command {
|
||||
context: CommandContext,
|
||||
args: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const extensionLoader = context.agentContext.config.getExtensionLoader();
|
||||
const extensionLoader = context.config.getExtensionLoader();
|
||||
if (!(extensionLoader instanceof ExtensionManager)) {
|
||||
return {
|
||||
name: this.name,
|
||||
@@ -269,7 +268,7 @@ export class LinkExtensionCommand implements Command {
|
||||
context: CommandContext,
|
||||
args: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const extensionLoader = context.agentContext.config.getExtensionLoader();
|
||||
const extensionLoader = context.config.getExtensionLoader();
|
||||
if (!(extensionLoader instanceof ExtensionManager)) {
|
||||
return {
|
||||
name: this.name,
|
||||
@@ -314,7 +313,7 @@ export class UninstallExtensionCommand implements Command {
|
||||
context: CommandContext,
|
||||
args: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const extensionLoader = context.agentContext.config.getExtensionLoader();
|
||||
const extensionLoader = context.config.getExtensionLoader();
|
||||
if (!(extensionLoader instanceof ExtensionManager)) {
|
||||
return {
|
||||
name: this.name,
|
||||
@@ -370,7 +369,7 @@ export class RestartExtensionCommand implements Command {
|
||||
context: CommandContext,
|
||||
args: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const extensionLoader = context.agentContext.config.getExtensionLoader();
|
||||
const extensionLoader = context.config.getExtensionLoader();
|
||||
if (!(extensionLoader instanceof ExtensionManager)) {
|
||||
return { name: this.name, data: 'Cannot restart extensions.' };
|
||||
}
|
||||
@@ -425,7 +424,7 @@ export class UpdateExtensionCommand implements Command {
|
||||
context: CommandContext,
|
||||
args: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const extensionLoader = context.agentContext.config.getExtensionLoader();
|
||||
const extensionLoader = context.config.getExtensionLoader();
|
||||
if (!(extensionLoader instanceof ExtensionManager)) {
|
||||
return { name: this.name, data: 'Cannot update extensions.' };
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export class InitCommand implements Command {
|
||||
context: CommandContext,
|
||||
_args: string[] = [],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const targetDir = context.agentContext.config.getTargetDir();
|
||||
const targetDir = context.config.getTargetDir();
|
||||
if (!targetDir) {
|
||||
throw new Error('Command requires a workspace.');
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export class ShowMemoryCommand implements Command {
|
||||
context: CommandContext,
|
||||
_: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const result = showMemory(context.agentContext.config);
|
||||
const result = showMemory(context.config);
|
||||
return { name: this.name, data: result.content };
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export class RefreshMemoryCommand implements Command {
|
||||
context: CommandContext,
|
||||
_: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const result = await refreshMemory(context.agentContext.config);
|
||||
const result = await refreshMemory(context.config);
|
||||
return { name: this.name, data: result.content };
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export class ListMemoryCommand implements Command {
|
||||
context: CommandContext,
|
||||
_: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const result = listMemoryFiles(context.agentContext.config);
|
||||
const result = listMemoryFiles(context.config);
|
||||
return { name: this.name, data: result.content };
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ export class AddMemoryCommand implements Command {
|
||||
return { name: this.name, data: result.content };
|
||||
}
|
||||
|
||||
const toolRegistry = context.agentContext.toolRegistry;
|
||||
const toolRegistry = context.config.getToolRegistry();
|
||||
const tool = toolRegistry.getTool(result.toolName);
|
||||
if (tool) {
|
||||
const abortController = new AbortController();
|
||||
@@ -106,10 +106,10 @@ export class AddMemoryCommand implements Command {
|
||||
await tool.buildAndExecute(result.toolArgs, signal, undefined, {
|
||||
shellExecutionConfig: {
|
||||
sanitizationConfig: DEFAULT_SANITIZATION_CONFIG,
|
||||
sandboxManager: context.agentContext.sandboxManager,
|
||||
sandboxManager: context.config.sandboxManager,
|
||||
},
|
||||
});
|
||||
await refreshMemory(context.agentContext.config);
|
||||
await refreshMemory(context.config);
|
||||
return {
|
||||
name: this.name,
|
||||
data: `Added memory: "${textToAdd}"`,
|
||||
|
||||
@@ -29,8 +29,7 @@ export class RestoreCommand implements Command {
|
||||
context: CommandContext,
|
||||
args: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
const { agentContext: agentContext, git: gitService } = context;
|
||||
const { config } = agentContext;
|
||||
const { config, git: gitService } = context;
|
||||
const argsStr = args.join(' ');
|
||||
|
||||
try {
|
||||
@@ -117,7 +116,7 @@ export class ListCheckpointsCommand implements Command {
|
||||
readonly description = 'Lists all available checkpoints.';
|
||||
|
||||
async execute(context: CommandContext): Promise<CommandExecutionResponse> {
|
||||
const { config } = context.agentContext;
|
||||
const { config } = context;
|
||||
|
||||
try {
|
||||
if (!config.getCheckpointingEnabled()) {
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { AgentLoopContext, GitService } from '@google/gemini-cli-core';
|
||||
import type { Config, GitService } from '@google/gemini-cli-core';
|
||||
import type { LoadedSettings } from '../../config/settings.js';
|
||||
|
||||
export interface CommandContext {
|
||||
agentContext: AgentLoopContext;
|
||||
config: Config;
|
||||
settings: LoadedSettings;
|
||||
git?: GitService;
|
||||
sendMessage: (text: string) => Promise<void>;
|
||||
|
||||
@@ -14,7 +14,7 @@ export class AcpFileSystemService implements FileSystemService {
|
||||
constructor(
|
||||
private readonly connection: acp.AgentSideConnection,
|
||||
private readonly sessionId: string,
|
||||
private readonly capabilities: acp.FileSystemCapabilities,
|
||||
private readonly capabilities: acp.FileSystemCapability,
|
||||
private readonly fallback: FileSystemService,
|
||||
) {}
|
||||
|
||||
|
||||
@@ -264,7 +264,6 @@ describe('mcp list command', () => {
|
||||
config: {
|
||||
'allowed-server': { url: 'http://allowed' },
|
||||
},
|
||||
requiredConfig: {},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ import {
|
||||
Config,
|
||||
resolveToRealPath,
|
||||
applyAdminAllowlist,
|
||||
applyRequiredServers,
|
||||
getAdminBlockedMcpServersMessage,
|
||||
type HookDefinition,
|
||||
type HookEventName,
|
||||
@@ -431,6 +430,8 @@ export async function loadCliConfig(
|
||||
const { cwd = process.cwd(), projectHooks } = options;
|
||||
const debugMode = isDebugMode(argv);
|
||||
|
||||
const loadedSettings = loadSettings(cwd);
|
||||
|
||||
if (argv.sandbox) {
|
||||
process.env['GEMINI_SANDBOX'] = 'true';
|
||||
}
|
||||
@@ -703,19 +704,6 @@ export async function loadCliConfig(
|
||||
? defaultModel
|
||||
: specifiedModel || defaultModel;
|
||||
const sandboxConfig = await loadSandboxConfig(settings, argv);
|
||||
if (sandboxConfig) {
|
||||
const existingPaths = sandboxConfig.allowedPaths || [];
|
||||
if (settings.tools.sandboxAllowedPaths?.length) {
|
||||
sandboxConfig.allowedPaths = [
|
||||
...new Set([...existingPaths, ...settings.tools.sandboxAllowedPaths]),
|
||||
];
|
||||
}
|
||||
if (settings.tools.sandboxNetworkAccess !== undefined) {
|
||||
sandboxConfig.networkAccess =
|
||||
sandboxConfig.networkAccess || settings.tools.sandboxNetworkAccess;
|
||||
}
|
||||
}
|
||||
|
||||
const screenReader =
|
||||
argv.screenReader !== undefined
|
||||
? argv.screenReader
|
||||
@@ -751,25 +739,6 @@ export async function loadCliConfig(
|
||||
}
|
||||
}
|
||||
|
||||
// Apply admin-required MCP servers (injected regardless of allowlist)
|
||||
if (mcpEnabled) {
|
||||
const requiredMcpConfig = settings.admin?.mcp?.requiredConfig;
|
||||
if (requiredMcpConfig && Object.keys(requiredMcpConfig).length > 0) {
|
||||
const requiredResult = applyRequiredServers(
|
||||
mcpServers ?? {},
|
||||
requiredMcpConfig,
|
||||
);
|
||||
mcpServers = requiredResult.mcpServers;
|
||||
|
||||
if (requiredResult.requiredServerNames.length > 0) {
|
||||
coreEvents.emitConsoleLog(
|
||||
'info',
|
||||
`Admin-required MCP servers injected: ${requiredResult.requiredServerNames.join(', ')}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isAcpMode = !!argv.acp || !!argv.experimentalAcp;
|
||||
let clientName: string | undefined = undefined;
|
||||
if (isAcpMode) {
|
||||
@@ -873,7 +842,6 @@ export async function loadCliConfig(
|
||||
skillsSupport: settings.skills?.enabled ?? true,
|
||||
disabledSkills: settings.skills?.disabled,
|
||||
experimentalJitContext: settings.experimental?.jitContext,
|
||||
experimentalMemoryManager: settings.experimental?.memoryManager,
|
||||
modelSteering: settings.experimental?.modelSteering,
|
||||
topicUpdateNarration: settings.experimental?.topicUpdateNarration,
|
||||
toolOutputMasking: settings.experimental?.toolOutputMasking,
|
||||
@@ -918,7 +886,7 @@ export async function loadCliConfig(
|
||||
hooks: settings.hooks || {},
|
||||
disabledHooks: settings.hooksConfig?.disabled || [],
|
||||
projectHooks: projectHooks || {},
|
||||
onModelChange: (model: string) => saveModelChange(loadSettings(cwd), model),
|
||||
onModelChange: (model: string) => saveModelChange(loadedSettings, model),
|
||||
onReload: async () => {
|
||||
const refreshedSettings = loadSettings(cwd);
|
||||
return {
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { copyExtension } from './extension-manager.js';
|
||||
|
||||
describe('copyExtension permissions', () => {
|
||||
let tempDir: string;
|
||||
let sourceDir: string;
|
||||
let destDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gemini-permission-test-'));
|
||||
sourceDir = path.join(tempDir, 'source');
|
||||
destDir = path.join(tempDir, 'dest');
|
||||
fs.mkdirSync(sourceDir);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Ensure we can delete the temp directory by making everything writable again
|
||||
const makeWritableSync = (p: string) => {
|
||||
try {
|
||||
const stats = fs.lstatSync(p);
|
||||
fs.chmodSync(p, stats.mode | 0o700);
|
||||
if (stats.isDirectory()) {
|
||||
fs.readdirSync(p).forEach((child) =>
|
||||
makeWritableSync(path.join(p, child)),
|
||||
);
|
||||
}
|
||||
} catch (_e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
};
|
||||
|
||||
if (fs.existsSync(tempDir)) {
|
||||
makeWritableSync(tempDir);
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('should make destination writable even if source is read-only', async () => {
|
||||
const fileName = 'test.txt';
|
||||
const filePath = path.join(sourceDir, fileName);
|
||||
fs.writeFileSync(filePath, 'hello');
|
||||
|
||||
// Make source read-only: 0o555 for directory, 0o444 for file
|
||||
fs.chmodSync(filePath, 0o444);
|
||||
fs.chmodSync(sourceDir, 0o555);
|
||||
|
||||
// Verify source is read-only
|
||||
expect(() => fs.writeFileSync(filePath, 'fail')).toThrow();
|
||||
|
||||
// Perform copy
|
||||
await copyExtension(sourceDir, destDir);
|
||||
|
||||
// Verify destination is writable
|
||||
const destFilePath = path.join(destDir, fileName);
|
||||
const destFileStats = fs.statSync(destFilePath);
|
||||
const destDirStats = fs.statSync(destDir);
|
||||
|
||||
// Check that owner write bits are set (0o200)
|
||||
expect(destFileStats.mode & 0o200).toBe(0o200);
|
||||
expect(destDirStats.mode & 0o200).toBe(0o200);
|
||||
|
||||
// Verify we can actually write to the destination file
|
||||
fs.writeFileSync(destFilePath, 'writable');
|
||||
expect(fs.readFileSync(destFilePath, 'utf-8')).toBe('writable');
|
||||
|
||||
// Verify we can delete the destination (which requires write bit on destDir)
|
||||
fs.rmSync(destFilePath);
|
||||
expect(fs.existsSync(destFilePath)).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle nested directories with restrictive permissions', async () => {
|
||||
const subDir = path.join(sourceDir, 'subdir');
|
||||
fs.mkdirSync(subDir);
|
||||
const fileName = 'nested.txt';
|
||||
const filePath = path.join(subDir, fileName);
|
||||
fs.writeFileSync(filePath, 'nested content');
|
||||
|
||||
// Make nested structure read-only
|
||||
fs.chmodSync(filePath, 0o444);
|
||||
fs.chmodSync(subDir, 0o555);
|
||||
fs.chmodSync(sourceDir, 0o555);
|
||||
|
||||
// Perform copy
|
||||
await copyExtension(sourceDir, destDir);
|
||||
|
||||
// Verify nested destination is writable
|
||||
const destSubDir = path.join(destDir, 'subdir');
|
||||
const destFilePath = path.join(destSubDir, fileName);
|
||||
|
||||
expect(fs.statSync(destSubDir).mode & 0o200).toBe(0o200);
|
||||
expect(fs.statSync(destFilePath).mode & 0o200).toBe(0o200);
|
||||
|
||||
// Verify we can delete the whole destination tree
|
||||
await fs.promises.rm(destDir, { recursive: true, force: true });
|
||||
expect(fs.existsSync(destDir)).toBe(false);
|
||||
});
|
||||
|
||||
it('should not follow symlinks or modify symlink targets', async () => {
|
||||
const symlinkTarget = path.join(tempDir, 'external-target');
|
||||
fs.writeFileSync(symlinkTarget, 'external content');
|
||||
// Target is read-only
|
||||
fs.chmodSync(symlinkTarget, 0o444);
|
||||
|
||||
const symlinkPath = path.join(sourceDir, 'symlink-file');
|
||||
fs.symlinkSync(symlinkTarget, symlinkPath);
|
||||
|
||||
// Perform copy
|
||||
await copyExtension(sourceDir, destDir);
|
||||
|
||||
const destSymlinkPath = path.join(destDir, 'symlink-file');
|
||||
const destSymlinkStats = fs.lstatSync(destSymlinkPath);
|
||||
|
||||
// Verify it is still a symlink in the destination
|
||||
expect(destSymlinkStats.isSymbolicLink()).toBe(true);
|
||||
|
||||
// Verify the target (external to the extension) was NOT modified
|
||||
const targetStats = fs.statSync(symlinkTarget);
|
||||
// Owner write bit should still NOT be set (0o200)
|
||||
expect(targetStats.mode & 0o200).toBe(0o000);
|
||||
|
||||
// Clean up
|
||||
fs.chmodSync(symlinkTarget, 0o644);
|
||||
});
|
||||
});
|
||||
@@ -15,10 +15,6 @@ import { createExtension } from '../test-utils/createExtension.js';
|
||||
import { EXTENSIONS_DIRECTORY_NAME } from './extensions/variables.js';
|
||||
|
||||
const mockHomedir = vi.hoisted(() => vi.fn(() => '/tmp/mock-home'));
|
||||
const mockIntegrityManager = vi.hoisted(() => ({
|
||||
verify: vi.fn().mockResolvedValue('verified'),
|
||||
store: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
vi.mock('node:os', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('node:os')>();
|
||||
@@ -35,9 +31,6 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
return {
|
||||
...actual,
|
||||
homedir: mockHomedir,
|
||||
ExtensionIntegrityManager: vi
|
||||
.fn()
|
||||
.mockImplementation(() => mockIntegrityManager),
|
||||
loadAgentsFromDirectory: vi
|
||||
.fn()
|
||||
.mockImplementation(async () => ({ agents: [], errors: [] })),
|
||||
@@ -71,7 +64,6 @@ describe('ExtensionManager skills validation', () => {
|
||||
requestConsent: vi.fn().mockResolvedValue(true),
|
||||
requestSetting: vi.fn(),
|
||||
workspaceDir: tempDir,
|
||||
integrityManager: mockIntegrityManager,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -147,7 +139,6 @@ describe('ExtensionManager skills validation', () => {
|
||||
requestConsent: vi.fn().mockResolvedValue(true),
|
||||
requestSetting: vi.fn(),
|
||||
workspaceDir: tempDir,
|
||||
integrityManager: mockIntegrityManager,
|
||||
});
|
||||
|
||||
// 4. Load extensions
|
||||
|
||||
@@ -1248,32 +1248,11 @@ function filterMcpConfig(original: MCPServerConfig): MCPServerConfig {
|
||||
return Object.freeze(rest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively ensures that the owner has write permissions for all files
|
||||
* and directories within the target path.
|
||||
*/
|
||||
async function makeWritableRecursive(targetPath: string): Promise<void> {
|
||||
const stats = await fs.promises.lstat(targetPath);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
// Ensure directory is rwx for the owner (0o700)
|
||||
await fs.promises.chmod(targetPath, stats.mode | 0o700);
|
||||
const children = await fs.promises.readdir(targetPath);
|
||||
for (const child of children) {
|
||||
await makeWritableRecursive(path.join(targetPath, child));
|
||||
}
|
||||
} else if (stats.isFile()) {
|
||||
// Ensure file is rw for the owner (0o600)
|
||||
await fs.promises.chmod(targetPath, stats.mode | 0o600);
|
||||
}
|
||||
}
|
||||
|
||||
export async function copyExtension(
|
||||
source: string,
|
||||
destination: string,
|
||||
): Promise<void> {
|
||||
await fs.promises.cp(source, destination, { recursive: true });
|
||||
await makeWritableRecursive(destination);
|
||||
}
|
||||
|
||||
function getContextFileNames(config: ExtensionConfig): string[] {
|
||||
|
||||
@@ -36,8 +36,6 @@ vi.mock('node:fs', async (importOriginal) => {
|
||||
rm: vi.fn(),
|
||||
cp: vi.fn(),
|
||||
readFile: vi.fn(),
|
||||
lstat: vi.fn(),
|
||||
chmod: vi.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -145,11 +143,6 @@ describe('extensionUpdates', () => {
|
||||
vi.mocked(fs.promises.rm).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.promises.cp).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.promises.readdir).mockResolvedValue([]);
|
||||
vi.mocked(fs.promises.lstat).mockResolvedValue({
|
||||
isDirectory: () => true,
|
||||
mode: 0o755,
|
||||
} as unknown as fs.Stats);
|
||||
vi.mocked(fs.promises.chmod).mockResolvedValue(undefined);
|
||||
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
||||
isTrusted: true,
|
||||
source: 'file',
|
||||
|
||||
@@ -516,9 +516,7 @@ describe('Policy Engine Integration Tests', () => {
|
||||
);
|
||||
expect(mcpServerRule?.priority).toBe(4.1); // MCP allowed server
|
||||
|
||||
const readOnlyToolRule = rules.find(
|
||||
(r) => r.toolName === 'glob' && !r.subagent,
|
||||
);
|
||||
const readOnlyToolRule = rules.find((r) => r.toolName === 'glob');
|
||||
// Priority 70 in default tier → 1.07 (Overriding Plan Mode Deny)
|
||||
expect(readOnlyToolRule?.priority).toBeCloseTo(1.07, 5);
|
||||
|
||||
@@ -675,7 +673,7 @@ describe('Policy Engine Integration Tests', () => {
|
||||
const server1Rule = rules.find((r) => r.toolName === 'mcp_server1_*');
|
||||
expect(server1Rule?.priority).toBe(4.1); // Allowed servers (user tier)
|
||||
|
||||
const globRule = rules.find((r) => r.toolName === 'glob' && !r.subagent);
|
||||
const globRule = rules.find((r) => r.toolName === 'glob');
|
||||
// Priority 70 in default tier → 1.07
|
||||
expect(globRule?.priority).toBeCloseTo(1.07, 5); // Auto-accept read-only
|
||||
|
||||
|
||||
@@ -338,8 +338,6 @@ describe('loadSandboxConfig', () => {
|
||||
sandbox: {
|
||||
enabled: true,
|
||||
command: 'podman',
|
||||
allowedPaths: [],
|
||||
networkAccess: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -355,8 +353,6 @@ describe('loadSandboxConfig', () => {
|
||||
sandbox: {
|
||||
enabled: true,
|
||||
image: 'custom/image',
|
||||
allowedPaths: [],
|
||||
networkAccess: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -371,8 +367,6 @@ describe('loadSandboxConfig', () => {
|
||||
tools: {
|
||||
sandbox: {
|
||||
enabled: false,
|
||||
allowedPaths: [],
|
||||
networkAccess: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -388,7 +382,6 @@ describe('loadSandboxConfig', () => {
|
||||
sandbox: {
|
||||
enabled: true,
|
||||
allowedPaths: ['/settings-path'],
|
||||
networkAccess: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -29,7 +29,6 @@ const VALID_SANDBOX_COMMANDS = [
|
||||
'sandbox-exec',
|
||||
'runsc',
|
||||
'lxc',
|
||||
'windows-native',
|
||||
];
|
||||
|
||||
function isSandboxCommand(
|
||||
@@ -76,15 +75,8 @@ function getSandboxCommand(
|
||||
'gVisor (runsc) sandboxing is only supported on Linux',
|
||||
);
|
||||
}
|
||||
// windows-native is only supported on Windows
|
||||
if (sandbox === 'windows-native' && os.platform() !== 'win32') {
|
||||
throw new FatalSandboxError(
|
||||
'Windows native sandboxing is only supported on Windows',
|
||||
);
|
||||
}
|
||||
|
||||
// confirm that specified command exists (unless it's built-in)
|
||||
if (sandbox !== 'windows-native' && !commandExists.sync(sandbox)) {
|
||||
// confirm that specified command exists
|
||||
if (!commandExists.sync(sandbox)) {
|
||||
throw new FatalSandboxError(
|
||||
`Missing sandbox command '${sandbox}' (from GEMINI_SANDBOX)`,
|
||||
);
|
||||
@@ -157,12 +149,7 @@ export async function loadSandboxConfig(
|
||||
customImage ??
|
||||
packageJson?.config?.sandboxImageUri;
|
||||
|
||||
const isNative =
|
||||
command === 'windows-native' ||
|
||||
command === 'sandbox-exec' ||
|
||||
command === 'lxc';
|
||||
|
||||
return command && (image || isNative)
|
||||
return command && image
|
||||
? { enabled: true, allowedPaths, networkAccess, command, image }
|
||||
: undefined;
|
||||
}
|
||||
|
||||
@@ -2751,28 +2751,6 @@ describe('Settings Loading and Merging', () => {
|
||||
expect(loadedSettings.merged.admin?.mcp?.config).toEqual(mcpServers);
|
||||
});
|
||||
|
||||
it('should map requiredMcpConfig from remote settings', () => {
|
||||
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
const requiredMcpConfig = {
|
||||
'corp-tool': {
|
||||
url: 'https://mcp.corp/tool',
|
||||
type: 'http' as const,
|
||||
trust: true,
|
||||
},
|
||||
};
|
||||
|
||||
loadedSettings.setRemoteAdminSettings({
|
||||
mcpSetting: {
|
||||
mcpEnabled: true,
|
||||
requiredMcpConfig,
|
||||
},
|
||||
});
|
||||
|
||||
expect(loadedSettings.merged.admin?.mcp?.requiredConfig).toEqual(
|
||||
requiredMcpConfig,
|
||||
);
|
||||
});
|
||||
|
||||
it('should set skills based on unmanagedCapabilitiesEnabled', () => {
|
||||
const loadedSettings = loadSettings();
|
||||
loadedSettings.setRemoteAdminSettings({
|
||||
|
||||
@@ -480,7 +480,6 @@ export class LoadedSettings {
|
||||
admin.mcp = {
|
||||
enabled: mcpSetting?.mcpEnabled,
|
||||
config: mcpSetting?.mcpConfig?.mcpServers,
|
||||
requiredConfig: mcpSetting?.requiredMcpConfig,
|
||||
};
|
||||
admin.extensions = {
|
||||
enabled: cliFeatureSetting?.extensionsSetting?.extensionsEnabled,
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
import {
|
||||
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
|
||||
DEFAULT_MODEL_CONFIGS,
|
||||
AuthProviderType,
|
||||
type MCPServerConfig,
|
||||
type RequiredMcpServerConfig,
|
||||
type BugCommandSettings,
|
||||
type TelemetrySettings,
|
||||
type AuthType,
|
||||
@@ -1083,20 +1081,6 @@ const SETTINGS_SCHEMA = {
|
||||
ref: 'ModelResolution',
|
||||
},
|
||||
},
|
||||
modelChains: {
|
||||
type: 'object',
|
||||
label: 'Model Chains',
|
||||
category: 'Model',
|
||||
requiresRestart: true,
|
||||
default: DEFAULT_MODEL_CONFIGS.modelChains,
|
||||
description:
|
||||
'Availability policy chains defining fallback behavior for models.',
|
||||
showInDialog: false,
|
||||
additionalProperties: {
|
||||
type: 'array',
|
||||
ref: 'ModelPolicy',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1360,30 +1344,10 @@ const SETTINGS_SCHEMA = {
|
||||
description: oneLine`
|
||||
Legacy full-process sandbox execution environment.
|
||||
Set to a boolean to enable or disable the sandbox, provide a string path to a sandbox profile,
|
||||
or specify an explicit sandbox command (e.g., "docker", "podman", "lxc", "windows-native").
|
||||
or specify an explicit sandbox command (e.g., "docker", "podman", "lxc").
|
||||
`,
|
||||
showInDialog: false,
|
||||
},
|
||||
sandboxAllowedPaths: {
|
||||
type: 'array',
|
||||
label: 'Sandbox Allowed Paths',
|
||||
category: 'Tools',
|
||||
requiresRestart: true,
|
||||
default: [] as string[],
|
||||
description:
|
||||
'List of additional paths that the sandbox is allowed to access.',
|
||||
showInDialog: true,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
sandboxNetworkAccess: {
|
||||
type: 'boolean',
|
||||
label: 'Sandbox Network Access',
|
||||
category: 'Tools',
|
||||
requiresRestart: true,
|
||||
default: false,
|
||||
description: 'Whether the sandbox is allowed to access the network.',
|
||||
showInDialog: true,
|
||||
},
|
||||
shell: {
|
||||
type: 'object',
|
||||
label: 'Shell',
|
||||
@@ -2081,16 +2045,6 @@ const SETTINGS_SCHEMA = {
|
||||
},
|
||||
},
|
||||
},
|
||||
memoryManager: {
|
||||
type: 'boolean',
|
||||
label: 'Memory Manager Agent',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: false,
|
||||
description:
|
||||
'Replace the built-in save_memory tool with a memory manager subagent that supports adding, removing, de-duplicating, and organizing memories.',
|
||||
showInDialog: true,
|
||||
},
|
||||
topicUpdateNarration: {
|
||||
type: 'boolean',
|
||||
label: 'Topic & Update Narration',
|
||||
@@ -2437,7 +2391,7 @@ const SETTINGS_SCHEMA = {
|
||||
category: 'Admin',
|
||||
requiresRestart: false,
|
||||
default: {} as Record<string, MCPServerConfig>,
|
||||
description: 'Admin-configured MCP servers (allowlist).',
|
||||
description: 'Admin-configured MCP servers.',
|
||||
showInDialog: false,
|
||||
mergeStrategy: MergeStrategy.REPLACE,
|
||||
additionalProperties: {
|
||||
@@ -2445,20 +2399,6 @@ const SETTINGS_SCHEMA = {
|
||||
ref: 'MCPServerConfig',
|
||||
},
|
||||
},
|
||||
requiredConfig: {
|
||||
type: 'object',
|
||||
label: 'Required MCP Config',
|
||||
category: 'Admin',
|
||||
requiresRestart: false,
|
||||
default: {} as Record<string, RequiredMcpServerConfig>,
|
||||
description: 'Admin-required MCP servers that are always injected.',
|
||||
showInDialog: false,
|
||||
mergeStrategy: MergeStrategy.REPLACE,
|
||||
additionalProperties: {
|
||||
type: 'object',
|
||||
ref: 'RequiredMcpServerConfig',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
skills: {
|
||||
@@ -2583,72 +2523,11 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record<
|
||||
type: 'string',
|
||||
description:
|
||||
'Authentication provider used for acquiring credentials (for example `dynamic_discovery`).',
|
||||
enum: Object.values(AuthProviderType),
|
||||
},
|
||||
targetAudience: {
|
||||
type: 'string',
|
||||
description:
|
||||
'OAuth target audience (CLIENT_ID.apps.googleusercontent.com).',
|
||||
},
|
||||
targetServiceAccount: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Service account email to impersonate (name@project.iam.gserviceaccount.com).',
|
||||
},
|
||||
},
|
||||
},
|
||||
RequiredMcpServerConfig: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Admin-required MCP server configuration (remote transports only).',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'URL for the required MCP server.',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'Transport type for the required server.',
|
||||
enum: ['sse', 'http'],
|
||||
},
|
||||
headers: {
|
||||
type: 'object',
|
||||
description: 'Additional HTTP headers sent to the server.',
|
||||
additionalProperties: { type: 'string' },
|
||||
},
|
||||
timeout: {
|
||||
type: 'number',
|
||||
description: 'Timeout in milliseconds for MCP requests.',
|
||||
},
|
||||
trust: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Marks the server as trusted. Defaults to true for admin-required servers.',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: 'Human-readable description of the server.',
|
||||
},
|
||||
includeTools: {
|
||||
type: 'array',
|
||||
description: 'Subset of tools enabled for this server.',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
excludeTools: {
|
||||
type: 'array',
|
||||
description: 'Tools disabled for this server.',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
oauth: {
|
||||
type: 'object',
|
||||
description: 'OAuth configuration for authenticating with the server.',
|
||||
additionalProperties: true,
|
||||
},
|
||||
authProviderType: {
|
||||
type: 'string',
|
||||
description: 'Authentication provider used for acquiring credentials.',
|
||||
enum: Object.values(AuthProviderType),
|
||||
enum: [
|
||||
'dynamic_discovery',
|
||||
'google_credentials',
|
||||
'service_account_impersonation',
|
||||
],
|
||||
},
|
||||
targetAudience: {
|
||||
type: 'string',
|
||||
@@ -2988,34 +2867,6 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record<
|
||||
},
|
||||
},
|
||||
},
|
||||
ModelPolicy: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Defines the policy for a single model in the availability chain.',
|
||||
properties: {
|
||||
model: { type: 'string' },
|
||||
isLastResort: { type: 'boolean' },
|
||||
actions: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
terminal: { type: 'string', enum: ['silent', 'prompt'] },
|
||||
transient: { type: 'string', enum: ['silent', 'prompt'] },
|
||||
not_found: { type: 'string', enum: ['silent', 'prompt'] },
|
||||
unknown: { type: 'string', enum: ['silent', 'prompt'] },
|
||||
},
|
||||
},
|
||||
stateTransitions: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
terminal: { type: 'string', enum: ['terminal', 'sticky_retry'] },
|
||||
transient: { type: 'string', enum: ['terminal', 'sticky_retry'] },
|
||||
not_found: { type: 'string', enum: ['terminal', 'sticky_retry'] },
|
||||
unknown: { type: 'string', enum: ['terminal', 'sticky_retry'] },
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['model'],
|
||||
},
|
||||
};
|
||||
|
||||
export function getSettingsSchema(): SettingsSchemaType {
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('Model Steering Integration', () => {
|
||||
configOverrides: { modelSteering: true },
|
||||
});
|
||||
await rig.initialize();
|
||||
await rig.render();
|
||||
rig.render();
|
||||
await rig.waitForIdle();
|
||||
|
||||
rig.setToolPolicy('list_directory', PolicyDecision.ASK_USER);
|
||||
|
||||
@@ -101,8 +101,18 @@ export async function startInteractiveUI(
|
||||
return (
|
||||
<SettingsContext.Provider value={settings}>
|
||||
<KeyMatchersProvider value={matchers}>
|
||||
<KeypressProvider config={config}>
|
||||
<MouseProvider mouseEventsEnabled={mouseEventsEnabled}>
|
||||
<KeypressProvider
|
||||
config={config}
|
||||
debugKeystrokeLogging={
|
||||
settings.merged.general.debugKeystrokeLogging
|
||||
}
|
||||
>
|
||||
<MouseProvider
|
||||
mouseEventsEnabled={mouseEventsEnabled}
|
||||
debugKeystrokeLogging={
|
||||
settings.merged.general.debugKeystrokeLogging
|
||||
}
|
||||
>
|
||||
<TerminalProvider>
|
||||
<ScrollProvider>
|
||||
<OverflowProvider>
|
||||
|
||||
@@ -65,9 +65,9 @@ export const handleSlashCommand = async (
|
||||
|
||||
const logger = new Logger(config?.getSessionId() || '', config?.storage);
|
||||
|
||||
const commandContext: CommandContext = {
|
||||
const context: CommandContext = {
|
||||
services: {
|
||||
agentContext: config,
|
||||
config,
|
||||
settings,
|
||||
git: undefined,
|
||||
logger,
|
||||
@@ -84,7 +84,7 @@ export const handleSlashCommand = async (
|
||||
},
|
||||
};
|
||||
|
||||
const result = await commandToExecute.action(commandContext, args);
|
||||
const result = await commandToExecute.action(context, args);
|
||||
|
||||
if (result) {
|
||||
switch (result.type) {
|
||||
|
||||
@@ -31,14 +31,11 @@ describe('AtFileProcessor', () => {
|
||||
|
||||
mockConfig = {
|
||||
// The processor only passes the config through, so we don't need a full mock.
|
||||
get config() {
|
||||
return this;
|
||||
},
|
||||
} as unknown as Config;
|
||||
|
||||
context = createMockCommandContext({
|
||||
services: {
|
||||
agentContext: mockConfig,
|
||||
config: mockConfig,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -63,7 +60,7 @@ describe('AtFileProcessor', () => {
|
||||
const prompt: PartUnion[] = [{ text: 'Analyze @{file.txt}' }];
|
||||
const contextWithoutConfig = createMockCommandContext({
|
||||
services: {
|
||||
agentContext: null,
|
||||
config: null,
|
||||
},
|
||||
});
|
||||
const result = await processor.process(prompt, contextWithoutConfig);
|
||||
|
||||
@@ -25,7 +25,7 @@ export class AtFileProcessor implements IPromptProcessor {
|
||||
input: PromptPipelineContent,
|
||||
context: CommandContext,
|
||||
): Promise<PromptPipelineContent> {
|
||||
const config = context.services.agentContext?.config;
|
||||
const config = context.services.config;
|
||||
if (!config) {
|
||||
return input;
|
||||
}
|
||||
|
||||
@@ -89,9 +89,6 @@ describe('ShellProcessor', () => {
|
||||
getPolicyEngine: vi.fn().mockReturnValue({
|
||||
check: mockPolicyEngineCheck,
|
||||
}),
|
||||
get config() {
|
||||
return this as unknown as Config;
|
||||
},
|
||||
};
|
||||
|
||||
context = createMockCommandContext({
|
||||
@@ -101,7 +98,7 @@ describe('ShellProcessor', () => {
|
||||
args: 'default args',
|
||||
},
|
||||
services: {
|
||||
agentContext: mockConfig as Config,
|
||||
config: mockConfig as Config,
|
||||
},
|
||||
session: {
|
||||
sessionShellAllowlist: new Set(),
|
||||
@@ -123,7 +120,7 @@ describe('ShellProcessor', () => {
|
||||
const prompt: PromptPipelineContent = createPromptPipelineContent('!{ls}');
|
||||
const contextWithoutConfig = createMockCommandContext({
|
||||
services: {
|
||||
agentContext: null,
|
||||
config: null,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ export class ShellProcessor implements IPromptProcessor {
|
||||
];
|
||||
}
|
||||
|
||||
const config = context.services.agentContext?.config;
|
||||
const config = context.services.config;
|
||||
if (!config) {
|
||||
throw new Error(
|
||||
`Security configuration not loaded. Cannot verify shell command permissions for '${this.commandName}'. Aborting.`,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, afterEach, expect } from 'vitest';
|
||||
import { act } from 'react';
|
||||
import { AppRig } from './AppRig.js';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
@@ -30,7 +31,7 @@ describe('AppRig', () => {
|
||||
configOverrides: { modelSteering: true },
|
||||
});
|
||||
await rig.initialize();
|
||||
await rig.render();
|
||||
rig.render();
|
||||
await rig.waitForIdle();
|
||||
|
||||
// Set breakpoints on the canonical tool names
|
||||
@@ -68,7 +69,12 @@ describe('AppRig', () => {
|
||||
);
|
||||
rig = new AppRig({ fakeResponsesPath });
|
||||
await rig.initialize();
|
||||
await rig.render();
|
||||
await act(async () => {
|
||||
rig!.render();
|
||||
// Allow async initializations (like banners) to settle within the act boundary
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
});
|
||||
|
||||
// Wait for initial render
|
||||
await rig.waitForIdle();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import { AppContainer } from '../ui/AppContainer.js';
|
||||
import { renderWithProviders, type RenderInstance } from './render.js';
|
||||
import { renderWithProviders } from './render.js';
|
||||
import {
|
||||
makeFakeConfig,
|
||||
type Config,
|
||||
@@ -155,7 +155,7 @@ export interface PendingConfirmation {
|
||||
}
|
||||
|
||||
export class AppRig {
|
||||
private renderResult: RenderInstance | undefined;
|
||||
private renderResult: ReturnType<typeof renderWithProviders> | undefined;
|
||||
private config: Config | undefined;
|
||||
private settings: LoadedSettings | undefined;
|
||||
private testDir: string;
|
||||
@@ -204,7 +204,6 @@ export class AppRig {
|
||||
enableEventDrivenScheduler: true,
|
||||
extensionLoader: new MockExtensionManager(),
|
||||
excludeTools: this.options.configOverrides?.excludeTools,
|
||||
useAlternateBuffer: false,
|
||||
...this.options.configOverrides,
|
||||
};
|
||||
this.config = makeFakeConfig(configParams);
|
||||
@@ -276,22 +275,19 @@ export class AppRig {
|
||||
enabled: false,
|
||||
hasSeenNudge: true,
|
||||
},
|
||||
ui: {
|
||||
useAlternateBuffer: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private stubRefreshAuth() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
||||
const gcConfig = this.config as any;
|
||||
gcConfig.refreshAuth = async (authMethod: AuthType) => {
|
||||
gcConfig.modelAvailabilityService.reset();
|
||||
|
||||
const newContentGeneratorConfig = {
|
||||
authType: authMethod,
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
proxy: gcConfig.getProxy(),
|
||||
apiKey: process.env['GEMINI_API_KEY'] || 'test-api-key',
|
||||
};
|
||||
@@ -393,12 +389,12 @@ export class AppRig {
|
||||
return isAnyToolActive || isAwaitingConfirmation;
|
||||
}
|
||||
|
||||
async render() {
|
||||
render() {
|
||||
if (!this.config || !this.settings)
|
||||
throw new Error('AppRig not initialized');
|
||||
|
||||
await act(async () => {
|
||||
this.renderResult = await renderWithProviders(
|
||||
act(() => {
|
||||
this.renderResult = renderWithProviders(
|
||||
<AppContainer
|
||||
config={this.config!}
|
||||
version="test-version"
|
||||
@@ -414,6 +410,7 @@ export class AppRig {
|
||||
config: this.config!,
|
||||
settings: this.settings!,
|
||||
width: this.options.terminalWidth ?? 120,
|
||||
useAlternateBuffer: false,
|
||||
uiState: {
|
||||
terminalHeight: this.options.terminalHeight ?? 40,
|
||||
},
|
||||
@@ -459,7 +456,7 @@ export class AppRig {
|
||||
const actualToolName = toolName === '*' ? undefined : toolName;
|
||||
this.config
|
||||
.getPolicyEngine()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
.removeRulesForTool(actualToolName as string, source);
|
||||
this.breakpointTools.delete(toolName);
|
||||
}
|
||||
@@ -732,7 +729,7 @@ export class AppRig {
|
||||
.getGeminiClient()
|
||||
?.getChatRecordingService();
|
||||
if (recordingService) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
|
||||
(recordingService as any).conversationFile = null;
|
||||
}
|
||||
}
|
||||
@@ -752,7 +749,7 @@ export class AppRig {
|
||||
MockShellExecutionService.reset();
|
||||
ideContextStore.clear();
|
||||
// Forcefully clear IdeClient singleton promise
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
|
||||
(IdeClient as any).instancePromise = null;
|
||||
vi.clearAllMocks();
|
||||
|
||||
|
||||
@@ -46,19 +46,15 @@ describe('createMockCommandContext', () => {
|
||||
|
||||
const overrides = {
|
||||
services: {
|
||||
agentContext: { config: mockConfig },
|
||||
config: mockConfig,
|
||||
},
|
||||
};
|
||||
|
||||
const context = createMockCommandContext(overrides);
|
||||
|
||||
expect(context.services.agentContext).toBeDefined();
|
||||
expect(context.services.agentContext?.config?.getModel()).toBe(
|
||||
'gemini-pro',
|
||||
);
|
||||
expect(context.services.agentContext?.config?.getProjectRoot()).toBe(
|
||||
'/test/project',
|
||||
);
|
||||
expect(context.services.config).toBeDefined();
|
||||
expect(context.services.config?.getModel()).toBe('gemini-pro');
|
||||
expect(context.services.config?.getProjectRoot()).toBe('/test/project');
|
||||
|
||||
// Verify a default property on the same nested object is still there
|
||||
expect(context.services.logger).toBeDefined();
|
||||
|
||||
@@ -36,15 +36,15 @@ export const createMockCommandContext = (
|
||||
args: '',
|
||||
},
|
||||
services: {
|
||||
agentContext: null,
|
||||
|
||||
config: null,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
settings: {
|
||||
merged: defaultMergedSettings,
|
||||
setValue: vi.fn(),
|
||||
forScope: vi.fn().mockReturnValue({ settings: {} }),
|
||||
} as unknown as LoadedSettings,
|
||||
git: undefined as GitService | undefined,
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-assignment
|
||||
logger: {
|
||||
log: vi.fn(),
|
||||
logMessage: vi.fn(),
|
||||
@@ -53,7 +53,7 @@ export const createMockCommandContext = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any, // Cast because Logger is a class.
|
||||
},
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-assignment
|
||||
ui: {
|
||||
addItem: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
@@ -72,7 +72,7 @@ export const createMockCommandContext = (
|
||||
} as any,
|
||||
session: {
|
||||
sessionShellAllowlist: new Set<string>(),
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
stats: {
|
||||
sessionStartTime: new Date(),
|
||||
lastPromptTokenCount: 0,
|
||||
@@ -93,12 +93,14 @@ export const createMockCommandContext = (
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const merge = (target: any, source: any): any => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const output = { ...target };
|
||||
|
||||
for (const key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const sourceValue = source[key];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const targetValue = output[key];
|
||||
|
||||
if (
|
||||
@@ -106,10 +108,11 @@ export const createMockCommandContext = (
|
||||
Object.prototype.toString.call(sourceValue) === '[object Object]' &&
|
||||
Object.prototype.toString.call(targetValue) === '[object Object]'
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
output[key] = merge(targetValue, sourceValue);
|
||||
} else {
|
||||
// If not, we do a direct assignment. This preserves Date objects and others.
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
output[key] = sourceValue;
|
||||
}
|
||||
}
|
||||
@@ -117,5 +120,6 @@ export const createMockCommandContext = (
|
||||
return output;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return merge(defaultMocks, overrides);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,9 @@ import { vi } from 'vitest';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import type React from 'react';
|
||||
import { act, useState } from 'react';
|
||||
import type { LoadedSettings } from '../config/settings.js';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { LoadedSettings } from '../config/settings.js';
|
||||
import { KeypressProvider } from '../ui/contexts/KeypressContext.js';
|
||||
import { SettingsContext } from '../ui/contexts/SettingsContext.js';
|
||||
import { ShellFocusContext } from '../ui/contexts/ShellFocusContext.js';
|
||||
@@ -42,7 +44,7 @@ import {
|
||||
type OverflowState,
|
||||
} from '../ui/contexts/OverflowContext.js';
|
||||
|
||||
import { type Config } from '@google/gemini-cli-core';
|
||||
import { makeFakeConfig, type Config } from '@google/gemini-cli-core';
|
||||
import { FakePersistentState } from './persistentStateFake.js';
|
||||
import { AppContext, type AppState } from '../ui/contexts/AppContext.js';
|
||||
import { createMockSettings } from './settings.js';
|
||||
@@ -51,7 +53,6 @@ import { themeManager, DEFAULT_THEME } from '../ui/themes/theme-manager.js';
|
||||
import { DefaultLight } from '../ui/themes/builtin/light/default-light.js';
|
||||
import { pickDefaultThemeName } from '../ui/themes/theme.js';
|
||||
import { generateSvgForTerminal } from './svg.js';
|
||||
import { loadCliConfig, type CliArgs } from '../config/config.js';
|
||||
|
||||
export const persistentStateMock = new FakePersistentState();
|
||||
|
||||
@@ -65,9 +66,7 @@ if (process.env['NODE_ENV'] === 'test') {
|
||||
}
|
||||
|
||||
vi.mock('../utils/persistentState.js', () => ({
|
||||
get persistentState() {
|
||||
return persistentStateMock;
|
||||
},
|
||||
persistentState: persistentStateMock,
|
||||
}));
|
||||
|
||||
vi.mock('../ui/utils/terminalUtils.js', () => ({
|
||||
@@ -417,10 +416,11 @@ export const render = (
|
||||
stdout.clear();
|
||||
act(() => {
|
||||
instance = inkRenderDirect(tree, {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
stdout: stdout as unknown as NodeJS.WriteStream,
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
stderr: stderr as unknown as NodeJS.WriteStream,
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
stdin: stdin as unknown as NodeJS.ReadStream,
|
||||
debug: false,
|
||||
exitOnCtrlC: false,
|
||||
@@ -487,7 +487,60 @@ export const simulateClick = async (
|
||||
});
|
||||
};
|
||||
|
||||
export const mockSettings = createMockSettings();
|
||||
let mockConfigInternal: Config | undefined;
|
||||
|
||||
const getMockConfigInternal = (): Config => {
|
||||
if (!mockConfigInternal) {
|
||||
mockConfigInternal = makeFakeConfig({
|
||||
targetDir: os.tmpdir(),
|
||||
enableEventDrivenScheduler: true,
|
||||
});
|
||||
}
|
||||
return mockConfigInternal;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const configProxy = new Proxy({} as Config, {
|
||||
get(_target, prop) {
|
||||
if (prop === 'getTargetDir') {
|
||||
return () =>
|
||||
path.join(
|
||||
path.parse(process.cwd()).root,
|
||||
'Users',
|
||||
'test',
|
||||
'project',
|
||||
'foo',
|
||||
'bar',
|
||||
'and',
|
||||
'some',
|
||||
'more',
|
||||
'directories',
|
||||
'to',
|
||||
'make',
|
||||
'it',
|
||||
'long',
|
||||
);
|
||||
}
|
||||
if (prop === 'getUseBackgroundColor') {
|
||||
return () => true;
|
||||
}
|
||||
const internal = getMockConfigInternal();
|
||||
if (prop in internal) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return internal[prop as keyof typeof internal];
|
||||
}
|
||||
throw new Error(`mockConfig does not have property ${String(prop)}`);
|
||||
},
|
||||
});
|
||||
|
||||
export const mockSettings = new LoadedSettings(
|
||||
{ path: '', settings: {}, originalSettings: {} },
|
||||
{ path: '', settings: {}, originalSettings: {} },
|
||||
{ path: '', settings: {}, originalSettings: {} },
|
||||
{ path: '', settings: {}, originalSettings: {} },
|
||||
true,
|
||||
[],
|
||||
);
|
||||
|
||||
// A minimal mock UIState to satisfy the context provider.
|
||||
// Tests that need specific UIState values should provide their own.
|
||||
@@ -596,7 +649,7 @@ const ContextCapture: React.FC<{ children: React.ReactNode }> = ({
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export const renderWithProviders = async (
|
||||
export const renderWithProviders = (
|
||||
component: React.ReactElement,
|
||||
{
|
||||
shellFocus = true,
|
||||
@@ -604,7 +657,9 @@ export const renderWithProviders = async (
|
||||
uiState: providedUiState,
|
||||
width,
|
||||
mouseEventsEnabled = false,
|
||||
config,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
config = configProxy as unknown as Config,
|
||||
useAlternateBuffer = true,
|
||||
uiActions,
|
||||
persistentState,
|
||||
appState = mockAppState,
|
||||
@@ -615,6 +670,7 @@ export const renderWithProviders = async (
|
||||
width?: number;
|
||||
mouseEventsEnabled?: boolean;
|
||||
config?: Config;
|
||||
useAlternateBuffer?: boolean;
|
||||
uiActions?: Partial<UIActions>;
|
||||
persistentState?: {
|
||||
get?: typeof persistentStateMock.get;
|
||||
@@ -622,26 +678,27 @@ export const renderWithProviders = async (
|
||||
};
|
||||
appState?: AppState;
|
||||
} = {},
|
||||
): Promise<
|
||||
RenderInstance & {
|
||||
simulateClick: (
|
||||
col: number,
|
||||
row: number,
|
||||
button?: 0 | 1 | 2,
|
||||
) => Promise<void>;
|
||||
}
|
||||
> => {
|
||||
): RenderInstance & {
|
||||
simulateClick: (
|
||||
col: number,
|
||||
row: number,
|
||||
button?: 0 | 1 | 2,
|
||||
) => Promise<void>;
|
||||
} => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const baseState: UIState = new Proxy(
|
||||
{ ...baseMockUiState, ...providedUiState },
|
||||
{
|
||||
get(target, prop) {
|
||||
if (prop in target) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return target[prop as keyof typeof target];
|
||||
}
|
||||
// For properties not in the base mock or provided state,
|
||||
// we'll check the original proxy to see if it's a defined but
|
||||
// unprovided property, and if not, throw.
|
||||
if (prop in baseMockUiState) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return baseMockUiState[prop as keyof typeof baseMockUiState];
|
||||
}
|
||||
throw new Error(`mockUiState does not have property ${String(prop)}`);
|
||||
@@ -659,14 +716,30 @@ export const renderWithProviders = async (
|
||||
persistentStateMock.mockClear();
|
||||
|
||||
const terminalWidth = width ?? baseState.terminalWidth;
|
||||
let finalSettings = settings;
|
||||
if (useAlternateBuffer !== undefined) {
|
||||
finalSettings = createMockSettings({
|
||||
...settings.merged,
|
||||
ui: {
|
||||
...settings.merged.ui,
|
||||
useAlternateBuffer,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
config = await loadCliConfig(
|
||||
settings.merged,
|
||||
'random-session-id',
|
||||
{} as unknown as CliArgs,
|
||||
{ cwd: '/' },
|
||||
);
|
||||
// Wrap config in a Proxy so useAlternateBuffer hook (which reads from Config) gets the correct value,
|
||||
// without replacing the entire config object and its other values.
|
||||
let finalConfig = config;
|
||||
if (useAlternateBuffer !== undefined) {
|
||||
finalConfig = new Proxy(config, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop === 'getUseAlternateBuffer') {
|
||||
return () => useAlternateBuffer;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return Reflect.get(target, prop, receiver);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const mainAreaWidth = terminalWidth;
|
||||
@@ -695,10 +768,10 @@ export const renderWithProviders = async (
|
||||
capturedOverflowState = undefined;
|
||||
capturedOverflowActions = undefined;
|
||||
|
||||
const wrapWithProviders = (comp: React.ReactElement) => (
|
||||
const renderResult = render(
|
||||
<AppContext.Provider value={appState}>
|
||||
<ConfigContext.Provider value={config}>
|
||||
<SettingsContext.Provider value={settings}>
|
||||
<ConfigContext.Provider value={finalConfig}>
|
||||
<SettingsContext.Provider value={finalSettings}>
|
||||
<UIStateContext.Provider value={finalUiState}>
|
||||
<VimModeProvider>
|
||||
<ShellFocusContext.Provider value={shellFocus}>
|
||||
@@ -709,7 +782,7 @@ export const renderWithProviders = async (
|
||||
<UIActionsContext.Provider value={finalUIActions}>
|
||||
<OverflowProvider>
|
||||
<ToolActionsProvider
|
||||
config={config}
|
||||
config={finalConfig}
|
||||
toolCalls={allToolCalls}
|
||||
>
|
||||
<AskUserActionsProvider
|
||||
@@ -730,7 +803,7 @@ export const renderWithProviders = async (
|
||||
flexGrow={0}
|
||||
flexDirection="column"
|
||||
>
|
||||
{comp}
|
||||
{component}
|
||||
</Box>
|
||||
</ContextCapture>
|
||||
</ScrollProvider>
|
||||
@@ -748,16 +821,12 @@ export const renderWithProviders = async (
|
||||
</UIStateContext.Provider>
|
||||
</SettingsContext.Provider>
|
||||
</ConfigContext.Provider>
|
||||
</AppContext.Provider>
|
||||
</AppContext.Provider>,
|
||||
terminalWidth,
|
||||
);
|
||||
|
||||
const renderResult = render(wrapWithProviders(component), terminalWidth);
|
||||
|
||||
return {
|
||||
...renderResult,
|
||||
rerender: (newComponent: React.ReactElement) => {
|
||||
renderResult.rerender(wrapWithProviders(newComponent));
|
||||
},
|
||||
capturedOverflowState,
|
||||
capturedOverflowActions,
|
||||
simulateClick: (col: number, row: number, button?: 0 | 1 | 2) =>
|
||||
@@ -778,8 +847,9 @@ export function renderHook<Result, Props>(
|
||||
waitUntilReady: () => Promise<void>;
|
||||
generateSvg: () => string;
|
||||
} {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const result = { current: undefined as unknown as Result };
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
let currentProps = options?.initialProps as Props;
|
||||
|
||||
function TestComponent({
|
||||
@@ -814,6 +884,7 @@ export function renderHook<Result, Props>(
|
||||
|
||||
function rerender(props?: Props) {
|
||||
if (arguments.length > 0) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
currentProps = props as Props;
|
||||
}
|
||||
act(() => {
|
||||
@@ -828,7 +899,7 @@ export function renderHook<Result, Props>(
|
||||
return { result, rerender, unmount, waitUntilReady, generateSvg };
|
||||
}
|
||||
|
||||
export async function renderHookWithProviders<Result, Props>(
|
||||
export function renderHookWithProviders<Result, Props>(
|
||||
renderCallback: (props: Props) => Result,
|
||||
options: {
|
||||
initialProps?: Props;
|
||||
@@ -840,14 +911,16 @@ export async function renderHookWithProviders<Result, Props>(
|
||||
width?: number;
|
||||
mouseEventsEnabled?: boolean;
|
||||
config?: Config;
|
||||
useAlternateBuffer?: boolean;
|
||||
} = {},
|
||||
): Promise<{
|
||||
): {
|
||||
result: { current: Result };
|
||||
rerender: (props?: Props) => void;
|
||||
unmount: () => void;
|
||||
waitUntilReady: () => Promise<void>;
|
||||
generateSvg: () => string;
|
||||
}> {
|
||||
} {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const result = { current: undefined as unknown as Result };
|
||||
|
||||
let setPropsFn: ((props: Props) => void) | undefined;
|
||||
@@ -866,10 +939,10 @@ export async function renderHookWithProviders<Result, Props>(
|
||||
|
||||
let renderResult: ReturnType<typeof render>;
|
||||
|
||||
await act(async () => {
|
||||
renderResult = await renderWithProviders(
|
||||
act(() => {
|
||||
renderResult = renderWithProviders(
|
||||
<Wrapper>
|
||||
{}
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion */}
|
||||
<TestComponent initialProps={options.initialProps as Props} />
|
||||
</Wrapper>,
|
||||
options,
|
||||
@@ -879,6 +952,7 @@ export async function renderHookWithProviders<Result, Props>(
|
||||
function rerender(newProps?: Props) {
|
||||
act(() => {
|
||||
if (arguments.length > 0 && setPropsFn) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
setPropsFn(newProps as Props);
|
||||
} else if (forceUpdateFn) {
|
||||
forceUpdateFn();
|
||||
|
||||
@@ -46,22 +46,23 @@ export const createMockSettings = (
|
||||
workspace,
|
||||
isTrusted,
|
||||
errors,
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
merged: mergedOverride,
|
||||
...settingsOverrides
|
||||
} = overrides;
|
||||
|
||||
const loaded = new LoadedSettings(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(system as any) || { path: '', settings: {}, originalSettings: {} },
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(systemDefaults as any) || { path: '', settings: {}, originalSettings: {} },
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(user as any) || {
|
||||
path: '',
|
||||
settings: settingsOverrides,
|
||||
originalSettings: settingsOverrides,
|
||||
},
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(workspace as any) || { path: '', settings: {}, originalSettings: {} },
|
||||
isTrusted ?? true,
|
||||
errors || [],
|
||||
@@ -75,6 +76,7 @@ export const createMockSettings = (
|
||||
// Assign any function overrides (e.g., vi.fn() for methods)
|
||||
for (const key in overrides) {
|
||||
if (typeof overrides[key] === 'function') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-assignment
|
||||
(loaded as any)[key] = overrides[key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import { describe, it, expect, vi, type Mock, beforeEach } from 'vitest';
|
||||
import type React from 'react';
|
||||
import { renderWithProviders } from '../test-utils/render.js';
|
||||
import { createMockSettings } from '../test-utils/settings.js';
|
||||
import { Text, useIsScreenReaderEnabled, type DOMElement } from 'ink';
|
||||
import { App } from './App.js';
|
||||
import { type UIState } from './contexts/UIStateContext.js';
|
||||
@@ -94,11 +93,11 @@ describe('App', () => {
|
||||
};
|
||||
|
||||
it('should render main content and composer when not quitting', async () => {
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState: mockUIState,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: false } }),
|
||||
useAlternateBuffer: false,
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -115,11 +114,11 @@ describe('App', () => {
|
||||
quittingMessages: [{ id: 1, type: 'user', text: 'test' }],
|
||||
} as UIState;
|
||||
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState: quittingUIState,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: false } }),
|
||||
useAlternateBuffer: false,
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -136,11 +135,11 @@ describe('App', () => {
|
||||
pendingHistoryItems: [{ type: 'user', text: 'pending item' }],
|
||||
} as UIState;
|
||||
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState: quittingUIState,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
|
||||
useAlternateBuffer: true,
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -156,11 +155,10 @@ describe('App', () => {
|
||||
dialogsVisible: true,
|
||||
} as UIState;
|
||||
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState: dialogUIState,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -183,11 +181,10 @@ describe('App', () => {
|
||||
[stateKey]: true,
|
||||
} as UIState;
|
||||
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -200,11 +197,10 @@ describe('App', () => {
|
||||
it('should render ScreenReaderAppLayout when screen reader is enabled', async () => {
|
||||
(useIsScreenReaderEnabled as Mock).mockReturnValue(true);
|
||||
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState: mockUIState,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -219,11 +215,10 @@ describe('App', () => {
|
||||
it('should render DefaultAppLayout when screen reader is not enabled', async () => {
|
||||
(useIsScreenReaderEnabled as Mock).mockReturnValue(false);
|
||||
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState: mockUIState,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -270,16 +265,15 @@ describe('App', () => {
|
||||
],
|
||||
} as UIState;
|
||||
|
||||
const configWithExperiment = makeFakeConfig({ useAlternateBuffer: true });
|
||||
const configWithExperiment = makeFakeConfig();
|
||||
vi.spyOn(configWithExperiment, 'isTrustedFolder').mockReturnValue(true);
|
||||
vi.spyOn(configWithExperiment, 'getIdeMode').mockReturnValue(false);
|
||||
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState: stateWithConfirmingTool,
|
||||
config: configWithExperiment,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -295,11 +289,10 @@ describe('App', () => {
|
||||
describe('Snapshots', () => {
|
||||
it('renders default layout correctly', async () => {
|
||||
(useIsScreenReaderEnabled as Mock).mockReturnValue(false);
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState: mockUIState,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -309,11 +302,10 @@ describe('App', () => {
|
||||
|
||||
it('renders screen reader layout correctly', async () => {
|
||||
(useIsScreenReaderEnabled as Mock).mockReturnValue(true);
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState: mockUIState,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -326,11 +318,10 @@ describe('App', () => {
|
||||
...mockUIState,
|
||||
dialogsVisible: true,
|
||||
} as UIState;
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<App />,
|
||||
{
|
||||
uiState: dialogUIState,
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
|
||||
@@ -95,8 +95,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
import ansiEscapes from 'ansi-escapes';
|
||||
import { type LoadedSettings } from '../config/settings.js';
|
||||
import { createMockSettings } from '../test-utils/settings.js';
|
||||
import { mergeSettings, type LoadedSettings } from '../config/settings.js';
|
||||
import type { InitializationResult } from '../core/initializer.js';
|
||||
import { useQuotaAndFallback } from './hooks/useQuotaAndFallback.js';
|
||||
import { StreamingState } from './types.js';
|
||||
@@ -212,7 +211,7 @@ import { useEditorSettings } from './hooks/useEditorSettings.js';
|
||||
import { useSettingsCommand } from './hooks/useSettingsCommand.js';
|
||||
import { useModelCommand } from './hooks/useModelCommand.js';
|
||||
import { useSlashCommandProcessor } from './hooks/slashCommandProcessor.js';
|
||||
import { useErrorCount } from './hooks/useConsoleMessages.js';
|
||||
import { useConsoleMessages } from './hooks/useConsoleMessages.js';
|
||||
import { useGeminiStream } from './hooks/useGeminiStream.js';
|
||||
import { useVim } from './hooks/vim.js';
|
||||
import { useFolderTrust } from './hooks/useFolderTrust.js';
|
||||
@@ -294,7 +293,7 @@ describe('AppContainer State Management', () => {
|
||||
const mockedUseSettingsCommand = useSettingsCommand as Mock;
|
||||
const mockedUseModelCommand = useModelCommand as Mock;
|
||||
const mockedUseSlashCommandProcessor = useSlashCommandProcessor as Mock;
|
||||
const mockedUseConsoleMessages = useErrorCount as Mock;
|
||||
const mockedUseConsoleMessages = useConsoleMessages as Mock;
|
||||
const mockedUseGeminiStream = useGeminiStream as Mock;
|
||||
const mockedUseVim = useVim as Mock;
|
||||
const mockedUseFolderTrust = useFolderTrust as Mock;
|
||||
@@ -396,9 +395,9 @@ describe('AppContainer State Management', () => {
|
||||
confirmationRequest: null,
|
||||
});
|
||||
mockedUseConsoleMessages.mockReturnValue({
|
||||
errorCount: 0,
|
||||
consoleMessages: [],
|
||||
handleNewMessage: vi.fn(),
|
||||
clearErrorCount: vi.fn(),
|
||||
clearConsoleMessages: vi.fn(),
|
||||
});
|
||||
mockedUseGeminiStream.mockReturnValue(DEFAULT_GEMINI_STREAM_MOCK);
|
||||
mockedUseVim.mockReturnValue({ handleInput: vi.fn() });
|
||||
@@ -485,18 +484,23 @@ describe('AppContainer State Management', () => {
|
||||
);
|
||||
|
||||
// Mock LoadedSettings
|
||||
mockSettings = createMockSettings({
|
||||
hideBanner: false,
|
||||
hideFooter: false,
|
||||
hideTips: false,
|
||||
showMemoryUsage: false,
|
||||
theme: 'default',
|
||||
ui: {
|
||||
showStatusInTitle: false,
|
||||
hideWindowTitle: false,
|
||||
useAlternateBuffer: false,
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
mockSettings = {
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
hideBanner: false,
|
||||
hideFooter: false,
|
||||
hideTips: false,
|
||||
showMemoryUsage: false,
|
||||
theme: 'default',
|
||||
ui: {
|
||||
...defaultMergedSettings.ui,
|
||||
showStatusInTitle: false,
|
||||
hideWindowTitle: false,
|
||||
useAlternateBuffer: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock InitializationResult
|
||||
mockInitResult = {
|
||||
@@ -1004,12 +1008,16 @@ describe('AppContainer State Management', () => {
|
||||
|
||||
describe('Settings Integration', () => {
|
||||
it('handles settings with all display options disabled', async () => {
|
||||
const settingsAllHidden = createMockSettings({
|
||||
hideBanner: true,
|
||||
hideFooter: true,
|
||||
hideTips: true,
|
||||
showMemoryUsage: false,
|
||||
});
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const settingsAllHidden = {
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
hideBanner: true,
|
||||
hideFooter: true,
|
||||
hideTips: true,
|
||||
showMemoryUsage: false,
|
||||
},
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
@@ -1021,9 +1029,16 @@ describe('AppContainer State Management', () => {
|
||||
});
|
||||
|
||||
it('handles settings with memory usage enabled', async () => {
|
||||
const settingsWithMemory = createMockSettings({
|
||||
showMemoryUsage: true,
|
||||
});
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const settingsWithMemory = {
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
hideBanner: false,
|
||||
hideFooter: false,
|
||||
hideTips: false,
|
||||
showMemoryUsage: true,
|
||||
},
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
@@ -1063,7 +1078,9 @@ describe('AppContainer State Management', () => {
|
||||
});
|
||||
|
||||
it('handles undefined settings gracefully', async () => {
|
||||
const undefinedSettings = createMockSettings();
|
||||
const undefinedSettings = {
|
||||
merged: mergeSettings({}, {}, {}, {}, true),
|
||||
} as LoadedSettings;
|
||||
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
@@ -1481,12 +1498,18 @@ describe('AppContainer State Management', () => {
|
||||
|
||||
it('should update terminal title with Working… when showStatusInTitle is false', () => {
|
||||
// Arrange: Set up mock settings with showStatusInTitle disabled
|
||||
const mockSettingsWithShowStatusFalse = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: false,
|
||||
hideWindowTitle: false,
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const mockSettingsWithShowStatusFalse = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
ui: {
|
||||
...defaultMergedSettings.ui,
|
||||
showStatusInTitle: false,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock the streaming state as Active
|
||||
mockedUseGeminiStream.mockReturnValue({
|
||||
@@ -1514,12 +1537,17 @@ describe('AppContainer State Management', () => {
|
||||
|
||||
it('should use legacy terminal title when dynamicWindowTitle is false', () => {
|
||||
// Arrange: Set up mock settings with dynamicWindowTitle disabled
|
||||
const mockSettingsWithDynamicTitleFalse = createMockSettings({
|
||||
ui: {
|
||||
dynamicWindowTitle: false,
|
||||
hideWindowTitle: false,
|
||||
const mockSettingsWithDynamicTitleFalse = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...mockSettings.merged,
|
||||
ui: {
|
||||
...mockSettings.merged.ui,
|
||||
dynamicWindowTitle: false,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock the streaming state
|
||||
mockedUseGeminiStream.mockReturnValue({
|
||||
@@ -1547,12 +1575,18 @@ describe('AppContainer State Management', () => {
|
||||
|
||||
it('should not update terminal title when hideWindowTitle is true', () => {
|
||||
// Arrange: Set up mock settings with hideWindowTitle enabled
|
||||
const mockSettingsWithHideTitleTrue = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: true,
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const mockSettingsWithHideTitleTrue = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
ui: {
|
||||
...defaultMergedSettings.ui,
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Act: Render the container
|
||||
const { unmount } = renderAppContainer({
|
||||
@@ -1570,12 +1604,18 @@ describe('AppContainer State Management', () => {
|
||||
|
||||
it('should update terminal title with thought subject when in active state', () => {
|
||||
// Arrange: Set up mock settings with showStatusInTitle enabled
|
||||
const mockSettingsWithTitleEnabled = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const mockSettingsWithTitleEnabled = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
ui: {
|
||||
...defaultMergedSettings.ui,
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock the streaming state and thought
|
||||
const thoughtSubject = 'Processing request';
|
||||
@@ -1604,12 +1644,18 @@ describe('AppContainer State Management', () => {
|
||||
|
||||
it('should update terminal title with default text when in Idle state and no thought subject', () => {
|
||||
// Arrange: Set up mock settings with showStatusInTitle enabled
|
||||
const mockSettingsWithTitleEnabled = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const mockSettingsWithTitleEnabled = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
ui: {
|
||||
...defaultMergedSettings.ui,
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock the streaming state as Idle with no thought
|
||||
mockedUseGeminiStream.mockReturnValue(DEFAULT_GEMINI_STREAM_MOCK);
|
||||
@@ -1633,12 +1679,18 @@ describe('AppContainer State Management', () => {
|
||||
|
||||
it('should update terminal title when in WaitingForConfirmation state with thought subject', async () => {
|
||||
// Arrange: Set up mock settings with showStatusInTitle enabled
|
||||
const mockSettingsWithTitleEnabled = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const mockSettingsWithTitleEnabled = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
ui: {
|
||||
...defaultMergedSettings.ui,
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock the streaming state and thought
|
||||
const thoughtSubject = 'Confirm tool execution';
|
||||
@@ -1690,12 +1742,17 @@ describe('AppContainer State Management', () => {
|
||||
vi.setSystemTime(startTime);
|
||||
|
||||
// Arrange: Set up mock settings with showStatusInTitle enabled
|
||||
const mockSettingsWithTitleEnabled = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
const mockSettingsWithTitleEnabled = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...mockSettings.merged,
|
||||
ui: {
|
||||
...mockSettings.merged.ui,
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock an active shell pty but not focused
|
||||
mockedUseGeminiStream.mockReturnValue({
|
||||
@@ -1744,12 +1801,17 @@ describe('AppContainer State Management', () => {
|
||||
vi.setSystemTime(startTime);
|
||||
|
||||
// Arrange: Set up mock settings with showStatusInTitle enabled
|
||||
const mockSettingsWithTitleEnabled = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
const mockSettingsWithTitleEnabled = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...mockSettings.merged,
|
||||
ui: {
|
||||
...mockSettings.merged.ui,
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock an active shell pty with redirection active
|
||||
mockedUseGeminiStream.mockReturnValue({
|
||||
@@ -1809,12 +1871,17 @@ describe('AppContainer State Management', () => {
|
||||
vi.setSystemTime(startTime);
|
||||
|
||||
// Arrange: Set up mock settings with showStatusInTitle enabled
|
||||
const mockSettingsWithTitleEnabled = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
const mockSettingsWithTitleEnabled = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...mockSettings.merged,
|
||||
ui: {
|
||||
...mockSettings.merged.ui,
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock an active shell pty with NO output since operation started (silent)
|
||||
mockedUseGeminiStream.mockReturnValue({
|
||||
@@ -1854,12 +1921,17 @@ describe('AppContainer State Management', () => {
|
||||
vi.setSystemTime(startTime);
|
||||
|
||||
// Arrange: Set up mock settings with showStatusInTitle enabled
|
||||
const mockSettingsWithTitleEnabled = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
const mockSettingsWithTitleEnabled = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...mockSettings.merged,
|
||||
ui: {
|
||||
...mockSettings.merged.ui,
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock an active shell pty but not focused
|
||||
let lastOutputTime = startTime + 1000;
|
||||
@@ -1933,12 +2005,18 @@ describe('AppContainer State Management', () => {
|
||||
|
||||
it('should pad title to exactly 80 characters', () => {
|
||||
// Arrange: Set up mock settings with showStatusInTitle enabled
|
||||
const mockSettingsWithTitleEnabled = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const mockSettingsWithTitleEnabled = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
ui: {
|
||||
...defaultMergedSettings.ui,
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock the streaming state and thought with a short subject
|
||||
const shortTitle = 'Short';
|
||||
@@ -1968,12 +2046,18 @@ describe('AppContainer State Management', () => {
|
||||
|
||||
it('should use correct ANSI escape code format', () => {
|
||||
// Arrange: Set up mock settings with showStatusInTitle enabled
|
||||
const mockSettingsWithTitleEnabled = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const mockSettingsWithTitleEnabled = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
ui: {
|
||||
...defaultMergedSettings.ui,
|
||||
showStatusInTitle: true,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock the streaming state and thought
|
||||
const title = 'Test Title';
|
||||
@@ -2001,12 +2085,17 @@ describe('AppContainer State Management', () => {
|
||||
|
||||
it('should use CLI_TITLE environment variable when set', () => {
|
||||
// Arrange: Set up mock settings with showStatusInTitle disabled (so it shows suffix)
|
||||
const mockSettingsWithTitleDisabled = createMockSettings({
|
||||
ui: {
|
||||
showStatusInTitle: false,
|
||||
hideWindowTitle: false,
|
||||
const mockSettingsWithTitleDisabled = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...mockSettings.merged,
|
||||
ui: {
|
||||
...mockSettings.merged.ui,
|
||||
showStatusInTitle: false,
|
||||
hideWindowTitle: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
// Mock CLI_TITLE environment variable
|
||||
vi.stubEnv('CLI_TITLE', 'Custom Gemini Title');
|
||||
@@ -2575,9 +2664,17 @@ describe('AppContainer State Management', () => {
|
||||
);
|
||||
|
||||
// Update settings for this test run
|
||||
const testSettings = createMockSettings({
|
||||
ui: { useAlternateBuffer: isAlternateMode },
|
||||
});
|
||||
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const testSettings = {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...defaultMergedSettings,
|
||||
ui: {
|
||||
...defaultMergedSettings.ui,
|
||||
useAlternateBuffer: isAlternateMode,
|
||||
},
|
||||
},
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
function TestChild() {
|
||||
useKeypress(childHandler || (() => {}), {
|
||||
@@ -3287,7 +3384,13 @@ describe('AppContainer State Management', () => {
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
unmount = renderAppContainer({
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: false } }),
|
||||
settings: {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...mockSettings.merged,
|
||||
ui: { ...mockSettings.merged.ui, useAlternateBuffer: false },
|
||||
},
|
||||
} as LoadedSettings,
|
||||
}).unmount;
|
||||
});
|
||||
|
||||
@@ -3323,7 +3426,13 @@ describe('AppContainer State Management', () => {
|
||||
let unmount: () => void;
|
||||
await act(async () => {
|
||||
unmount = renderAppContainer({
|
||||
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
|
||||
settings: {
|
||||
...mockSettings,
|
||||
merged: {
|
||||
...mockSettings.merged,
|
||||
ui: { ...mockSettings.merged.ui, useAlternateBuffer: true },
|
||||
},
|
||||
} as LoadedSettings,
|
||||
}).unmount;
|
||||
});
|
||||
|
||||
@@ -3592,9 +3701,16 @@ describe('AppContainer State Management', () => {
|
||||
});
|
||||
|
||||
it('DOES set showIsExpandableHint when overflow occurs in Alternate Buffer Mode', async () => {
|
||||
const settingsWithAlternateBuffer = createMockSettings({
|
||||
ui: { useAlternateBuffer: true },
|
||||
});
|
||||
const alternateSettings = mergeSettings({}, {}, {}, {}, true);
|
||||
const settingsWithAlternateBuffer = {
|
||||
merged: {
|
||||
...alternateSettings,
|
||||
ui: {
|
||||
...alternateSettings.ui,
|
||||
useAlternateBuffer: true,
|
||||
},
|
||||
},
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
vi.spyOn(mockConfig, 'getUseAlternateBuffer').mockReturnValue(true);
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ import {
|
||||
useOverflowActions,
|
||||
useOverflowState,
|
||||
} from './contexts/OverflowContext.js';
|
||||
import { useErrorCount } from './hooks/useConsoleMessages.js';
|
||||
import { useConsoleMessages } from './hooks/useConsoleMessages.js';
|
||||
import { useTerminalSize } from './hooks/useTerminalSize.js';
|
||||
import { calculatePromptWidths } from './components/InputPrompt.js';
|
||||
import { calculateMainAreaWidth } from './utils/ui-sizing.js';
|
||||
@@ -552,7 +552,8 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
};
|
||||
}, [settings]);
|
||||
|
||||
const { errorCount, clearErrorCount } = useErrorCount();
|
||||
const { consoleMessages, clearConsoleMessages: clearConsoleMessagesState } =
|
||||
useConsoleMessages();
|
||||
|
||||
const mainAreaWidth = calculateMainAreaWidth(terminalWidth, config);
|
||||
// Derive widths for InputPrompt using shared helper
|
||||
@@ -1007,18 +1008,10 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
Date.now(),
|
||||
);
|
||||
try {
|
||||
let flattenedMemory: string;
|
||||
let fileCount: number;
|
||||
const { memoryContent, fileCount } =
|
||||
await refreshServerHierarchicalMemory(config);
|
||||
|
||||
if (config.isJitContextEnabled()) {
|
||||
await config.getContextManager()?.refresh();
|
||||
flattenedMemory = flattenMemory(config.getUserMemory());
|
||||
fileCount = config.getGeminiMdFileCount();
|
||||
} else {
|
||||
const result = await refreshServerHierarchicalMemory(config);
|
||||
flattenedMemory = flattenMemory(result.memoryContent);
|
||||
fileCount = result.fileCount;
|
||||
}
|
||||
const flattenedMemory = flattenMemory(memoryContent);
|
||||
|
||||
historyManager.addItem(
|
||||
{
|
||||
@@ -1379,11 +1372,11 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
// Explicitly hide the expansion hint and clear its x-second timer when clearing the screen.
|
||||
triggerExpandHint(null);
|
||||
historyManager.clearItems();
|
||||
clearErrorCount();
|
||||
clearConsoleMessagesState();
|
||||
refreshStatic();
|
||||
}, [
|
||||
historyManager,
|
||||
clearErrorCount,
|
||||
clearConsoleMessagesState,
|
||||
refreshStatic,
|
||||
reset,
|
||||
triggerExpandHint,
|
||||
@@ -1684,6 +1677,11 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
|
||||
const handleGlobalKeypress = useCallback(
|
||||
(key: Key): boolean => {
|
||||
// Debug log keystrokes if enabled
|
||||
if (settings.merged.general.debugKeystrokeLogging) {
|
||||
debugLogger.log('[DEBUG] Keystroke:', JSON.stringify(key));
|
||||
}
|
||||
|
||||
if (shortcutsHelpVisible && isHelpDismissKey(key)) {
|
||||
setShortcutsHelpVisible(false);
|
||||
}
|
||||
@@ -1862,6 +1860,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
activePtyId,
|
||||
handleSuspend,
|
||||
embeddedShellFocused,
|
||||
settings.merged.general.debugKeystrokeLogging,
|
||||
refreshStatic,
|
||||
setCopyModeEnabled,
|
||||
tabFocusTimeoutRef,
|
||||
@@ -1990,6 +1989,22 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
};
|
||||
}, [historyManager]);
|
||||
|
||||
const filteredConsoleMessages = useMemo(() => {
|
||||
if (config.getDebugMode()) {
|
||||
return consoleMessages;
|
||||
}
|
||||
return consoleMessages.filter((msg) => msg.type !== 'debug');
|
||||
}, [consoleMessages, config]);
|
||||
|
||||
// Computed values
|
||||
const errorCount = useMemo(
|
||||
() =>
|
||||
filteredConsoleMessages
|
||||
.filter((msg) => msg.type === 'error')
|
||||
.reduce((total, msg) => total + msg.count, 0),
|
||||
[filteredConsoleMessages],
|
||||
);
|
||||
|
||||
const nightly = props.version.includes('nightly');
|
||||
|
||||
const dialogsVisible =
|
||||
@@ -2224,6 +2239,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
constrainHeight,
|
||||
showErrorDetails,
|
||||
showFullTodos,
|
||||
filteredConsoleMessages,
|
||||
ideContextState,
|
||||
renderMarkdown,
|
||||
ctrlCPressedOnce: ctrlCPressCount >= 1,
|
||||
@@ -2351,6 +2367,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
constrainHeight,
|
||||
showErrorDetails,
|
||||
showFullTodos,
|
||||
filteredConsoleMessages,
|
||||
ideContextState,
|
||||
renderMarkdown,
|
||||
ctrlCPressCount,
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
|
||||
import { renderWithProviders } from '../test-utils/render.js';
|
||||
import { render } from '../test-utils/render.js';
|
||||
import { act } from 'react';
|
||||
import { IdeIntegrationNudge } from './IdeIntegrationNudge.js';
|
||||
import { KeypressProvider } from './contexts/KeypressContext.js';
|
||||
import { debugLogger } from '@google/gemini-cli-core';
|
||||
|
||||
// Mock debugLogger
|
||||
@@ -53,8 +54,10 @@ describe('IdeIntegrationNudge', () => {
|
||||
});
|
||||
|
||||
it('renders correctly with default options', async () => {
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
<IdeIntegrationNudge {...defaultProps} />,
|
||||
const { lastFrame, waitUntilReady, unmount } = render(
|
||||
<KeypressProvider>
|
||||
<IdeIntegrationNudge {...defaultProps} />
|
||||
</KeypressProvider>,
|
||||
);
|
||||
await waitUntilReady();
|
||||
const frame = lastFrame();
|
||||
@@ -68,8 +71,10 @@ describe('IdeIntegrationNudge', () => {
|
||||
|
||||
it('handles "Yes" selection', async () => {
|
||||
const onComplete = vi.fn();
|
||||
const { stdin, waitUntilReady, unmount } = await renderWithProviders(
|
||||
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />,
|
||||
const { stdin, waitUntilReady, unmount } = render(
|
||||
<KeypressProvider>
|
||||
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />
|
||||
</KeypressProvider>,
|
||||
);
|
||||
|
||||
await waitUntilReady();
|
||||
@@ -89,8 +94,10 @@ describe('IdeIntegrationNudge', () => {
|
||||
|
||||
it('handles "No" selection', async () => {
|
||||
const onComplete = vi.fn();
|
||||
const { stdin, waitUntilReady, unmount } = await renderWithProviders(
|
||||
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />,
|
||||
const { stdin, waitUntilReady, unmount } = render(
|
||||
<KeypressProvider>
|
||||
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />
|
||||
</KeypressProvider>,
|
||||
);
|
||||
|
||||
await waitUntilReady();
|
||||
@@ -115,8 +122,10 @@ describe('IdeIntegrationNudge', () => {
|
||||
|
||||
it('handles "Dismiss" selection', async () => {
|
||||
const onComplete = vi.fn();
|
||||
const { stdin, waitUntilReady, unmount } = await renderWithProviders(
|
||||
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />,
|
||||
const { stdin, waitUntilReady, unmount } = render(
|
||||
<KeypressProvider>
|
||||
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />
|
||||
</KeypressProvider>,
|
||||
);
|
||||
|
||||
await waitUntilReady();
|
||||
@@ -146,8 +155,10 @@ describe('IdeIntegrationNudge', () => {
|
||||
|
||||
it('handles Escape key press', async () => {
|
||||
const onComplete = vi.fn();
|
||||
const { stdin, waitUntilReady, unmount } = await renderWithProviders(
|
||||
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />,
|
||||
const { stdin, waitUntilReady, unmount } = render(
|
||||
<KeypressProvider>
|
||||
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />
|
||||
</KeypressProvider>,
|
||||
);
|
||||
|
||||
await waitUntilReady();
|
||||
@@ -173,10 +184,11 @@ describe('IdeIntegrationNudge', () => {
|
||||
vi.stubEnv('GEMINI_CLI_IDE_WORKSPACE_PATH', '/tmp');
|
||||
|
||||
const onComplete = vi.fn();
|
||||
const { lastFrame, stdin, waitUntilReady, unmount } =
|
||||
await renderWithProviders(
|
||||
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />,
|
||||
);
|
||||
const { lastFrame, stdin, waitUntilReady, unmount } = render(
|
||||
<KeypressProvider>
|
||||
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />
|
||||
</KeypressProvider>,
|
||||
);
|
||||
|
||||
await waitUntilReady();
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ describe('AuthDialog', () => {
|
||||
for (const [key, value] of Object.entries(env)) {
|
||||
vi.stubEnv(key, value as string);
|
||||
}
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -161,7 +161,7 @@ describe('AuthDialog', () => {
|
||||
|
||||
it('filters auth types when enforcedType is set', async () => {
|
||||
props.settings.merged.security.auth.enforcedType = AuthType.USE_GEMINI;
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -173,7 +173,7 @@ describe('AuthDialog', () => {
|
||||
|
||||
it('sets initial index to 0 when enforcedType is set', async () => {
|
||||
props.settings.merged.security.auth.enforcedType = AuthType.USE_GEMINI;
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -213,7 +213,7 @@ describe('AuthDialog', () => {
|
||||
},
|
||||
])('selects initial auth type $desc', async ({ setup, expected }) => {
|
||||
setup();
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -226,7 +226,7 @@ describe('AuthDialog', () => {
|
||||
describe('handleAuthSelect', () => {
|
||||
it('calls onAuthError if validation fails', async () => {
|
||||
mockedValidateAuthMethod.mockReturnValue('Invalid method');
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -245,7 +245,7 @@ describe('AuthDialog', () => {
|
||||
|
||||
it('sets auth context with requiresRestart: true for LOGIN_WITH_GOOGLE', async () => {
|
||||
mockedValidateAuthMethod.mockReturnValue(null);
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -261,7 +261,7 @@ describe('AuthDialog', () => {
|
||||
|
||||
it('sets auth context with empty object for other auth types', async () => {
|
||||
mockedValidateAuthMethod.mockReturnValue(null);
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -278,7 +278,7 @@ describe('AuthDialog', () => {
|
||||
vi.stubEnv('GEMINI_API_KEY', 'test-key-from-env');
|
||||
// props.settings.merged.security.auth.selectedType is undefined here, simulating initial setup
|
||||
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -297,7 +297,7 @@ describe('AuthDialog', () => {
|
||||
vi.stubEnv('GEMINI_API_KEY', ''); // Empty string
|
||||
// props.settings.merged.security.auth.selectedType is undefined here
|
||||
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -316,7 +316,7 @@ describe('AuthDialog', () => {
|
||||
// process.env['GEMINI_API_KEY'] is not set
|
||||
// props.settings.merged.security.auth.selectedType is undefined here, simulating initial setup
|
||||
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -337,7 +337,7 @@ describe('AuthDialog', () => {
|
||||
props.settings.merged.security.auth.selectedType =
|
||||
AuthType.LOGIN_WITH_GOOGLE;
|
||||
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -360,7 +360,7 @@ describe('AuthDialog', () => {
|
||||
vi.mocked(props.config.isBrowserLaunchSuppressed).mockReturnValue(true);
|
||||
mockedValidateAuthMethod.mockReturnValue(null);
|
||||
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -383,7 +383,7 @@ describe('AuthDialog', () => {
|
||||
|
||||
it('displays authError when provided', async () => {
|
||||
props.authError = 'Something went wrong';
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -429,7 +429,7 @@ describe('AuthDialog', () => {
|
||||
},
|
||||
])('$desc', async ({ setup, expectations }) => {
|
||||
setup();
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -442,7 +442,7 @@ describe('AuthDialog', () => {
|
||||
|
||||
describe('Snapshots', () => {
|
||||
it('renders correctly with default props', async () => {
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -452,7 +452,7 @@ describe('AuthDialog', () => {
|
||||
|
||||
it('renders correctly with auth error', async () => {
|
||||
props.authError = 'Something went wrong';
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
@@ -462,7 +462,7 @@ describe('AuthDialog', () => {
|
||||
|
||||
it('renders correctly with enforced auth type', async () => {
|
||||
props.settings.merged.security.auth.enforcedType = AuthType.USE_GEMINI;
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<AuthDialog {...props} />,
|
||||
);
|
||||
await waitUntilReady();
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('BannedAccountDialog', () => {
|
||||
});
|
||||
|
||||
it('renders the suspension message from accountSuspensionInfo', async () => {
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<BannedAccountDialog
|
||||
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
|
||||
onExit={onExit}
|
||||
@@ -89,7 +89,7 @@ describe('BannedAccountDialog', () => {
|
||||
});
|
||||
|
||||
it('renders menu options with appeal link text from response', async () => {
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<BannedAccountDialog
|
||||
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
|
||||
onExit={onExit}
|
||||
@@ -109,7 +109,7 @@ describe('BannedAccountDialog', () => {
|
||||
const infoWithoutUrl: AccountSuspensionInfo = {
|
||||
message: 'Account suspended.',
|
||||
};
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<BannedAccountDialog
|
||||
accountSuspensionInfo={infoWithoutUrl}
|
||||
onExit={onExit}
|
||||
@@ -129,7 +129,7 @@ describe('BannedAccountDialog', () => {
|
||||
message: 'Account suspended.',
|
||||
appealUrl: 'https://example.com/appeal',
|
||||
};
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<BannedAccountDialog
|
||||
accountSuspensionInfo={infoWithoutLinkText}
|
||||
onExit={onExit}
|
||||
@@ -143,7 +143,7 @@ describe('BannedAccountDialog', () => {
|
||||
});
|
||||
|
||||
it('opens browser when appeal option is selected', async () => {
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<BannedAccountDialog
|
||||
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
|
||||
onExit={onExit}
|
||||
@@ -162,7 +162,7 @@ describe('BannedAccountDialog', () => {
|
||||
|
||||
it('shows URL when browser cannot be launched', async () => {
|
||||
mockedShouldLaunchBrowser.mockReturnValue(false);
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<BannedAccountDialog
|
||||
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
|
||||
onExit={onExit}
|
||||
@@ -180,7 +180,7 @@ describe('BannedAccountDialog', () => {
|
||||
});
|
||||
|
||||
it('calls onExit when "Exit" is selected', async () => {
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<BannedAccountDialog
|
||||
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
|
||||
onExit={onExit}
|
||||
@@ -196,7 +196,7 @@ describe('BannedAccountDialog', () => {
|
||||
});
|
||||
|
||||
it('calls onChangeAuth when "Change authentication" is selected', async () => {
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<BannedAccountDialog
|
||||
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
|
||||
onExit={onExit}
|
||||
@@ -212,7 +212,7 @@ describe('BannedAccountDialog', () => {
|
||||
});
|
||||
|
||||
it('exits on escape key', async () => {
|
||||
const { waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { waitUntilReady, unmount } = renderWithProviders(
|
||||
<BannedAccountDialog
|
||||
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
|
||||
onExit={onExit}
|
||||
@@ -227,7 +227,7 @@ describe('BannedAccountDialog', () => {
|
||||
});
|
||||
|
||||
it('renders snapshot correctly', async () => {
|
||||
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
|
||||
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||
<BannedAccountDialog
|
||||
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
|
||||
onExit={onExit}
|
||||
|
||||
@@ -36,12 +36,10 @@ describe('aboutCommand', () => {
|
||||
beforeEach(() => {
|
||||
mockContext = createMockCommandContext({
|
||||
services: {
|
||||
agentContext: {
|
||||
config: {
|
||||
getModel: vi.fn(),
|
||||
getIdeMode: vi.fn().mockReturnValue(true),
|
||||
getUserTierName: vi.fn().mockReturnValue(undefined),
|
||||
},
|
||||
config: {
|
||||
getModel: vi.fn(),
|
||||
getIdeMode: vi.fn().mockReturnValue(true),
|
||||
getUserTierName: vi.fn().mockReturnValue(undefined),
|
||||
},
|
||||
settings: {
|
||||
merged: {
|
||||
@@ -59,10 +57,9 @@ describe('aboutCommand', () => {
|
||||
} as unknown as CommandContext);
|
||||
|
||||
vi.mocked(getVersion).mockResolvedValue('test-version');
|
||||
vi.spyOn(
|
||||
mockContext.services.agentContext!.config,
|
||||
'getModel',
|
||||
).mockReturnValue('test-model');
|
||||
vi.spyOn(mockContext.services.config!, 'getModel').mockReturnValue(
|
||||
'test-model',
|
||||
);
|
||||
process.env['GOOGLE_CLOUD_PROJECT'] = 'test-gcp-project';
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'test-os',
|
||||
@@ -163,9 +160,9 @@ describe('aboutCommand', () => {
|
||||
});
|
||||
|
||||
it('should display the tier when getUserTierName returns a value', async () => {
|
||||
vi.mocked(
|
||||
mockContext.services.agentContext!.config.getUserTierName,
|
||||
).mockReturnValue('Enterprise Tier');
|
||||
vi.mocked(mockContext.services.config!.getUserTierName).mockReturnValue(
|
||||
'Enterprise Tier',
|
||||
);
|
||||
if (!aboutCommand.action) {
|
||||
throw new Error('The about command must have an action.');
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user