# google-gemini/gemini-cli

> Google's Gemini-only terminal coding agent: a streamed tool loop gated by a tiered TOML policy engine and opt-in OS sandboxes.

- Category: [Open-source coding agents](https://llms-technical-reviews.com/coding-agents/)
- Repository: https://github.com/google-gemini/gemini-cli (reviewed at commit `fb972b2f87fe7d5b06d37eac711490162d98de2c`, 2026-10-02)
- Stars: 107245 · Language: TypeScript · License: Apache-2.0
- Canonical page: https://llms-technical-reviews.com/p/gemini-cli/

## Overview

Gemini CLI is Google's terminal coding agent. You run `gemini` in a project directory, type a request, and the agent reads files, searches with ripgrep, edits code, runs shell commands and fetches web pages until the task is done. It also runs headless (`gemini -p "..."`, with text, JSON or streaming-JSON output), as an Agent Client Protocol (ACP) server for editors, and as a library through a small TypeScript SDK.

The repository is an npm-workspaces monorepo. `packages/core` holds everything that matters for the agent: the Gemini client and chat history, the turn loop, the tool scheduler, built-in tools, the policy engine, sandboxing, model routing, context compression, hooks, skills and subagents. `packages/cli` is the Ink/React terminal UI, the headless runner and the ACP server. `packages/a2a-server`, `packages/sdk` and `packages/vscode-ide-companion` are further front ends on the same core.

The model side is Gemini only. Requests go through `@google/genai`, with Google login, a Gemini API key, Vertex AI, Cloud Shell or compute credentials, or a custom base URL (`GOOGLE_GEMINI_BASE_URL`, the `gateway` auth type) ([contentGenerator.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/contentGenerator.ts#L63-L70)). The local Gemma path (LiteRT-LM) is used for routing classification, not as a chat backend. If you want a model-agnostic agent, look elsewhere. If you want a Gemini agent with a serious safety and policy layer, this is one of the most complete open-source codebases available.

## Architecture

```mermaid
flowchart LR
  UI["CLI UI / headless / ACP / SDK"] --> GC["GeminiClient.sendMessageStream"]
  GC --> CTX["Compression + masking"]
  GC --> RT["ModelRouterService"]
  GC --> TURN["Turn.run"]
  TURN --> CHAT["GeminiChat (retry, fallback)"]
  CHAT --> API["@google/genai"]
  TURN -->|"ToolCallRequest events"| UI
  UI --> SCH["Scheduler"]
  SCH --> HOOK["BeforeTool hooks"]
  SCH --> POL["PolicyEngine"]
  POL -->|"ask_user"| BUS["MessageBus -> confirm UI"]
  SCH --> EXE["ToolExecutor"]
  EXE --> TOOLS["Built-in, MCP, discovered tools"]
  TOOLS --> SBX["SandboxManager (bwrap / seatbelt / Windows)"]
  EXE -->|"function responses"| UI
```

| Component | Path | Role |
|---|---|---|
| Client | `packages/core/src/core/client.ts` | `GeminiClient`: one model round per call, plus compression, routing, loop detection, next-speaker check, agent hooks |
| Turn | `packages/core/src/core/turn.ts` | Streams one model response and converts chunks into content, thought and tool-call events |
| Chat | `packages/core/src/core/geminiChat.ts` | History, `retryWithBackoff`, model fallback, recording |
| Scheduler | `packages/core/src/scheduler/` | Queues tool calls; hook check, policy, confirmation, execution, result recording |
| Policy | `packages/core/src/policy/` | TOML rule engine with tiers (default to admin) and approval modes |
| Tools | `packages/core/src/tools/` | `read_file`, `grep`, `glob`, `replace`, `write_file`, `run_shell_command`, `web_fetch`, `web_search`, memory, todos, MCP |
| Sandbox | `packages/core/src/sandbox/`, `packages/cli/src/utils/sandbox.ts` | Per-command bwrap/seatbelt/Windows sandbox, and whole-process Docker/Podman/gVisor/sandbox-exec |
| Routing | `packages/core/src/routing/` | Strategy chain that picks the model for each request sequence |
| Context | `packages/core/src/context/` | `ChatCompressionService` (default) and an experimental graph-based `ContextManager` |
| Subagents | `packages/core/src/agents/` | `LocalAgentExecutor`, remote A2A agents, built-in investigator and generalist agents |
| Front ends | `packages/cli/src/` | `interactiveCli.tsx`, `nonInteractiveCli.ts`, `acp/` |

## How a request flows

The headless path is the easiest to follow, and it shows the main design choice: `GeminiClient` does one model round, and the *caller* runs the tool loop.

1. **Outer loop.** `runNonInteractive` keeps a `while (true)` loop and counts turns against `maxSessionTurns`. Each pass calls `geminiClient.sendMessageStream` with the current message parts ([nonInteractiveCli.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/cli/src/nonInteractiveCli.ts#L311-L331)). The interactive UI does the same thing in `useGeminiStream`.
2. **Agent hooks.** `sendMessageStream` resets the loop detector for a new prompt and fires the `BeforeAgent` hook, which can stop, block or add `<hook_context>` to the request ([client.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/client.ts#L924-L975)).
3. **Prepare context.** `processTurn` checks session limits, then compresses history with `tryCompressChat` (or renders it through `ContextManager` when that experimental setting is on), masks old tool outputs, and stops early with `ContextWindowWillOverflow` if the request will not fit ([client.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/client.ts#L628-L730)).
4. **Route and stream.** If no model is pinned for this sequence, `ModelRouterService.route` picks one. The client then runs `Turn.run`, and every streamed event passes through `loopDetector.addAndCheck` ([client.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/client.ts#L761-L860)). `Turn.run` calls `GeminiChat.sendMessageStream` and emits a `ToolCallRequest` event for each function call ([turn.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/turn.ts#L270-L300), [L380-L388](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/turn.ts#L380-L388)).
5. **Continue or stop.** With no pending tool calls, a small LLM call (`checkNextSpeaker`) decides whether the model meant to keep going. If so, the client recurses with "Please continue." ([client.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/client.ts#L889-L921)). An `AfterAgent` hook can also force another round with a reason ([client.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/client.ts#L977-L1045)). Both recursions are capped by `MAX_TURNS = 100`.
6. **Run tools.** Back in the caller, collected requests go to `scheduler.schedule(...)` ([nonInteractiveCli.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/cli/src/nonInteractiveCli.ts#L482-L487)). For each call, `_processToolCall` runs the `BeforeTool` hook, asks the policy engine, raises `ALLOW` to `ASK_USER` (or `DENY` when headless) for taint or build-file risk, and asks for confirmation over the message bus when needed ([scheduler.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/scheduler/scheduler.ts#L628-L755)).
7. **Feed back.** Function responses are recorded on the chat and become the next message of the outer loop.

## Key components

### Policy engine and approval modes

Every tool call gets a decision of `allow`, `deny` or `ask_user` from TOML rules. Default rules are shipped in `policy/policies/*.toml`. Extension, workspace, user and admin rules sit in higher tiers, so an admin rule always beats a user rule. The four approval modes are `default`, `autoEdit`, `yolo` and `plan` ([types.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/policy/types.ts#L48-L63)). Plan mode is not "ask for everything". It starts from a catch-all `deny` at priority 40. Higher-priority rules then let through read-only tools, two named subagents, and `write_file`/`replace` only for Markdown plan files ([plan.toml](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/policy/policies/plan.toml#L74-L103)). Shell commands get extra checks: commands outside the workspace, git in an untrusted folder, and commands rated dangerous are forced to `ask_user`, and known-safe commands can be raised to `allow`.

### Sandboxing

There are two separate layers, and both are opt-in. The older one relaunches the whole CLI inside `sandbox-exec` (macOS profiles), Docker, Podman or gVisor (`runsc`). The newer one wraps individual commands. `createSandboxManager` returns a Windows, Linux or macOS manager only when `sandbox.enabled` is set, and a no-op manager otherwise ([sandboxManagerFactory.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/services/sandboxManagerFactory.ts#L22-L43)). On Linux, `LinuxSandboxManager` builds bubblewrap arguments and adds a seccomp BPF filter that blocks `ptrace` ([LinuxSandboxManager.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/sandbox/linux/LinuxSandboxManager.ts#L295-L325)). Network access is off unless granted. `.gitignore`, `.geminiignore` and `.git` are write-protected inside any sandbox ([sandboxManager.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/services/sandboxManager.ts#L194-L202)).

### Editing

The `replace` tool is search and replace with `old_string` and `new_string`. `calculateReplacement` tries four strategies in order: exact, whitespace-flexible, a token regex, and Levenshtein fuzzy matching with a 10% threshold ([edit.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/tools/edit.ts#L303-L356)). If all of them fail, `attemptSelfCorrection` re-reads the file (in case it changed on disk) and asks a model to repair the search string with `FixLLMEditWithInstruction` ([edit.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/tools/edit.ts#L547-L598)). Whole-file writes go through `write_file`.

### Context compression

By default, `ChatCompressionService` compresses when history passes 50% of the model's token limit. It keeps the newest 30% verbatim and summarises the rest into a `<state_snapshot>`. Then it makes a second call in which the model critiques its own snapshot and outputs a final one ([chatCompressionService.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/context/chatCompressionService.ts#L586-L634)). The graph-based `ContextManager`, with truncation, distillation, masking and rolling-summary processors, is behind an experimental setting that defaults to `false` ([settingsSchema.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/cli/src/config/settingsSchema.ts#L2461-L2468)).

### Model routing

`ModelRouterService` composes fallback, override, approval-mode, optional Gemma-classifier, classifier and numerical-classifier strategies, and ends with a default ([modelRouterService.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/routing/modelRouterService.ts#L39-L66)). With an `auto` model setting, one sequence can run on Pro and the next on Flash. The choice then stays fixed for the rest of the prompt (`currentSequenceModel`).

### Subagents

`LocalAgentExecutor` runs a subagent with its own `GeminiChat`, its own tool registry and a turn limit. The subagent must finish by calling `complete_task` ([local-executor.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/agents/local-executor.ts#L125-L131)). Remote agents speak A2A. Built-in definitions include a codebase investigator and a generalist agent.

## Extending it

- **MCP servers** in settings. Their tools are registered as `mcp_<server>_<tool>` and go through the same policy engine.
- **Hooks** on `BeforeTool`, `AfterTool`, `BeforeAgent`, `AfterAgent`, `BeforeModel`, `AfterModel`, `BeforeToolSelection`, `PreCompress`, session start/end and notifications ([types.ts](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/hooks/types.ts#L43-L55)). Hooks can block, rewrite tool arguments or add context.
- **Skills** in `.gemini/skills/`, activated during a session by a tool. **Custom commands** in `.gemini/commands/`. `GEMINI.md` files for persistent instructions.
- **Extensions** bundle MCP servers, commands, skills, policies and context into one installable unit (`gemini extensions install`).
- **Policies** in workspace or user TOML files, for example to allow `git status` without asking or to deny a tool everywhere.
- **Embedding**: the SDK's `GeminiCliAgent` creates sessions programmatically. The ACP server (built on `@agentclientprotocol/sdk`) lets an editor drive the agent over JSON-RPC on stdio.

## Running it

- `npm install -g @google/gemini-cli` (or `npx`), then `gemini`. Node.js 20 or newer is required.
- Sign in with Google, or set `GEMINI_API_KEY`, or configure Vertex AI.
- Headless: `gemini -p "..." --output-format json` or `stream-json`. The headless runner denies anything that would need a confirmation unless policy allows it.
- `--approval-mode yolo|auto_edit|plan` changes the default decisions. `--sandbox` (or `sandbox.enabled` in settings) turns on sandboxing.

## Strengths and caveats

- **Strength: policy is first-class.** Tiered TOML rules, per-argument patterns, taint escalation and admin overrides are more detailed than in most open-source agents, and the same engine covers MCP tools and subagents.
- **Strength: forgiving edits.** Four matching strategies plus an LLM repair step mean fewer failed `replace` calls, and telemetry records which strategy matched.
- **Strength: many front ends on one core.** TUI, headless JSON, ACP, A2A and SDK all reuse the same client, scheduler and tools.
- **Caveat: Gemini only.** No OpenAI, Anthropic or local chat backends. The base-URL gateway still expects a Gemini-compatible API.
- **Caveat: hidden extra model calls.** The next-speaker check, the compression self-critique, edit repair, routing classifiers and loop detection can all call a model. This adds cost and latency that the main turn count does not show.
- **Caveat: sandboxing is off by default.** Without `--sandbox`, shell commands run directly on the host, and only the policy prompts stand between the model and your machine.
- **Caveat: the loop is split.** The tool loop lives in each front end (`useGeminiStream`, `nonInteractiveCli`, ACP) and not in core. Behaviour can differ between modes, and embedding means rebuilding that loop or using the SDK.

*Sources: code at fb972b2, verified Q&A.*

## How google-gemini/gemini-cli answers the Open-source coding agents questions

### How is the agent loop implemented? (answered)

The agent loop is implemented as a single, internally recursive loop in `GeminiClient.sendMessageStream()` (`packages/core/src/core/client.ts:924`). There is no separate Planner module. The loop begins by calling `processTurn()` (line 979), which sets up context, routes to a model via `ModelRouterService.route()`, then yields control to `Turn.run()` (`packages/core/src/core/turn.ts:270`). `Turn.run()` streams events from the Gemini API: text content, tool call requests, thoughts, citations, and finish reasons. Tool call requests are emitted as `ServerGeminiToolCallRequestEvent` events (line 382-384) with a `callId`, `name`, and `args`. After the Turn finishes, if there are pending tool calls, `sendMessageStream` passes the request through the scheduler where `ToolExecutor.execute()` (`packages/core/src/scheduler/tool-executor.ts:63`) runs each tool and returns results. The tool results are fed back into the model as a new request, and `sendMessageStream` recurses (line 910 and 1040), decrementing the turn budget. Stop conditions include: exceeding `MAX_TURNS` (100, line 79 in client.ts), the model finishing with no pending tools, loop detection via `LoopDetectionService` (lines 761, 827), user cancellation (`GeminiEventType.UserCancelled`), context window overflow, errors, and hooks returning stop or block decisions. Sub-agents are implemented through the `AgentTool` (`packages/core/src/agents/agent-tool.ts:43`), which delegates to invocations like `LocalSubagentInvocation` or `RemoteAgentInvocation`, each running a nested `GeminiClient.sendMessageStream` in a derived `AgentLoopContext`. The event architecture uses `AgentSession` (`packages/core/src/agent/agent-session.ts:18`) wrapping an `AgentProtocol` with a subscribe/notify pattern, and `event-translator.ts` converts Gemini's event types into uniform `AgentEvent` discriminated unions.

> **Editor's note.** Correction: `GeminiClient.sendMessageStream` does one model round and yields `ToolCallRequest` events; the tool loop lives in the caller (`nonInteractiveCli.ts` and the UI's `useGeminiStream`), which runs `scheduler.schedule` and sends the function responses back. The recursive calls in client.ts are the next-speaker "Please continue." check and the AfterAgent-hook continuation, and subagents run in `LocalAgentExecutor` with their own `GeminiChat`, not a nested `GeminiClient`.

Citations: [packages/core/src/core/client.ts:924-975](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/client.ts#L924-L975) · [packages/core/src/core/turn.ts:254-295](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/turn.ts#L254-L295) · [packages/core/src/scheduler/tool-executor.ts:56-130](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/scheduler/tool-executor.ts#L56-L130) · [packages/core/src/agents/agent-tool.ts:43-70](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/agents/agent-tool.ts#L43-L70) · [packages/core/src/agent/agent-session.ts:18-57](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/agent/agent-session.ts#L18-L57) · [packages/core/src/agent/event-translator.ts:97-295](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/agent/event-translator.ts#L97-L295)

### How is repository context gathered and kept within the context window? (answered)

Repository context is gathered via built-in tools — `grep`, `rip_grep`, `glob`, `ls`, `read_file`, and `read_many_files` — which the model calls as needed. These are defined as core tools in `packages/core/src/tools/definitions/coreTools.ts` and registered in `Config` (`packages/core/src/config/config.ts:32-44`). There are no embeddings or vector search; all file discovery is query-driven via ripgrep/glob. The newer context management system (`packages/core/src/context/contextManager.ts:26`) uses a graph-based approach where conversation turns are nodes in a `ConcreteNode` graph, processed by a pipeline of processors (`packages/core/src/context/processors/`) that can truncate, distill, mask, roll up summaries, and degrade blob nodes. The older system uses `ChatCompressionService` (`packages/core/src/context/chatCompressionService.ts:466`), which compresses history via two mechanisms: (1) collapsing older tool function responses into short summaries with byte limits (the `collapseOlderFunctionResponses()` function, line 131), exempting retrieval tools like `read_file` and `grep`; (2) LLM summarization that creates a `<state_snapshot>` using a compression model (e.g., chat-compression-3-flash), followed by a verification turn (lines 618-641) where the model self-critiques the snapshot. The `truncateHistoryToBudget()` function (line 364) enforces a 50,000-token budget for function responses in preserved history. The `AgentHistoryProvider` (`packages/core/src/context/agentHistoryProvider.ts`) manages history without compression. `ToolOutputDistillationService` (`packages/core/src/context/toolDistillationService.ts`) and `ToolOutputMaskingService` (`packages/core/src/context/toolOutputMaskingService.ts`) further reduce tool output sizes. The `memoryContextManager.ts` handles persistent file-based memory via `.gemini/` files.


Citations: [packages/core/src/context/contextManager.ts:26-100](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/context/contextManager.ts#L26-L100) · [packages/core/src/context/chatCompressionService.ts:131-270](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/context/chatCompressionService.ts#L131-L270) · [packages/core/src/context/chatCompressionService.ts:466-720](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/context/chatCompressionService.ts#L466-L720) · [packages/core/src/tools/definitions/coreTools.ts:1-80](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/tools/definitions/coreTools.ts#L1-L80) · [packages/core/src/context/chatCompressionService.ts:364-464](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/context/chatCompressionService.ts#L364-L464)

### How are code edits applied? (answered)

Code edits use a search/replace format — there is no unified diff or full-file patch format. The `EditTool` (`packages/core/src/tools/edit.ts`) takes `old_string` and `new_string` parameters and applies them to a target file. The core replacement logic in `calculateReplacement()` (line 303) tries multiple strategies in order: **exact** (literal line-by-line match, line 139), **flexible** (whitespace-insensitive line-matching via trimmed comparison, line 178), **regex** (token-based multi-line regex built from the search string, line 238), and optionally **fuzzy** (Levenshtein-based with configurable 10% threshold, line 68-70). Each strategy counts occurrences and refuses multi-matches unless `allow_multiple` is set. Edits are validated through a retry system: if the exact match fails, the LLM-based `FixLLMEditWithInstruction` (`packages/core/src/utils/llm-edit-fixer.ts:1`) is invoked with the file content, failed search/replace strings, and error message. It produces a corrected search string via an LLM call with JSON schema enforcement (line 77-80). Additionally, `ensureCorrectFileContent()` (`packages/core/src/utils/editCorrector.ts:31`) handles escaping corrections (e.g., `
` vs `
`) with an LRU cache. The `WriteFileTool` (`packages/core/src/tools/write-file.ts`) supports writing whole files. For in-editor modification flow, `ToolModificationHandler.handleModifyWithEditor()` (`packages/core/src/scheduler/tool-modifier.ts:27`) launches an external editor. The `IdeClient` (`packages/core/src/ide/ide-client.ts`) bridges IDE-integrated editing. Git integration is available through `GitService` for commit/status operations and `getDiffContextSnippet` for diff display, but edits themselves are direct filesystem operations, not git-am patches.


Citations: [packages/core/src/tools/edit.ts:86-107](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/tools/edit.ts#L86-L107) · [packages/core/src/tools/edit.ts:139-320](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/tools/edit.ts#L139-L320) · [packages/core/src/utils/llm-edit-fixer.ts:1-80](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/utils/llm-edit-fixer.ts#L1-L80) · [packages/core/src/utils/editCorrector.ts:31-100](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/utils/editCorrector.ts#L31-L100) · [packages/core/src/scheduler/tool-modifier.ts:27-60](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/scheduler/tool-modifier.ts#L27-L60)

### How are shell commands and file writes kept safe? (answered)

Shell commands pass through multiple safety layers. At the outermost level, the **Policy Engine** (`packages/core/src/policy/policy-engine.ts`) evaluates every tool call against configured rules with decisions: `ALLOW`, `DENY`, or `ASK_USER`. Rules can match by tool name, MCP server name, approval mode, subagent, and specific argument patterns. The `MessageBus` (`packages/core/src/confirmation-bus/message-bus.ts:15`) publishes tool confirmation requests that the UI renders as user prompts. There are four **Approval Modes** (`packages/core/src/policy/types.ts:48-53`): `DEFAULT` (ask for dangerous operations), `AUTO_EDIT` (auto-approve edits but ask for shell), `YOLO` (auto-approve everything), and `PLAN` (ask for everything). For filesystem operations, the `SandboxManager` interface (`packages/core/src/services/sandboxManager.ts:162`) provides `prepareCommand()` which wraps arbitrary commands. On Linux, `LinuxSandboxManager` (`packages/core/src/sandbox/linux/LinuxSandboxManager.ts`) uses **bubblewrap (bwrap)** with a seccomp BPF filter that blocks ptrace (lines 50-100), restricts network unless explicitly granted, and confines filesystem access to a read-only base with writable mount points only for allowed paths defined in `SandboxPermissions` (`packages/core/src/services/sandboxManager.ts:64-74`). Network access is controlled per-command via the `network` flag. The `environmentSanitization.ts` scrubs sensitive environment variables. Shell commands are additionally analyzed by `commandSafety.ts` (`isKnownSafeCommand`/`isDangerousCommand`), and the `untrustedContextTracker.ts` monitors build file modifications. Governance files (`.gitignore`, `package.json`, etc.) are write-protected (`sandboxManager.ts:198`). File write paths are resolved through `resolveDefensiveToolPath()` for symlink/safety validation, and path locking via `withPathLock()` prevents concurrent edits.

> **Editor's note.** Correction: Plan mode is a catch-all deny (plan.toml, priority 40) that only lets through read-only tools, two named subagents and Markdown plan-file writes; it does not ask for everything. Governance files are `.gitignore`, `.geminiignore` and `.git` (not `package.json`), and the bwrap/seatbelt sandbox is only used when `sandbox.enabled` is set; otherwise a no-op manager runs commands on the host.

Citations: [packages/core/src/policy/policy-engine.ts:1-80](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/policy/policy-engine.ts#L1-L80) · [packages/core/src/policy/types.ts:48-66](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/policy/types.ts#L48-L66) · [packages/core/src/sandbox/linux/LinuxSandboxManager.ts:1-100](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/sandbox/linux/LinuxSandboxManager.ts#L1-L100) · [packages/core/src/services/sandboxManager.ts:64-200](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/services/sandboxManager.ts#L64-L200) · [packages/core/src/confirmation-bus/message-bus.ts:15-60](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/confirmation-bus/message-bus.ts#L15-L60)

### Which models are supported and how are they called? (answered)

The system is built exclusively for the Gemini model family, accessed via the `@google/genai` SDK. Model constants are defined in `packages/core/src/config/models.ts` — including `PREVIEW_GEMINI_MODEL` (gemini-3-pro-preview), `DEFAULT_GEMINI_MODEL` (gemini-2.5-pro), `BASE_GEMINI_FLASH_MODEL` (gemini-3.5-flash), `LATEST_GEMINI_FLASH_MODEL` (gemini-3.8-flash), `BASE_GEMINI_FLASH_LITE_MODEL` (gemini-3.1-flash-lite), and `PREVIEW_GEMINI_3_1_MODEL` (gemini-3.1-pro-preview) — along with model resolution helpers like `resolveModel()`, `isPreviewModel()`, `supportsModernFeatures()`, and `isGemini2Model()`. Model routing is handled by `ModelRouterService` (`packages/core/src/routing/modelRouterService.ts:30`), which chains strategies: `FallbackStrategy` → `OverrideStrategy` → `ApprovalModeStrategy` → optional `GemmaClassifierStrategy` → `ClassifierStrategy` → `NumericalClassifierStrategy` → `DefaultStrategy`. This chain can route requests to different models based on complexity, approval mode, or explicit overrides. The routing context includes history, request content, signal, and requested model. Per-model prompt tuning comes from `packages/core/src/core/prompts.ts` with `getCoreSystemPrompt()` used to build the system instruction during `GeminiClient.updateSystemInstruction()` (client.ts:384). Token limit checking uses `tokenLimits.ts` with `tokenLimit()` per-model. **Local models** are supported via the `LocalLiteRtLmClient` (for Gemma models) in `packages/core/src/core/localLiteRtLmClient.ts`. Usage/cost tracking is done via the `Usage` event type in `agent/types.ts:353-359`, populated by `mapUsage()` in `event-translator.ts:458-472` from the Gemini API's `usageMetadata`. Billing logic is in `packages/core/src/billing/`. There is no support for OpenAI, Anthropic, Mistral, or Cohere providers — only Gemini APIs.


Citations: [packages/core/src/config/models.ts:59-80](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/config/models.ts#L59-L80) · [packages/core/src/routing/modelRouterService.ts:30-80](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/routing/modelRouterService.ts#L30-L80) · [packages/core/src/core/client.ts:384-392](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/core/client.ts#L384-L392) · [packages/core/src/agent/event-translator.ts:458-472](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/agent/event-translator.ts#L458-L472)

### How can it be extended and customised? (answered)

The system provides multiple extension mechanisms. **MCP (Model Context Protocol)** is supported via `McpClient` (`packages/core/src/tools/mcp-client.ts`), `McpClientManager` (`packages/core/src/tools/mcp-client-manager.ts`), and `DiscoveredMCPTool` (`packages/core/src/tools/mcp-tool.ts`). MCP tools are prefixed with `mcp_` and qualified as `mcp_{server}_{tool}`. Users configure MCP servers in their settings. **ACP (Agent Communication Protocol)** is a custom Gemini-originated protocol in `packages/cli/src/acp/` with an RPC dispatcher, file system service, session manager, and transport layer — enabling remote agent orchestration. **Hooks** (`packages/core/src/hooks/hookSystem.ts`) provide lifecycle events: `BeforeAgent`, `AfterAgent`, `BeforeModel`, `AfterModel`, `BeforeTool`, `AfterTool`, `PreCompress`, etc. Hooks can block execution, modify requests/responses, inject context, or add policy rules. They are loaded from project/user settings as trusted or untrusted. **Skills** (`packages/core/src/skills/skillManager.ts`) are packaged `.gemini/skills/` directories containing instructions and references. The `ActivateSkillTool` dynamically activates skills during a session. Built-in skills exist for code review, PR creation, CI, etc. **Extensions** (`packages/core/src/utils/extensionLoader.ts`) are the most powerful mechanism — they bundle MCP servers, skills, prompt registry entries, resource registry entries, custom commands, policy rules, and safety checkers in one package. Extensions are installed via `gemini extensions install` (`packages/cli/src/commands/extensions/`). **Headless/SDK mode** is supported via `packages/sdk/` which exports the core loop for programmatic use. The `PromptRegistry` and `ResourceRegistry` allow registering custom prompts and resources that the agent can reference. The `.gemini/commands/` directory supports custom slash commands, and `.geminiignore` works like `.gitignore` for context exclusion.

> **Editor's note.** Correction: ACP here is the Agent Client Protocol (`@agentclientprotocol/sdk`), a JSON-RPC-over-stdio interface that lets editors drive the CLI agent, not a custom Gemini protocol for remote agent orchestration.

Citations: [packages/core/src/tools/mcp-tool.ts:1-60](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/tools/mcp-tool.ts#L1-L60) · [packages/core/src/hooks/hookSystem.ts:1-80](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/hooks/hookSystem.ts#L1-L80) · [packages/core/src/skills/skillManager.ts:17-60](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/skills/skillManager.ts#L17-L60) · [packages/core/src/utils/extensionLoader.ts:10-100](https://github.com/google-gemini/gemini-cli/blob/fb972b2f87fe7d5b06d37eac711490162d98de2c/packages/core/src/utils/extensionLoader.ts#L10-L100)
