Compare commits

...

150 Commits

Author SHA1 Message Date
Dmitry Lyalin da258412df Merge branch 'main' into workspace-command-scope-20737 2026-03-10 18:56:50 -04:00
nityam e22d9917b7 fix: preserve prompt text when cancelling streaming (#21103)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-03-10 22:35:04 +00:00
N. Taylor Mullen d63053cb59 fix(cli): stabilize prompt layout to prevent jumping when typing (#21081) 2026-03-10 21:29:29 +00:00
gemini-cli-robot a9500d6a6c chore/release: bump version to 0.34.0-nightly.20260310.4653b126f (#21816) 2026-03-10 21:22:37 +00:00
gemini-cli-robot acbd914d5e Changelog for v0.33.0-preview.13 (#21927)
Co-authored-by: gemini-cli-robot <224641728+gemini-cli-robot@users.noreply.github.com>
2026-03-10 21:22:04 +00:00
christine betts 300929a32b Add ExtensionDetails dialog and support install (#20845) 2026-03-10 21:12:47 +00:00
Dmitry Lyalin a9532b3ac4 Merge branch 'main' into workspace-command-scope-20737 2026-03-10 17:02:26 -04:00
Coco Sheng 519c75f410 fix: prevent hangs in non-interactive mode and improve agent guidance (#20893)
Co-authored-by: Keith Schaab <keith.schaab@gmail.com>
2026-03-10 20:30:58 +00:00
Gaurav 3ff68a9e55 fix: patch gaxios v7 Array.toString() stream corruption (#21884) 2026-03-10 20:25:24 +00:00
kevinjwang1 5b8ad9cd65 Add extensionRegistryURI setting to change where the registry is read from (#20463) 2026-03-10 20:22:03 +00:00
Aditya Bijalwan 5caa192cfc feat: add pulsating blue border automation overlay to browser agent (#21173)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
2026-03-10 20:15:03 +00:00
Ayush Debnath 9eae91a489 feat(voice): implement speech-friendly response formatter (#20989)
Co-authored-by: Spencer <spencertang@google.com>
2026-03-10 19:57:23 +00:00
Coco Sheng 1b69637032 feat(a2a): switch from callback-based to event-driven tool scheduler (#21467)
Co-authored-by: Abhi <abhipatel@google.com>
Co-authored-by: Adam Weidman <adamfweidman@google.com>
2026-03-10 19:36:17 +00:00
Adib234 e5615f47c4 fix(plan): prevent plan truncation in approval dialog by supporting unconstrained heights (#21037)
Co-authored-by: jacob314 <jacob314@gmail.com>
2026-03-10 19:34:10 +00:00
Yongrui Lin bc75a61982 docs(cli): mention per-model token usage in stream-json result event (#21908) 2026-03-10 19:29:18 +00:00
N. Taylor Mullen 5d213764fb feat(cli): enable skill activation via slash commands (#21758)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2026-03-10 19:24:54 +00:00
Dmitry Lyalin dba383ef3c Merge branch 'main' into workspace-command-scope-20737 2026-03-10 15:20:46 -04:00
Alisa be67470432 feat(a2a): implement standardized normalization and streaming reassembly (#21402)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2026-03-10 19:19:48 +00:00
Adam Weidman 00a39b3da9 refactor: remove agent_card_requires_auth config flag (#21914) 2026-03-10 19:16:46 +00:00
Tommaso Sciortino 077c1a1e2d make command names consistent (#21907) 2026-03-10 19:15:45 +00:00
Abhi 0d60d68cf9 docs(mcp): standardize mcp tool fqn documentation (#21664) 2026-03-10 19:13:00 +00:00
Mark McLaughlin b404fc02e7 fix(auth): update terminology to 'sign in' and 'sign out' (#20892)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-03-10 19:10:26 +00:00
Aashir Javed b00d7c88ad fix(ui): prevent empty tool-group border stubs after filtering (#21852)
Co-authored-by: jacob314 <jacob314@gmail.com>
2026-03-10 19:09:07 +00:00
anj-s 2dd037682c Add behavioral evals for tracker (#20069) 2026-03-10 18:51:54 +00:00
skyvanguard 7aae5435fa fix(cli): prevent race condition in loop detection retry (#17916)
Co-authored-by: cynthialong0-0 <82900738+cynthialong0-0@users.noreply.github.com>
2026-03-10 18:41:16 +00:00
Jerop Kipruto 13f78bd9eb docs: remove experimental badge from plan mode in sidebar (#21906) 2026-03-10 18:22:07 +00:00
Yongrui Lin 4da0366eed feat(core): add per-model token usage to stream-json output (#21839) 2026-03-10 17:39:04 +00:00
Jerop Kipruto 18112c474e docs: clarify telemetry setup and comprehensive data map (#21879) 2026-03-10 17:20:25 +00:00
Jerop Kipruto 80cf2fe444 fix(release): Improve Patch Release Workflow Comments: Clearer Approval Guidance (#21894) 2026-03-10 17:15:04 +00:00
Prasanna Pal 0b78de9601 fix(core): handle AbortError when ESC cancels tool execution (#20863) 2026-03-10 17:11:08 +00:00
Spencer a220874281 feat(policy): support auto-add to policy by default and scoped persistence (#20361) 2026-03-10 17:01:41 +00:00
Dmitry Lyalin 31819197dd Merge branch 'main' into workspace-command-scope-20737 2026-03-10 12:49:15 -04:00
Jacob Richman 49ea9b0457 Handle dirty worktrees better and warn about running scripts/review.sh on untrusted code. (#21791) 2026-03-10 16:38:26 +00:00
JAYADITYA 556825f81c feat(cli): give visibility to /tools list command in the TUI and follow the subcommand pattern of other commands (#21213) 2026-03-10 16:36:12 +00:00
Sandy Tao b158c96465 feat(core): add OAuth2 Authorization Code auth provider for A2A agents (#21496)
Co-authored-by: Adam Weidman <adamfweidman@google.com>
2026-03-10 15:24:44 +00:00
Dmitry Lyalin 1f07e5b9e3 Merge branch 'main' into workspace-command-scope-20737 2026-03-10 11:00:47 -04:00
Coco Sheng e91f86c248 feat(telemetry): add specific PR, issue, and custom tracking IDs for GitHub Actions (#21129) 2026-03-10 14:59:13 +00:00
Gaurav 47e4f6b13f fix(ui): handle headless execution in credits and upgrade dialogs (#21850) 2026-03-10 14:54:15 +00:00
Gaurav 94ab449e65 fix(core): treat retryable errors with >5 min delay as terminal quota errors (#21881) 2026-03-10 14:53:51 +00:00
Dmitry Lyalin 73da95c6c7 Merge branch 'main' into workspace-command-scope-20737 2026-03-10 10:35:49 -04:00
Dmitry Lyalin c8d422986f Regenerate settings docs 2026-03-10 10:34:55 -04:00
Yuna Seol 0486a1675a refactor(core): improve API response error logging when retry (#21784) 2026-03-10 14:29:35 +00:00
Adam Weidman f9fc83089c fix(core): update @a2a-js/sdk to 0.3.11 (#21875) 2026-03-10 14:14:39 +00:00
Dmitry Lyalin b3ff5454ab Merge branch 'main' into workspace-command-scope-20737 2026-03-10 10:11:37 -04:00
Dmitry Lyalin 8b5102e55e Rename directory command to workspace 2026-03-10 09:46:48 -04:00
Jerop Kipruto 5dd2dab189 fix(core): ensure correct flash model steering in plan mode implementation phase (#21871) 2026-03-10 13:30:41 +00:00
Jerop Kipruto a6b95897ad docs: clarify global policy rules application in plan mode (#21864) 2026-03-10 13:10:17 +00:00
Jacob Richman 6ae6c810ba fix(cli): make footer items equally spaced (#21843) 2026-03-10 08:07:26 +00:00
Gaurav 02d4451e77 fix(core): override toolRegistry property for sub-agent schedulers (#21766) 2026-03-10 07:41:54 +00:00
Sehoon Shon c25ff94608 build(release): restrict npm bundling to non-stable tags (#21821) 2026-03-10 04:17:46 +00:00
Tommaso Sciortino 14412c3a72 refactor(cli): rename 'return' key to 'enter' internally (#21796) 2026-03-10 02:32:40 +00:00
Shehab ec7773eb7b fix(docs): update theme screenshots and add missing themes (#20689)
Co-authored-by: Chris Williams <chrisjwilliams@google.com>
Co-authored-by: Chris Williams <diodesign@gmail.com>
Co-authored-by: Sam Roberts <158088236+g-samroberts@users.noreply.github.com>
2026-03-10 00:35:10 +00:00
Nicholas Bardy 4653b126f3 fix(core): destroy PTY on kill() and exception to prevent fd leak (#21693)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-03-10 00:08:16 +00:00
sinisterchill 1e1e7e349d fix(core): handle GUI editor non-zero exit codes gracefully (#20376)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-03-09 23:51:10 +00:00
christine betts 43eb74ac59 Add support for updating extension sources and names (#21715) 2026-03-09 23:31:31 +00:00
Tommaso Sciortino 215f8f3f15 refactor(ui): unify keybinding infrastructure and support string initialization (#21776) 2026-03-09 23:26:33 +00:00
Francesco Camporeale b89944c3a3 docs: fix heading formatting in commands.md and phrasing in tools-api.md (#20679)
Co-authored-by: Sam Roberts <158088236+g-samroberts@users.noreply.github.com>
2026-03-09 23:26:04 +00:00
Muhammad Usman f88488d1f9 fix(core): resolve Windows line ending and path separation bugs across CLI (#21068) 2026-03-09 22:40:22 +00:00
Sehoon Shon 1fd42802be perf(cli): cache loadSettings to reduce redundant disk I/O at startup (#21521) 2026-03-09 21:33:16 +00:00
Tommaso Sciortino ab64b15d51 refactor: migrate to useKeyMatchers hook (#21753) 2026-03-09 20:48:09 +00:00
Jacob Richman e406dcc249 feat/redesign header compact (#20922) 2026-03-09 20:40:46 +00:00
Vroot 680077631d fix(docs): fix headless mode docs (#21287)
Co-authored-by: Sam Roberts <158088236+g-samroberts@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-09 20:13:14 +00:00
Jacob Richman 9fc03a0c12 Add script to speed up reviewing PRs adding a worktree. (#21748) 2026-03-09 19:43:22 +00:00
Adib234 4f4431e4e1 fix(core): prevent infinite recursion in symlink resolution (#21750) 2026-03-09 19:38:45 +00:00
AK 527074b50a feat(policy): support subagent-specific policies in TOML (#21431) 2026-03-09 19:22:46 +00:00
joshualitt a17691f0fc feat(core): improve subagent result display (#20378) 2026-03-09 19:20:15 +00:00
Francesco Camporeale d246315cea docs: fix broken markdown syntax and anchor links in /tools (#20902)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Sam Roberts <158088236+g-samroberts@users.noreply.github.com>
2026-03-09 19:03:50 +00:00
Alisa e92ccec6c8 feat(security): implement robust IP validation and safeFetch foundation (#21401) 2026-03-09 19:02:07 +00:00
Pyush Sinha b68d7bc0f9 refactor(cli): better react patterns for BaseSettingsDialog (#21206) 2026-03-09 18:35:08 +00:00
Jacob Richman 4c9f9bb3e2 robustness(core): static checks to validate history is immutable (#21228) 2026-03-09 18:26:03 +00:00
David Pierce e7b20c49ac Update mcp's list function to check for disablement. (#21148) 2026-03-09 18:10:00 +00:00
David Pierce 743d05b37f Unmarshall update (#21721)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-09 17:32:15 +00:00
Zheyuan Lin 95074a1a84 fix(settings): display objects as JSON instead of [object Object] (#21458)
Co-authored-by: Zheyuan <zlin252@emory.edu>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-03-09 17:32:13 +00:00
Aditya Bijalwan 759575faa8 Feat/browser agent progress emission (#21218)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-09 17:26:00 +00:00
Sehoon Shon d485e08606 feat(cli): implement /upgrade command (#21511) 2026-03-09 17:17:30 +00:00
Sehoon Shon 0f1258305a perf(core): cache loadApiKey to reduce redundant keychain access (#21520) 2026-03-09 16:53:24 +00:00
Abhijit Balaji 09e99824d4 docs: document tool exclusion from memory via deny policy (#21428)
Co-authored-by: Sam Roberts <158088236+g-samroberts@users.noreply.github.com>
2026-03-09 16:29:52 +00:00
joshualitt 96b939f63a feat(core): Introduce AgentLoopContext. (#21198) 2026-03-09 16:02:20 +00:00
Adib234 7837194ab5 fix(core): resolve symlinks for non-existent paths during validation (#21487) 2026-03-09 16:02:13 +00:00
Jerop Kipruto 35ee2a841a feat(plan): enable Plan Mode by default (#21713) 2026-03-09 15:58:46 +00:00
Jack Wotherspoon a253938ac5 feat(cli): add /compact alias for /compress command (#21711) 2026-03-09 15:45:42 +00:00
aworki 37ffd608fd feat(cli): expose /tools desc as explicit subcommand for discoverability (#21241)
Co-authored-by: Coco Sheng <cocosheng@google.com>
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
2026-03-09 15:31:05 +00:00
Chris Williams 0f019122b0 Docs: Make documentation links relative (#21490) 2026-03-09 15:23:00 +00:00
Gaurav 936f6240dd fix(core): sanitize SSE-corrupted JSON and domain strings in error classification (#21702) 2026-03-09 14:08:33 +00:00
anj-s f8dd6f4f4c fix(core): display actual graph output in tracker_visualize tool (#21455) 2026-03-09 13:36:16 +00:00
Gyanranjan Priyam ca7ac00030 docs: fix typo 'allowslisted' -> 'allowlisted' in mcp-server.md (#21665) 2026-03-09 05:38:00 +00:00
Dmitry Lyalin d41735d6a9 feat(cli): unify /chat and /resume command UX (#20256) 2026-03-08 22:50:51 +00:00
Jacob Richman d012929a28 Code review comments as a pr (#21209) 2026-03-08 08:36:54 +00:00
Jenna Inouye 97dfbd4598 Docs: Add documentation for notifications (experimental)(macOS) (#21163) 2026-03-08 06:31:24 +00:00
Keith Guerin 237864eb63 feat(cli): Invert quota language to 'percent used' (#20100)
Co-authored-by: jacob314 <jacob314@gmail.com>
2026-03-07 23:17:10 +00:00
Keith Guerin dc6741097c refactor(cli): standardize on 'reload' verb for all components (#20654)
Co-authored-by: Krishna Korade <MushuEE@users.noreply.github.com>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-03-07 22:56:11 +00:00
Christian Gunderman dac3735626 Disallow underspecified types (#21485) 2026-03-07 21:05:38 +00:00
Jacob Richman 245b68e9f1 Make test suite pass when the GEMINI_SYSTEM_MD env variable or GEMINI_WRITE_SYSTEM_MD variable happens to be set locally/ (#21480) 2026-03-07 20:04:17 +00:00
Jacob Richman e89cf5d86e fix(cli): correct shell height reporting (#21492) 2026-03-07 19:31:09 +00:00
Jarrod Whelan 54b0344fc5 fix(ui): unify Ctrl+O expansion hint experience across buffer modes (#21474) 2026-03-07 19:04:22 +00:00
Keith Guerin e5d58c2b5a feat(cli): overhaul thinking UI (#18725) 2026-03-07 04:20:27 +00:00
Sandy Tao 9455ecd78c fix(core): add in-memory cache to ChatRecordingService to prevent OOM (#21502) 2026-03-07 03:45:36 +00:00
Jeffrey Ying 0fd09e0150 fix: expand paste placeholders in TextInput on submit (#19946)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-07 03:29:38 +00:00
Gaurav 9a7427197b fix(billing): fix overage strategy lifecycle and settings integration (#21236) 2026-03-07 03:14:44 +00:00
Arnav Raj 33be30ab04 fix(core): whitelist TERM and COLORTERM in environment sanitization (#20514)
Co-authored-by: Sri Pasumarthi <sripas@google.com>
Co-authored-by: Sri Pasumarthi <111310667+sripasg@users.noreply.github.com>
2026-03-07 02:51:08 +00:00
Mark McLaughlin 6c3a90645a feat(skills): refine string-reviewer guidelines and description (#20368) 2026-03-06 22:46:11 +00:00
Abhi 32e61529f5 feat(cli): hide gemma settings from display and mark as experimental (#21471) 2026-03-06 22:34:59 +00:00
Abhi 931e668b47 refactor(core): standardize MCP tool naming to mcp_ FQN format (#21425) 2026-03-06 22:17:28 +00:00
Emily Hedlund 7989c28d2e feat(cli): implement dot-prefixing for slash command conflicts (#20979) 2026-03-06 20:43:20 +00:00
gemini-cli-robot 2965c115f1 Changelog for v0.33.0-preview.4 (#21354)
Co-authored-by: gemini-cli-robot <224641728+gemini-cli-robot@users.noreply.github.com>
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
Co-authored-by: galz10 <galzahavi@google.com>
Co-authored-by: Sam Roberts <158088236+g-samroberts@users.noreply.github.com>
2026-03-06 20:36:18 +00:00
Sébastien Prud'homme 7d31d5f4ec fix(core): append correct OTLP paths for HTTP exporters (#16836) 2026-03-06 19:58:00 +00:00
Srinath Padmanabhan 1a7d4184cd Update quota and pricing documentation with subscription tiers (#21351)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Jenna Inouye <jinouye@google.com>
Co-authored-by: Srinath Padmanabhan <srithreepo@google.com>
2026-03-06 19:46:03 +00:00
Sehoon Shon 7dce23e5d9 feat(core): implement generic CacheService and optimize setupUser (#21374) 2026-03-06 19:39:50 +00:00
ruomeng 06a176e33e fix(plan): keep approved plan during chat compression (#21284) 2026-03-06 19:36:05 +00:00
Sandy Tao 42d367d72f fix(cli): gracefully handle --resume when no sessions exist (#21429) 2026-03-06 19:02:33 +00:00
Emily Hedlund 6f579934db feat(core): implement unified KeychainService and migrate token storage (#21344) 2026-03-06 18:56:08 +00:00
Tommaso Sciortino 6d607a5953 feat(ui): dynamically generate all keybinding hints (#21346) 2026-03-06 18:34:26 +00:00
Abhi 4669148a4c feat(core): add concurrency safety guidance for subagent delegation (#17753) (#21278) 2026-03-06 18:09:45 +00:00
nityam 82316ef6e4 fix(core): deduplicate GEMINI.md files by device/inode on case-insensitive filesystems (#19904) (#19915) 2026-03-06 17:52:08 +00:00
Aarchi Kumari 337e4bc8c6 fix(hooks): propagate stopHookActive in AfterAgent retry path (#20426) (#20439) 2026-03-06 17:06:23 +00:00
Horizon_Architect_07 d97eaf3420 Fix(accessibility): add screen reader support to RewindViewer (#20750) 2026-03-06 15:48:36 +00:00
Adam Boudj 0452f787b2 fix(partUtils): display media type and size for inline data parts (#21358) 2026-03-06 15:45:43 +00:00
anj-s c1317aafb3 fix: logic for task tracker strategy and remove tracker tools (#21355) 2026-03-06 15:17:49 +00:00
Mohammed Abdelaziz f17f05373e docs(cli): clarify ! command output visibility in shell commands tutorial (#21041)
Co-authored-by: Sam Roberts <158088236+g-samroberts@users.noreply.github.com>
2026-03-06 08:47:29 +00:00
MumuTW 6691fac50e fix(core): handle AbortError thrown during processTurn (#21296)
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
2026-03-06 06:29:56 +00:00
JATIN 35c1999341 documentiong ensures ripgrep (#21298)
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
2026-03-06 06:10:12 +00:00
kt 5575c5ff66 docs: fix incorrect relative links to command reference (#20964) 2026-03-06 03:27:25 +00:00
Pavan Shinde 0833aca64b docs: format release times as HH:MM UTC (#20726)
Co-authored-by: Sam Roberts <158088236+g-samroberts@users.noreply.github.com>
2026-03-06 03:06:38 +00:00
Sehoon Shon 509d4ae0a9 fix(cli): implement --all flag for extensions uninstall (#21319) 2026-03-06 03:02:01 +00:00
gemini-cli-robot 4d310dda68 Changelog for v0.33.0-preview.3 (#21347)
Co-authored-by: gemini-cli-robot <224641728+gemini-cli-robot@users.noreply.github.com>
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
Co-authored-by: galz10 <galzahavi@google.com>
2026-03-06 02:11:30 +00:00
gemini-cli-robot e7be0d7bcc Changelog for v0.33.0-preview.2 (#21333)
Co-authored-by: gemini-cli-robot <224641728+gemini-cli-robot@users.noreply.github.com>
2026-03-06 00:35:13 +00:00
anj-s a8f507352b feat (core): Implement tracker related SI changes (#19964) 2026-03-06 00:18:05 +00:00
Mark McLaughlin 6aa6630137 fix(theme): improve theme color contrast for macOS Terminal.app (#21175) 2026-03-05 23:33:49 +00:00
Shreya Keshive 7ec477d40d feat(acp): Add support for AI Gateway auth (#21305) 2026-03-05 22:15:23 +00:00
Tommaso Sciortino 19c9508fd1 remove wildcard behavior on keybindings (#21315) 2026-03-05 22:11:53 +00:00
Jacob Richman e8bc7bea44 Fix code colorizer ansi escape bug. (#21321) 2026-03-05 21:43:13 +00:00
Sam Roberts 1ba9d71284 docs(links): update docs-writer skill and fix broken link (#21314) 2026-03-05 21:20:52 +00:00
Sehoon Shon 1aa4a32066 test(core): improve testing for API request/response parsing (#21227) 2026-03-05 21:00:01 +00:00
Sehoon Shon 22d962e761 feat(core): fallback to 2.5 models with no access for toolcalls (#21283) 2026-03-05 20:37:15 +00:00
Shreya Keshive 0135b03c8a fix(acp): rename --experimental-acp to --acp & remove Zed-specific refrences (#21171) 2026-03-05 19:57:28 +00:00
kartik 9773a084c9 fix:reorder env var redaction checks to scan values first (#21059)
Signed-off-by: Kartik Angiras <angiraskartik@gmail.com>
2026-03-05 19:22:45 +00:00
Juhyuk a830858f91 fix(core): prevent unhandled AbortError crash during stream loop detection (#21123)
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
Co-authored-by: ruomeng <ruomeng@google.com>
2026-03-05 19:12:09 +00:00
Jerop Kipruto f47cb3c136 docs: use absolute paths for internal links in plan-mode.md (#21299) 2026-03-05 19:11:57 +00:00
Zheyuan Lin 291639633f feat(cli): add native gVisor (runsc) sandboxing support (#21062)
Co-authored-by: Zheyuan <zlin252@emory.edu>
Co-authored-by: Kartik Angiras <angiraskartik@gmail.com>
2026-03-05 18:39:57 +00:00
Jacob Richman c7e2dbe0cf Fix so shell calls are formatted (#21237) 2026-03-05 18:39:42 +00:00
Yuna Seol 31d65f40bd feat(telemetry): implement Clearcut logging for startup statistics (#21172)
Co-authored-by: Yuna Seol <yunaseol@google.com>
2026-03-05 18:21:55 +00:00
Sam Roberts 93d9adc03c feat(triage): add area/documentation to issue triage (#21222) 2026-03-05 18:14:47 +00:00
Sam Roberts d2726d4a43 DOCS: Update quota and pricing page (#21194) 2026-03-05 18:09:14 +00:00
Tommaso Sciortino 1c3c7a343f fix(cli): replace hardcoded keybinding strings with dynamic formatters (#21159) 2026-03-05 17:52:01 +00:00
Dev Randalpura f20e45175e fix(ui): add partial output to cancelled shell UI (#21178)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-05 17:30:20 +00:00
Sandy Tao 0228c2b9f0 refactor(core): extract shared OAuth flow primitives from MCPOAuthProvider (#20895) 2026-03-05 17:01:37 +00:00
Tommaso Sciortino ddafd79661 fix(cli): fix enabling kitty codes on Windows Terminal (#21136) 2026-03-05 15:29:12 +00:00
Aditya Bijalwan 09259a8ec4 test: add browser agent integration tests (#21151) 2026-03-05 13:29:35 +00:00
Adib234 173376ba67 fix(core): explicitly allow codebase_investigator and cli_help in read-only mode (#21157) 2026-03-05 13:20:04 +00:00
JAYADITYA 013d5dfbb0 refactor(cli): categorize built-in themes into dark/ and light/ directories (#18634) 2026-03-05 09:20:46 +00:00
700 changed files with 29721 additions and 11976 deletions
+1 -1
View File
@@ -107,7 +107,7 @@ Gemini CLI project.
set.
- **Logging**: Use `debugLogger` for rethrown errors to avoid duplicate logging.
- **Keyboard Shortcuts**: Define all new keyboard shortcuts in
`packages/cli/src/config/keyBindings.ts` and document them in
`packages/cli/src/ui/key/keyBindings.ts` and document them in
`docs/cli/keyboard-shortcuts.md`. Be careful of keybindings that require the
`Meta` key, as only certain meta key shortcuts are supported on Mac. Avoid
function keys and shortcuts commonly bound in VSCode.
+4 -1
View File
@@ -118,6 +118,8 @@ documentation.
reflects existing code.
- **Structure:** Apply "Structure (New Docs)" rules (BLUF, headings, etc.) when
adding new sections to existing pages.
- **Headers**: If you change a header, you must check for links that lead to
that header and update them.
- **Tone:** Ensure the tone is active and engaging. Use "you" and contractions.
- **Clarity:** Correct awkward wording, spelling, and grammar. Rephrase
sentences to make them easier for users to understand.
@@ -133,7 +135,8 @@ and that all links are functional.
technical behavior.
2. **Self-review:** Re-read changes for formatting, correctness, and flow.
3. **Link check:** Verify all new and existing links leading to or from modified
pages.
pages. If you changed a header, ensure that any links that lead to it are
updated.
4. **Format:** Once all changes are complete, ask to execute `npm run format`
to ensure consistent formatting across the project. If the user confirms,
execute the command.
+99
View File
@@ -0,0 +1,99 @@
---
name: string-reviewer
description: >
Use this skill when asked to review text and user-facing strings within the codebase. It ensures that these strings follow rules on clarity,
usefulness, brevity and style.
---
# String Reviewer
## Instructions
Act as a Senior UX Writer. Look for user-facing strings that are too long,
unclear, or inconsistent. This includes inline text, error messages, and other
user-facing text.
Do NOT automatically change strings without user approval. You must only suggest
changes and do not attempt to rewrite them directly unless the user explicitly
asks you to do so.
## Core voice principles
The system prioritizes deterministic clarity over conversational fluff. We
provide telemetry, not etiquette, ensuring the user retains absolute agency..
1. **Deterministic clarity:** Distinguish between certain system/service states
(Cloud Billing, IAM, the System) and probabilistic AI analysis (Gemini).
2. **System transparency:** Replace "Loading..." with active technical telemetry
(e.g., Tracing stack traces...). Keep status updates under 5 words.
3. **Front-loaded actionability:** Always use the [Goal] + [Action] pattern.
Lead with intent so users can scan left-to-right.
4. **Agentic error recovery:** Every error must be a pivot point. Pair failures
with one-click recovery commands or suggested prompts.
5. **Contextual humility:** Reserve disclaimers and "be careful" warnings for P0
(destructive/irreversible) tasks only. Stop warning-fatigue.
## The writing checklist
Use this checklist to audit UI strings and AI responses.
### Identity and voice
- **Eliminate the "I":** Remove all first-person pronouns (I, me, my, mine).
- **Subject attribution:** Refer to the AI as Gemini and the infrastructure as
the - system or the CLI.
- **Active voice:** Ensure the subject (Gemini or the system) is clearly
performing the action.
- **Ownership rule:** Use the system for execution (doing) and Gemini for
analysis (thinking)
### Structural scannability
- **The skip test:** Do the first 3 words describe the users intent? If not,
rewrite.
- **Goal-first sequence:** Use the template: [To Accomplish X] + [Do Y].
- **The 5-word rule:** Keep status updates and loading states under 5 words.
- **Telemetry over etiquette:** Remove polite filler (Please wait, Thank you,
Certainly). Replace with raw data or progress indicators.
- **Micro-state cycles:** For tasks $> 3$ seconds, cycle through specific
sub-states (e.g., Parsing logs... ➔ Identifying patterns...) to show momentum.
### Technical accuracy and humility
- **Verb signal check:** Use deterministic verbs (is, will, must) for system
state/infrastructure.
- Use probabilistic verbs (suggests, appears, may, identifies) for AI output.
- **No 100% certainty:** Never attribute absolute certainty to model-generated
content.
- **Precision over fuzziness:** Use technical metrics (latency, tokens, compute) instead of "speed" or "cost."
- **Instructional warnings:** Every warning must include a specific corrective action (e.g., "Perform a dry-run first" or "Review line 42").
### Agentic error recovery
- **The one-step rule:** Pair every error message with exactly one immediate
path to a fix (command, link, or prompt).
- **Human-first:** Provide a human-readable explanation before machine error
codes (e.g., 404, 500).
- **Suggested prompts:** Offer specific text for the user to copy/click like
“Ask Gemini: 'Explain this port error.'”
### Use consistent terminology
Ensure all terminology aligns with the project [word
list](./references/word-list.md).
If a string uses a term marked "do not use" or "use with caution," provide a
correction based on the preferred terms.
## Ensure consistent style for settings
If `packages/cli/src/config/settingsSchema.ts` is modified, confirm labels and
descriptions specifically follow the unique [Settings
guidelines](./references/settings.md).
## Output format
When suggesting changes, always present your review using the following list
format. Do not provide suggestions outside of this list..
```
1. **{Rationale/Principle Violated}**
- ❌ "{incorrect phrase}"
- ✅ `"{corrected phrase}"`
```
@@ -0,0 +1,28 @@
# Settings
## Noun-First Labeling (Scannability)
Labels must start with the subject of the setting, not the action. This allows
users to scan for the feature they want to change.
- **Rule:** `[Noun]` `[Attribute/Action]`
- **Example:** `Show line numbers` becomes simply `Line numbers`
## Positive Boolean Logic (Cognitive Ease)
Eliminate "double negatives." Booleans should represent the presence of a
feature, not its absence.
- **Rule:** Replace `Disable {feature}` or `Hide {Feature}` with
`{Feature} enabled` or simply `{Feature}`.
- **Example:** Change "Disable auto update" to "Auto update".
- **Implementation:** Invert the boolean value in your config loader so true
always equals `On`
## Verb Stripping (Brevity)
Remove redundant leading verbs like "Enable," "Use," "Display," or "Show" unless
they are part of a specific technical term.
- **Rule**: If the label works without the verb, remove it
- **Example**: Change `Enable prompt completion` to `Prompt completion`
@@ -0,0 +1,61 @@
## Terms
### Preferred
- Use **create** when a user is creating or setting up something.
- Use **allow** instead of **may** to indicate that permission has been granted
to perform some action.
- Use **canceled**, not **cancelled**.
- Use **configure** to refer to the process of changing the attributes of a
feature, even if that includes turning on or off the feature.
- Use **delete** when the action being performed is destructive.
- Use **enable** for binary operations that turn a feature or API on. Use "turn
on" and "turn off" instead of "enable" and "disable" for other situations.
- Use **key combination** to refer to pressing multiple keys simultaneously.
- Use **key sequence** to refer to pressing multiple keys separately in order.
- Use **modify** to refer to something that has changed vs obtaining the latest
version of something.
- Use **remove** when the action being performed takes an item out of a larger
whole, but doesn't destroy the item itself.
- Use **set up** as a verb. Use **setup** as a noun or adjective.
- Use **show**. In general, use paired with **hide**.
- Use **sign in**, **sign out** as a verb. Use **sign-in** or **sign-out** as a
noun or adjective.
- Use **update** when you mean to obtain the latest version of something.
- Use **want** instead of **like** or **would like**.
#### Don't use
- Don't use **etc.** It's redundant. To convey that a series is incomplete,
introduce it with "such as" instead.
- Don't use **hostname**, use "host name" instead.
- Don't use **in order to**. It's too formal. "Before you can" is usually better
in UI text.
- Don't use **one or more**. Specify the quantity where possible. Use "at least
one" when the quantity is 1+ but you can't be sure of the number. Likewise,
use "at least one" when the user must choose a quantity of 1+.
- Don't use the terms **log in**, **log on**, **login**, **logout** or **log
out**.
- Don't use **like** or **would you like**. Use **want** instead. Better yet,
rephrase so that it's not referring to the user's emotional state, but rather
what is required.
#### Use with caution
- Avoid using **leverage**, especially as a verb. "Leverage" is considered a
buzzword largely devoid of meaning apart from the simpler "use".
- Avoid using **once** as a synonym for "after". Typically, when "once" is used
in this way, it is followed by a verb in the perfect tense.
- Don't use **e.g.** Use "example", "such as", "like", or "for example". The
phrase is always followed by a comma.
- Don't use **i.e.** unless absolutely essential to make text fit. Use "that is"
instead.
- Use **disable** for binary operations that turn a feature or API off. Use
"turn on" and "turn off" instead of "enable" and "disable" for other
situations. For UI elements that are not available, use "dimmed" instead of
"disabled".
- Use **please** only when you're asking the user to do something inconvenient,
not just following the instructions in a typical flow.
- Use **really** sparingly in such constructions as "Do you really want to..."
Because of the weight it puts on the decision, it should be used to confirm
actions that the user is extremely unlikely to make.
+1 -1
View File
@@ -193,7 +193,7 @@ runs:
INPUTS_A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}'
- name: '📦 Prepare bundled CLI for npm release'
if: "inputs.npm-registry-url != 'https://npm.pkg.github.com/'"
if: "inputs.npm-registry-url != 'https://npm.pkg.github.com/' && inputs.npm-tag != 'latest'"
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
run: |
+21
View File
@@ -264,6 +264,27 @@ jobs:
run: 'npm run build'
shell: 'pwsh'
- name: 'Ensure Chrome is available'
shell: 'pwsh'
run: |
$chromePaths = @(
"${env:ProgramFiles}\Google\Chrome\Application\chrome.exe",
"${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe"
)
$chromeExists = $chromePaths | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $chromeExists) {
Write-Host 'Chrome not found, installing via Chocolatey...'
choco install googlechrome -y --no-progress --ignore-checksums
}
$installed = $chromePaths | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($installed) {
Write-Host "Chrome found at: $installed"
& $installed --version
} else {
Write-Error 'Chrome installation failed'
exit 1
}
- name: 'Run E2E tests'
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
+1 -1
View File
@@ -169,7 +169,7 @@ jobs:
npm run test:ci --workspace @google/gemini-cli
else
# Explicitly list non-cli packages to ensure they are sharded correctly
npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present
npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present -- --coverage.enabled=false
npm run test:scripts
fi
@@ -121,6 +121,7 @@ jobs:
'area/security',
'area/platform',
'area/extensions',
'area/documentation',
'area/unknown'
];
const labelNames = labels.map(label => label.name).filter(name => allowedLabels.includes(name));
@@ -255,6 +256,14 @@ jobs:
"Issues with a specific extension."
"Feature request for the extension ecosystem."
area/documentation
- Description: Issues related to user-facing documentation and other content on the documentation website.
- Example Issues:
"A typo in a README file."
"DOCS: A command is not working as described in the documentation."
"A request for a new documentation page."
"Instructions missing for skills feature"
area/unknown
- Description: Issues that do not clearly fit into any other defined area/ category, or where information is too limited to make a determination. Use this when no other area is appropriate.
@@ -63,7 +63,7 @@ jobs:
echo '🔍 Finding issues missing area labels...'
NO_AREA_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
--search 'is:open is:issue -label:area/core -label:area/agent -label:area/enterprise -label:area/non-interactive -label:area/security -label:area/platform -label:area/extensions -label:area/unknown' --limit 100 --json number,title,body)"
--search 'is:open is:issue -label:area/core -label:area/agent -label:area/enterprise -label:area/non-interactive -label:area/security -label:area/platform -label:area/extensions -label:area/documentation -label:area/unknown' --limit 100 --json number,title,body)"
echo '🔍 Finding issues missing kind labels...'
NO_KIND_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
@@ -204,6 +204,7 @@ jobs:
Categorization Guidelines (Area):
area/agent: Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality
area/core: User Interface, OS Support, Core Functionality
area/documentation: End-user and contributor-facing documentation, website-related
area/enterprise: Telemetry, Policy, Quota / Licensing
area/extensions: Gemini CLI extensions capability
area/non-interactive: GitHub Actions, SDK, 3P Integrations, Shell Scripting, Command line automation
@@ -120,6 +120,9 @@ jobs:
if (recentRuns.length > 0) {
core.setOutput('dispatched_run_urls', recentRuns.map(r => r.html_url).join(','));
core.setOutput('dispatched_run_ids', recentRuns.map(r => r.id).join(','));
const markdownLinks = recentRuns.map(r => `- [View dispatched workflow run](${r.html_url})`).join('\n');
core.setOutput('dispatched_run_links', markdownLinks);
}
- name: 'Comment on Failure'
@@ -138,16 +141,19 @@ jobs:
token: '${{ secrets.GITHUB_TOKEN }}'
issue-number: '${{ github.event.issue.number }}'
body: |
**Patch workflow(s) dispatched successfully!**
🚀 **[Step 1/4] Patch workflow(s) waiting for approval!**
**📋 Details:**
- **Channels**: `${{ steps.dispatch_patch.outputs.dispatched_channels }}`
- **Commit**: `${{ steps.pr_status.outputs.MERGE_COMMIT_SHA }}`
- **Workflows Created**: ${{ steps.dispatch_patch.outputs.dispatched_run_count }}
**⏳ Status:** The patch creation workflow has been triggered and is waiting for deployment approval. Please visit the specific workflow links below and approve the runs.
**🔗 Track Progress:**
- [View patch workflows](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
- [This workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
${{ steps.dispatch_patch.outputs.dispatched_run_links }}
- [View patch workflow history](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
- [This trigger workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
- name: 'Final Status Comment - Dispatch Success (No URL)'
if: "always() && startsWith(github.event.comment.body, '/patch') && steps.dispatch_patch.outcome == 'success' && !steps.dispatch_patch.outputs.dispatched_run_urls"
@@ -156,16 +162,18 @@ jobs:
token: '${{ secrets.GITHUB_TOKEN }}'
issue-number: '${{ github.event.issue.number }}'
body: |
**Patch workflow(s) dispatched successfully!**
🚀 **[Step 1/4] Patch workflow(s) waiting for approval!**
**📋 Details:**
- **Channels**: `${{ steps.dispatch_patch.outputs.dispatched_channels }}`
- **Commit**: `${{ steps.pr_status.outputs.MERGE_COMMIT_SHA }}`
- **Workflows Created**: ${{ steps.dispatch_patch.outputs.dispatched_run_count }}
**⏳ Status:** The patch creation workflow has been triggered and is waiting for deployment approval. Please visit the workflow history link below and approve the runs.
**🔗 Track Progress:**
- [View patch workflows](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
- [This workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
- [View patch workflow history](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
- [This trigger workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
- name: 'Final Status Comment - Failure'
if: "always() && startsWith(github.event.comment.body, '/patch') && (steps.dispatch_patch.outcome == 'failure' || steps.dispatch_patch.outcome == 'cancelled')"
@@ -174,7 +182,7 @@ jobs:
token: '${{ secrets.GITHUB_TOKEN }}'
issue-number: '${{ github.event.issue.number }}'
body: |
❌ **Patch workflow dispatch failed!**
❌ **[Step 1/4] Patch workflow dispatch failed!**
There was an error dispatching the patch creation workflow.
+2
View File
@@ -62,3 +62,5 @@ gemini-debug.log
.gemini-clipboard/
.eslintcache
evals/logs/
temp_agents/
+42 -18
View File
@@ -60,20 +60,45 @@ All submissions, including submissions by project members, require review. We
use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests)
for this purpose.
If your pull request involves changes to `packages/cli` (the frontend), we
recommend running our automated frontend review tool. **Note: This tool is
currently experimental.** It helps detect common React anti-patterns, testing
issues, and other frontend-specific best practices that are easy to miss.
To assist with the review process, we provide an automated review tool that
helps detect common anti-patterns, testing issues, and other best practices that
are easy to miss.
To run the review tool, enter the following command from within Gemini CLI:
#### Using the automated review tool
```text
/review-frontend <PR_NUMBER>
```
You can run the review tool in two ways:
Replace `<PR_NUMBER>` with your pull request number. Authors are encouraged to
run this on their own PRs for self-review, and reviewers should use it to
augment their manual review process.
1. **Using the helper script (Recommended):** We provide a script that
automatically handles checking out the PR into a separate worktree,
installing dependencies, building the project, and launching the review
tool.
```bash
./scripts/review.sh <PR_NUMBER> [model]
```
**Warning:** If you run `scripts/review.sh`, you must have first verified
that the code for the PR being reviewed is safe to run and does not contain
data exfiltration attacks.
**Authors are strongly encouraged to run this script on their own PRs**
immediately after creation. This allows you to catch and fix simple issues
locally before a maintainer performs a full review.
**Note on Models:** By default, the script uses the latest Pro model
(`gemini-3.1-pro-preview`). If you do not have enough Pro quota, you can run
it with the latest Flash model instead:
`./scripts/review.sh <PR_NUMBER> gemini-3-flash-preview`.
2. **Manually from within Gemini CLI:** If you already have the PR checked out
and built, you can run the tool directly from the CLI prompt:
```text
/review-frontend <PR_NUMBER>
```
Replace `<PR_NUMBER>` with your pull request number. Reviewers should use this
tool to augment, not replace, their manual review process.
### Self-assigning and unassigning issues
@@ -267,7 +292,8 @@ npm run test:e2e
```
For more detailed information on the integration testing framework, please see
the [Integration Tests documentation](/docs/integration-tests.md).
the
[Integration Tests documentation](https://geminicli.com/docs/integration-tests).
### Linting and preflight checks
@@ -320,11 +346,9 @@ npm run lint
- Please adhere to the coding style, patterns, and conventions used throughout
the existing codebase.
- Consult
[GEMINI.md](https://github.com/google-gemini/gemini-cli/blob/main/GEMINI.md)
(typically found in the project root) for specific instructions related to
AI-assisted development, including conventions for React, comments, and Git
usage.
- Consult [GEMINI.md](../GEMINI.md) (typically found in the project root) for
specific instructions related to AI-assisted development, including
conventions for React, comments, and Git usage.
- **Imports:** Pay special attention to import paths. The project uses ESLint to
enforce restrictions on relative imports between packages.
@@ -548,7 +572,7 @@ Before submitting your documentation pull request, please:
If you have questions about contributing documentation:
- Check our [FAQ](/docs/resources/faq.md).
- Check our [FAQ](https://geminicli.com/docs/resources/faq).
- Review existing documentation for examples.
- Open [an issue](https://github.com/google-gemini/gemini-cli/issues) to discuss
your proposed changes.
+6 -6
View File
@@ -6,7 +6,7 @@
[![License](https://img.shields.io/github/license/google-gemini/gemini-cli)](https://github.com/google-gemini/gemini-cli/blob/main/LICENSE)
[![View Code Wiki](https://assets.codewiki.google/readme-badge/static.svg)](https://codewiki.google/github.com/google-gemini/gemini-cli?utm_source=badge&utm_medium=github&utm_campaign=github.com/google-gemini/gemini-cli)
![Gemini CLI Screenshot](./docs/assets/gemini-screenshot.png)
![Gemini CLI Screenshot](/docs/assets/gemini-screenshot.png)
Gemini CLI is an open-source AI agent that brings the power of Gemini directly
into your terminal. It provides lightweight access to Gemini, giving you the
@@ -77,7 +77,7 @@ See [Releases](./docs/releases.md) for more details.
### Preview
New preview releases will be published each week at UTC 2359 on Tuesdays. These
New preview releases will be published each week at UTC 23:59 on Tuesdays. These
releases will not have been fully vetted and may contain regressions or other
outstanding issues. Please help us test and install with `preview` tag.
@@ -87,7 +87,7 @@ npm install -g @google/gemini-cli@preview
### Stable
- New stable releases will be published each week at UTC 2000 on Tuesdays, this
- New stable releases will be published each week at UTC 20:00 on Tuesdays, this
will be the full promotion of last week's `preview` release + any bug fixes
and validations. Use `latest` tag.
@@ -97,7 +97,7 @@ npm install -g @google/gemini-cli@latest
### Nightly
- New releases will be published each day at UTC 0000. This will be all changes
- New releases will be published each day at UTC 00:00. This will be all changes
from the main branch as represented at time of release. It should be assumed
there are pending validations and issues. Use `nightly` tag.
@@ -147,7 +147,7 @@ Integrate Gemini CLI directly into your GitHub workflows with
Choose the authentication method that best fits your needs:
### Option 1: Login with Google (OAuth login using your Google Account)
### Option 1: Sign in with Google (OAuth login using your Google Account)
**✨ Best for:** Individual developers as well as anyone who has a Gemini Code
Assist License. (see
@@ -161,7 +161,7 @@ for details)
- **No API key management** - just sign in with your Google account
- **Automatic updates** to latest models
#### Start Gemini CLI, then choose _Login with Google_ and follow the browser authentication flow when prompted
#### Start Gemini CLI, then choose _Sign in with Google_ and follow the browser authentication flow when prompted
```bash
gemini
Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 120 KiB

+89 -71
View File
@@ -1,6 +1,6 @@
# Preview release: v0.33.0-preview.1
# Preview release: v0.33.0-preview.13
Released: March 04, 2026
Released: March 10, 2026
Our preview release includes the latest, new, and experimental features. This
release may not be as stable as our [latest weekly release](latest.md).
@@ -29,159 +29,177 @@ npm install -g @google/gemini-cli@preview
## What's Changed
- 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 7ec477d to release/v0.33.0-preview.3-pr-21305 to patch
version v0.33.0-preview.3 and create version 0.33.0-preview.4 by
@gemini-cli-robot in
[#21349](https://github.com/google-gemini/gemini-cli/pull/21349)
- fix(patch): cherry-pick 0135b03 to release/v0.33.0-preview.2-pr-21171
[CONFLICTS] by @gemini-cli-robot in
[#21336](https://github.com/google-gemini/gemini-cli/pull/21336)
- fix(patch): cherry-pick 173376b to release/v0.33.0-preview.1-pr-21157 to patch
version v0.33.0-preview.1 and create version 0.33.0-preview.2 by
@gemini-cli-robot in
[#21300](https://github.com/google-gemini/gemini-cli/pull/21300)
- fix(patch): cherry-pick 173376b to release/v0.33.0-preview.1-pr-21157 to patch
version v0.33.0-preview.1 and create version 0.33.0-preview.2 by
@gemini-cli-robot in
[#21300](https://github.com/google-gemini/gemini-cli/pull/21300)
- fix(patch): cherry-pick 0659ad1 to release/v0.33.0-preview.0-pr-21042 to patch
version v0.33.0-preview.0 and create version 0.33.0-preview.1 by
@gemini-cli-robot in
[#21047](https://github.com/google-gemini/gemini-cli/pull/21047)
* Docs: Update model docs to remove Preview Features. by @jkcinouye in
- 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
- 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
- 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
- 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
- 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
- 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
- chore/release: bump version to 0.33.0-nightly.20260228.1ca5c05d0 by
@gemini-cli-robot in
[#20644](https://github.com/google-gemini/gemini-cli/pull/20644)
* Changelog for v0.31.0 by @gemini-cli-robot in
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- feat(core): centralize read_file limits and update gemini-3 description by
@aishaneeshah in
[#20619](https://github.com/google-gemini/gemini-cli/pull/20619)
* Do not block CI on evals by @gundermanc in
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- feat(admin): enable 30 day default retention for chat history & remove warning
by @skeshive in
[#20853](https://github.com/google-gemini/gemini-cli/pull/20853)
* feat(plan): support annotating plans with feedback for iteration by @Adib234
- 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
- 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
- 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
- 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
- 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
- Build binary by @aswinashok44 in
[#18933](https://github.com/google-gemini/gemini-cli/pull/18933)
* Code review fixes as a pr by @jacob314 in
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- feat(acp): add set models interface by @skeshive in
[#20991](https://github.com/google-gemini/gemini-cli/pull/20991)
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.32.0-preview.0...v0.33.0-preview.1
https://github.com/google-gemini/gemini-cli/compare/v0.32.0-preview.0...v0.33.0-preview.13
+21 -5
View File
@@ -8,7 +8,8 @@ and parameters.
| Command | Description | Example |
| ---------------------------------- | ---------------------------------- | ------------------------------------------------------------ |
| `gemini` | Start interactive REPL | `gemini` |
| `gemini "query"` | Query non-interactively, then exit | `gemini "explain this project"` |
| `gemini -p "query"` | Query non-interactively | `gemini -p "summarize README.md"` |
| `gemini "query"` | Query and continue interactively | `gemini "explain this project"` |
| `cat file \| gemini` | Process piped content | `cat logs.txt \| gemini`<br>`Get-Content logs.txt \| gemini` |
| `gemini -i "query"` | Execute and continue interactively | `gemini -i "What is the purpose of this project?"` |
| `gemini -r "latest"` | Continue most recent session | `gemini -r "latest"` |
@@ -20,9 +21,24 @@ and parameters.
### Positional arguments
| Argument | Type | Description |
| -------- | ----------------- | ------------------------------------------------------------------------------------------------------------------ |
| `query` | string (variadic) | Positional prompt. Defaults to one-shot mode. Use `-i/--prompt-interactive` to execute and continue interactively. |
| Argument | Type | Description |
| -------- | ----------------- | ---------------------------------------------------------------------------------------------------------- |
| `query` | string (variadic) | Positional prompt. Defaults to interactive mode in a TTY. Use `-p/--prompt` for non-interactive execution. |
## Interactive commands
These commands are available within the interactive REPL.
| Command | Description |
| -------------------- | ---------------------------------------- |
| `/skills reload` | Reload discovered skills from disk |
| `/agents reload` | Reload the agent registry |
| `/commands reload` | Reload custom slash commands |
| `/memory reload` | Reload context files (e.g., `GEMINI.md`) |
| `/mcp reload` | Restart and reload MCP servers |
| `/extensions reload` | Reload all active extensions |
| `/help` | Show help for all commands |
| `/quit` | Exit the interactive session |
## CLI Options
@@ -32,7 +48,7 @@ and parameters.
| `--version` | `-v` | - | - | Show CLI version number and exit |
| `--help` | `-h` | - | - | Show help information |
| `--model` | `-m` | string | `auto` | Model to use. See [Model Selection](#model-selection) for available values. |
| `--prompt` | `-p` | string | - | Prompt text. Appended to stdin input if provided. **Deprecated:** Use positional arguments instead. |
| `--prompt` | `-p` | string | - | Prompt text. Appended to stdin input if provided. Forces non-interactive mode. |
| `--prompt-interactive` | `-i` | string | - | Execute prompt and continue in interactive mode |
| `--sandbox` | `-s` | boolean | `false` | Run in a sandboxed environment for safer execution |
| `--approval-mode` | - | string | `default` | Approval mode for tool execution. Choices: `default`, `auto_edit`, `yolo` |
+1 -1
View File
@@ -63,7 +63,7 @@ You can interact with the loaded context files by using the `/memory` command.
- **`/memory show`**: Displays the full, concatenated content of the current
hierarchical memory. This lets you inspect the exact instructional context
being provided to the model.
- **`/memory refresh`**: Forces a re-scan and reload of all `GEMINI.md` files
- **`/memory reload`**: Forces a re-scan and reload of all `GEMINI.md` files
from all configured locations.
- **`/memory add <text>`**: Appends your text to your global
`~/.gemini/GEMINI.md` file. This lets you add persistent memories on the fly.
+3 -2
View File
@@ -6,7 +6,7 @@ structured text or JSON output without an interactive terminal UI.
## Technical reference
Headless mode is triggered when the CLI is run in a non-TTY environment or when
providing a query as a positional argument without the interactive flag.
providing a query with the `-p` (or `--prompt`) flag.
### Output formats
@@ -31,7 +31,8 @@ Returns a stream of newline-delimited JSON (JSONL) events.
- `tool_use`: Tool call requests with arguments.
- `tool_result`: Output from executed tools.
- `error`: Non-fatal warnings and system errors.
- `result`: Final outcome with aggregated statistics.
- `result`: Final outcome with aggregated statistics and per-model token usage
breakdowns.
## Exit codes
+58
View File
@@ -0,0 +1,58 @@
# Notifications (experimental)
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.
> **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
CLI works in the background.
## Requirements
Currently, system notifications are only supported on macOS.
### Terminal support
The CLI uses the OSC 9 terminal escape sequence to trigger system notifications.
This is supported by several modern terminal emulators. If your terminal does
not support OSC 9 notifications, Gemini CLI falls back to a system alert sound
to get your attention.
## Enable notifications
Notifications are disabled by default. You can enable them using the `/settings`
command or by updating your `settings.json` file.
1. Open the settings dialog by typing `/settings` in an interactive session.
2. Navigate to the **General** category.
3. Toggle the **Enable Notifications** setting to **On**.
Alternatively, add the following to your `settings.json`:
```json
{
"general": {
"enableNotifications": true
}
}
```
## Types of notifications
Gemini CLI sends notifications for the following events:
- **Action required:** Triggered when the model is waiting for user input or
tool approval. This helps you know when the CLI has paused and needs you to
intervene.
- **Session complete:** Triggered when a session finishes successfully. This is
useful for tracking the completion of automated tasks.
## Next steps
- Start planning with [Plan Mode](./plan-mode.md).
- Configure your experience with other [settings](./settings.md).
+96 -91
View File
@@ -1,4 +1,4 @@
# Plan Mode (experimental)
# Plan Mode
Plan Mode is a read-only environment for architecting robust solutions before
implementation. With Plan Mode, you can:
@@ -8,27 +8,8 @@ implementation. With Plan Mode, you can:
- **Design:** Understand problems, evaluate trade-offs, and choose a solution.
- **Plan:** Align on an execution strategy before any code is modified.
> **Note:** This is a preview feature currently under active development. Your
> feedback is invaluable as we refine this feature. If you have ideas,
> suggestions, or encounter issues:
>
> - [Open an issue] on GitHub.
> - Use the **/bug** command within Gemini CLI to file an issue.
## How to enable Plan Mode
Enable Plan Mode in **Settings** or by editing your configuration file.
- **Settings:** Use the `/settings` command and set **Plan** to `true`.
- **Configuration:** Add the following to your `settings.json`:
```json
{
"experimental": {
"plan": true
}
}
```
Plan Mode is enabled by default. You can manage this setting using the
`/settings` command.
## How to enter Plan Mode
@@ -62,8 +43,11 @@ To start Plan Mode while using Gemini CLI:
- **Command:** Type `/plan` in the input box.
- **Natural Language:** Ask Gemini CLI to "start a plan for...". Gemini CLI
calls the [`enter_plan_mode`] tool to switch modes.
> **Note:** This tool is not available when Gemini CLI is in [YOLO mode].
calls the
[`enter_plan_mode`](../tools/planning.md#1-enter_plan_mode-enterplanmode) tool
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
@@ -74,7 +58,8 @@ Gemini CLI takes action.
will then enter Plan Mode (if it's not already) to research the task.
2. **Review research and provide input:** As Gemini CLI analyzes your codebase,
it may ask you questions or present different implementation options using
[`ask_user`]. Provide your preferences to help guide the design.
[`ask_user`](../tools/ask-user.md). Provide your preferences to help guide
the design.
3. **Review the plan:** Once Gemini CLI has a proposed strategy, it creates a
detailed implementation plan as a Markdown file in your plans directory. You
can open and read this file to understand the proposed changes.
@@ -116,25 +101,33 @@ Plan Mode enforces strict safety policies to prevent accidental changes.
These are the only allowed tools:
- **FileSystem (Read):** [`read_file`], [`list_directory`], [`glob`]
- **Search:** [`grep_search`], [`google_web_search`]
- **Research Subagents:** [`codebase_investigator`], [`cli_help`]
- **Interaction:** [`ask_user`]
- **MCP tools (Read):** Read-only [MCP tools] (for example, `github_read_issue`,
`postgres_read_schema`) are allowed.
- **Planning (Write):** [`write_file`] and [`replace`] only allowed for `.md`
- **FileSystem (Read):**
[`read_file`](../tools/file-system.md#2-read_file-readfile),
[`list_directory`](../tools/file-system.md#1-list_directory-readfolder),
[`glob`](../tools/file-system.md#4-glob-findfiles)
- **Search:** [`grep_search`](../tools/file-system.md#5-grep_search-searchtext),
[`google_web_search`](../tools/web-search.md)
- **Research Subagents:**
[`codebase_investigator`](../core/subagents.md#codebase-investigator),
[`cli_help`](../core/subagents.md#cli-help-agent)
- **Interaction:** [`ask_user`](../tools/ask-user.md)
- **MCP tools (Read):** Read-only [MCP tools](../tools/mcp-server.md) (for
example, `github_read_issue`, `postgres_read_schema`) are allowed.
- **Planning (Write):**
[`write_file`](../tools/file-system.md#3-write_file-writefile) and
[`replace`](../tools/file-system.md#6-replace-edit) only allowed for `.md`
files in the `~/.gemini/tmp/<project>/<session-id>/plans/` directory or your
[custom plans directory](#custom-plan-directory-and-policies).
- **Memory:** [`save_memory`]
- **Skills:** [`activate_skill`] (allows loading specialized instructions and
resources in a read-only manner)
- **Memory:** [`save_memory`](../tools/memory.md)
- **Skills:** [`activate_skill`](../cli/skills.md) (allows loading specialized
instructions and resources in a read-only manner)
### Custom planning with skills
You can use [Agent Skills] to customize how Gemini CLI approaches planning for
specific types of tasks. When a skill is activated during Plan Mode, its
specialized instructions and procedural workflows will guide the research,
design, and planning phases.
You can use [Agent Skills](../cli/skills.md) to customize how Gemini CLI
approaches planning for specific types of tasks. When a skill is activated
during Plan Mode, its specialized instructions and procedural workflows will
guide the research, design, and planning phases.
For example:
@@ -151,10 +144,32 @@ based on the task description.
### Custom policies
Plan Mode's default tool restrictions are managed by the [policy engine] and
defined in the built-in [`plan.toml`] file. The built-in policy (Tier 1)
enforces the read-only state, but you can customize these rules by creating your
own policies in your `~/.gemini/policies/` directory (Tier 2).
Plan Mode's default tool restrictions are managed by the
[policy engine](../reference/policy-engine.md) and defined in the built-in
[`plan.toml`] file. The built-in policy (Tier 1) enforces the read-only state,
but you can customize these rules by creating your own policies in your
`~/.gemini/policies/` directory (Tier 2).
#### Global vs. mode-specific rules
As described in the
[policy engine documentation](../reference/policy-engine.md#approval-modes), any
rule that does not explicitly specify `modes` is considered "always active" and
will apply to Plan Mode as well.
If you want a rule to apply to other modes but _not_ to Plan Mode, you must
explicitly specify the target modes. For example, to allow `npm test` in default
and Auto-Edit modes but not in Plan Mode:
```toml
[[rule]]
toolName = "run_shell_command"
commandPrefix = "npm test"
decision = "allow"
priority = 100
# By omitting "plan", this rule will not be active in Plan Mode.
modes = ["default", "autoEdit"]
```
#### Example: Automatically approve read-only MCP tools
@@ -173,8 +188,8 @@ priority = 100
modes = ["plan"]
```
For more information on how the policy engine works, see the [policy engine]
docs.
For more information on how the policy engine works, see the
[policy engine](../reference/policy-engine.md) docs.
#### Example: Allow git commands in Plan Mode
@@ -194,9 +209,12 @@ modes = ["plan"]
#### Example: Enable custom subagents in Plan Mode
Built-in research [subagents] like [`codebase_investigator`] and [`cli_help`]
are enabled by default in Plan Mode. You can enable additional [custom
subagents] by adding a rule to your policy.
Built-in research [subagents](../core/subagents.md) like
[`codebase_investigator`](../core/subagents.md#codebase-investigator) and
[`cli_help`](../core/subagents.md#cli-help-agent) are enabled by default in Plan
Mode. You can enable additional
[custom subagents](../core/subagents.md#creating-custom-subagents) by adding a
rule to your policy.
`~/.gemini/policies/research-subagents.toml`
@@ -235,10 +253,11 @@ locations defined within a project's workspace cannot be used to escape and
overwrite sensitive files elsewhere. Any user-configured directory must reside
within the project boundary.
Using a custom directory requires updating your [policy engine] configurations
to allow `write_file` and `replace` in that specific location. For example, to
allow writing to the `.gemini/plans` directory within your project, create a
policy file at `~/.gemini/policies/plan-custom-directory.toml`:
Using a custom directory requires updating your
[policy engine](../reference/policy-engine.md) configurations to allow
`write_file` and `replace` in that specific location. For example, to allow
writing to the `.gemini/plans` directory within your project, create a policy
file at `~/.gemini/policies/plan-custom-directory.toml`:
```toml
[[rule]]
@@ -254,13 +273,16 @@ argsPattern = "\"file_path\":\"[^\"]+[\\\\/]+\\.gemini[\\\\/]+plans[\\\\/]+[\\w-
## Planning workflows
Plan Mode provides building blocks for structured research and design. These are
implemented as [extensions] using core planning tools like [`enter_plan_mode`],
[`exit_plan_mode`], and [`ask_user`].
implemented as [extensions](../extensions/index.md) using core planning tools
like [`enter_plan_mode`](../tools/planning.md#1-enter_plan_mode-enterplanmode),
[`exit_plan_mode`](../tools/planning.md#2-exit_plan_mode-exitplanmode), and
[`ask_user`](../tools/ask-user.md).
### Built-in planning workflow
The built-in planner uses an adaptive workflow to analyze your project, consult
you on trade-offs via [`ask_user`], and draft a plan for your approval.
you on trade-offs via [`ask_user`](../tools/ask-user.md), and draft a plan for
your approval.
### Custom planning workflows
@@ -272,23 +294,29 @@ You can install or create specialized planners to suit your workflow.
"tracks" and stores persistent artifacts in your project's `conductor/`
directory:
- **Automate transitions:** Switches to read-only mode via [`enter_plan_mode`].
- **Streamline decisions:** Uses [`ask_user`] for architectural choices.
- **Automate transitions:** Switches to read-only mode via
[`enter_plan_mode`](../tools/planning.md#1-enter_plan_mode-enterplanmode).
- **Streamline decisions:** Uses [`ask_user`](../tools/ask-user.md) for
architectural choices.
- **Maintain project context:** Stores artifacts in the project directory using
[custom plan directory and policies](#custom-plan-directory-and-policies).
- **Handoff execution:** Transitions to implementation via [`exit_plan_mode`].
- **Handoff execution:** Transitions to implementation via
[`exit_plan_mode`](../tools/planning.md#2-exit_plan_mode-exitplanmode).
#### Build your own
Since Plan Mode is built on modular building blocks, you can develop your own
custom planning workflow as an [extensions]. By leveraging core tools and
[custom policies](#custom-policies), you can define how Gemini CLI researches
and stores plans for your specific domain.
custom planning workflow as an [extensions](../extensions/index.md). By
leveraging core tools and [custom policies](#custom-policies), you can define
how Gemini CLI researches and stores plans for your specific domain.
To build a custom planning workflow, you can use:
- **Tool usage:** Use core tools like [`enter_plan_mode`], [`ask_user`], and
[`exit_plan_mode`] to manage the research and design process.
- **Tool usage:** Use core tools like
[`enter_plan_mode`](../tools/planning.md#1-enter_plan_mode-enterplanmode),
[`ask_user`](../tools/ask-user.md), and
[`exit_plan_mode`](../tools/planning.md#2-exit_plan_mode-exitplanmode) to
manage the research and design process.
- **Customization:** Set your own storage locations and policy rules using
[custom plan directories](#custom-plan-directory-and-policies) and
[custom policies](#custom-policies).
@@ -302,8 +330,9 @@ high-reasoning model routing.
## Automatic Model Routing
When using an [auto model], Gemini CLI automatically optimizes [model routing]
based on the current phase of your task:
When using an [auto model](../reference/configuration.md#model), Gemini CLI
automatically optimizes [model routing](../cli/telemetry.md#model-routing) based
on the current phase of your task:
1. **Planning Phase:** While in Plan Mode, the CLI routes requests to a
high-reasoning **Pro** model to ensure robust architectural decisions and
@@ -334,7 +363,8 @@ associated plan files and task trackers.
- **Default behavior:** Sessions (and their plans) are retained for **30 days**.
- **Configuration:** You can customize this behavior via the `/settings` command
(search for **Session Retention**) or in your `settings.json` file. See
[session retention] for more details.
[session retention](../cli/session-management.md#session-retention) for more
details.
Manual deletion also removes all associated artifacts:
@@ -344,32 +374,7 @@ 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.
[`list_directory`]: ../tools/file-system.md#1-list_directory-readfolder
[`read_file`]: ../tools/file-system.md#2-read_file-readfile
[`grep_search`]: ../tools/file-system.md#5-grep_search-searchtext
[`write_file`]: ../tools/file-system.md#3-write_file-writefile
[`glob`]: ../tools/file-system.md#4-glob-findfiles
[`google_web_search`]: ../tools/web-search.md
[`replace`]: ../tools/file-system.md#6-replace-edit
[MCP tools]: ../tools/mcp-server.md
[`save_memory`]: ../tools/memory.md
[`activate_skill`]: ./skills.md
[`codebase_investigator`]: ../core/subagents.md#codebase_investigator
[`cli_help`]: ../core/subagents.md#cli_help
[subagents]: ../core/subagents.md
[custom subagents]: ../core/subagents.md#creating-custom-subagents
[policy engine]: ../reference/policy-engine.md
[`enter_plan_mode`]: ../tools/planning.md#1-enter_plan_mode-enterplanmode
[`exit_plan_mode`]: ../tools/planning.md#2-exit_plan_mode-exitplanmode
[`ask_user`]: ../tools/ask-user.md
[YOLO mode]: ../reference/configuration.md#command-line-arguments
[`plan.toml`]:
https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/policy/policies/plan.toml
[auto model]: ../reference/configuration.md#model-settings
[model routing]: ./telemetry.md#model-routing
[preferred external editor]: ../reference/configuration.md#general
[session retention]: ./session-management.md#session-retention
[extensions]: ../extensions/index.md
[Conductor]: https://github.com/gemini-cli-extensions/conductor
[open an issue]: https://github.com/google-gemini/gemini-cli/issues
[Agent Skills]: ./skills.md
+26 -2
View File
@@ -50,7 +50,31 @@ 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. LXC/LXD (Linux only, experimental)
### 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
system calls and handles them in a sandboxed kernel written in Go, providing a
strong security barrier between AI operations and the host OS.
**Prerequisites:**
- Linux (gVisor supports Linux only)
- Docker installed and running
- gVisor/runsc runtime configured
When you set `sandbox: "runsc"`, Gemini CLI runs
`docker run --runtime=runsc ...` to execute containers with gVisor isolation.
runsc is not auto-detected; you must specify it explicitly (e.g.
`GEMINI_SANDBOX=runsc` or `sandbox: "runsc"`).
To set up runsc:
1. Install the runsc binary.
2. Configure the Docker daemon to use the runsc runtime.
3. Verify the installation.
### 4. LXC/LXD (Linux only, experimental)
Full-system container sandboxing using LXC/LXD. Unlike Docker/Podman, LXC
containers run a complete Linux system with `systemd`, `snapd`, and other system
@@ -133,7 +157,7 @@ gemini -p "run the test suite"
1. **Command flag**: `-s` or `--sandbox`
2. **Environment variable**:
`GEMINI_SANDBOX=true|docker|podman|sandbox-exec|lxc`
`GEMINI_SANDBOX=true|docker|podman|sandbox-exec|runsc|lxc`
3. **Settings file**: `"sandbox": true` in the `tools` object of your
`settings.json` file (e.g., `{"tools": {"sandbox": true}}`).
+24
View File
@@ -61,6 +61,15 @@ Browser**:
/resume
```
When typing `/resume` (or `/chat`) in slash completion, commands are grouped
under titled separators:
- `-- auto --` (session browser)
- `list` is selectable and opens the session browser
- `-- checkpoints --` (manual tagged checkpoint commands)
Unique prefixes such as `/resum` and `/cha` resolve to the same grouped menu.
The Session Browser provides an interactive interface where you can perform the
following actions:
@@ -72,6 +81,21 @@ following actions:
- **Select:** Press **Enter** to resume the selected session.
- **Esc:** Press **Esc** to exit the Session Browser.
### Manual chat checkpoints
For named branch points inside a session, use chat checkpoints:
```text
/resume save decision-point
/resume list
/resume resume decision-point
```
Compatibility aliases:
- `/chat ...` works for the same commands.
- `/resume checkpoints ...` also remains supported during migration.
## Managing sessions
You can list and delete sessions to keep your history organized and manage disk
+5 -5
View File
@@ -57,7 +57,7 @@ they appear in the UI.
| Show Shortcuts Hint | `ui.showShortcutsHint` | Show the "? for shortcuts" hint above the input. | `true` |
| Hide Banner | `ui.hideBanner` | Hide the application banner | `false` |
| Hide Context Summary | `ui.hideContextSummary` | Hide the context summary (GEMINI.md, MCP servers) above the input. | `false` |
| Hide CWD | `ui.footer.hideCWD` | Hide the current working directory in the footer. | `false` |
| Hide Workspace Path | `ui.footer.hideCWD` | Hide the workspace path in the footer. | `false` |
| Hide Sandbox Status | `ui.footer.hideSandboxStatus` | Hide the sandbox status indicator in the footer. | `false` |
| Hide Model Info | `ui.footer.hideModelInfo` | Hide the model name and context usage in the footer. | `false` |
| Hide Context Window Percentage | `ui.footer.hideContextPercentage` | Hides the context window usage percentage. | `true` |
@@ -66,7 +66,7 @@ they appear in the UI.
| Show Line Numbers | `ui.showLineNumbers` | Show line numbers in the chat. | `true` |
| Show Citations | `ui.showCitations` | Show citations for generated text in the chat. | `false` |
| Show Model Info In Chat | `ui.showModelInfoInChat` | Show the model name in the chat for each model turn. | `false` |
| Show User Identity | `ui.showUserIdentity` | Show the logged-in user's identity (e.g. email) in the UI. | `true` |
| Show User Identity | `ui.showUserIdentity` | Show the signed-in user's identity (e.g. email) in the UI. | `true` |
| Use Alternate Screen Buffer | `ui.useAlternateBuffer` | Use an alternate screen buffer for the UI, preserving shell history. | `false` |
| Use Background Color | `ui.useBackgroundColor` | Whether to use background colors in the UI. | `true` |
| Incremental Rendering | `ui.incrementalRendering` | Enable incremental rendering for the UI. This option will reduce flickering but may cause rendering artifacts. Only supported when useAlternateBuffer is enabled. | `true` |
@@ -102,7 +102,7 @@ they appear in the UI.
| UI Label | Setting | Description | Default |
| ------------------------------------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| Memory Discovery Max Dirs | `context.discoveryMaxDirs` | Maximum number of directories to search for memory. | `200` |
| Load Memory From Include Directories | `context.loadMemoryFromIncludeDirectories` | Controls how /memory refresh loads GEMINI.md files. When true, include directories are scanned; when false, only the current directory is used. | `false` |
| Load Memory From Include Directories | `context.loadMemoryFromIncludeDirectories` | Controls how /memory reload loads GEMINI.md files. When true, include directories are scanned; when false, only the current directory is used. | `false` |
| Respect .gitignore | `context.fileFiltering.respectGitIgnore` | Respect .gitignore files when searching. | `true` |
| Respect .geminiignore | `context.fileFiltering.respectGeminiIgnore` | Respect .geminiignore files when searching. | `true` |
| Enable Recursive File Search | `context.fileFiltering.enableRecursiveFileSearch` | Enable recursive file search functionality when completing @ references in the prompt. | `true` |
@@ -125,6 +125,7 @@ they appear in the UI.
| ------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- |
| Disable YOLO Mode | `security.disableYoloMode` | Disable YOLO mode, even if enabled by a flag. | `false` |
| Allow Permanent Tool Approval | `security.enablePermanentToolApproval` | Enable the "Allow for all future sessions" option in tool confirmation dialogs. | `false` |
| Auto-add to Policy by Default | `security.autoAddToPolicyByDefault` | When enabled, the "Allow for all future sessions" option becomes the default choice for low-risk tools in trusted workspaces. | `false` |
| Blocks extensions from Git | `security.blockGitExtensions` | Blocks installing and loading extensions from Git. | `false` |
| Extension Source Regex Allowlist | `security.allowedExtensions` | List of Regex patterns for allowed extensions. If nonempty, only extensions that match the patterns in this list are allowed. Overrides the blockGitExtensions setting. | `[]` |
| Folder Trust | `security.folderTrust.enabled` | Setting to track whether Folder trust is enabled. | `true` |
@@ -144,10 +145,9 @@ they appear in the UI.
| Enable Tool Output Masking | `experimental.toolOutputMasking.enabled` | Enables tool output masking to save tokens. | `true` |
| Use OSC 52 Paste | `experimental.useOSC52Paste` | Use OSC 52 for pasting. This may be more robust than the default system when using remote terminal sessions (if your terminal is configured to allow it). | `false` |
| Use OSC 52 Copy | `experimental.useOSC52Copy` | Use OSC 52 for copying. This may be more robust than the default system when using remote terminal sessions (if your terminal is configured to allow it). | `false` |
| Plan | `experimental.plan` | Enable planning features (Plan Mode and tools). | `false` |
| 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` |
| Enable Gemma Model Router | `experimental.gemmaModelRouter.enabled` | Enable the Gemma Model Router. Requires a local endpoint serving Gemma via the Gemini API using LiteRT-LM shim. | `false` |
### Skills
+955 -688
View File
File diff suppressed because it is too large Load Diff
+26 -16
View File
@@ -16,6 +16,8 @@ using the `/theme` command within Gemini CLI:
- `Default`
- `Dracula`
- `GitHub`
- `Holiday`
- `Shades Of Purple`
- `Solarized Dark`
- **Light themes:**
- `ANSI Light`
@@ -185,7 +187,7 @@ untrusted sources.
### Example custom theme
<img src="../assets/theme-custom.png" alt="Custom theme example" width="600" />
<img src="/docs/assets/theme-custom.png" alt="Custom theme example" width="600" />
### Using your custom theme
@@ -212,58 +214,66 @@ identify their source, for example: `shades-of-green (green-extension)`.
### ANSI
<img src="/assets/theme-ansi.png" alt="ANSI theme" width="600" />
<img src="/docs/assets/theme-ansi-dark.png" alt="ANSI theme" width="600">
### Atom OneDark
### Atom One
<img src="/assets/theme-atom-one.png" alt="Atom One theme" width="600">
<img src="/docs/assets/theme-atom-one-dark.png" alt="Atom One theme" width="600">
### Ayu
<img src="/assets/theme-ayu.png" alt="Ayu theme" width="600">
<img src="/docs/assets/theme-ayu-dark.png" alt="Ayu theme" width="600">
### Default
<img src="/assets/theme-default.png" alt="Default theme" width="600">
<img src="/docs/assets/theme-default-dark.png" alt="Default theme" width="600">
### Dracula
<img src="/assets/theme-dracula.png" alt="Dracula theme" width="600">
<img src="/docs/assets/theme-dracula-dark.png" alt="Dracula theme" width="600">
### GitHub
<img src="/assets/theme-github.png" alt="GitHub theme" width="600">
<img src="/docs/assets/theme-github-dark.png" alt="GitHub theme" width="600">
### Holiday
<img src="/docs/assets/theme-holiday-dark.png" alt="Holiday theme" width="600">
### Shades Of Purple
<img src="/docs/assets/theme-shades-of-purple-dark.png" alt="Shades Of Purple theme" width="600">
### Solarized Dark
<img src="/assets/theme-solarized-dark.png" alt="Solarized Dark theme" width="600">
<img src="/docs/assets/theme-solarized-dark.png" alt="Solarized Dark theme" width="600">
## Light themes
### ANSI Light
<img src="/assets/theme-ansi-light.png" alt="ANSI Light theme" width="600">
<img src="/docs/assets/theme-ansi-light.png" alt="ANSI Light theme" width="600">
### Ayu Light
<img src="/assets/theme-ayu-light.png" alt="Ayu Light theme" width="600">
<img src="/docs/assets/theme-ayu-light.png" alt="Ayu Light theme" width="600">
### Default Light
<img src="/assets/theme-default-light.png" alt="Default Light theme" width="600">
<img src="/docs/assets/theme-default-light.png" alt="Default Light theme" width="600">
### GitHub Light
<img src="/assets/theme-github-light.png" alt="GitHub Light theme" width="600">
<img src="/docs/assets/theme-github-light.png" alt="GitHub Light theme" width="600">
### Google Code
<img src="/assets/theme-google-light.png" alt="Google Code theme" width="600">
<img src="/docs/assets/theme-google-light.png" alt="Google Code theme" width="600">
### Solarized Light
<img src="/assets/theme-solarized-light.png" alt="Solarized Light theme" width="600">
<img src="/docs/assets/theme-solarized-light.png" alt="Solarized Light theme" width="600">
### Xcode
<img src="/assets/theme-xcode-light.png" alt="Xcode Light theme" width="600">
<img src="/docs/assets/theme-xcode-light.png" alt="Xcode Light theme" width="600">
+12 -11
View File
@@ -19,14 +19,15 @@ Headless mode runs Gemini CLI once and exits. It's perfect for:
## How to use headless mode
Run Gemini CLI in headless mode by providing a prompt as a positional argument.
This bypasses the interactive chat interface and prints the response to standard
output (stdout).
Run Gemini CLI in headless mode by providing a prompt with the `-p` (or
`--prompt`) flag. This bypasses the interactive chat interface and prints the
response to standard output (stdout). Positional arguments without the flag
default to interactive mode, unless the input or output is piped or redirected.
Run a single command:
```bash
gemini "Write a poem about TypeScript"
gemini -p "Write a poem about TypeScript"
```
## How to pipe input to Gemini CLI
@@ -40,19 +41,19 @@ Pipe a file:
**macOS/Linux**
```bash
cat error.log | gemini "Explain why this failed"
cat error.log | gemini -p "Explain why this failed"
```
**Windows (PowerShell)**
```powershell
Get-Content error.log | gemini "Explain why this failed"
Get-Content error.log | gemini -p "Explain why this failed"
```
Pipe a command:
```bash
git diff | gemini "Write a commit message for these changes"
git diff | gemini -p "Write a commit message for these changes"
```
## Use Gemini CLI output in scripts
@@ -78,7 +79,7 @@ one.
echo "Generating docs for $file..."
# Ask Gemini CLI to generate the documentation and print it to stdout
gemini "Generate a Markdown documentation summary for @$file. Print the
gemini -p "Generate a Markdown documentation summary for @$file. Print the
result to standard output." > "${file%.py}.md"
done
```
@@ -92,7 +93,7 @@ one.
$newName = $_.Name -replace '\.py$', '.md'
# Ask Gemini CLI to generate the documentation and print it to stdout
gemini "Generate a Markdown documentation summary for @$($_.Name). Print the result to standard output." | Out-File -FilePath $newName -Encoding utf8
gemini -p "Generate a Markdown documentation summary for @$($_.Name). Print the result to standard output." | Out-File -FilePath $newName -Encoding utf8
}
```
@@ -214,7 +215,7 @@ wrapper that writes the message for you.
# Ask Gemini to write the message
echo "Generating commit message..."
msg=$(echo "$diff" | gemini "Write a concise Conventional Commit message for this diff. Output ONLY the message.")
msg=$(echo "$diff" | gemini -p "Write a concise Conventional Commit message for this diff. Output ONLY the message.")
# Commit with the generated message
git commit -m "$msg"
@@ -251,7 +252,7 @@ wrapper that writes the message for you.
# Ask Gemini to write the message
Write-Host "Generating commit message..."
$msg = $diff | gemini "Write a concise Conventional Commit message for this diff. Output ONLY the message."
$msg = $diff | gemini -p "Write a concise Conventional Commit message for this diff. Output ONLY the message."
# Commit with the generated message
git commit -m "$msg"
+3 -3
View File
@@ -89,7 +89,7 @@ don't need to learn special commands; just ask in natural language.
The agent will:
1. Recognize the request matches a GitHub tool.
2. Call `github_list_pull_requests`.
2. Call `mcp_github_list_pull_requests`.
3. Present the data to you.
### Scenario: Creating an issue
@@ -101,8 +101,8 @@ The agent will:
- **Server won't start?** Try running the docker command manually in your
terminal to see if it prints an error (e.g., "image not found").
- **Tools not found?** Run `/mcp refresh` to force the CLI to re-query the
server for its capabilities.
- **Tools not found?** Run `/mcp reload` to force the CLI to re-query the server
for its capabilities.
## Next steps
+1 -1
View File
@@ -105,7 +105,7 @@ excellent for debugging why the agent might be ignoring a rule.
If you edit a `GEMINI.md` file while a session is running, the agent won't know
immediately. Force a reload with:
**Command:** `/memory refresh`
**Command:** `/memory reload`
## Best practices
+4 -4
View File
@@ -89,9 +89,9 @@ Gemini gives you granular control over the undo process. You can choose to:
Sometimes you want to try two different approaches to the same problem.
1. Start a session and get to a decision point.
2. Save the current state with `/chat save decision-point`.
2. Save the current state with `/resume save decision-point`.
3. Try your first approach.
4. Later, use `/chat resume decision-point` to fork the conversation back to
4. Later, use `/resume resume decision-point` to fork the conversation back to
that moment and try a different approach.
This creates a new branch of history without losing your original work.
@@ -101,5 +101,5 @@ This creates a new branch of history without losing your original work.
- Learn about [Checkpointing](../../cli/checkpointing.md) to understand the
underlying safety mechanism.
- Explore [Task planning](task-planning.md) to keep complex sessions organized.
- See the [Command reference](../../reference/commands.md) for all `/chat` and
`/resume` options.
- See the [Command reference](../../reference/commands.md) for `/resume`
options, grouped checkpoint menus, and `/chat` compatibility aliases.
+4 -3
View File
@@ -17,9 +17,10 @@ prefix.
**Example:** `!ls -la`
This executes `ls -la` immediately and prints the output to your terminal. The
AI doesn't "see" this output unless you paste it back into the chat or use it in
a prompt.
This executes `ls -la` immediately and prints the output to your terminal.
Gemini CLI also records the command and its output in the current session
context, so the model can reference it in follow-up prompts. Very large outputs
may be truncated.
### Scenario: Entering Shell mode
+1 -1
View File
@@ -75,7 +75,7 @@ Markdown file.
Users can manage subagents using the following commands within the Gemini CLI:
- `/agents list`: Displays all available local and remote subagents.
- `/agents refresh`: Reloads the agent registry. Use this after adding or
- `/agents reload`: Reloads the agent registry. Use this after adding or
modifying agent definition files.
- `/agents enable <agent_name>`: Enables a specific subagent.
- `/agents disable <agent_name>`: Disables a specific subagent.
+2 -2
View File
@@ -194,7 +194,7 @@ returns coordinates and element descriptions that the browser agent uses with
the `click_at` tool for precise, coordinate-based interactions.
> **Note:** The visual agent requires API key or Vertex AI authentication. It is
> not available when using Google Login.
> not available when using "Sign in with Google".
## Creating custom subagents
@@ -297,7 +297,7 @@ Gemini CLI can also delegate tasks to remote subagents using the Agent-to-Agent
> **Note: Remote subagents are currently an experimental feature.**
See the [Remote Subagents documentation](/docs/core/remote-agents) for detailed
See the [Remote Subagents documentation](remote-agents) for detailed
configuration and usage instructions.
## Extension subagents
+8 -2
View File
@@ -123,6 +123,7 @@ The manifest file defines the extension's behavior and configuration.
},
"contextFileName": "GEMINI.md",
"excludeTools": ["run_shell_command"],
"migratedTo": "https://github.com/new-owner/new-extension-repo",
"plan": {
"directory": ".gemini/plans"
}
@@ -138,6 +139,9 @@ The manifest file defines the extension's behavior and configuration.
- `version`: The version of the extension.
- `description`: A short description of the extension. This will be displayed on
[geminicli.com/extensions](https://geminicli.com/extensions).
- `migratedTo`: The URL of the new repository source for the extension. If this
is set, the CLI will automatically check this new source for updates and
migrate the extension's installation to the new source if an update is found.
- `mcpServers`: A map of MCP servers to settings. The key is the name of the
server, and the value is the server configuration. These servers will be
loaded on startup just like MCP servers defined in a
@@ -258,12 +262,14 @@ but lower priority than user or admin policies.
```toml
[[rule]]
toolName = "my_server__dangerous_tool"
mcpName = "my_server"
toolName = "dangerous_tool"
decision = "ask_user"
priority = 100
[[safety_checker]]
toolName = "my_server__write_data"
mcpName = "my_server"
toolName = "write_data"
priority = 200
[safety_checker.checker]
type = "in-process"
+26
View File
@@ -152,3 +152,29 @@ jobs:
release/linux.arm64.my-tool.tar.gz
release/win32.arm64.my-tool.zip
```
## Migrating an Extension Repository
If you need to move your extension to a new repository (e.g., from a personal
account to an organization) or rename it, you can use the `migratedTo` property
in your `gemini-extension.json` file to seamlessly transition your users.
1. **Create the new repository**: Setup your extension in its new location.
2. **Update the old repository**: In your original repository, update the
`gemini-extension.json` file to include the `migratedTo` property, pointing
to the new repository URL, and bump the version number. You can optionally
change the `name` of your extension at this time in the new repository.
```json
{
"name": "my-extension",
"version": "1.1.0",
"migratedTo": "https://github.com/new-owner/new-extension-repo"
}
```
3. **Release the update**: Publish this new version in your old repository.
When users check for updates, the Gemini CLI will detect the `migratedTo` field,
verify that the new repository contains a valid extension update, and
automatically update their local installation to track the new source and name
moving forward. All extension settings will automatically migrate to the new
installation.
+11 -7
View File
@@ -4,6 +4,10 @@ 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.
> **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/).
For most users, we recommend starting Gemini CLI and logging in with your
personal Google account.
@@ -13,8 +17,8 @@ Select the authentication method that matches your situation in the table below:
| User Type / Scenario | Recommended Authentication Method | Google Cloud Project Required |
| :--------------------------------------------------------------------- | :--------------------------------------------------------------- | :---------------------------------------------------------- |
| Individual Google accounts | [Login with Google](#login-google) | No, with exceptions |
| Organization users with a company, school, or Google Workspace account | [Login with Google](#login-google) | [Yes](#set-gcp) |
| Individual Google accounts | [Sign in with Google](#login-google) | No, with exceptions |
| Organization users with a company, school, or Google Workspace account | [Sign in with Google](#login-google) | [Yes](#set-gcp) |
| AI Studio user with a Gemini API key | [Use Gemini API Key](#gemini-api) | No |
| Google Cloud Vertex AI user | [Vertex AI](#vertex-ai) | [Yes](#set-gcp) |
| [Headless mode](#headless) | [Use Gemini API Key](#gemini-api) or<br> [Vertex AI](#vertex-ai) | No (for Gemini API Key)<br> [Yes](#set-gcp) (for Vertex AI) |
@@ -32,7 +36,7 @@ Select the authentication method that matches your situation in the table below:
[Google AI Ultra for Business](https://support.google.com/a/answer/16345165)
subscriptions.
## (Recommended) Login with Google <a id="login-google"></a>
## (Recommended) Sign in with Google <a id="login-google"></a>
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
@@ -50,9 +54,9 @@ To authenticate and use Gemini CLI:
gemini
```
2. Select **Login with Google**. Gemini CLI opens a login prompt using your web
browser. Follow the on-screen instructions. Your credentials will be cached
locally for future sessions.
2. Select **Sign in with Google**. Gemini CLI opens a sign in prompt using your
web browser. Follow the on-screen instructions. Your credentials will be
cached locally for future sessions.
### Do I need to set my Google Cloud project?
@@ -387,7 +391,7 @@ on this page.
[Headless mode](../cli/headless) will use your existing authentication method,
if an existing authentication credential is cached.
If you have not already logged in with an authentication credential, you must
If you have not already signed in with an authentication credential, you must
configure authentication using environment variables:
- [Use Gemini API Key](#gemini-api)
+4
View File
@@ -39,6 +39,10 @@ When you encounter that limit, youll be given the option to switch to Gemini
2.5 Pro, upgrade for higher limits, or stop. Youll also be told when your usage
limit resets and Gemini 3 Pro can be used again.
> **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/).
Similarly, when you reach your daily usage limit for Gemini 2.5 Pro, youll see
a message prompting fallback to Gemini 2.5 Flash.
+2 -2
View File
@@ -38,7 +38,7 @@ cases, you can log in with your existing Google account:
```
2. When asked "How would you like to authenticate for this project?" select **1.
Login with Google**.
Sign in with Google**.
3. Select your Google account.
@@ -72,7 +72,7 @@ session's token usage, as well as your overall quota and usage for the supported
models.
For more information on the `/stats` command and its subcommands, see the
[Command Reference](../../reference/commands.md#stats).
[Command Reference](../reference/commands.md#stats).
## Next steps
+1 -1
View File
@@ -70,7 +70,7 @@ gemini
```
For a list of options and additional commands, see the
[CLI cheatsheet](/docs/cli/cli-reference.md).
[CLI cheatsheet](../cli/cli-reference.md).
You can also run Gemini CLI using one of the following advanced methods:
+1 -1
View File
@@ -449,7 +449,7 @@ When you open a project with hooks defined in `.gemini/settings.json`:
Hooks inherit the environment of the Gemini CLI process, which may include
sensitive API keys. Gemini CLI provides a
[redaction system](/docs/reference/configuration.md#environment-variable-redaction)
[redaction system](../reference/configuration.md#environment-variable-redaction)
that automatically filters variables matching sensitive patterns (e.g., `KEY`,
`TOKEN`).
+6 -6
View File
@@ -22,11 +22,11 @@ With hooks, you can:
### Getting started
- **[Writing hooks guide](/docs/hooks/writing-hooks)**: A tutorial on creating
your first hook with comprehensive examples.
- **[Best practices](/docs/hooks/best-practices)**: Guidelines on security,
- **[Writing hooks guide](../hooks/writing-hooks)**: A tutorial on creating your
first hook with comprehensive examples.
- **[Best practices](../hooks/best-practices)**: Guidelines on security,
performance, and debugging.
- **[Hooks reference](/docs/hooks/reference)**: The definitive technical
- **[Hooks reference](../hooks/reference)**: The definitive technical
specification of I/O schemas and exit codes.
## Core concepts
@@ -152,8 +152,8 @@ Gemini CLI **fingerprints** project hooks. If a hook's name or command changes
(e.g., via `git pull`), it is treated as a **new, untrusted hook** and you will
be warned before it executes.
See [Security Considerations](/docs/hooks/best-practices#using-hooks-securely)
for a detailed threat model.
See [Security Considerations](../hooks/best-practices#using-hooks-securely) for
a detailed threat model.
## Managing hooks
+3 -3
View File
@@ -82,10 +82,10 @@ For `BeforeTool` and `AfterTool` events, the `matcher` field in your settings is
compared against the name of the tool being executed.
- **Built-in Tools**: You can match any built-in tool (e.g., `read_file`,
`run_shell_command`). See the [Tools Reference](/docs/reference/tools) for a
full list of available tool names.
`run_shell_command`). See the [Tools Reference](../reference/tools) for a full
list of available tool names.
- **MCP Tools**: Tools from MCP servers follow the naming pattern
`mcp__<server_name>__<tool_name>`.
`mcp_<server_name>_<tool_name>`.
- **Regex Support**: Matchers support regular expressions (e.g.,
`matcher: "read_.*"` matches all file reading tools).
+71 -27
View File
@@ -1,23 +1,22 @@
# Local development guide
This guide provides instructions for setting up and using local development
features, such as tracing.
features for Gemini CLI.
## Tracing
Traces are OpenTelemetry (OTel) records that help you debug your code by
instrumenting key events like model calls, tool scheduler operations, and tool
calls.
Gemini CLI uses OpenTelemetry (OTel) to record traces that help you debug agent
behavior. Traces instrument key events like model calls, tool scheduler
operations, and tool calls.
Traces provide deep visibility into agent behavior and are invaluable for
debugging complex issues. They are captured automatically when telemetry is
enabled.
Traces provide deep visibility into agent behavior and help you debug complex
issues. They are captured automatically when you enable telemetry.
### Viewing traces
### View traces
You can view traces using either Jaeger or the Genkit Developer UI.
You can view traces using Genkit Developer UI, Jaeger, or Google Cloud.
#### Using Genkit
#### Use Genkit
Genkit provides a web-based UI for viewing traces and other telemetry data.
@@ -29,11 +28,8 @@ Genkit provides a web-based UI for viewing traces and other telemetry data.
npm run telemetry -- --target=genkit
```
The script will output the URL for the Genkit Developer UI, for example:
```
Genkit Developer UI: http://localhost:4000
```
The script will output the URL for the Genkit Developer UI. For example:
`Genkit Developer UI: http://localhost:4000`
2. **Run Gemini CLI:**
@@ -48,21 +44,22 @@ Genkit provides a web-based UI for viewing traces and other telemetry data.
Open the Genkit Developer UI URL in your browser and navigate to the
**Traces** tab to view the traces.
#### Using Jaeger
#### Use Jaeger
You can view traces in the Jaeger UI. To get started, follow these steps:
You can view traces in the Jaeger UI for local development.
1. **Start the telemetry collector:**
Run the following command in your terminal to download and start Jaeger and
an OTEL collector:
an OTel collector:
```bash
npm run telemetry -- --target=local
```
This command also configures your workspace for local telemetry and provides
a link to the Jaeger UI (usually `http://localhost:16686`).
This command configures your workspace for local telemetry and provides a
link to the Jaeger UI (usually `http://localhost:16686`).
- **Collector logs:** `~/.gemini/tmp/<projectHash>/otel/collector.log`
2. **Run Gemini CLI:**
@@ -77,16 +74,63 @@ You can view traces in the Jaeger UI. To get started, follow these steps:
After running your command, open the Jaeger UI link in your browser to view
the traces.
#### Use Google Cloud
You can use an OpenTelemetry collector to forward telemetry data to Google Cloud
Trace for custom processing or routing.
> **Warning:** Ensure you complete the
> [Google Cloud telemetry prerequisites](./cli/telemetry.md#prerequisites)
> (Project ID, authentication, IAM roles, and APIs) before using this method.
1. **Configure `.gemini/settings.json`:**
```json
{
"telemetry": {
"enabled": true,
"target": "gcp",
"useCollector": true
}
}
```
2. **Start the telemetry collector:**
Run the following command to start a local OTel collector that forwards to
Google Cloud:
```bash
npm run telemetry -- --target=gcp
```
The script outputs links to view traces, metrics, and logs in the Google
Cloud Console.
- **Collector logs:** `~/.gemini/tmp/<projectHash>/otel/collector-gcp.log`
3. **Run Gemini CLI:**
In a separate terminal, run your Gemini CLI command:
```bash
gemini
```
4. **View logs, metrics, and traces:**
After sending prompts, view your data in the Google Cloud Console. See the
[telemetry documentation](./cli/telemetry.md#view-google-cloud-telemetry)
for links to Logs, Metrics, and Trace explorers.
For more detailed information on telemetry, see the
[telemetry documentation](./cli/telemetry.md).
### Instrumenting code with traces
### Instrument code with traces
You can add traces to your own code for more detailed instrumentation. This is
useful for debugging and understanding the flow of execution.
You can add traces to your own code for more detailed instrumentation.
Use the `runInDevTraceSpan` function to wrap any section of code in a trace
span.
Adding traces helps you debug and understand the flow of execution. Use the
`runInDevTraceSpan` function to wrap any section of code in a trace span.
Here is a basic example:
@@ -102,13 +146,13 @@ await runInDevTraceSpan(
},
},
async ({ metadata }) => {
// The `metadata` object allows you to record the input and output of the
// metadata allows you to record the input and output of the
// operation as well as other attributes.
metadata.input = { key: 'value' };
// Set custom attributes.
metadata.attributes['custom.attribute'] = 'custom.value';
// Your code to be traced goes here
// Your code to be traced goes here.
try {
const output = await somethingRisky();
metadata.output = output;
+52 -15
View File
@@ -28,24 +28,33 @@ Slash commands provide meta-level control over the CLI itself.
### `/chat`
- **Description:** Save and resume conversation history for branching
conversation state interactively, or resuming a previous state from a later
session.
- **Description:** Alias for `/resume`. Both commands now expose the same
session browser action and checkpoint subcommands.
- **Menu layout when typing `/chat` (or `/resume`)**:
- `-- auto --`
- `list` (selecting this opens the auto-saved session browser)
- `-- checkpoints --`
- `list`, `save`, `resume`, `delete`, `share` (manual tagged checkpoints)
- **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.
- **`delete <tag>`**
- **Description:** Deletes a saved conversation checkpoint.
- **Equivalent:** `/resume delete <tag>`
- **`list`**
- **Description:** Lists available tags for chat state resumption.
- **Description:** Lists available tags for manually saved checkpoints.
- **Note:** This command only lists chats saved within the current project.
Because chat history is project-scoped, chats saved in other project
directories will not be displayed.
- **Equivalent:** `/resume list`
- **`resume <tag>`**
- **Description:** Resumes a conversation from a previous save.
- **Note:** You can only resume chats that were saved within the current
project. To resume a chat from a different project, you must run the
Gemini CLI from that project's directory.
- **Equivalent:** `/resume resume <tag>`
- **`save <tag>`**
- **Description:** Saves the current conversation history. You must add a
`<tag>` for identifying the conversation state.
@@ -60,10 +69,12 @@ Slash commands provide meta-level control over the CLI itself.
conversation states. For automatic checkpoints created before file
modifications, see the
[Checkpointing documentation](../cli/checkpointing.md).
- **Equivalent:** `/resume save <tag>`
- **`share [filename]`**
- **Description** Writes the current conversation to a provided Markdown or
- **Description:** Writes the current conversation to a provided Markdown or
JSON file. If no filename is provided, then the CLI will generate one.
- **Usage** `/chat share file.md` or `/chat share file.json`.
- **Usage:** `/chat share file.md` or `/chat share file.json`.
- **Equivalent:** `/resume share [filename]`
### `/clear`
@@ -104,7 +115,7 @@ Slash commands provide meta-level control over the CLI itself.
- On macOS, it requires `pbcopy`, and on Windows, it requires `clip`. These
tools are typically pre-installed on their respective systems.
### `/directory` (or `/dir`)
### `/workspace` (or `/directory`, `/dir`)
- **Description:** Manage workspace directories for multi-directory support.
- **Sub-commands:**
@@ -112,13 +123,13 @@ Slash commands provide meta-level control over the CLI itself.
- **Description:** Add a directory to the workspace. The path can be
absolute or relative to the current working directory. Moreover, the
reference from home directory is supported as well.
- **Usage:** `/directory add <path1>,<path2>`
- **Usage:** `/workspace add <path1>,<path2>`
- **Note:** Disabled in restrictive sandbox profiles. If you're using that,
use `--include-directories` when starting the session instead.
- **`show`**:
- **Description:** Display all directories added by `/directory add` and
- **Description:** Display all directories added by `/workspace add` and
`--include-directories`.
- **Usage:** `/directory show`
- **Usage:** `/workspace show`
### `/docs`
@@ -268,8 +279,8 @@ Slash commands provide meta-level control over the CLI itself.
- **Description:** Switch to Plan Mode (read-only) and view the current plan if
one has been generated.
- **Note:** This feature requires the `experimental.plan` setting to be
enabled in your configuration.
- **Note:** This feature is enabled by default. It can be disabled via the
`experimental.plan` setting in your configuration.
- **Sub-commands:**
- **`copy`**:
- **Description:** Copy the currently approved plan to your clipboard.
@@ -314,10 +325,13 @@ Slash commands provide meta-level control over the CLI itself.
### `/resume`
- **Description:** Browse and resume previous conversation sessions. Opens an
interactive session browser where you can search, filter, and select from
automatically saved conversations.
- **Description:** Browse and resume previous conversation sessions, and manage
manual chat checkpoints.
- **Features:**
- **Auto sessions:** Run `/resume` to open the interactive session browser for
automatically saved conversations.
- **Chat checkpoints:** Use checkpoint subcommands directly (`/resume save`,
`/resume resume`, etc.).
- **Management:** Delete unwanted sessions directly from the browser
- **Resume:** Select any session to resume and continue the conversation
- **Search:** Use `/` to search through conversation content across all
@@ -328,6 +342,23 @@ Slash commands provide meta-level control over the CLI itself.
- **Note:** All conversations are automatically saved as you chat - no manual
saving required. See [Session Management](../cli/session-management.md) for
complete details.
- **Alias:** `/chat` provides the same behavior and subcommands.
- **Sub-commands:**
- **`list`**
- **Description:** Lists available tags for manual chat checkpoints.
- **`save <tag>`**
- **Description:** Saves the current conversation as a tagged checkpoint.
- **`resume <tag>`** (alias: `load`)
- **Description:** Loads a previously saved tagged checkpoint.
- **`delete <tag>`**
- **Description:** Deletes a tagged checkpoint.
- **`share [filename]`**
- **Description:** Exports the current conversation to Markdown or JSON.
- **`debug`**
- **Description:** Export the most recent API request as JSON payload
(nightly builds).
- **Compatibility alias:** `/resume checkpoints ...` is still accepted for the
same checkpoint commands.
### `/settings`
@@ -408,6 +439,12 @@ Slash commands provide meta-level control over the CLI itself.
- **`nodesc`** or **`nodescriptions`**:
- **Description:** Hide tool descriptions, showing only the tool names.
### `/upgrade`
- **Description:** Open the Gemini Code Assist upgrade page in your browser.
This lets you upgrade your tier for higher usage limits.
- **Note:** This command is only available when logged in with Google.
### `/vim`
- **Description:** Toggle vim mode on or off. When vim mode is enabled, the
+33 -15
View File
@@ -261,7 +261,7 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `true`
- **`ui.footer.hideCWD`** (boolean):
- **Description:** Hide the current working directory in the footer.
- **Description:** Hide the workspace path in the footer.
- **Default:** `false`
- **`ui.footer.hideSandboxStatus`** (boolean):
@@ -297,7 +297,7 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **`ui.showUserIdentity`** (boolean):
- **Description:** Show the logged-in user's identity (e.g. email) in the UI.
- **Description:** Show the signed-in user's identity (e.g. email) in the UI.
- **Default:** `true`
- **`ui.useAlternateBuffer`** (boolean):
@@ -719,7 +719,7 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `[]`
- **`context.loadMemoryFromIncludeDirectories`** (boolean):
- **Description:** Controls how /memory refresh loads GEMINI.md files. When
- **Description:** Controls how /memory reload loads GEMINI.md files. When
true, include directories are scanned; when false, only the current
directory is used.
- **Default:** `false`
@@ -872,6 +872,11 @@ their corresponding top-level category object in your `settings.json` file.
confirmation dialogs.
- **Default:** `false`
- **`security.autoAddToPolicyByDefault`** (boolean):
- **Description:** When enabled, the "Allow for all future sessions" option
becomes the default choice for low-risk tools in trusted workspaces.
- **Default:** `false`
- **`security.blockGitExtensions`** (boolean):
- **Description:** Blocks installing and loading extensions from Git.
- **Default:** `false`
@@ -998,6 +1003,12 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.extensionRegistryURI`** (string):
- **Description:** The URI (web URL or local file path) of the extension
registry.
- **Default:** `"https://geminicli.com/extensions.json"`
- **Requires restart:** Yes
- **`experimental.extensionReloading`** (boolean):
- **Description:** Enables extension loading/unloading within the CLI session.
- **Default:** `false`
@@ -1021,8 +1032,8 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **`experimental.plan`** (boolean):
- **Description:** Enable planning features (Plan Mode and tools).
- **Default:** `false`
- **Description:** Enable Plan Mode.
- **Default:** `true`
- **Requires restart:** Yes
- **`experimental.taskTracker`** (boolean):
@@ -1041,8 +1052,8 @@ their corresponding top-level category object in your `settings.json` file.
- **Requires restart:** Yes
- **`experimental.gemmaModelRouter.enabled`** (boolean):
- **Description:** Enable the Gemma Model Router. Requires a local endpoint
serving Gemma via the Gemini API using LiteRT-LM shim.
- **Description:** Enable the Gemma Model Router (experimental). Requires a
local endpoint serving Gemma via the Gemini API using LiteRT-LM shim.
- **Default:** `false`
- **Requires restart:** Yes
@@ -1171,13 +1182,20 @@ their corresponding top-level category object in your `settings.json` file.
Configures connections to one or more Model-Context Protocol (MCP) servers for
discovering and using custom tools. Gemini CLI attempts to connect to each
configured MCP server to discover available tools. If multiple MCP servers
expose a tool with the same name, the tool names will be prefixed with the
server alias you defined in the configuration (e.g.,
`serverAlias__actualToolName`) to avoid conflicts. Note that the system might
strip certain schema properties from MCP tool definitions 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`.
configured MCP server to discover available tools. Every discovered tool is
prepended with the `mcp_` prefix and its server alias to form a fully qualified
name (FQN) (e.g., `mcp_serverAlias_actualToolName`) to avoid conflicts. Note
that the system might strip certain schema properties from MCP tool definitions
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`.
> **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
> misidentify the server name, which can cause security policies to fail
> silently.
- **`mcpServers.<SERVER_NAME>`** (object): The server parameters for the named
server.
@@ -1705,7 +1723,7 @@ conventions and context.
loaded, allowing you to verify the hierarchy and content being used by the
AI.
- See the [Commands documentation](./commands.md#memory) for full details on
the `/memory` command and its sub-commands (`show` and `refresh`).
the `/memory` command and its sub-commands (`show` and `reload`).
By understanding and utilizing these configuration layers and the hierarchical
nature of context files, you can effectively manage the AI's memory and tailor
+94 -90
View File
@@ -8,119 +8,123 @@ available combinations.
#### Basic Controls
| Action | Keys |
| --------------------------------------------------------------- | --------------------- |
| Confirm the current selection or choice. | `Enter` |
| Dismiss dialogs or cancel the current focus. | `Esc`<br />`Ctrl + [` |
| Cancel the current request or quit the CLI when input is empty. | `Ctrl + C` |
| Exit the CLI when the input buffer is empty. | `Ctrl + D` |
| Action | Keys |
| --------------------------------------------------------------- | ------------------- |
| Confirm the current selection or choice. | `Enter` |
| Dismiss dialogs or cancel the current focus. | `Esc`<br />`Ctrl+[` |
| Cancel the current request or quit the CLI when input is empty. | `Ctrl+C` |
| Exit the CLI when the input buffer is empty. | `Ctrl+D` |
#### Cursor Movement
| Action | Keys |
| ------------------------------------------- | ------------------------------------------------------------ |
| Move the cursor to the start of the line. | `Ctrl + A`<br />`Home (no Shift, Ctrl)` |
| Move the cursor to the end of the line. | `Ctrl + E`<br />`End (no Shift, Ctrl)` |
| Move the cursor up one line. | `Up Arrow (no Shift, Alt, Ctrl, Cmd)` |
| Move the cursor down one line. | `Down Arrow (no Shift, Alt, Ctrl, Cmd)` |
| Move the cursor one character to the left. | `Left Arrow (no Shift, Alt, Ctrl, Cmd)` |
| Move the cursor one character to the right. | `Right Arrow (no Shift, Alt, Ctrl, Cmd)`<br />`Ctrl + F` |
| Move the cursor one word to the left. | `Ctrl + Left Arrow`<br />`Alt + Left Arrow`<br />`Alt + B` |
| Move the cursor one word to the right. | `Ctrl + Right Arrow`<br />`Alt + Right Arrow`<br />`Alt + F` |
| Action | Keys |
| ------------------------------------------- | ------------------------------------------ |
| Move the cursor to the start of the line. | `Ctrl+A`<br />`Home` |
| Move the cursor to the end of the line. | `Ctrl+E`<br />`End` |
| Move the cursor up one line. | `Up` |
| Move the cursor down one line. | `Down` |
| Move the cursor one character to the left. | `Left` |
| Move the cursor one character to the right. | `Right`<br />`Ctrl+F` |
| Move the cursor one word to the left. | `Ctrl+Left`<br />`Alt+Left`<br />`Alt+B` |
| Move the cursor one word to the right. | `Ctrl+Right`<br />`Alt+Right`<br />`Alt+F` |
#### Editing
| Action | Keys |
| ------------------------------------------------ | ---------------------------------------------------------------- |
| Delete from the cursor to the end of the line. | `Ctrl + K` |
| Delete from the cursor to the start of the line. | `Ctrl + U` |
| Clear all text in the input field. | `Ctrl + C` |
| Delete the previous word. | `Ctrl + Backspace`<br />`Alt + Backspace`<br />`Ctrl + W` |
| Delete the next word. | `Ctrl + Delete`<br />`Alt + Delete`<br />`Alt + D` |
| Delete the character to the left. | `Backspace`<br />`Ctrl + H` |
| Delete the character to the right. | `Delete`<br />`Ctrl + D` |
| Undo the most recent text edit. | `Cmd + Z (no Shift)`<br />`Alt + Z (no Shift)` |
| Redo the most recent undone text edit. | `Shift + Ctrl + Z`<br />`Shift + Cmd + Z`<br />`Shift + Alt + Z` |
| Action | Keys |
| ------------------------------------------------ | -------------------------------------------------------- |
| Delete from the cursor to the end of the line. | `Ctrl+K` |
| Delete from the cursor to the start of the line. | `Ctrl+U` |
| Clear all text in the input field. | `Ctrl+C` |
| Delete the previous word. | `Ctrl+Backspace`<br />`Alt+Backspace`<br />`Ctrl+W` |
| Delete the next word. | `Ctrl+Delete`<br />`Alt+Delete`<br />`Alt+D` |
| Delete the character to the left. | `Backspace`<br />`Ctrl+H` |
| Delete the character to the right. | `Delete`<br />`Ctrl+D` |
| Undo the most recent text edit. | `Cmd/Win+Z`<br />`Alt+Z` |
| Redo the most recent undone text edit. | `Ctrl+Shift+Z`<br />`Shift+Cmd/Win+Z`<br />`Alt+Shift+Z` |
#### Scrolling
| Action | Keys |
| ------------------------ | --------------------------------- |
| Scroll content up. | `Shift + Up Arrow` |
| Scroll content down. | `Shift + Down Arrow` |
| Scroll to the top. | `Ctrl + Home`<br />`Shift + Home` |
| Scroll to the bottom. | `Ctrl + End`<br />`Shift + End` |
| Scroll up by one page. | `Page Up` |
| Scroll down by one page. | `Page Down` |
| Action | Keys |
| ------------------------ | ----------------------------- |
| Scroll content up. | `Shift+Up` |
| Scroll content down. | `Shift+Down` |
| Scroll to the top. | `Ctrl+Home`<br />`Shift+Home` |
| Scroll to the bottom. | `Ctrl+End`<br />`Shift+End` |
| Scroll up by one page. | `Page Up` |
| Scroll down by one page. | `Page Down` |
#### History & Search
| Action | Keys |
| -------------------------------------------- | --------------------- |
| Show the previous entry in history. | `Ctrl + P (no Shift)` |
| Show the next entry in history. | `Ctrl + N (no Shift)` |
| Start reverse search through history. | `Ctrl + R` |
| Submit the selected reverse-search match. | `Enter (no Ctrl)` |
| Accept a suggestion while reverse searching. | `Tab (no Shift)` |
| Browse and rewind previous interactions. | `Double Esc` |
| Action | Keys |
| -------------------------------------------- | -------- |
| Show the previous entry in history. | `Ctrl+P` |
| Show the next entry in history. | `Ctrl+N` |
| Start reverse search through history. | `Ctrl+R` |
| Submit the selected reverse-search match. | `Enter` |
| Accept a suggestion while reverse searching. | `Tab` |
#### Navigation
| Action | Keys |
| -------------------------------------------------- | ------------------------------------------- |
| Move selection up in lists. | `Up Arrow (no Shift)` |
| Move selection down in lists. | `Down Arrow (no Shift)` |
| Move up within dialog options. | `Up Arrow (no Shift)`<br />`K (no Shift)` |
| Move down within dialog options. | `Down Arrow (no Shift)`<br />`J (no Shift)` |
| Move to the next item or question in a dialog. | `Tab (no Shift)` |
| Move to the previous item or question in a dialog. | `Shift + Tab` |
| Action | Keys |
| -------------------------------------------------- | --------------- |
| Move selection up in lists. | `Up` |
| Move selection down in lists. | `Down` |
| Move up within dialog options. | `Up`<br />`K` |
| Move down within dialog options. | `Down`<br />`J` |
| Move to the next item or question in a dialog. | `Tab` |
| Move to the previous item or question in a dialog. | `Shift+Tab` |
#### Suggestions & Completions
| Action | Keys |
| --------------------------------------- | -------------------------------------------------- |
| Accept the inline suggestion. | `Tab (no Shift)`<br />`Enter (no Ctrl)` |
| Move to the previous completion option. | `Up Arrow (no Shift)`<br />`Ctrl + P (no Shift)` |
| Move to the next completion option. | `Down Arrow (no Shift)`<br />`Ctrl + N (no Shift)` |
| Expand an inline suggestion. | `Right Arrow` |
| Collapse an inline suggestion. | `Left Arrow` |
| Action | Keys |
| --------------------------------------- | -------------------- |
| Accept the inline suggestion. | `Tab`<br />`Enter` |
| Move to the previous completion option. | `Up`<br />`Ctrl+P` |
| Move to the next completion option. | `Down`<br />`Ctrl+N` |
| Expand an inline suggestion. | `Right` |
| Collapse an inline suggestion. | `Left` |
#### Text Input
| Action | Keys |
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Submit the current prompt. | `Enter (no Shift, Alt, Ctrl, Cmd)` |
| Insert a newline without submitting. | `Ctrl + Enter`<br />`Cmd + Enter`<br />`Alt + Enter`<br />`Shift + Enter`<br />`Ctrl + J` |
| Open the current prompt or the plan in an external editor. | `Ctrl + X` |
| Paste from the clipboard. | `Ctrl + V`<br />`Cmd + V`<br />`Alt + V` |
| Action | Keys |
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Submit the current prompt. | `Enter` |
| Insert a newline without submitting. | `Ctrl+Enter`<br />`Cmd/Win+Enter`<br />`Alt+Enter`<br />`Shift+Enter`<br />`Ctrl+J` |
| Open the current prompt or the plan in an external editor. | `Ctrl+X` |
| Paste from the clipboard. | `Ctrl+V`<br />`Cmd/Win+V`<br />`Alt+V` |
#### App Controls
| Action | Keys |
| -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
| Toggle detailed error information. | `F12` |
| Toggle the full TODO list. | `Ctrl + T` |
| Show IDE context details. | `Ctrl + G` |
| Toggle Markdown rendering. | `Alt + M` |
| Toggle copy mode when in alternate buffer mode. | `Ctrl + S` |
| Toggle YOLO (auto-approval) mode for tool calls. | `Ctrl + Y` |
| Cycle through approval modes: default (prompt), auto_edit (auto-approve edits), and plan (read-only). Plan mode is skipped when the agent is busy. | `Shift + Tab` |
| Expand and collapse blocks of content when not in alternate buffer mode. | `Ctrl + O` |
| Expand or collapse a paste placeholder when cursor is over placeholder. | `Ctrl + O` |
| Toggle current background shell visibility. | `Ctrl + B` |
| Toggle background shell list. | `Ctrl + L` |
| Kill the active background shell. | `Ctrl + K` |
| Confirm selection in background shell list. | `Enter` |
| Dismiss background shell list. | `Esc` |
| Move focus from background shell to Gemini. | `Shift + Tab` |
| Move focus from background shell list to Gemini. | `Tab (no Shift)` |
| Show warning when trying to move focus away from background shell. | `Tab (no Shift)` |
| Show warning when trying to move focus away from shell input. | `Tab (no Shift)` |
| Move focus from Gemini to the active shell. | `Tab (no Shift)` |
| Move focus from the shell back to Gemini. | `Shift + Tab` |
| Clear the terminal screen and redraw the UI. | `Ctrl + L` |
| Restart the application. | `R` |
| Suspend the CLI and move it to the background. | `Ctrl + Z` |
| Action | Keys |
| -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| Toggle detailed error information. | `F12` |
| Toggle the full TODO list. | `Ctrl+T` |
| Show IDE context details. | `Ctrl+G` |
| Toggle Markdown rendering. | `Alt+M` |
| Toggle copy mode when in alternate buffer mode. | `Ctrl+S` |
| Toggle YOLO (auto-approval) mode for tool calls. | `Ctrl+Y` |
| Cycle through approval modes: default (prompt), auto_edit (auto-approve edits), and plan (read-only). Plan mode is skipped when the agent is busy. | `Shift+Tab` |
| Expand and collapse blocks of content when not in alternate buffer mode. | `Ctrl+O` |
| Expand or collapse a paste placeholder when cursor is over placeholder. | `Ctrl+O` |
| Move focus from Gemini to the active shell. | `Tab` |
| Move focus from the shell back to Gemini. | `Shift+Tab` |
| Clear the terminal screen and redraw the UI. | `Ctrl+L` |
| Restart the application. | `R`<br />`Shift+R` |
| Suspend the CLI and move it to the background. | `Ctrl+Z` |
| Show warning when trying to move focus away from shell input. | `Tab` |
#### Background Shell Controls
| Action | Keys |
| ------------------------------------------------------------------ | ----------- |
| Dismiss background shell list. | `Esc` |
| Confirm selection in background shell list. | `Enter` |
| Toggle current background shell visibility. | `Ctrl+B` |
| Toggle background shell list. | `Ctrl+L` |
| Kill the active background shell. | `Ctrl+K` |
| Move focus from background shell to Gemini. | `Shift+Tab` |
| Move focus from background shell list to Gemini. | `Tab` |
| Show warning when trying to move focus away from background shell. | `Tab` |
<!-- KEYBINDINGS-AUTOGEN:END -->
@@ -156,7 +160,7 @@ available combinations.
## Limitations
- On [Windows Terminal](https://en.wikipedia.org/wiki/Windows_Terminal):
- `shift+enter` is not supported.
- `shift+enter` is only supported in version 1.25 and higher.
- `shift+tab`
[is not supported](https://github.com/google-gemini/gemini-cli/issues/20314)
on Node 20 and earlier versions of Node 22.
+34 -12
View File
@@ -76,9 +76,13 @@ The `toolName` in the rule must match the name of the tool being called.
- **Wildcards**: You can use wildcards to match multiple tools.
- `*`: Matches **any tool** (built-in or MCP).
- `server__*`: Matches any tool from a specific MCP server.
- `*__toolName`: Matches a specific tool name across **all** MCP servers.
- `*__*`: Matches **any tool from any MCP server**.
- `mcp_server_*`: Matches any tool from a specific MCP server.
- `mcp_*_toolName`: Matches a specific tool name across **all** MCP servers.
- `mcp_*`: Matches **any tool from any MCP server**.
> **Recommendation:** While FQN wildcards are supported, the recommended
> approach for MCP tools is to use the `mcpName` field in your TOML rules. See
> [Special syntax for MCP tools](#special-syntax-for-mcp-tools).
#### Arguments pattern
@@ -91,10 +95,17 @@ the arguments don't match the pattern, the rule does not apply.
There are three possible decisions a rule can enforce:
- `allow`: The tool call is executed automatically without user interaction.
- `deny`: The tool call is blocked and is not executed.
- `deny`: The tool call is blocked and is not executed. For global rules (those
without an `argsPattern`), tools that are denied are **completely excluded
from the model's memory**. This means the model will not even see the tool as
an option, which is more secure and saves context window space.
- `ask_user`: The user is prompted to approve or deny the tool call. (In
non-interactive mode, this is treated as `deny`.)
> **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.
### Priority system and tiers
The policy engine uses a sophisticated priority system to resolve conflicts when
@@ -143,8 +154,8 @@ always active.
confirmation.
- `autoEdit`: Optimized for automated code editing; some write tools may be
auto-approved.
- `plan`: A strict, read-only mode for research and design. See [Customizing
Plan Mode Policies].
- `plan`: A strict, read-only mode for research and design. See
[Customizing Plan Mode Policies](../cli/plan-mode.md#customizing-policies).
- `yolo`: A mode where all tools are auto-approved (use with extreme caution).
## Rule matching
@@ -157,8 +168,8 @@ A rule matches a tool call if all of its conditions are met:
1. **Tool name**: The `toolName` in the rule must match the name of the tool
being called.
- **Wildcards**: You can use wildcards like `*`, `server__*`, or
`*__toolName` to match multiple tools. See [Tool Name](#tool-name) for
- **Wildcards**: You can use wildcards like `*`, `mcp_server_*`, or
`mcp_*_toolName` to match multiple tools. See [Tool Name](#tool-name) for
details.
2. **Arguments pattern**: If `argsPattern` is specified, the tool's arguments
are converted to a stable JSON string, which is then tested against the
@@ -212,8 +223,12 @@ Here is a breakdown of the fields available in a TOML policy rule:
# A unique name for the tool, or an array of names.
toolName = "run_shell_command"
# (Optional) The name of a subagent. If provided, the rule only applies to tool calls
# made by this specific subagent.
subagent = "generalist"
# (Optional) The name of an MCP server. Can be combined with toolName
# to form a composite name like "mcpName__toolName".
# to form a composite FQN internally like "mcp_mcpName_toolName".
mcpName = "my-custom-server"
# (Optional) Metadata hints provided by the tool. A rule matches if all
@@ -290,7 +305,16 @@ priority = 100
### Special syntax for MCP tools
You can create rules that target tools from Model Context Protocol (MCP) servers
using the `mcpName` field or composite wildcard patterns.
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.
> **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
> misinterpret the server identity, which can cause wildcard rules and security
> policies to fail silently.
**1. Targeting a specific tool on a server**
@@ -360,5 +384,3 @@ out-of-the-box experience.
- In **`yolo`** mode, a high-priority rule allows all tools.
- In **`autoEdit`** mode, rules allow certain write operations to happen without
prompting.
[Customizing Plan Mode Policies]: /docs/cli/plan-mode.md#customizing-policies
+2 -2
View File
@@ -79,8 +79,8 @@ manually run through this checklist.
- [ ] Verify version: `gemini --version`
- **Authentication:**
- [ ] In interactive mode run `/auth` and verify all login flows work:
- [ ] Login With Google
- [ ] In interactive mode run `/auth` and verify all sign in flows work:
- [ ] Sign in with Google
- [ ] API Key
- [ ] Vertex AI
+72 -38
View File
@@ -1,14 +1,13 @@
# Gemini CLI: Quotas and pricing
Gemini CLI offers a generous free tier that covers many individual developers'
use cases. For enterprise or professional usage, or if you need higher limits,
use cases. For enterprise or professional usage, or if you need increased quota,
several options are available depending on your authentication account type.
See [privacy and terms](./tos-privacy.md) for details on the Privacy Policy and
Terms of Service.
For a high-level comparison of available subscriptions and to select the right
quota for your needs, see the [Plans page](https://geminicli.com/plans/).
> **Note:** Published prices are list price; additional negotiated commercial
> discounting may apply.
## Overview
This article outlines the specific quotas and pricing applicable to Gemini CLI
when using different authentication methods.
@@ -23,10 +22,11 @@ Generally, there are three categories to choose from:
## Free usage
Your journey begins with a generous free tier, perfect for experimentation and
light use.
Access to Gemini CLI begins with a generous free tier, perfect for
experimentation and light use.
Your free usage limits depend on your authorization type.
Your free usage is governed by the following limits, which depend on your
authorization type.
### Log in with Google (Gemini Code Assist for individuals)
@@ -69,6 +69,19 @@ Learn more at
If you use up your initial number of requests, you can continue to benefit from
Gemini CLI by upgrading to one of the following subscriptions:
### Individuals
These tiers apply when you sign in with a personal account. To verify whether
you're on a personal account, visit
[Google One](https://one.google.com/about/plans?hl=en-US&g1_landing_page=0):
- If you are on a personal account, you will see your personal dashboard.
- If you are not on a personal account, you will see: "You're currently signed
in to your Google Workspace Account."
**Supported tiers:** _- Tiers not listed above, including Google AI Plus, are
not supported._
- [Google AI Pro and AI Ultra](https://gemini.google/subscriptions/). This is
recommended for individual developers. Quotas and pricing are based on a fixed
price subscription.
@@ -78,14 +91,26 @@ Gemini CLI by upgrading to one of the following subscriptions:
Learn more at
[Gemini Code Assist Quotas and Limits](https://developers.google.com/gemini-code-assist/resources/quotas)
- [Purchase a Gemini Code Assist Subscription through Google Cloud ](https://cloud.google.com/gemini/docs/codeassist/overview)
by signing up in the Google Cloud console. Learn more at
[Set up Gemini Code Assist](https://cloud.google.com/gemini/docs/discover/set-up-gemini).
### Through your organization
These tiers are applicable when you are signing in with a Google Workspace
account.
- To verify your account type, visit
[the Google One page](https://one.google.com/about/plans?hl=en-US&g1_landing_page=0).
- You are on a workspace account if you see the message "You're currently signed
in to your Google Workspace Account".
**Supported tiers:** _- Tiers not listed above, including Workspace AI
Standard/Plus and AI Expanded, are not supported._
- [Workspace AI Ultra Access](https://workspace.google.com/products/ai-ultra/).
- [Purchase a Gemini Code Assist Subscription through Google Cloud](https://cloud.google.com/gemini/docs/codeassist/overview).
Quotas and pricing are based on a fixed price subscription with assigned
license seats. For predictable costs, you can sign in with Google.
This includes:
This includes the following request limits:
- Gemini Code Assist Standard edition:
- 1500 model requests / user / day
- 120 model requests / user / minute
@@ -95,7 +120,7 @@ Gemini CLI by upgrading to one of the following subscriptions:
- Model requests will be made across the Gemini model family as determined by
Gemini CLI.
[Learn more about Gemini Code Assist Standard and Enterprise license limits](https://developers.google.com/gemini-code-assist/resources/quotas#quotas-for-agent-mode-gemini-cli).
[Learn more about Gemini Code Assist license limits](https://developers.google.com/gemini-code-assist/resources/quotas#quotas-for-agent-mode-gemini-cli).
## Pay as you go
@@ -106,18 +131,27 @@ recommended path for uninterrupted access.
To do this, log in using a Gemini API key or Vertex AI.
- Vertex AI (Regular Mode):
- Quota: Governed by a dynamic shared quota system or pre-purchased
provisioned throughput.
- Cost: Based on model and token usage.
### Vertex AI (regular mode)
An enterprise-grade platform for building, deploying, and managing AI models,
including Gemini. It offers enhanced security, data governance, and integration
with other Google Cloud services.
- Quota: Governed by a dynamic shared quota system or pre-purchased provisioned
throughput.
- Cost: Based on model and token usage.
Learn more at
[Vertex AI Dynamic Shared Quota](https://cloud.google.com/vertex-ai/generative-ai/docs/resources/dynamic-shared-quota)
and [Vertex AI Pricing](https://cloud.google.com/vertex-ai/pricing).
- Gemini API key:
- Quota: Varies by pricing tier.
- Cost: Varies by pricing tier and model/token usage.
### Gemini API key
Ideal for developers who want to quickly build applications with the Gemini
models. This is the most direct way to use the models.
- Quota: Varies by pricing tier.
- Cost: Varies by pricing tier and model/token usage.
Learn more at
[Gemini API Rate Limits](https://ai.google.dev/gemini-api/docs/rate-limits),
@@ -125,7 +159,8 @@ Learn more at
Its important to highlight that when using an API key, you pay per token/call.
This can be more expensive for many small calls with few tokens, but it's the
only way to ensure your workflow isn't interrupted by quota limits.
only way to ensure your workflow isn't interrupted by reaching a limit on your
quota.
## Gemini for workspace plans
@@ -135,31 +170,30 @@ Flow video editor). These plans do not apply to the API usage which powers the
Gemini CLI. Supporting these plans is under active consideration for future
support.
## Check usage and quota
## Check usage and limits
You can check your current token usage and quota information using the
You can check your current token usage and applicable limits using the
`/stats model` command. This command provides a snapshot of your current
session's token usage, as well as your overall quota and usage for the supported
models.
session's token usage, as well as information about the limits associated with
your current quota.
For more information on the `/stats` command and its subcommands, see the
[Command Reference](../../reference/commands.md#stats).
[Command Reference](../reference/commands.md#stats).
A summary of model usage is also presented on exit at the end of a session.
## Tips to avoid high costs
When using a Pay as you Go API key, be mindful of your usage to avoid unexpected
When using a pay-as-you-go plan, be mindful of your usage to avoid unexpected
costs.
- Don't blindly accept every suggestion, especially for computationally
intensive tasks like refactoring large codebases.
- Be intentional with your prompts and commands. You are paying per call, so
think about the most efficient way to get the job done.
## Gemini API vs. Vertex
- Gemini API (gemini developer api): This is the fastest way to use the Gemini
models directly.
- Vertex AI: This is the enterprise-grade platform for building, deploying, and
managing Gemini models with specific security and control requirements.
- **Be selective with suggestions**: Before accepting a suggestion, especially
for a computationally intensive task like refactoring a large codebase,
consider if it's the most cost-effective approach.
- **Use precise prompts**: You are paying per call, so think about the most
efficient way to get your desired result. A well-crafted prompt can often get
you the answer you need in a single call, rather than multiple back-and-forth
interactions.
- **Monitor your usage**: Use the `/stats model` command to track your token
usage during a session. This can help you stay aware of your spending in real
time.
+5 -5
View File
@@ -16,8 +16,8 @@ account.
Your Gemini CLI Usage Statistics are handled in accordance with Google's Privacy
Policy.
**Note:** See [quotas and pricing](/docs/resources/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
@@ -46,7 +46,7 @@ for further information.
| Gemini Developer API Key | Gemini API - Paid Services | [Gemini API Terms of Service - Paid Services](https://ai.google.dev/gemini-api/terms#paid-services) | [Google Privacy Policy](https://policies.google.com/privacy) |
| Vertex AI GenAI API Key | Vertex AI GenAI API | [Google Cloud Platform Terms of Service](https://cloud.google.com/terms/service-terms/) | [Google Cloud Privacy Notice](https://cloud.google.com/terms/cloud-privacy-notice) |
## 1. If you have logged in with your Google account to Gemini Code Assist
## 1. If you have signed in with your Google account to Gemini Code Assist
For users who use their Google account to access
[Gemini Code Assist](https://codeassist.google), these Terms of Service and
@@ -68,7 +68,7 @@ Code Assist Standard or Enterprise edition, the terms and privacy policy of
Gemini Code Assist Standard or Enterprise edition will apply to all your use of
Gemini Code Assist._
## 2. If you have logged in with a Gemini API key to the Gemini Developer API
## 2. If you have signed in with a Gemini API key to the Gemini Developer API
If you are using a Gemini API key for authentication with the
[Gemini Developer API](https://ai.google.dev/gemini-api/docs), these Terms of
@@ -84,7 +84,7 @@ Service and Privacy Notice documents apply:
- Privacy Notice: The collection and use of your data is described in the
[Google Privacy Policy](https://policies.google.com/privacy).
## 3. If you have logged in with a Gemini API key to the Vertex AI GenAI API
## 3. If you have signed in with a Gemini API key to the Vertex AI GenAI API
If you are using a Gemini API key for authentication with a
[Vertex AI GenAI API](https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest)
+2 -2
View File
@@ -29,13 +29,13 @@ topics on:
added to your organization's Gemini Code Assist subscription.
- **Error:
`Failed to login. Message: Your current account is not eligible... because it is not currently available in your location.`**
`Failed to sign in. Message: Your current account is not eligible... because it is not currently available in your location.`**
- **Cause:** Gemini CLI does not currently support your location. For a full
list of supported locations, see the following pages:
- Gemini Code Assist for individuals:
[Available locations](https://developers.google.com/gemini-code-assist/resources/available-locations#americas)
- **Error: `Failed to login. Message: Request contains an invalid argument`**
- **Error: `Failed to sign in. Message: Request contains an invalid argument`**
- **Cause:** Users with Google Workspace accounts or Google Cloud accounts
associated with their Gmail accounts may not be able to activate the free
tier of the Google Code Assist plan.
+6 -1
View File
@@ -106,7 +106,12 @@
{ "label": "MCP servers", "slug": "docs/tools/mcp-server" },
{ "label": "Model routing", "slug": "docs/cli/model-routing" },
{ "label": "Model selection", "slug": "docs/cli/model" },
{ "label": "Plan mode", "badge": "🔬", "slug": "docs/cli/plan-mode" },
{
"label": "Notifications",
"badge": "🔬",
"slug": "docs/cli/notifications"
},
{ "label": "Plan mode", "slug": "docs/cli/plan-mode" },
{
"label": "Subagents",
"badge": "🔬",
+2 -2
View File
@@ -67,7 +67,7 @@ Finds files matching specific glob patterns across the workspace.
`Found 5 file(s) matching "*.ts" within src, sorted by modification time (newest first):\nsrc/file1.ts\nsrc/subdir/file2.ts...`
- **Confirmation:** No.
## 5. `grep_search` (SearchText)
### `grep_search` (SearchText)
`grep_search` searches for a regular expression pattern within the content of
files in a specified directory. Can filter files by a glob pattern. Returns the
@@ -103,7 +103,7 @@ lines containing matches, along with their file paths and line numbers.
```
- **Confirmation:** No.
## 6. `replace` (Edit)
### `replace` (Edit)
`replace` replaces text within a file. By default, the tool expects to find and
replace exactly ONE occurrence of `old_string`. If you want to replace multiple
+28 -15
View File
@@ -372,7 +372,7 @@ To authenticate with a server using Service Account Impersonation, you must set
the `authProviderType` to `service_account_impersonation` and provide the
following properties:
- **`targetAudience`** (string): The OAuth Client ID allowslisted on the
- **`targetAudience`** (string): The OAuth Client ID allowlisted on the
IAP-protected application you are trying to access.
- **`targetServiceAccount`** (string): The email address of the Google Cloud
Service Account to impersonate.
@@ -555,21 +555,34 @@ Upon successful connection:
`excludeTools` configuration
4. **Name sanitization:** Tool names are cleaned to meet Gemini API
requirements:
- Invalid characters (non-alphanumeric, underscore, dot, hyphen) are replaced
with underscores
- Characters other than letters, numbers, underscore (`_`), hyphen (`-`), dot
(`.`), and colon (`:`) are replaced with underscores
- Names longer than 63 characters are truncated with middle replacement
(`___`)
(`...`)
### 3. Conflict resolution
### 3. Tool naming and namespaces
When multiple servers expose tools with the same name:
To prevent collisions across multiple servers or conflicting built-in tools,
every discovered MCP tool is assigned a strict namespace.
1. **First registration wins:** The first server to register a tool name gets
the unprefixed name
2. **Automatic prefixing:** Subsequent servers get prefixed names:
`serverName__toolName`
3. **Registry tracking:** The tool registry maintains mappings between server
names and their tools
1. **Automatic FQN:** All MCP tools are unconditionally assigned a fully
qualified name (FQN) using the format `mcp_{serverName}_{toolName}`.
2. **Registry tracking:** The tool registry maintains metadata mappings between
these FQNs and their original server identities.
3. **Overwrites:** If two servers share the exact same alias in your
configuration and provide tools with the exact same name, the last registered
tool overwrites the previous one.
4. **Policies:** To configure permissions (like auto-approval or denial) for MCP
tools, see
[Special syntax for MCP tools](../reference/policy-engine.md#special-syntax-for-mcp-tools)
in the Policy Engine documentation.
> **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
> misinterpret the server identity, which can cause wildcard rules and security
> policies to fail silently.
### 4. Schema processing
@@ -695,7 +708,7 @@ MCP Servers Status:
🐳 dockerizedServer (CONNECTED)
Command: docker run -i --rm -e API_KEY my-mcp-server:latest
Tools: docker__deploy, docker__status
Tools: mcp_dockerizedServer_docker_deploy, mcp_dockerizedServer_docker_status
Discovery State: COMPLETED
```
@@ -998,7 +1011,7 @@ gemini mcp add [options] <name> <commandOrUrl> [args...]
**Options (flags):**
- `-s, --scope`: Configuration scope (user or project). [default: "project"]
- `-s, --scope`: Configuration scope (user or workspace). [default: "workspace"]
- `-t, --transport`: Transport type (stdio, sse, http). [default: "stdio"]
- `-e, --env`: Set environment variables (e.g. -e KEY=value).
- `-H, --header`: Set HTTP headers for SSE and HTTP transports (e.g. -H
@@ -1109,7 +1122,7 @@ gemini mcp remove <name>
**Options (flags):**
- `-s, --scope`: Configuration scope (user or project). [default: "project"]
- `-s, --scope`: Configuration scope (user or workspace). [default: "workspace"]
**Example:**
+16 -1
View File
@@ -35,6 +35,11 @@ const commonRestrictedSyntaxRules = [
message:
'Do not throw string literals or non-Error objects. Throw new Error("...") instead.',
},
{
selector: 'CallExpression[callee.name="fetch"]',
message:
'Use safeFetch() from "@/utils/fetch" instead of the global fetch() to ensure SSRF protection. If you are implementing a custom security layer, use an eslint-disable comment and explain why.',
},
];
export default tseslint.config(
@@ -132,7 +137,16 @@ export default tseslint.config(
'no-cond-assign': 'error',
'no-debugger': 'error',
'no-duplicate-case': 'error',
'no-restricted-syntax': ['error', ...commonRestrictedSyntaxRules],
'no-restricted-syntax': [
'error',
...commonRestrictedSyntaxRules,
{
selector:
'UnaryExpression[operator="typeof"] > MemberExpression[computed=true][property.type="Literal"]',
message:
'Do not use typeof to check object properties. Define a TypeScript interface and a type guard function instead.',
},
],
'no-unsafe-finally': 'error',
'no-unused-expressions': 'off', // Disable base rule
'@typescript-eslint/no-unused-expressions': [
@@ -263,6 +277,7 @@ export default tseslint.config(
...vitest.configs.recommended.rules,
'vitest/expect-expect': 'off',
'vitest/no-commented-out-tests': 'off',
'no-restricted-syntax': ['error', ...commonRestrictedSyntaxRules],
},
},
{
+56
View File
@@ -0,0 +1,56 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { expect } from 'vitest';
import { evalTest } from './test-helper.js';
const MUTATION_AGENT_DEFINITION = `---
name: mutation-agent
description: An agent that modifies the workspace (writes, deletes, git operations, etc).
max_turns: 1
tools:
- write_file
---
You are the mutation agent. Do the mutation requested.
`;
describe('concurrency safety eval test cases', () => {
evalTest('USUALLY_PASSES', {
name: 'mutation agents are run in parallel when explicitly requested',
params: {
settings: {
experimental: {
enableAgents: true,
},
},
},
prompt:
'Update A.txt to say "A" and update B.txt to say "B". Delegate these tasks to two separate mutation-agent subagents. You MUST run these subagents in parallel at the same time.',
files: {
'.gemini/agents/mutation-agent.md': MUTATION_AGENT_DEFINITION,
},
assert: async (rig) => {
const logs = rig.readToolLogs();
const mutationCalls = logs.filter(
(log) => log.toolRequest?.name === 'mutation-agent',
);
expect(
mutationCalls.length,
'Agent should have called the mutation-agent at least twice',
).toBeGreaterThanOrEqual(2);
const firstPromptId = mutationCalls[0].toolRequest.prompt_id;
const secondPromptId = mutationCalls[1].toolRequest.prompt_id;
expect(
firstPromptId,
'mutation agents should be called in parallel (same turn / prompt_ids) when explicitly requested',
).toEqual(secondPromptId);
},
});
});
+1
View File
@@ -112,6 +112,7 @@ export function evalTest(policy: EvalPolicy, evalCase: EvalCase) {
// commands.
execSync('git config core.editor "true"', execOptions);
execSync('git config core.pager "cat"', execOptions);
execSync('git config commit.gpgsign false', execOptions);
execSync('git add .', execOptions);
execSync('git commit --allow-empty -m "Initial commit"', execOptions);
}
+116
View File
@@ -0,0 +1,116 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect } from 'vitest';
import {
TRACKER_CREATE_TASK_TOOL_NAME,
TRACKER_UPDATE_TASK_TOOL_NAME,
} from '@google/gemini-cli-core';
import { evalTest, assertModelHasOutput } from './test-helper.js';
import fs from 'node:fs';
import path from 'node:path';
const FILES = {
'package.json': JSON.stringify({
name: 'test-project',
version: '1.0.0',
scripts: { test: 'echo "All tests passed!"' },
}),
'src/login.js':
'function login(username, password) {\n if (!username) throw new Error("Missing username");\n // BUG: missing password check\n return true;\n}',
} as const;
describe('tracker_mode', () => {
evalTest('USUALLY_PASSES', {
name: 'should manage tasks in the tracker when explicitly requested during a bug fix',
params: {
settings: { experimental: { taskTracker: true } },
},
files: FILES,
prompt:
'We have a bug in src/login.js: the password check is missing. First, create a task in the tracker to fix it. Then fix the bug, and mark the task as closed.',
assert: async (rig, result) => {
const wasCreateCalled = await rig.waitForToolCall(
TRACKER_CREATE_TASK_TOOL_NAME,
);
expect(
wasCreateCalled,
'Expected tracker_create_task tool to be called',
).toBe(true);
const toolLogs = rig.readToolLogs();
const createCall = toolLogs.find(
(log) => log.toolRequest.name === TRACKER_CREATE_TASK_TOOL_NAME,
);
expect(createCall).toBeDefined();
const args = JSON.parse(createCall!.toolRequest.args);
expect(
(args.title?.toLowerCase() ?? '') +
(args.description?.toLowerCase() ?? ''),
).toContain('login');
const wasUpdateCalled = await rig.waitForToolCall(
TRACKER_UPDATE_TASK_TOOL_NAME,
);
expect(
wasUpdateCalled,
'Expected tracker_update_task tool to be called',
).toBe(true);
const updateCall = toolLogs.find(
(log) => log.toolRequest.name === TRACKER_UPDATE_TASK_TOOL_NAME,
);
expect(updateCall).toBeDefined();
const updateArgs = JSON.parse(updateCall!.toolRequest.args);
expect(updateArgs.status).toBe('closed');
const loginContent = fs.readFileSync(
path.join(rig.testDir!, 'src/login.js'),
'utf-8',
);
expect(loginContent).not.toContain('// BUG: missing password check');
assertModelHasOutput(result);
},
});
evalTest('USUALLY_PASSES', {
name: 'should implicitly create tasks when asked to build a feature plan',
params: {
settings: { experimental: { taskTracker: true } },
},
files: FILES,
prompt:
'I need to build a complex new feature for user authentication in our project. Create a detailed implementation plan and organize the work into bite-sized chunks. Do not actually implement the code yet, just plan it.',
assert: async (rig, result) => {
// The model should proactively use tracker_create_task to organize the work
const wasToolCalled = await rig.waitForToolCall(
TRACKER_CREATE_TASK_TOOL_NAME,
);
expect(
wasToolCalled,
'Expected tracker_create_task to be called implicitly to organize plan',
).toBe(true);
const toolLogs = rig.readToolLogs();
const createCalls = toolLogs.filter(
(log) => log.toolRequest.name === TRACKER_CREATE_TASK_TOOL_NAME,
);
// We expect it to create at least one task for authentication, likely more.
expect(createCalls.length).toBeGreaterThan(0);
// Verify it didn't write any code since we asked it to just plan
const loginContent = fs.readFileSync(
path.join(rig.testDir!, 'src/login.js'),
'utf-8',
);
expect(loginContent).toContain('// BUG: missing password check');
assertModelHasOutput(result);
},
});
});
+2 -2
View File
@@ -55,7 +55,7 @@ describe.skip('ACP Environment and Auth', () => {
const bundlePath = join(import.meta.dirname, '..', 'bundle/gemini.js');
child = spawn('node', [bundlePath, '--experimental-acp'], {
child = spawn('node', [bundlePath, '--acp'], {
cwd: rig.homeDir!,
stdio: ['pipe', 'pipe', 'inherit'],
env: {
@@ -120,7 +120,7 @@ describe.skip('ACP Environment and Auth', () => {
const bundlePath = join(import.meta.dirname, '..', 'bundle/gemini.js');
child = spawn('node', [bundlePath, '--experimental-acp'], {
child = spawn('node', [bundlePath, '--acp'], {
cwd: rig.homeDir!,
stdio: ['pipe', 'pipe', 'inherit'],
env: {
+1 -1
View File
@@ -58,7 +58,7 @@ describe('ACP telemetry', () => {
'node',
[
bundlePath,
'--experimental-acp',
'--acp',
'--fake-responses',
join(rig.testDir!, 'fake-responses.json'),
],
@@ -0,0 +1 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Part 1. "}],"role":"model"},"index":0}]},{"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":10,"totalTokenCount":110}},{"candidates":[{"content":{"parts":[{"text":"Part 2."}],"role":"model"},"index":0}],"finishReason":"STOP"}]}
+50
View File
@@ -0,0 +1,50 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
describe('API Resilience E2E', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => {
await rig.cleanup();
});
it('should not crash when receiving metadata-only chunks in a stream', async () => {
await rig.setup('api-resilience-metadata-only', {
fakeResponsesPath: join(
dirname(fileURLToPath(import.meta.url)),
'api-resilience.responses',
),
settings: {
planSettings: { modelRouting: false },
},
});
// Run the CLI with a simple prompt.
// The fake responses will provide a stream with a metadata-only chunk in the middle.
// We use gemini-3-pro-preview to minimize internal service calls.
const result = await rig.run({
args: ['hi', '--model', 'gemini-3-pro-preview'],
});
// Verify the output contains text from the normal chunks.
// If the CLI crashed on the metadata chunk, rig.run would throw.
expect(result).toContain('Part 1.');
expect(result).toContain('Part 2.');
// Verify telemetry event for the prompt was still generated
const hasUserPromptEvent = await rig.waitForTelemetryEvent('user_prompt');
expect(hasUserPromptEvent).toBe(true);
});
});
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I'll open https://example.com and check the page title for you."},{"functionCall":{"name":"browser_agent","args":{"task":"Open https://example.com and get the page title"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":35,"totalTokenCount":135}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The page title of https://example.com is \"Example Domain\". The browser session has been completed and cleaned up successfully."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":200,"candidatesTokenCount":30,"totalTokenCount":230}}]}
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I'll navigate to https://example.com and analyze the links on the page."},{"functionCall":{"name":"browser_agent","args":{"task":"Go to https://example.com and find all links on the page, then describe them"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":40,"totalTokenCount":140}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"After analyzing https://example.com, I found the following links:\n\n1. **\"More information...\"** - This is the main link on the page that points to the IANA (Internet Assigned Numbers Authority) website for more details about reserved domains.\n\nThe page is quite minimal with just this single informational link, which is typical for example domains used in documentation."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":200,"candidatesTokenCount":70,"totalTokenCount":270}}]}
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I'll help you open https://example.com and analyze the page. Let me use the browser agent to navigate and capture the page information."},{"functionCall":{"name":"browser_agent","args":{"task":"Navigate to https://example.com and capture the accessibility tree to get the page title and main content"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":50,"totalTokenCount":150}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Based on the browser analysis of https://example.com:\n\n**Page Title**: Example Domain\n\n**Main Content**: The page contains a simple heading \"Example Domain\" and explanatory text stating \"This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\"\n\nThe page has a clean, minimal layout typical of placeholder domains used for documentation and examples."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":200,"candidatesTokenCount":80,"totalTokenCount":280}}]}
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I'll navigate to https://example.com and take a screenshot for you."},{"functionCall":{"name":"browser_agent","args":{"task":"Navigate to https://example.com and take a screenshot of the page"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":40,"totalTokenCount":140}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I've successfully navigated to https://example.com and captured a screenshot. The page shows the familiar \"Example Domain\" header with explanatory text below it. The screenshot captures the clean, minimal layout of this demonstration website."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":200,"candidatesTokenCount":50,"totalTokenCount":250}}]}
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I'll help you navigate to https://example.com and perform both operations - capturing the accessibility tree and taking a screenshot."},{"functionCall":{"name":"browser_agent","args":{"task":"Navigate to https://example.com, take a snapshot of the accessibility tree, then take a screenshot"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":45,"totalTokenCount":145}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I've successfully completed both operations on https://example.com:\n\n**Accessibility Tree Snapshot**: The page has a clear structure with the main heading \"Example Domain\" and descriptive text about the domain's purpose for documentation examples.\n\n**Screenshot**: Captured a visual representation of the page showing the clean, minimal layout with the heading and explanatory text.\n\nBoth the accessibility data and visual screenshot confirm this is the standard example domain page used for documentation purposes."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":200,"candidatesTokenCount":80,"totalTokenCount":280}}]}
+206
View File
@@ -0,0 +1,206 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Integration tests for the browser agent.
*
* These tests verify the complete end-to-end flow from CLI prompt through
* browser_agent delegation to MCP/Chrome DevTools and back. Unlike the unit
* tests in packages/core/src/agents/browser/ which mock all MCP components,
* these tests launch real Chrome instances in headless mode.
*
* Tests are skipped on systems without Chrome/Chromium installed.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig, assertModelHasOutput } from './test-helper.js';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { execSync } from 'node:child_process';
import { existsSync } from 'node:fs';
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') {
// Check standard Windows installation paths using Node.js fs
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) {
// Fall back to PATH check
execSync('where chrome || where chromium', { stdio: 'ignore' });
}
} else {
return false;
}
return true;
} catch {
return false;
}
})();
describe.skipIf(!chromeAvailable)('browser-agent', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should navigate to a page and capture accessibility tree', async () => {
rig.setup('browser-navigate-and-snapshot', {
fakeResponsesPath: join(
__dirname,
'browser-agent.navigate-snapshot.responses',
),
settings: {
agents: {
browser_agent: {
headless: true,
sessionMode: 'isolated',
},
},
},
});
const result = await rig.run({
args: 'Open https://example.com in the browser and tell me the page title and main content.',
});
assertModelHasOutput(result);
const toolLogs = rig.readToolLogs();
const browserAgentCall = toolLogs.find(
(t) => t.toolRequest.name === 'browser_agent',
);
expect(
browserAgentCall,
'Expected browser_agent to be called',
).toBeDefined();
});
it('should take screenshots of web pages', async () => {
rig.setup('browser-screenshot', {
fakeResponsesPath: join(__dirname, 'browser-agent.screenshot.responses'),
settings: {
agents: {
browser_agent: {
headless: true,
sessionMode: 'isolated',
},
},
},
});
const result = await rig.run({
args: 'Navigate to https://example.com and take a screenshot.',
});
const toolLogs = rig.readToolLogs();
const browserCalls = toolLogs.filter(
(t) => t.toolRequest.name === 'browser_agent',
);
expect(browserCalls.length).toBeGreaterThan(0);
assertModelHasOutput(result);
});
it('should interact with page elements', async () => {
rig.setup('browser-interaction', {
fakeResponsesPath: join(__dirname, 'browser-agent.interaction.responses'),
settings: {
agents: {
browser_agent: {
headless: true,
sessionMode: 'isolated',
},
},
},
});
const result = await rig.run({
args: 'Go to https://example.com, find any links on the page, and describe them.',
});
const toolLogs = rig.readToolLogs();
const browserAgentCall = toolLogs.find(
(t) => t.toolRequest.name === 'browser_agent',
);
expect(
browserAgentCall,
'Expected browser_agent to be called',
).toBeDefined();
assertModelHasOutput(result);
});
it('should clean up browser processes after completion', async () => {
rig.setup('browser-cleanup', {
fakeResponsesPath: join(__dirname, 'browser-agent.cleanup.responses'),
settings: {
agents: {
browser_agent: {
headless: true,
sessionMode: 'isolated',
},
},
},
});
await rig.run({
args: 'Open https://example.com in the browser and check the page title.',
});
// Test passes if we reach here, relying on Vitest's timeout mechanism
// to detect hanging browser processes.
});
it('should handle multiple browser operations in sequence', async () => {
rig.setup('browser-sequential', {
fakeResponsesPath: join(__dirname, 'browser-agent.sequential.responses'),
settings: {
agents: {
browser_agent: {
headless: true,
sessionMode: 'isolated',
},
},
},
});
const result = await rig.run({
args: 'Navigate to https://example.com, take a snapshot of the accessibility tree, then take a screenshot.',
});
const toolLogs = rig.readToolLogs();
const browserCalls = toolLogs.filter(
(t) => t.toolRequest.name === 'browser_agent',
);
expect(browserCalls.length).toBeGreaterThan(0);
// Should successfully complete all operations
assertModelHasOutput(result);
});
});
+2 -2
View File
@@ -104,7 +104,7 @@ describe('extension reloading', () => {
return (
output.includes(
'test-server (from test-extension) - Ready (1 tool)',
) && output.includes('- hello')
) && output.includes('- mcp_test-server_hello')
);
},
30000, // 30s timeout
@@ -148,7 +148,7 @@ describe('extension reloading', () => {
return (
output.includes(
'test-server (from test-extension) - Ready (1 tool)',
) && output.includes('- goodbye')
) && output.includes('- mcp_test-server_goodbye')
);
},
30000,
+19 -9
View File
@@ -182,14 +182,22 @@ describe('Hooks Agent Flow', () => {
);
const afterAgentScript = `
console.log(JSON.stringify({
decision: 'block',
reason: 'Security policy triggered',
hookSpecificOutput: {
hookEventName: 'AfterAgent',
clearContext: true
}
}));
const fs = require('fs');
const input = JSON.parse(fs.readFileSync(0, 'utf-8'));
if (input.stop_hook_active) {
// Retry turn: allow execution to proceed (breaks the loop)
console.log(JSON.stringify({ decision: 'allow' }));
} else {
// First call: block and clear context to trigger the retry
console.log(JSON.stringify({
decision: 'block',
reason: 'Security policy triggered',
hookSpecificOutput: {
hookEventName: 'AfterAgent',
clearContext: true
}
}));
}
`;
const afterAgentScriptPath = rig.createScript(
'after_agent_clear.cjs',
@@ -198,8 +206,10 @@ describe('Hooks Agent Flow', () => {
rig.setup('should process clearContext in AfterAgent hook output', {
settings: {
hooks: {
hooksConfig: {
enabled: true,
},
hooks: {
BeforeModel: [
{
hooks: [
@@ -1,2 +1,3 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Hi there!"}],"role":"model"},"finishReason":"STOP","index":0}]}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Clarification: I am a bot."}],"role":"model"},"finishReason":"STOP","index":0}]}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Security policy triggered"}],"role":"model"},"finishReason":"STOP","index":0}]}]}
+15 -2
View File
@@ -68,6 +68,7 @@ async function verifyToolExecution(
promptCommand: PromptCommand,
result: string,
expectAllowed: boolean,
expectedDenialString?: string,
) {
const log = await waitForToolCallLog(
rig,
@@ -78,10 +79,13 @@ async function verifyToolExecution(
if (expectAllowed) {
expect(log!.toolRequest.success).toBe(true);
expect(result).not.toContain('Tool execution denied by policy');
expect(result).not.toContain(`Tool "${promptCommand.tool}" not found`);
expect(result).toContain(promptCommand.expectedSuccessResult);
} else {
expect(log!.toolRequest.success).toBe(false);
expect(result).toContain('Tool execution denied by policy');
expect(result).toContain(
expectedDenialString || 'Tool execution denied by policy',
);
expect(result).toContain(promptCommand.expectedFailureResult);
}
}
@@ -92,6 +96,7 @@ interface TestCase {
promptCommand: PromptCommand;
policyContent?: string;
expectAllowed: boolean;
expectedDenialString?: string;
}
describe('Policy Engine Headless Mode', () => {
@@ -125,7 +130,13 @@ describe('Policy Engine Headless Mode', () => {
approvalMode: 'default',
});
await verifyToolExecution(rig, tc.promptCommand, result, tc.expectAllowed);
await verifyToolExecution(
rig,
tc.promptCommand,
result,
tc.expectAllowed,
tc.expectedDenialString,
);
};
const testCases = [
@@ -134,6 +145,7 @@ describe('Policy Engine Headless Mode', () => {
responsesFile: 'policy-headless-shell-denied.responses',
promptCommand: ECHO_PROMPT,
expectAllowed: false,
expectedDenialString: 'Tool "run_shell_command" not found',
},
{
name: 'should allow ASK_USER tools in headless mode if explicitly allowed via policy file',
@@ -178,6 +190,7 @@ describe('Policy Engine Headless Mode', () => {
priority = 100
`,
expectAllowed: false,
expectedDenialString: 'Tool execution denied by policy',
},
];
+851 -1202
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli",
"version": "0.34.0-nightly.20260304.28af4e127",
"version": "0.34.0-nightly.20260310.4653b126f",
"engines": {
"node": ">=20.0.0"
},
@@ -14,7 +14,7 @@
"url": "git+https://github.com/google-gemini/gemini-cli.git"
},
"config": {
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.34.0-nightly.20260304.28af4e127"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.34.0-nightly.20260310.4653b126f"
},
"scripts": {
"start": "cross-env NODE_ENV=development node scripts/start.js",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-a2a-server",
"version": "0.34.0-nightly.20260304.28af4e127",
"version": "0.34.0-nightly.20260310.4653b126f",
"description": "Gemini CLI A2A Server",
"repository": {
"type": "git",
@@ -25,7 +25,7 @@
"dist"
],
"dependencies": {
"@a2a-js/sdk": "^0.3.8",
"@a2a-js/sdk": "0.3.11",
"@google-cloud/storage": "^7.16.0",
"@google/gemini-cli-core": "file:../core",
"express": "^5.1.0",
@@ -36,7 +36,7 @@
"winston": "^3.17.0"
},
"devDependencies": {
"@google/genai": "^1.30.0",
"@google/genai": "1.30.0",
"@types/express": "^5.0.3",
"@types/fs-extra": "^11.0.4",
"@types/supertest": "^6.0.3",
@@ -0,0 +1,248 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { CoderAgentExecutor } from './executor.js';
import type {
ExecutionEventBus,
RequestContext,
TaskStore,
} from '@a2a-js/sdk/server';
import { EventEmitter } from 'node:events';
import { requestStorage } from '../http/requestStorage.js';
// Mocks for constructor dependencies
vi.mock('../config/config.js', () => ({
loadConfig: vi.fn().mockReturnValue({
getSessionId: () => 'test-session',
getTargetDir: () => '/tmp',
getCheckpointingEnabled: () => false,
}),
loadEnvironment: vi.fn(),
setTargetDir: vi.fn().mockReturnValue('/tmp'),
}));
vi.mock('../config/settings.js', () => ({
loadSettings: vi.fn().mockReturnValue({}),
}));
vi.mock('../config/extension.js', () => ({
loadExtensions: vi.fn().mockReturnValue([]),
}));
vi.mock('../http/requestStorage.js', () => ({
requestStorage: {
getStore: vi.fn(),
},
}));
vi.mock('./task.js', () => {
const mockTaskInstance = (taskId: string, contextId: string) => ({
id: taskId,
contextId,
taskState: 'working',
acceptUserMessage: vi
.fn()
.mockImplementation(async function* (context, aborted) {
const isConfirmation = (
context.userMessage.parts as Array<{ kind: string }>
).some((p) => p.kind === 'confirmation');
// Hang only for main user messages (text), allow confirmations to finish quickly
if (!isConfirmation && aborted) {
await new Promise((resolve) => {
aborted.addEventListener('abort', resolve, { once: true });
});
}
yield { type: 'content', value: 'hello' };
}),
acceptAgentMessage: vi.fn().mockResolvedValue(undefined),
scheduleToolCalls: vi.fn().mockResolvedValue(undefined),
waitForPendingTools: vi.fn().mockResolvedValue(undefined),
getAndClearCompletedTools: vi.fn().mockReturnValue([]),
addToolResponsesToHistory: vi.fn(),
sendCompletedToolsToLlm: vi.fn().mockImplementation(async function* () {}),
cancelPendingTools: vi.fn(),
setTaskStateAndPublishUpdate: vi.fn(),
dispose: vi.fn(),
getMetadata: vi.fn().mockResolvedValue({}),
geminiClient: {
initialize: vi.fn().mockResolvedValue(undefined),
},
toSDKTask: () => ({
id: taskId,
contextId,
kind: 'task',
status: { state: 'working', timestamp: new Date().toISOString() },
metadata: {},
history: [],
artifacts: [],
}),
});
const MockTask = vi.fn().mockImplementation(mockTaskInstance);
(MockTask as unknown as { create: Mock }).create = vi
.fn()
.mockImplementation(async (taskId: string, contextId: string) =>
mockTaskInstance(taskId, contextId),
);
return { Task: MockTask };
});
describe('CoderAgentExecutor', () => {
let executor: CoderAgentExecutor;
let mockTaskStore: TaskStore;
let mockEventBus: ExecutionEventBus;
beforeEach(() => {
vi.clearAllMocks();
mockTaskStore = {
save: vi.fn().mockResolvedValue(undefined),
load: vi.fn().mockResolvedValue(undefined),
delete: vi.fn().mockResolvedValue(undefined),
list: vi.fn().mockResolvedValue([]),
} as unknown as TaskStore;
mockEventBus = new EventEmitter() as unknown as ExecutionEventBus;
mockEventBus.publish = vi.fn();
mockEventBus.finished = vi.fn();
executor = new CoderAgentExecutor(mockTaskStore);
});
it('should distinguish between primary and secondary execution', async () => {
const taskId = 'test-task';
const contextId = 'test-context';
const mockSocket = new EventEmitter();
const requestContext = {
userMessage: {
messageId: 'msg-1',
taskId,
contextId,
parts: [{ kind: 'text', text: 'hi' }],
metadata: {
coderAgent: { kind: 'agent-settings', workspacePath: '/tmp' },
},
},
} as unknown as RequestContext;
// Mock requestStorage for primary
(requestStorage.getStore as Mock).mockReturnValue({
req: { socket: mockSocket },
});
// First execution (Primary)
const primaryPromise = executor.execute(requestContext, mockEventBus);
// Give it enough time to reach line 490 in executor.ts
await new Promise((resolve) => setTimeout(resolve, 50));
expect(
(
executor as unknown as { executingTasks: Set<string> }
).executingTasks.has(taskId),
).toBe(true);
const wrapper = executor.getTask(taskId);
expect(wrapper).toBeDefined();
// Mock requestStorage for secondary
const secondarySocket = new EventEmitter();
(requestStorage.getStore as Mock).mockReturnValue({
req: { socket: secondarySocket },
});
const secondaryRequestContext = {
userMessage: {
messageId: 'msg-2',
taskId,
contextId,
parts: [{ kind: 'confirmation', callId: '1', outcome: 'proceed' }],
metadata: {
coderAgent: { kind: 'agent-settings', workspacePath: '/tmp' },
},
},
} as unknown as RequestContext;
const secondaryPromise = executor.execute(
secondaryRequestContext,
mockEventBus,
);
// Secondary execution should NOT add to executingTasks (already there)
// and should return early after its loop
await secondaryPromise;
// Task should still be in executingTasks and NOT disposed
expect(
(
executor as unknown as { executingTasks: Set<string> }
).executingTasks.has(taskId),
).toBe(true);
expect(wrapper?.task.dispose).not.toHaveBeenCalled();
// Now simulate secondary socket closure - it should NOT affect primary
secondarySocket.emit('end');
expect(
(
executor as unknown as { executingTasks: Set<string> }
).executingTasks.has(taskId),
).toBe(true);
expect(wrapper?.task.dispose).not.toHaveBeenCalled();
// Set to terminal state to verify disposal on finish
wrapper!.task.taskState = 'completed';
// Now close primary socket
mockSocket.emit('end');
await primaryPromise;
expect(
(
executor as unknown as { executingTasks: Set<string> }
).executingTasks.has(taskId),
).toBe(false);
expect(wrapper?.task.dispose).toHaveBeenCalled();
});
it('should evict task from cache when it reaches terminal state', async () => {
const taskId = 'test-task-terminal';
const contextId = 'test-context';
const mockSocket = new EventEmitter();
(requestStorage.getStore as Mock).mockReturnValue({
req: { socket: mockSocket },
});
const requestContext = {
userMessage: {
messageId: 'msg-1',
taskId,
contextId,
parts: [{ kind: 'text', text: 'hi' }],
metadata: {
coderAgent: { kind: 'agent-settings', workspacePath: '/tmp' },
},
},
} as unknown as RequestContext;
const primaryPromise = executor.execute(requestContext, mockEventBus);
await new Promise((resolve) => setTimeout(resolve, 50));
const wrapper = executor.getTask(taskId)!;
expect(wrapper).toBeDefined();
// Simulate terminal state
wrapper.task.taskState = 'completed';
// Finish primary execution
mockSocket.emit('end');
await primaryPromise;
expect(executor.getTask(taskId)).toBeUndefined();
expect(wrapper.task.dispose).toHaveBeenCalled();
});
});
+55 -16
View File
@@ -252,6 +252,10 @@ export class CoderAgentExecutor implements AgentExecutor {
);
await this.taskStore?.save(wrapper.toSDKTask());
logger.info(`[CoderAgentExecutor] Task ${taskId} state CANCELED saved.`);
// Cleanup listener subscriptions to avoid memory leaks.
wrapper.task.dispose();
this.tasks.delete(taskId);
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : 'Unknown error';
@@ -320,23 +324,26 @@ export class CoderAgentExecutor implements AgentExecutor {
if (store) {
// Grab the raw socket from the request object
const socket = store.req.socket;
const onClientEnd = () => {
const onSocketEnd = () => {
logger.info(
`[CoderAgentExecutor] Client socket closed for task ${taskId}. Cancelling execution.`,
`[CoderAgentExecutor] Socket ended for message ${userMessage.messageId} (task ${taskId}). Aborting execution loop.`,
);
if (!abortController.signal.aborted) {
abortController.abort();
}
// Clean up the listener to prevent memory leaks
socket.removeListener('close', onClientEnd);
socket.removeListener('end', onSocketEnd);
};
// Listen on the socket's 'end' event (remote closed the connection)
socket.on('end', onClientEnd);
socket.on('end', onSocketEnd);
socket.once('close', () => {
socket.removeListener('end', onSocketEnd);
});
// It's also good practice to remove the listener if the task completes successfully
abortSignal.addEventListener('abort', () => {
socket.removeListener('end', onClientEnd);
socket.removeListener('end', onSocketEnd);
});
logger.info(
`[CoderAgentExecutor] Socket close handler set up for task ${taskId}.`,
@@ -457,6 +464,26 @@ export class CoderAgentExecutor implements AgentExecutor {
return;
}
// Check if this is the primary/initial execution for this task
const isPrimaryExecution = !this.executingTasks.has(taskId);
if (!isPrimaryExecution) {
logger.info(
`[CoderAgentExecutor] Primary execution already active for task ${taskId}. Starting secondary loop for message ${userMessage.messageId}.`,
);
currentTask.eventBus = eventBus;
for await (const _ of currentTask.acceptUserMessage(
requestContext,
abortController.signal,
)) {
logger.info(
`[CoderAgentExecutor] Processing user message ${userMessage.messageId} in secondary execution loop for task ${taskId}.`,
);
}
// End this execution-- the original/source will be resumed.
return;
}
logger.info(
`[CoderAgentExecutor] Starting main execution for message ${userMessage.messageId} for task ${taskId}.`,
);
@@ -598,18 +625,30 @@ export class CoderAgentExecutor implements AgentExecutor {
}
}
} finally {
this.executingTasks.delete(taskId);
logger.info(
`[CoderAgentExecutor] Saving final state for task ${taskId}.`,
);
try {
await this.taskStore?.save(wrapper.toSDKTask());
logger.info(`[CoderAgentExecutor] Task ${taskId} state saved.`);
} catch (saveError) {
logger.error(
`[CoderAgentExecutor] Failed to save task ${taskId} state in finally block:`,
saveError,
if (isPrimaryExecution) {
this.executingTasks.delete(taskId);
logger.info(
`[CoderAgentExecutor] Saving final state for task ${taskId}.`,
);
try {
await this.taskStore?.save(wrapper.toSDKTask());
logger.info(`[CoderAgentExecutor] Task ${taskId} state saved.`);
} catch (saveError) {
logger.error(
`[CoderAgentExecutor] Failed to save task ${taskId} state in finally block:`,
saveError,
);
}
if (
['canceled', 'failed', 'completed'].includes(currentTask.taskState)
) {
logger.info(
`[CoderAgentExecutor] Task ${taskId} reached terminal state ${currentTask.taskState}. Evicting and disposing.`,
);
wrapper.task.dispose();
this.tasks.delete(taskId);
}
}
}
}
@@ -0,0 +1,655 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { Task } from './task.js';
import {
type Config,
MessageBusType,
ToolConfirmationOutcome,
ApprovalMode,
Scheduler,
type MessageBus,
} from '@google/gemini-cli-core';
import { createMockConfig } from '../utils/testing_utils.js';
import type { ExecutionEventBus } from '@a2a-js/sdk/server';
describe('Task Event-Driven Scheduler', () => {
let mockConfig: Config;
let mockEventBus: ExecutionEventBus;
let messageBus: MessageBus;
beforeEach(() => {
vi.clearAllMocks();
mockConfig = createMockConfig({
isEventDrivenSchedulerEnabled: () => true,
}) as Config;
messageBus = mockConfig.getMessageBus();
mockEventBus = {
publish: vi.fn(),
on: vi.fn(),
off: vi.fn(),
once: vi.fn(),
removeAllListeners: vi.fn(),
finished: vi.fn(),
};
});
it('should instantiate Scheduler when enabled', () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
expect(task.scheduler).toBeInstanceOf(Scheduler);
});
it('should subscribe to TOOL_CALLS_UPDATE and map status changes', async () => {
// @ts-expect-error - Calling private constructor
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const toolCall = {
request: { callId: '1', name: 'ls', args: {} },
status: 'executing',
};
// Simulate MessageBus event
// Simulate MessageBus event
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
if (!handler) {
throw new Error('TOOL_CALLS_UPDATE handler not found');
}
handler({
type: MessageBusType.TOOL_CALLS_UPDATE,
toolCalls: [toolCall],
});
expect(mockEventBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
status: expect.objectContaining({
state: 'submitted', // initial task state
}),
metadata: expect.objectContaining({
coderAgent: expect.objectContaining({
kind: 'tool-call-update',
}),
}),
}),
);
});
it('should handle tool confirmations by publishing to MessageBus', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const toolCall = {
request: { callId: '1', name: 'ls', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-1',
confirmationDetails: { type: 'info', title: 'test', prompt: 'test' },
};
// Simulate MessageBus event to stash the correlationId
// Simulate MessageBus event
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
if (!handler) {
throw new Error('TOOL_CALLS_UPDATE handler not found');
}
handler({
type: MessageBusType.TOOL_CALLS_UPDATE,
toolCalls: [toolCall],
});
// Simulate A2A client confirmation
const part = {
kind: 'data',
data: {
callId: '1',
outcome: 'proceed_once',
},
};
const handled = await (
task as unknown as {
_handleToolConfirmationPart: (part: unknown) => Promise<boolean>;
}
)._handleToolConfirmationPart(part);
expect(handled).toBe(true);
expect(messageBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: 'corr-1',
confirmed: true,
outcome: ToolConfirmationOutcome.ProceedOnce,
}),
);
});
it('should handle Rejection (Cancel) and Modification (ModifyWithEditor)', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const toolCall = {
request: { callId: '1', name: 'ls', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-1',
confirmationDetails: { type: 'info', title: 'test', prompt: 'test' },
};
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
handler({ type: MessageBusType.TOOL_CALLS_UPDATE, toolCalls: [toolCall] });
// Simulate Rejection (Cancel)
const handled = await (
task as unknown as {
_handleToolConfirmationPart: (part: unknown) => Promise<boolean>;
}
)._handleToolConfirmationPart({
kind: 'data',
data: { callId: '1', outcome: 'cancel' },
});
expect(handled).toBe(true);
expect(messageBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: 'corr-1',
confirmed: false,
}),
);
const toolCall2 = {
request: { callId: '2', name: 'ls', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-2',
confirmationDetails: { type: 'info', title: 'test', prompt: 'test' },
};
handler({ type: MessageBusType.TOOL_CALLS_UPDATE, toolCalls: [toolCall2] });
// Simulate ModifyWithEditor
const handled2 = await (
task as unknown as {
_handleToolConfirmationPart: (part: unknown) => Promise<boolean>;
}
)._handleToolConfirmationPart({
kind: 'data',
data: { callId: '2', outcome: 'modify_with_editor' },
});
expect(handled2).toBe(true);
expect(messageBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: 'corr-2',
confirmed: false,
outcome: ToolConfirmationOutcome.ModifyWithEditor,
payload: undefined,
}),
);
});
it('should handle MCP Server tool operations correctly', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const toolCall = {
request: { callId: '1', name: 'call_mcp_tool', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-mcp-1',
confirmationDetails: {
type: 'mcp',
title: 'MCP Server Operation',
prompt: 'test_mcp',
},
};
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
handler({ type: MessageBusType.TOOL_CALLS_UPDATE, toolCalls: [toolCall] });
// Simulate ProceedOnce for MCP
const handled = await (
task as unknown as {
_handleToolConfirmationPart: (part: unknown) => Promise<boolean>;
}
)._handleToolConfirmationPart({
kind: 'data',
data: { callId: '1', outcome: 'proceed_once' },
});
expect(handled).toBe(true);
expect(messageBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: 'corr-mcp-1',
confirmed: true,
outcome: ToolConfirmationOutcome.ProceedOnce,
}),
);
});
it('should handle MCP Server tool ProceedAlwaysServer outcome', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const toolCall = {
request: { callId: '1', name: 'call_mcp_tool', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-mcp-2',
confirmationDetails: {
type: 'mcp',
title: 'MCP Server Operation',
prompt: 'test_mcp',
},
};
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
handler({ type: MessageBusType.TOOL_CALLS_UPDATE, toolCalls: [toolCall] });
const handled = await (
task as unknown as {
_handleToolConfirmationPart: (part: unknown) => Promise<boolean>;
}
)._handleToolConfirmationPart({
kind: 'data',
data: { callId: '1', outcome: 'proceed_always_server' },
});
expect(handled).toBe(true);
expect(messageBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: 'corr-mcp-2',
confirmed: true,
outcome: ToolConfirmationOutcome.ProceedAlwaysServer,
}),
);
});
it('should handle MCP Server tool ProceedAlwaysTool outcome', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const toolCall = {
request: { callId: '1', name: 'call_mcp_tool', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-mcp-3',
confirmationDetails: {
type: 'mcp',
title: 'MCP Server Operation',
prompt: 'test_mcp',
},
};
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
handler({ type: MessageBusType.TOOL_CALLS_UPDATE, toolCalls: [toolCall] });
const handled = await (
task as unknown as {
_handleToolConfirmationPart: (part: unknown) => Promise<boolean>;
}
)._handleToolConfirmationPart({
kind: 'data',
data: { callId: '1', outcome: 'proceed_always_tool' },
});
expect(handled).toBe(true);
expect(messageBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: 'corr-mcp-3',
confirmed: true,
outcome: ToolConfirmationOutcome.ProceedAlwaysTool,
}),
);
});
it('should handle MCP Server tool ProceedAlwaysAndSave outcome', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const toolCall = {
request: { callId: '1', name: 'call_mcp_tool', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-mcp-4',
confirmationDetails: {
type: 'mcp',
title: 'MCP Server Operation',
prompt: 'test_mcp',
},
};
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
handler({ type: MessageBusType.TOOL_CALLS_UPDATE, toolCalls: [toolCall] });
const handled = await (
task as unknown as {
_handleToolConfirmationPart: (part: unknown) => Promise<boolean>;
}
)._handleToolConfirmationPart({
kind: 'data',
data: { callId: '1', outcome: 'proceed_always_and_save' },
});
expect(handled).toBe(true);
expect(messageBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: 'corr-mcp-4',
confirmed: true,
outcome: ToolConfirmationOutcome.ProceedAlwaysAndSave,
}),
);
});
it('should execute without confirmation in YOLO mode and not transition to input-required', async () => {
// Enable YOLO mode
const yoloConfig = createMockConfig({
isEventDrivenSchedulerEnabled: () => true,
getApprovalMode: () => ApprovalMode.YOLO,
}) as Config;
const yoloMessageBus = yoloConfig.getMessageBus();
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', yoloConfig, mockEventBus);
task.setTaskStateAndPublishUpdate = vi.fn();
const toolCall = {
request: { callId: '1', name: 'ls', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-1',
confirmationDetails: { type: 'info', title: 'test', prompt: 'test' },
};
const handler = (yoloMessageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
handler({ type: MessageBusType.TOOL_CALLS_UPDATE, toolCalls: [toolCall] });
// Should NOT auto-publish ProceedOnce anymore, because PolicyEngine handles it directly
expect(yoloMessageBus.publish).not.toHaveBeenCalledWith(
expect.objectContaining({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
}),
);
// Should NOT transition to input-required since it was auto-approved
expect(task.setTaskStateAndPublishUpdate).not.toHaveBeenCalledWith(
'input-required',
expect.anything(),
undefined,
undefined,
true,
);
});
it('should handle output updates via the message bus', async () => {
// @ts-expect-error - Calling private constructor
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const toolCall = {
request: { callId: '1', name: 'ls', args: {} },
status: 'executing',
liveOutput: 'chunk1',
};
// Simulate MessageBus event
// Simulate MessageBus event
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
if (!handler) {
throw new Error('TOOL_CALLS_UPDATE handler not found');
}
handler({
type: MessageBusType.TOOL_CALLS_UPDATE,
toolCalls: [toolCall],
});
// Should publish artifact update for output
expect(mockEventBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
kind: 'artifact-update',
artifact: expect.objectContaining({
artifactId: 'tool-1-output',
parts: [{ kind: 'text', text: 'chunk1' }],
}),
}),
);
});
it('should complete artifact creation without hanging', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const toolCallId = 'create-file-123';
task['_registerToolCall'](toolCallId, 'executing');
const toolCall = {
request: {
callId: toolCallId,
name: 'writeFile',
args: { path: 'test.sh' },
},
status: 'success',
result: { ok: true },
};
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
handler({ type: MessageBusType.TOOL_CALLS_UPDATE, toolCalls: [toolCall] });
// The tool should be complete and registered appropriately, eventually
// triggering the toolCompletionPromise resolution when all clear.
const internalTask = task as unknown as {
completedToolCalls: unknown[];
pendingToolCalls: Map<string, string>;
};
expect(internalTask.completedToolCalls.length).toBe(1);
expect(internalTask.pendingToolCalls.size).toBe(0);
});
it('should preserve messageId across multiple text chunks to prevent UI duplication', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
// Initialize the ID for the first turn (happens internally upon LLM stream)
task.currentAgentMessageId = 'test-id-123';
// Simulate sending multiple text chunks
task._sendTextContent('chunk 1');
task._sendTextContent('chunk 2');
// Both text contents should have been published with the same messageId
const textCalls = (mockEventBus.publish as Mock).mock.calls.filter(
(call) => call[0].status?.message?.kind === 'message',
);
expect(textCalls.length).toBe(2);
expect(textCalls[0][0].status.message.messageId).toBe('test-id-123');
expect(textCalls[1][0].status.message.messageId).toBe('test-id-123');
// Simulate starting a new turn by calling getAndClearCompletedTools
// (which precedes sendCompletedToolsToLlm where a new ID is minted)
task.getAndClearCompletedTools();
// sendCompletedToolsToLlm internally rolls the ID forward.
// Simulate what sendCompletedToolsToLlm does:
const internalTask = task as unknown as {
setTaskStateAndPublishUpdate: (state: string, change: unknown) => void;
};
internalTask.setTaskStateAndPublishUpdate('working', {});
// Simulate what sendCompletedToolsToLlm does: generate a new UUID for the next turn
task.currentAgentMessageId = 'test-id-456';
task._sendTextContent('chunk 3');
const secondTurnCalls = (mockEventBus.publish as Mock).mock.calls.filter(
(call) => call[0].status?.message?.messageId === 'test-id-456',
);
expect(secondTurnCalls.length).toBe(1);
expect(secondTurnCalls[0][0].status.message.parts[0].text).toBe('chunk 3');
});
it('should handle parallel tool calls correctly', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const toolCall1 = {
request: { callId: '1', name: 'ls', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-1',
confirmationDetails: { type: 'info', title: 'test 1', prompt: 'test 1' },
};
const toolCall2 = {
request: { callId: '2', name: 'pwd', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-2',
confirmationDetails: { type: 'info', title: 'test 2', prompt: 'test 2' },
};
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
// Publish update for both tool calls simultaneously
handler({
type: MessageBusType.TOOL_CALLS_UPDATE,
toolCalls: [toolCall1, toolCall2],
});
// Confirm first tool call
const handled1 = await (
task as unknown as {
_handleToolConfirmationPart: (part: unknown) => Promise<boolean>;
}
)._handleToolConfirmationPart({
kind: 'data',
data: { callId: '1', outcome: 'proceed_once' },
});
expect(handled1).toBe(true);
expect(messageBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: 'corr-1',
confirmed: true,
}),
);
// Confirm second tool call
const handled2 = await (
task as unknown as {
_handleToolConfirmationPart: (part: unknown) => Promise<boolean>;
}
)._handleToolConfirmationPart({
kind: 'data',
data: { callId: '2', outcome: 'cancel' },
});
expect(handled2).toBe(true);
expect(messageBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: 'corr-2',
confirmed: false,
}),
);
});
it('should wait for executing tools before transitioning to input-required state', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
task.setTaskStateAndPublishUpdate = vi.fn();
// Register tool 1 as executing
task['_registerToolCall']('1', 'executing');
const toolCall1 = {
request: { callId: '1', name: 'ls', args: {} },
status: 'executing',
};
const toolCall2 = {
request: { callId: '2', name: 'pwd', args: {} },
status: 'awaiting_approval',
correlationId: 'corr-2',
confirmationDetails: { type: 'info', title: 'test 2', prompt: 'test 2' },
};
const handler = (messageBus.subscribe as Mock).mock.calls.find(
(call: unknown[]) => call[0] === MessageBusType.TOOL_CALLS_UPDATE,
)?.[1];
handler({
type: MessageBusType.TOOL_CALLS_UPDATE,
toolCalls: [toolCall1, toolCall2],
});
// Should NOT transition to input-required yet
expect(task.setTaskStateAndPublishUpdate).not.toHaveBeenCalledWith(
'input-required',
expect.anything(),
undefined,
undefined,
true,
);
// Complete tool 1
const toolCall1Complete = {
...toolCall1,
status: 'success',
result: { ok: true },
};
handler({
type: MessageBusType.TOOL_CALLS_UPDATE,
toolCalls: [toolCall1Complete, toolCall2],
});
// Now it should transition
expect(task.setTaskStateAndPublishUpdate).toHaveBeenCalledWith(
'input-required',
expect.anything(),
undefined,
undefined,
true,
);
});
it('should ignore confirmations for unknown tool calls', async () => {
// @ts-expect-error - Calling private constructor
const task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
const handled = await (
task as unknown as {
_handleToolConfirmationPart: (part: unknown) => Promise<boolean>;
}
)._handleToolConfirmationPart({
kind: 'data',
data: { callId: 'unknown-id', outcome: 'proceed_once' },
});
// Should return false for unhandled tool call
expect(handled).toBe(false);
// Should not publish anything to the message bus
expect(messageBus.publish).not.toHaveBeenCalled();
});
});
+28 -2
View File
@@ -504,13 +504,14 @@ describe('Task', () => {
});
describe('auto-approval', () => {
it('should auto-approve tool calls when autoExecute is true', () => {
it('should NOT publish ToolCallConfirmationEvent when autoExecute is true', () => {
task.autoExecute = true;
const onConfirmSpy = vi.fn();
const toolCalls = [
{
request: { callId: '1' },
status: 'awaiting_approval',
correlationId: 'test-corr-id',
confirmationDetails: {
type: 'edit',
onConfirm: onConfirmSpy,
@@ -524,9 +525,17 @@ describe('Task', () => {
expect(onConfirmSpy).toHaveBeenCalledWith(
ToolConfirmationOutcome.ProceedOnce,
);
const calls = (mockEventBus.publish as Mock).mock.calls;
// Search if ToolCallConfirmationEvent was published
const confEvent = calls.find(
(call) =>
call[0].metadata?.coderAgent?.kind ===
CoderAgentEvent.ToolCallConfirmationEvent,
);
expect(confEvent).toBeUndefined();
});
it('should auto-approve tool calls when approval mode is YOLO', () => {
it('should NOT publish ToolCallConfirmationEvent when approval mode is YOLO', () => {
(mockConfig.getApprovalMode as Mock).mockReturnValue(ApprovalMode.YOLO);
task.autoExecute = false;
const onConfirmSpy = vi.fn();
@@ -534,6 +543,7 @@ describe('Task', () => {
{
request: { callId: '1' },
status: 'awaiting_approval',
correlationId: 'test-corr-id',
confirmationDetails: {
type: 'edit',
onConfirm: onConfirmSpy,
@@ -547,6 +557,14 @@ describe('Task', () => {
expect(onConfirmSpy).toHaveBeenCalledWith(
ToolConfirmationOutcome.ProceedOnce,
);
const calls = (mockEventBus.publish as Mock).mock.calls;
// Search if ToolCallConfirmationEvent was published
const confEvent = calls.find(
(call) =>
call[0].metadata?.coderAgent?.kind ===
CoderAgentEvent.ToolCallConfirmationEvent,
);
expect(confEvent).toBeUndefined();
});
it('should NOT auto-approve when autoExecute is false and mode is not YOLO', () => {
@@ -567,6 +585,14 @@ describe('Task', () => {
task._schedulerToolCallsUpdate(toolCalls);
expect(onConfirmSpy).not.toHaveBeenCalled();
const calls = (mockEventBus.publish as Mock).mock.calls;
// Search if ToolCallConfirmationEvent was published
const confEvent = calls.find(
(call) =>
call[0].metadata?.coderAgent?.kind ===
CoderAgentEvent.ToolCallConfirmationEvent,
);
expect(confEvent).toBeDefined();
});
});
});

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