mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-31 20:21:01 -07:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b2091d402 | |||
| c627d09326 | |||
| 0e5c5b6f49 | |||
| 8573650253 | |||
| 220888ac2d | |||
| b2f775921d | |||
| f7b2632939 | |||
| 8379099e85 | |||
| 3061555d28 |
@@ -2,8 +2,7 @@
|
||||
"experimental": {
|
||||
"extensionReloading": true,
|
||||
"modelSteering": true,
|
||||
"memoryManager": true,
|
||||
"topicUpdateNarration": true
|
||||
"memoryManager": true
|
||||
},
|
||||
"general": {
|
||||
"devtools": true
|
||||
|
||||
@@ -85,17 +85,25 @@ accessible.
|
||||
|
||||
- **Callouts**: Use GitHub-flavored markdown alerts to highlight important
|
||||
information. To ensure the formatting is preserved by `npm run format`, place
|
||||
an empty line, then the `<!-- prettier-ignore -->` comment directly before
|
||||
the callout block. The callout type (`[!TYPE]`) should be on the first line,
|
||||
followed by a newline, and then the content, with each subsequent line of
|
||||
content starting with `>`. Available types are `NOTE`, `TIP`, `IMPORTANT`,
|
||||
`WARNING`, and `CAUTION`.
|
||||
an empty line, then a prettier ignore comment directly before the callout
|
||||
block. Use `<!-- prettier-ignore -->` for standard Markdown files (`.md`) and
|
||||
`{/* prettier-ignore */}` for MDX files (`.mdx`). The callout type (`[!TYPE]`)
|
||||
should be on the first line, followed by a newline, and then the content, with
|
||||
each subsequent line of content starting with `>`. Available types are `NOTE`,
|
||||
`TIP`, `IMPORTANT`, `WARNING`, and `CAUTION`.
|
||||
|
||||
Example:
|
||||
Example (.md):
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an example of a multi-line note that will be preserved
|
||||
> by Prettier.
|
||||
|
||||
Example (.mdx):
|
||||
|
||||
{/* prettier-ignore */}
|
||||
> [!NOTE]
|
||||
> This is an example of a multi-line note that will be preserved
|
||||
> by Prettier.
|
||||
|
||||
### Links
|
||||
@@ -118,6 +126,7 @@ accessible.
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development.
|
||||
(Note: Use `{/* prettier-ignore */}` if editing an `.mdx` file.)
|
||||
|
||||
- **Headings:** Use hierarchical headings to support the user journey.
|
||||
- **Procedures:**
|
||||
|
||||
@@ -22,3 +22,4 @@ Thumbs.db
|
||||
.pytest_cache
|
||||
**/SKILL.md
|
||||
packages/sdk/test-data/*.json
|
||||
*.mdx
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
# Auto Memory
|
||||
|
||||
Auto Memory is an experimental feature that mines your past Gemini CLI sessions
|
||||
in the background and turns recurring workflows into reusable
|
||||
[Agent Skills](./skills.md). You review, accept, or discard each extracted skill
|
||||
before it becomes available to future sessions.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development.
|
||||
|
||||
## Overview
|
||||
|
||||
Every session you run with Gemini CLI is recorded locally as a transcript. Auto
|
||||
Memory scans those transcripts for procedural patterns that recur across
|
||||
sessions, then drafts each pattern as a `SKILL.md` file in a project-local
|
||||
inbox. You inspect the draft, decide whether it captures real expertise, and
|
||||
promote it to your global or workspace skills directory if you want it.
|
||||
|
||||
You'll use Auto Memory when you want to:
|
||||
|
||||
- **Capture team workflows** that you find yourself walking the agent through
|
||||
more than once.
|
||||
- **Codify hard-won fixes** for project-specific landmines so future sessions
|
||||
avoid them.
|
||||
- **Bootstrap a skills library** without writing every `SKILL.md` by hand.
|
||||
|
||||
Auto Memory complements—but does not replace—the
|
||||
[`save_memory` tool](../tools/memory.md), which captures single facts into
|
||||
`GEMINI.md`. Auto Memory captures multi-step procedures into skills.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Gemini CLI installed and authenticated.
|
||||
- At least 10 user messages across recent, idle sessions in the project. Auto
|
||||
Memory ignores active or trivial sessions.
|
||||
|
||||
## How to enable Auto Memory
|
||||
|
||||
Auto Memory is off by default. Enable it in your settings file:
|
||||
|
||||
1. Open your global settings file at `~/.gemini/settings.json`. If you only
|
||||
want Auto Memory in one project, edit `.gemini/settings.json` in that
|
||||
project instead.
|
||||
|
||||
2. Add the experimental flag:
|
||||
|
||||
```json
|
||||
{
|
||||
"experimental": {
|
||||
"autoMemory": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Restart Gemini CLI. The flag requires a restart because the extraction
|
||||
service starts during session boot.
|
||||
|
||||
## How Auto Memory works
|
||||
|
||||
Auto Memory runs as a background task on session startup. It does not block the
|
||||
UI, consume your interactive turns, or surface tool prompts.
|
||||
|
||||
1. **Eligibility scan.** The service indexes recent sessions from
|
||||
`~/.gemini/tmp/<project>/chats/`. Sessions are eligible only if they have
|
||||
been idle for at least three hours and contain at least 10 user messages.
|
||||
2. **Lock acquisition.** A lock file in the project's memory directory
|
||||
coordinates across multiple CLI instances so extraction runs at most once at
|
||||
a time.
|
||||
3. **Sub-agent extraction.** A specialized sub-agent (named `confucius`)
|
||||
reviews the session index, reads any sessions that look like they contain
|
||||
repeated procedural workflows, and drafts new `SKILL.md` files. Its
|
||||
instructions tell it to default to creating zero skills unless the evidence
|
||||
is strong, so most runs produce no inbox items.
|
||||
4. **Patch validation.** If the sub-agent proposes edits to skills outside the
|
||||
inbox (for example, an existing global skill), it writes a unified diff
|
||||
`.patch` file. Auto Memory dry-runs each patch and discards any that do not
|
||||
apply cleanly.
|
||||
5. **Notification.** When a run produces new skills or patches, Gemini CLI
|
||||
surfaces an inline message telling you how many items are waiting.
|
||||
|
||||
## How to review extracted skills
|
||||
|
||||
Use the `/memory inbox` slash command to open the inbox dialog at any time:
|
||||
|
||||
**Command:** `/memory inbox`
|
||||
|
||||
The dialog lists each draft skill with its name, description, and source
|
||||
sessions. From there you can:
|
||||
|
||||
- **Read** the full `SKILL.md` body before deciding.
|
||||
- **Promote** a skill to your user (`~/.gemini/skills/`) or workspace
|
||||
(`.gemini/skills/`) directory.
|
||||
- **Discard** a skill you do not want.
|
||||
- **Apply** or reject a `.patch` proposal against an existing skill.
|
||||
|
||||
Promoted skills become discoverable in the next session and follow the standard
|
||||
[skill discovery precedence](./skills.md#skill-discovery-tiers).
|
||||
|
||||
## How to disable Auto Memory
|
||||
|
||||
To turn off background extraction, set the flag back to `false` in your settings
|
||||
file and restart Gemini CLI:
|
||||
|
||||
```json
|
||||
{
|
||||
"experimental": {
|
||||
"autoMemory": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Disabling the flag stops the background service immediately on the next session
|
||||
start. Existing inbox items remain on disk; you can either drain them with
|
||||
`/memory inbox` first or remove the project memory directory manually.
|
||||
|
||||
## Data and privacy
|
||||
|
||||
- Auto Memory only reads session files that already exist locally on your
|
||||
machine. Nothing is uploaded to Gemini outside the normal API calls the
|
||||
extraction sub-agent makes during its run.
|
||||
- The sub-agent is instructed to redact secrets, tokens, and credentials it
|
||||
encounters and to never copy large tool outputs verbatim.
|
||||
- Drafted skills live in your project's memory directory until you promote or
|
||||
discard them. They are not automatically loaded into any session.
|
||||
|
||||
## Limitations
|
||||
|
||||
- The sub-agent runs on a preview Gemini Flash model. Extraction quality depends
|
||||
on the model's ability to recognize durable patterns versus one-off incidents.
|
||||
- Auto Memory does not extract skills from the current session. It only
|
||||
considers sessions that have been idle for three hours or more.
|
||||
- Inbox items are stored per project. Skills extracted in one workspace are not
|
||||
visible from another until you promote them to the user-scope skills
|
||||
directory.
|
||||
|
||||
## Next steps
|
||||
|
||||
- Learn how skills are discovered and activated in [Agent Skills](./skills.md).
|
||||
- Explore the [memory management tutorial](./tutorials/memory-management.md) for
|
||||
the complementary `save_memory` and `GEMINI.md` workflows.
|
||||
- Review the experimental settings catalog in
|
||||
[Settings](./settings.md#experimental).
|
||||
@@ -507,7 +507,7 @@ events. For more information, see the [telemetry documentation](./telemetry.md).
|
||||
You can enforce a specific authentication method for all users by setting the
|
||||
`security.auth.enforcedType` in the system-level `settings.json` file. This
|
||||
prevents users from choosing a different authentication method. See the
|
||||
[Authentication docs](../get-started/authentication.md) for more details.
|
||||
[Authentication docs](../get-started/authentication.mdx) for more details.
|
||||
|
||||
**Example:** Enforce the use of Google login for all users.
|
||||
|
||||
|
||||
+12
-11
@@ -39,6 +39,7 @@ they appear in the UI.
|
||||
| Debug Keystroke Logging | `general.debugKeystrokeLogging` | Enable debug logging of keystrokes to the console. | `false` |
|
||||
| Enable Session Cleanup | `general.sessionRetention.enabled` | Enable automatic session cleanup | `true` |
|
||||
| Keep chat history | `general.sessionRetention.maxAge` | Automatically delete chats older than this time period (e.g., "30d", "7d", "24h", "1w") | `"30d"` |
|
||||
| Topic & Update Narration | `general.topicUpdateNarration` | Enable the Topic & Update communication model for reduced chattiness and structured progress reporting. | `true` |
|
||||
|
||||
### Output
|
||||
|
||||
@@ -160,17 +161,17 @@ they appear in the UI.
|
||||
|
||||
### Experimental
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ---------------------------------------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| Enable Git Worktrees | `experimental.worktrees` | Enable automated Git worktree management for parallel work. | `false` |
|
||||
| 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` |
|
||||
| Model Steering | `experimental.modelSteering` | Enable model steering (user hints) to guide the model during tool execution. | `false` |
|
||||
| Direct Web Fetch | `experimental.directWebFetch` | Enable web fetch behavior that bypasses LLM summarization. | `false` |
|
||||
| Memory Manager Agent | `experimental.memoryManager` | Replace the built-in save_memory tool with a memory manager subagent that supports adding, removing, de-duplicating, and organizing memories. | `false` |
|
||||
| Use the generalist profile to manage agent contexts. | `experimental.generalistProfile` | Suitable for general coding and software development tasks. | `false` |
|
||||
| Enable Context Management | `experimental.contextManagement` | Enable logic for context management. | `false` |
|
||||
| Topic & Update Narration | `experimental.topicUpdateNarration` | Enable the experimental Topic & Update communication model for reduced chattiness and structured progress reporting. | `false` |
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ---------------------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| Enable Git Worktrees | `experimental.worktrees` | Enable automated Git worktree management for parallel work. | `false` |
|
||||
| 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` |
|
||||
| Model Steering | `experimental.modelSteering` | Enable model steering (user hints) to guide the model during tool execution. | `false` |
|
||||
| Direct Web Fetch | `experimental.directWebFetch` | Enable web fetch behavior that bypasses LLM summarization. | `false` |
|
||||
| Memory Manager Agent | `experimental.memoryManager` | Replace the built-in save_memory tool with a memory manager subagent that supports adding, removing, de-duplicating, and organizing memories. | `false` |
|
||||
| Auto Memory | `experimental.autoMemory` | Automatically extract reusable skills from past sessions in the background. Review results with /memory inbox. | `false` |
|
||||
| Use the generalist profile to manage agent contexts. | `experimental.generalistProfile` | Suitable for general coding and software development tasks. | `false` |
|
||||
| Enable Context Management | `experimental.contextManagement` | Enable logic for context management. | `false` |
|
||||
|
||||
### Skills
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ project-specific behavior or create a customized persona.
|
||||
|
||||
You can set the environment variable temporarily in your shell, or persist it
|
||||
via a `.gemini/.env` file. See
|
||||
[Persisting Environment Variables](../get-started/authentication.md#persisting-environment-variables).
|
||||
[Persisting Environment Variables](../get-started/authentication.mdx#persisting-environment-variables).
|
||||
|
||||
- Use the project default path (`.gemini/system.md`):
|
||||
- `GEMINI_SYSTEM_MD=true` or `GEMINI_SYSTEM_MD=1`
|
||||
|
||||
@@ -124,3 +124,5 @@ immediately. Force a reload with:
|
||||
- Explore the [Command reference](../../reference/commands.md) for more
|
||||
`/memory` options.
|
||||
- Read the technical spec for [Project context](../../cli/gemini-md.md).
|
||||
- Try the experimental [Auto Memory](../auto-memory.md) feature to extract
|
||||
reusable skills from your past sessions automatically.
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
# Gemini CLI authentication setup
|
||||
|
||||
To use Gemini CLI, you'll need to authenticate with Google. This guide helps you
|
||||
quickly find the best way to sign in based on your account type and how you're
|
||||
using the CLI.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> Looking for a high-level comparison of all available subscriptions?
|
||||
> To compare features and find the right quota for your needs, see our
|
||||
@@ -23,7 +24,7 @@ Select the authentication method that matches your situation in the table below:
|
||||
| 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) |
|
||||
| [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) |
|
||||
|
||||
### What is my Google account type?
|
||||
|
||||
@@ -84,19 +85,24 @@ To authenticate and use Gemini CLI with a Gemini API key:
|
||||
|
||||
2. Set the `GEMINI_API_KEY` environment variable to your key. For example:
|
||||
|
||||
**macOS/Linux**
|
||||
<Tabs>
|
||||
<TabItem label="macOS/Linux">
|
||||
|
||||
```bash
|
||||
# Replace YOUR_GEMINI_API_KEY with the key from AI Studio
|
||||
export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
|
||||
```
|
||||
```bash
|
||||
# Replace YOUR_GEMINI_API_KEY with the key from AI Studio
|
||||
export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**
|
||||
</TabItem>
|
||||
<TabItem label="Windows (PowerShell)">
|
||||
|
||||
```powershell
|
||||
# Replace YOUR_GEMINI_API_KEY with the key from AI Studio
|
||||
$env:GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
|
||||
```
|
||||
```powershell
|
||||
# Replace YOUR_GEMINI_API_KEY with the key from AI Studio
|
||||
$env:GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
To make this setting persistent, see
|
||||
[Persisting Environment Variables](#persisting-vars).
|
||||
@@ -109,7 +115,6 @@ To authenticate and use Gemini CLI with a Gemini API key:
|
||||
|
||||
4. Select **Use Gemini API key**.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Treat API keys, especially for services like Gemini, as sensitive
|
||||
> credentials. Protect them to prevent unauthorized access and potential misuse
|
||||
@@ -131,21 +136,26 @@ or the location where you want to run your jobs.
|
||||
|
||||
For example:
|
||||
|
||||
**macOS/Linux**
|
||||
<Tabs>
|
||||
<TabItem label="macOS/Linux">
|
||||
|
||||
```bash
|
||||
```bash
|
||||
# Replace with your project ID and desired location (for example, us-central1)
|
||||
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"
|
||||
```
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**
|
||||
</TabItem>
|
||||
<TabItem label="Windows (PowerShell)">
|
||||
|
||||
```powershell
|
||||
```powershell
|
||||
# Replace with your project ID and desired location (for example, us-central1)
|
||||
$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
$env:GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"
|
||||
```
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
To make any Vertex AI environment variable settings persistent, see
|
||||
[Persisting Environment Variables](#persisting-vars).
|
||||
@@ -157,17 +167,22 @@ Consider this authentication method if you have Google Cloud CLI installed.
|
||||
If you have previously set `GOOGLE_API_KEY` or `GEMINI_API_KEY`, you must unset
|
||||
them to use ADC.
|
||||
|
||||
**macOS/Linux**
|
||||
<Tabs>
|
||||
<TabItem label="macOS/Linux">
|
||||
|
||||
```bash
|
||||
```bash
|
||||
unset GOOGLE_API_KEY GEMINI_API_KEY
|
||||
```
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**
|
||||
</TabItem>
|
||||
<TabItem label="Windows (PowerShell)">
|
||||
|
||||
```powershell
|
||||
```powershell
|
||||
Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
```
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
1. Verify you have a Google Cloud project and Vertex AI API is enabled.
|
||||
|
||||
@@ -195,17 +210,22 @@ pipelines, or if your organization restricts user-based ADC or API key creation.
|
||||
If you have previously set `GOOGLE_API_KEY` or `GEMINI_API_KEY`, you must unset
|
||||
them:
|
||||
|
||||
**macOS/Linux**
|
||||
<Tabs>
|
||||
<TabItem label="macOS/Linux">
|
||||
|
||||
```bash
|
||||
```bash
|
||||
unset GOOGLE_API_KEY GEMINI_API_KEY
|
||||
```
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**
|
||||
</TabItem>
|
||||
<TabItem label="Windows (PowerShell)">
|
||||
|
||||
```powershell
|
||||
```powershell
|
||||
Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
```
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
1. [Create a service account and key](https://cloud.google.com/iam/docs/keys-create-delete)
|
||||
and download the provided JSON file. Assign the "Vertex AI User" role to the
|
||||
@@ -214,19 +234,24 @@ Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
2. Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the JSON
|
||||
file's absolute path. For example:
|
||||
|
||||
**macOS/Linux**
|
||||
<Tabs>
|
||||
<TabItem label="macOS/Linux">
|
||||
|
||||
```bash
|
||||
# Replace /path/to/your/keyfile.json with the actual path
|
||||
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/keyfile.json"
|
||||
```
|
||||
```bash
|
||||
# Replace /path/to/your/keyfile.json with the actual path
|
||||
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/keyfile.json"
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**
|
||||
</TabItem>
|
||||
<TabItem label="Windows (PowerShell)">
|
||||
|
||||
```powershell
|
||||
# Replace C:\path\to\your\keyfile.json with the actual path
|
||||
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\path\to\your\keyfile.json"
|
||||
```
|
||||
```powershell
|
||||
# Replace C:\path\to\your\keyfile.json with the actual path
|
||||
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\path\to\your\keyfile.json"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
3. [Configure your Google Cloud Project](#set-gcp).
|
||||
|
||||
@@ -238,7 +263,6 @@ Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
|
||||
5. Select **Vertex AI**.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Protect your service account key file as it gives access to
|
||||
> your resources.
|
||||
@@ -250,19 +274,24 @@ Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
|
||||
2. Set the `GOOGLE_API_KEY` environment variable:
|
||||
|
||||
**macOS/Linux**
|
||||
<Tabs>
|
||||
<TabItem label="macOS/Linux">
|
||||
|
||||
```bash
|
||||
# Replace YOUR_GOOGLE_API_KEY with your Vertex AI API key
|
||||
export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
|
||||
```
|
||||
```bash
|
||||
# Replace YOUR_GOOGLE_API_KEY with your Vertex AI API key
|
||||
export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**
|
||||
</TabItem>
|
||||
<TabItem label="Windows (PowerShell)">
|
||||
|
||||
```powershell
|
||||
# Replace YOUR_GOOGLE_API_KEY with your Vertex AI API key
|
||||
$env:GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
|
||||
```
|
||||
```powershell
|
||||
# Replace YOUR_GOOGLE_API_KEY with your Vertex AI API key
|
||||
$env:GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
If you see errors like `"API keys are not supported by this API..."`, your
|
||||
organization might restrict API key usage for this service. Try the other
|
||||
@@ -280,7 +309,6 @@ Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
|
||||
|
||||
## Set your Google Cloud project <a id="set-gcp"></a>
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!IMPORTANT]
|
||||
> Most individual Google accounts (free and paid) don't require a
|
||||
> Google Cloud project for authentication.
|
||||
@@ -308,19 +336,24 @@ To configure Gemini CLI to use a Google Cloud project, do the following:
|
||||
|
||||
For example, to set the `GOOGLE_CLOUD_PROJECT_ID` variable:
|
||||
|
||||
**macOS/Linux**
|
||||
<Tabs>
|
||||
<TabItem label="macOS/Linux">
|
||||
|
||||
```bash
|
||||
# Replace YOUR_PROJECT_ID with your actual Google Cloud project ID
|
||||
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
```
|
||||
```bash
|
||||
# Replace YOUR_PROJECT_ID with your actual Google Cloud project ID
|
||||
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**
|
||||
</TabItem>
|
||||
<TabItem label="Windows (PowerShell)">
|
||||
|
||||
```powershell
|
||||
# Replace YOUR_PROJECT_ID with your actual Google Cloud project ID
|
||||
$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
```
|
||||
```powershell
|
||||
# Replace YOUR_PROJECT_ID with your actual Google Cloud project ID
|
||||
$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
To make this setting persistent, see
|
||||
[Persisting Environment Variables](#persisting-vars).
|
||||
@@ -333,21 +366,29 @@ persist them with the following methods:
|
||||
1. **Add your environment variables to your shell configuration file:** Append
|
||||
the environment variable commands to your shell's startup file.
|
||||
|
||||
**macOS/Linux** (for example, `~/.bashrc`, `~/.zshrc`, or `~/.profile`):
|
||||
<Tabs>
|
||||
<TabItem label="macOS/Linux">
|
||||
|
||||
```bash
|
||||
echo 'export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
(for example, `~/.bashrc`, `~/.zshrc`, or `~/.profile`):
|
||||
|
||||
**Windows (PowerShell)** (for example, `$PROFILE`):
|
||||
```bash
|
||||
echo 'export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
```powershell
|
||||
Add-Content -Path $PROFILE -Value '$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"'
|
||||
. $PROFILE
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem label="Windows (PowerShell)">
|
||||
|
||||
(for example, `$PROFILE`):
|
||||
|
||||
```powershell
|
||||
Add-Content -Path $PROFILE -Value '$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"'
|
||||
. $PROFILE
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Be aware that when you export API keys or service account
|
||||
> paths in your shell configuration file, any process launched from that
|
||||
@@ -361,25 +402,30 @@ persist them with the following methods:
|
||||
|
||||
Example for user-wide settings:
|
||||
|
||||
**macOS/Linux**
|
||||
<Tabs>
|
||||
<TabItem label="macOS/Linux">
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.gemini
|
||||
cat >> ~/.gemini/.env <<'EOF'
|
||||
GOOGLE_CLOUD_PROJECT="your-project-id"
|
||||
# Add other variables like GEMINI_API_KEY as needed
|
||||
EOF
|
||||
```
|
||||
```bash
|
||||
mkdir -p ~/.gemini
|
||||
cat >> ~/.gemini/.env <<'EOF'
|
||||
GOOGLE_CLOUD_PROJECT="your-project-id"
|
||||
# Add other variables like GEMINI_API_KEY as needed
|
||||
EOF
|
||||
```
|
||||
|
||||
**Windows (PowerShell)**
|
||||
</TabItem>
|
||||
<TabItem label="Windows (PowerShell)">
|
||||
|
||||
```powershell
|
||||
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.gemini"
|
||||
@"
|
||||
GOOGLE_CLOUD_PROJECT="your-project-id"
|
||||
# Add other variables like GEMINI_API_KEY as needed
|
||||
"@ | Out-File -FilePath "$env:USERPROFILE\.gemini\.env" -Encoding utf8 -Append
|
||||
```
|
||||
```powershell
|
||||
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.gemini"
|
||||
@"
|
||||
GOOGLE_CLOUD_PROJECT="your-project-id"
|
||||
# Add other variables like GEMINI_API_KEY as needed
|
||||
"@ | Out-File -FilePath "$env:USERPROFILE\.gemini\.env" -Encoding utf8 -Append
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Variables are loaded from the first file found, not merged.
|
||||
|
||||
@@ -24,7 +24,8 @@ Once Gemini CLI is installed, run Gemini CLI from your command line:
|
||||
gemini
|
||||
```
|
||||
|
||||
For more installation options, see [Gemini CLI Installation](./installation.md).
|
||||
For more installation options, see
|
||||
[Gemini CLI Installation](./installation.mdx).
|
||||
|
||||
## Authenticate
|
||||
|
||||
@@ -46,7 +47,7 @@ cases, you can log in with your existing Google account:
|
||||
|
||||
Certain account types may require you to configure a Google Cloud project. For
|
||||
more information, including other authentication methods, see
|
||||
[Gemini CLI Authentication Setup](./authentication.md).
|
||||
[Gemini CLI Authentication Setup](./authentication.mdx).
|
||||
|
||||
## Configure
|
||||
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
# Gemini CLI installation, execution, and releases
|
||||
|
||||
This document provides an overview of Gemini CLI's system requirements,
|
||||
installation methods, and release types.
|
||||
|
||||
## Recommended system specifications
|
||||
|
||||
- **Operating System:**
|
||||
- macOS 15+
|
||||
- Windows 11 24H2+
|
||||
- Ubuntu 20.04+
|
||||
- **Hardware:**
|
||||
- "Casual" usage: 4GB+ RAM (short sessions, common tasks and edits)
|
||||
- "Power" usage: 16GB+ RAM (long sessions, large codebases, deep context)
|
||||
- **Runtime:** Node.js 20.0.0+
|
||||
- **Shell:** Bash, Zsh, or PowerShell
|
||||
- **Location:**
|
||||
[Gemini Code Assist supported locations](https://developers.google.com/gemini-code-assist/resources/available-locations#americas)
|
||||
- **Internet connection required**
|
||||
|
||||
## Install Gemini CLI
|
||||
|
||||
We recommend most users install Gemini CLI using one of the following
|
||||
installation methods:
|
||||
|
||||
- npm
|
||||
- Homebrew
|
||||
- MacPorts
|
||||
- Anaconda
|
||||
|
||||
Note that Gemini CLI comes pre-installed on
|
||||
[**Cloud Shell**](https://docs.cloud.google.com/shell/docs) and
|
||||
[**Cloud Workstations**](https://cloud.google.com/workstations).
|
||||
|
||||
### Install globally with npm
|
||||
|
||||
```bash
|
||||
npm install -g @google/gemini-cli
|
||||
```
|
||||
|
||||
### Install globally with Homebrew (macOS/Linux)
|
||||
|
||||
```bash
|
||||
brew install gemini-cli
|
||||
```
|
||||
|
||||
### Install globally with MacPorts (macOS)
|
||||
|
||||
```bash
|
||||
sudo port install gemini-cli
|
||||
```
|
||||
|
||||
### Install with Anaconda (for restricted environments)
|
||||
|
||||
```bash
|
||||
# Create and activate a new environment
|
||||
conda create -y -n gemini_env -c conda-forge nodejs
|
||||
conda activate gemini_env
|
||||
|
||||
# Install Gemini CLI globally via npm (inside the environment)
|
||||
npm install -g @google/gemini-cli
|
||||
```
|
||||
|
||||
## Run Gemini CLI
|
||||
|
||||
For most users, we recommend running Gemini CLI with the `gemini` command:
|
||||
|
||||
```bash
|
||||
gemini
|
||||
```
|
||||
|
||||
For a list of options and additional commands, see the
|
||||
[CLI cheatsheet](../cli/cli-reference.md).
|
||||
|
||||
You can also run Gemini CLI using one of the following advanced methods:
|
||||
|
||||
- Run instantly with npx. You can run Gemini CLI without permanent installation.
|
||||
- In a sandbox. This method offers increased security and isolation.
|
||||
- From the source. This is recommended for contributors to the project.
|
||||
|
||||
### Run instantly with npx
|
||||
|
||||
```bash
|
||||
# Using npx (no installation required)
|
||||
npx @google/gemini-cli
|
||||
```
|
||||
|
||||
You can also execute the CLI directly from the main branch on GitHub, which is
|
||||
helpful for testing features still in development:
|
||||
|
||||
```bash
|
||||
npx https://github.com/google-gemini/gemini-cli
|
||||
```
|
||||
|
||||
### Run in a sandbox (Docker/Podman)
|
||||
|
||||
For security and isolation, Gemini CLI can be run inside a container. This is
|
||||
the default way that the CLI executes tools that might have side effects.
|
||||
|
||||
- **Directly from the registry:** You can run the published sandbox image
|
||||
directly. This is useful for environments where you only have Docker and want
|
||||
to run the CLI.
|
||||
```bash
|
||||
# Run the published sandbox image
|
||||
docker run --rm -it us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.1.1
|
||||
```
|
||||
- **Using the `--sandbox` flag:** If you have Gemini CLI installed locally
|
||||
(using the standard installation described above), you can instruct it to run
|
||||
inside the sandbox container.
|
||||
```bash
|
||||
gemini --sandbox -y -p "your prompt here"
|
||||
```
|
||||
|
||||
### Run from source (recommended for Gemini CLI contributors)
|
||||
|
||||
Contributors to the project will want to run the CLI directly from the source
|
||||
code.
|
||||
|
||||
- **Development mode:** This method provides hot-reloading and is useful for
|
||||
active development.
|
||||
```bash
|
||||
# From the root of the repository
|
||||
npm run start
|
||||
```
|
||||
- **Production mode (React optimizations):** This method runs the CLI with React
|
||||
production mode enabled, which is useful for testing performance without
|
||||
development overhead.
|
||||
```bash
|
||||
# From the root of the repository
|
||||
npm run start:prod
|
||||
```
|
||||
- **Production-like mode (linked package):** This method simulates a global
|
||||
installation by linking your local package. It's useful for testing a local
|
||||
build in a production workflow.
|
||||
|
||||
```bash
|
||||
# Link the local cli package to your global node_modules
|
||||
npm link packages/cli
|
||||
|
||||
# Now you can run your local version using the `gemini` command
|
||||
gemini
|
||||
```
|
||||
|
||||
## Releases
|
||||
|
||||
Gemini CLI has three release channels: nightly, preview, and stable. For most
|
||||
users, we recommend the stable release, which is the default installation.
|
||||
|
||||
### Stable
|
||||
|
||||
New stable releases are published each week. The stable release is the promotion
|
||||
of last week's `preview` release along with any bug fixes. The stable release
|
||||
uses `latest` tag, but omitting the tag also installs the latest stable release
|
||||
by default:
|
||||
|
||||
```bash
|
||||
# Both commands install the latest stable release.
|
||||
npm install -g @google/gemini-cli
|
||||
npm install -g @google/gemini-cli@latest
|
||||
```
|
||||
|
||||
### Preview
|
||||
|
||||
New preview releases will be published each week. These releases are not fully
|
||||
vetted and may contain regressions or other outstanding issues. Try out the
|
||||
preview release by using the `preview` tag:
|
||||
|
||||
```bash
|
||||
npm install -g @google/gemini-cli@preview
|
||||
```
|
||||
|
||||
### Nightly
|
||||
|
||||
Nightly releases are published every day. The nightly release includes all
|
||||
changes from the main branch at time of release. It should be assumed there are
|
||||
pending validations and issues. You can help test the latest changes by
|
||||
installing with the `nightly` tag:
|
||||
|
||||
```bash
|
||||
npm install -g @google/gemini-cli@nightly
|
||||
```
|
||||
@@ -0,0 +1,201 @@
|
||||
import { Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
# Gemini CLI installation, execution, and releases
|
||||
|
||||
This document provides an overview of Gemini CLI's system requirements,
|
||||
installation methods, and release types.
|
||||
|
||||
## Recommended system specifications
|
||||
|
||||
- **Operating System:**
|
||||
- macOS 15+
|
||||
- Windows 11 24H2+
|
||||
- Ubuntu 20.04+
|
||||
- **Hardware:**
|
||||
- "Casual" usage: 4GB+ RAM (short sessions, common tasks and edits)
|
||||
- "Power" usage: 16GB+ RAM (long sessions, large codebases, deep context)
|
||||
- **Runtime:** Node.js 20.0.0+
|
||||
- **Shell:** Bash, Zsh, or PowerShell
|
||||
- **Location:**
|
||||
[Gemini Code Assist supported locations](https://developers.google.com/gemini-code-assist/resources/available-locations#americas)
|
||||
- **Internet connection required**
|
||||
|
||||
## Install Gemini CLI
|
||||
|
||||
We recommend most users install Gemini CLI using one of the following
|
||||
installation methods. Note that Gemini CLI comes pre-installed on
|
||||
[**Cloud Shell**](https://docs.cloud.google.com/shell/docs) and
|
||||
[**Cloud Workstations**](https://cloud.google.com/workstations).
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="npm">
|
||||
|
||||
Install globally with npm:
|
||||
|
||||
```bash
|
||||
npm install -g @google/gemini-cli
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Homebrew (macOS/Linux)">
|
||||
|
||||
Install globally with Homebrew:
|
||||
|
||||
```bash
|
||||
brew install gemini-cli
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="MacPorts (macOS)">
|
||||
|
||||
Install globally with MacPorts:
|
||||
|
||||
```bash
|
||||
sudo port install gemini-cli
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Anaconda">
|
||||
|
||||
Install with Anaconda (for restricted environments):
|
||||
|
||||
```bash
|
||||
# Create and activate a new environment
|
||||
conda create -y -n gemini_env -c conda-forge nodejs
|
||||
conda activate gemini_env
|
||||
|
||||
# Install Gemini CLI globally via npm (inside the environment)
|
||||
npm install -g @google/gemini-cli
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Run Gemini CLI
|
||||
|
||||
For most users, we recommend running Gemini CLI with the `gemini` command:
|
||||
|
||||
```bash
|
||||
gemini
|
||||
```
|
||||
|
||||
For a list of options and additional commands, see the
|
||||
[CLI cheatsheet](../cli/cli-reference.md).
|
||||
|
||||
You can also run Gemini CLI using one of the following advanced methods:
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="npx">
|
||||
|
||||
Run instantly with npx. You can run Gemini CLI without permanent installation.
|
||||
|
||||
```bash
|
||||
# Using npx (no installation required)
|
||||
npx @google/gemini-cli
|
||||
```
|
||||
|
||||
You can also execute the CLI directly from the main branch on GitHub, which is
|
||||
helpful for testing features still in development:
|
||||
|
||||
```bash
|
||||
npx https://github.com/google-gemini/gemini-cli
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Docker/Podman Sandbox">
|
||||
|
||||
For security and isolation, Gemini CLI can be run inside a container. This is
|
||||
the default way that the CLI executes tools that might have side effects.
|
||||
|
||||
- **Directly from the registry:** You can run the published sandbox image
|
||||
directly. This is useful for environments where you only have Docker and want
|
||||
to run the CLI.
|
||||
```bash
|
||||
# Run the published sandbox image
|
||||
docker run --rm -it us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.1.1
|
||||
```
|
||||
- **Using the `--sandbox` flag:** If you have Gemini CLI installed locally
|
||||
(using the standard installation described above), you can instruct it to run
|
||||
inside the sandbox container.
|
||||
```bash
|
||||
gemini --sandbox -y -p "your prompt here"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="From source">
|
||||
|
||||
Contributors to the project will want to run the CLI directly from the source
|
||||
code.
|
||||
|
||||
- **Development mode:** This method provides hot-reloading and is useful for
|
||||
active development.
|
||||
```bash
|
||||
# From the root of the repository
|
||||
npm run start
|
||||
```
|
||||
- **Production mode (React optimizations):** This method runs the CLI with React
|
||||
production mode enabled, which is useful for testing performance without
|
||||
development overhead.
|
||||
```bash
|
||||
# From the root of the repository
|
||||
npm run start:prod
|
||||
```
|
||||
- **Production-like mode (linked package):** This method simulates a global
|
||||
installation by linking your local package. It's useful for testing a local
|
||||
build in a production workflow.
|
||||
|
||||
```bash
|
||||
# Link the local cli package to your global node_modules
|
||||
npm link packages/cli
|
||||
|
||||
# Now you can run your local version using the `gemini` command
|
||||
gemini
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Releases
|
||||
|
||||
Gemini CLI has three release channels: stable, preview, and nightly. For most
|
||||
users, we recommend the stable release, which is the default installation.
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Stable">
|
||||
|
||||
Stable releases are published each week. A stable release is created from the
|
||||
previous week's preview release along with any bug fixes. The stable release
|
||||
uses the `latest` tag. Omitting the tag also installs the latest stable
|
||||
release by default.
|
||||
|
||||
```bash
|
||||
# Both commands install the latest stable release.
|
||||
npm install -g @google/gemini-cli
|
||||
npm install -g @google/gemini-cli@latest
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Preview">
|
||||
|
||||
New preview releases will be published each week. These releases are not fully
|
||||
vetted and may contain regressions or other outstanding issues. Try out the
|
||||
preview release by using the `preview` tag:
|
||||
|
||||
```bash
|
||||
npm install -g @google/gemini-cli@preview
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Nightly">
|
||||
|
||||
Nightly releases are published every day. The nightly release includes all
|
||||
changes from the main branch at time of release. It should be assumed there are
|
||||
pending validations and issues. You can help test the latest changes by
|
||||
installing with the `nightly` tag:
|
||||
|
||||
```bash
|
||||
npm install -g @google/gemini-cli@nightly
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
+2
-2
@@ -15,9 +15,9 @@ npm install -g @google/gemini-cli
|
||||
Jump in to Gemini CLI.
|
||||
|
||||
- **[Quickstart](./get-started/index.md):** Your first session with Gemini CLI.
|
||||
- **[Installation](./get-started/installation.md):** How to install Gemini CLI
|
||||
- **[Installation](./get-started/installation.mdx):** How to install Gemini CLI
|
||||
on your system.
|
||||
- **[Authentication](./get-started/authentication.md):** Setup instructions for
|
||||
- **[Authentication](./get-started/authentication.mdx):** Setup instructions for
|
||||
personal and enterprise accounts.
|
||||
- **[CLI cheatsheet](./cli/cli-reference.md):** A quick reference for common
|
||||
commands and options.
|
||||
|
||||
@@ -198,6 +198,11 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Description:** Minimum retention period (safety limit, defaults to "1d")
|
||||
- **Default:** `"1d"`
|
||||
|
||||
- **`general.topicUpdateNarration`** (boolean):
|
||||
- **Description:** Enable the Topic & Update communication model for reduced
|
||||
chattiness and structured progress reporting.
|
||||
- **Default:** `true`
|
||||
|
||||
#### `output`
|
||||
|
||||
- **`output.format`** (enum):
|
||||
@@ -1724,6 +1729,12 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.autoMemory`** (boolean):
|
||||
- **Description:** Automatically extract reusable skills from past sessions in
|
||||
the background. Review results with /memory inbox.
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.generalistProfile`** (boolean):
|
||||
- **Description:** Suitable for general coding and software development tasks.
|
||||
- **Default:** `false`
|
||||
@@ -1735,8 +1746,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.topicUpdateNarration`** (boolean):
|
||||
- **Description:** Enable the experimental Topic & Update communication model
|
||||
for reduced chattiness and structured progress reporting.
|
||||
- **Description:** Deprecated: Use general.topicUpdateNarration instead.
|
||||
- **Default:** `false`
|
||||
|
||||
#### `skills`
|
||||
@@ -2075,7 +2085,7 @@ within your user's home folder.
|
||||
Environment variables are a common way to configure applications, especially for
|
||||
sensitive information like API keys or for settings that might change between
|
||||
environments. For authentication setup, see the
|
||||
[Authentication documentation](../get-started/authentication.md) which covers
|
||||
[Authentication documentation](../get-started/authentication.mdx) which covers
|
||||
all available authentication methods.
|
||||
|
||||
The CLI automatically loads environment variables from an `.env` file. The
|
||||
@@ -2096,7 +2106,7 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
|
||||
- **`GEMINI_API_KEY`**:
|
||||
- Your API key for the Gemini API.
|
||||
- One of several available
|
||||
[authentication methods](../get-started/authentication.md).
|
||||
[authentication methods](../get-started/authentication.mdx).
|
||||
- Set this in your shell profile (for example, `~/.bashrc`, `~/.zshrc`) or an
|
||||
`.env` file.
|
||||
- **`GEMINI_MODEL`**:
|
||||
|
||||
@@ -92,6 +92,21 @@ each tool.
|
||||
| [`ask_user`](../tools/ask-user.md) | `Communicate` | Requests clarification or missing information via an interactive dialog. |
|
||||
| [`write_todos`](../tools/todos.md) | `Other` | Maintains an internal list of subtasks. The model uses this to track its own progress. |
|
||||
|
||||
### Task Tracker (Experimental)
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development. Enable via `experimental.taskTracker`.
|
||||
|
||||
| Tool | Kind | Description |
|
||||
| :---------------------------------------------- | :------ | :-------------------------------------------------------------------------- |
|
||||
| [`tracker_create_task`](../tools/tracker.md) | `Other` | Creates a new task in the experimental tracker. |
|
||||
| [`tracker_update_task`](../tools/tracker.md) | `Other` | Updates an existing task's status, description, or dependencies. |
|
||||
| [`tracker_get_task`](../tools/tracker.md) | `Other` | Retrieves the full details of a specific task. |
|
||||
| [`tracker_list_tasks`](../tools/tracker.md) | `Other` | Lists tasks in the tracker, optionally filtered by status, type, or parent. |
|
||||
| [`tracker_add_dependency`](../tools/tracker.md) | `Other` | Adds a dependency between two tasks, ensuring topological execution. |
|
||||
| [`tracker_visualize`](../tools/tracker.md) | `Other` | Renders an ASCII tree visualization of the current task graph. |
|
||||
|
||||
### MCP
|
||||
|
||||
| Tool | Kind | Description |
|
||||
|
||||
@@ -96,6 +96,11 @@
|
||||
]
|
||||
},
|
||||
{ "label": "Agent Skills", "slug": "docs/cli/skills" },
|
||||
{
|
||||
"label": "Auto Memory",
|
||||
"badge": "🔬",
|
||||
"slug": "docs/cli/auto-memory"
|
||||
},
|
||||
{ "label": "Checkpointing", "slug": "docs/cli/checkpointing" },
|
||||
{ "label": "Headless mode", "slug": "docs/cli/headless" },
|
||||
{
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Tracker tools (`tracker_*`)
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development.
|
||||
|
||||
The `tracker_*` tools allow the Gemini agent to maintain an internal, persistent
|
||||
graph of tasks and dependencies for multi-step requests. This suite of tools
|
||||
provides a more robust and granular way to manage execution plans than the
|
||||
legacy `write_todos` tool.
|
||||
|
||||
## Technical reference
|
||||
|
||||
The agent uses these tools to manage its execution plan, decompose complex goals
|
||||
into actionable sub-tasks, and provide real-time progress updates to the CLI
|
||||
interface. The task state is stored in the `.gemini/tmp/tracker/<session-id>`
|
||||
directory, allowing the agent to manage its plan for the current session.
|
||||
|
||||
### Available Tools
|
||||
|
||||
- `tracker_create_task`: Creates a new task in the tracker. You can specify a
|
||||
title, description, and task type (`epic`, `task`, `bug`).
|
||||
- `tracker_update_task`: Updates an existing task's status (`open`,
|
||||
`in_progress`, `blocked`, `closed`), description, or dependencies.
|
||||
- `tracker_get_task`: Retrieves the full details of a specific task by its
|
||||
6-character hex ID.
|
||||
- `tracker_list_tasks`: Lists tasks in the tracker, optionally filtered by
|
||||
status, type, or parent ID.
|
||||
- `tracker_add_dependency`: Adds a dependency between two tasks, ensuring
|
||||
topological execution.
|
||||
- `tracker_visualize`: Renders an ASCII tree visualization of the current task
|
||||
graph.
|
||||
|
||||
## Technical behavior
|
||||
|
||||
- **Interface:** Updates the progress indicator and task tree above the CLI
|
||||
input prompt.
|
||||
- **Persistence:** Task state is saved automatically to the
|
||||
`.gemini/tmp/tracker/<session-id>` directory. Task states are session-specific
|
||||
and do not persist across different sessions.
|
||||
- **Dependencies:** Tasks can depend on other tasks, forming a directed acyclic
|
||||
graph (DAG). The agent must resolve dependencies before starting blocked
|
||||
tasks.
|
||||
- **Interaction:** Users can view the current state of the tracker by asking the
|
||||
agent to visualize it, or by running `gemini-cli` commands if implemented.
|
||||
|
||||
## Use cases
|
||||
|
||||
- Coordinating multi-file refactoring projects.
|
||||
- Breaking down a mission into a hierarchy of epics and tasks for better
|
||||
visibility.
|
||||
- Tracking bugs and feature requests directly within the context of an active
|
||||
codebase.
|
||||
- Providing visibility into the agent's current focus and remaining work.
|
||||
|
||||
## Next steps
|
||||
|
||||
- Follow the [Task planning tutorial](../cli/tutorials/task-planning.md) for
|
||||
usage details and migration from the legacy todo list.
|
||||
- Learn about [Session management](../cli/session-management.md) for context on
|
||||
persistent state.
|
||||
@@ -149,12 +149,12 @@ async function readSkillBodies(skillsDir: string): Promise<string[]> {
|
||||
|
||||
/**
|
||||
* Shared configOverrides for all skill extraction component evals.
|
||||
* - experimentalMemoryManager: enables the memory extraction pipeline.
|
||||
* - experimentalAutoMemory: enables the Auto Memory skill extraction pipeline.
|
||||
* - approvalMode: YOLO auto-approves tool calls (write_file, read_file) so the
|
||||
* background agent can execute without interactive confirmation.
|
||||
*/
|
||||
const EXTRACTION_CONFIG_OVERRIDES = {
|
||||
experimentalMemoryManager: true,
|
||||
experimentalAutoMemory: true,
|
||||
approvalMode: ApprovalMode.YOLO,
|
||||
};
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ describe('update_topic_behavior', () => {
|
||||
2,
|
||||
),
|
||||
'.gemini/settings.json': JSON.stringify({
|
||||
experimental: {
|
||||
general: {
|
||||
topicUpdateNarration: true,
|
||||
},
|
||||
}),
|
||||
@@ -127,7 +127,7 @@ describe('update_topic_behavior', () => {
|
||||
'Explain the difference between Map and Object in JavaScript and provide a performance-focused code snippet for each.',
|
||||
files: {
|
||||
'.gemini/settings.json': JSON.stringify({
|
||||
experimental: {
|
||||
general: {
|
||||
topicUpdateNarration: true,
|
||||
},
|
||||
}),
|
||||
@@ -156,7 +156,7 @@ describe('update_topic_behavior', () => {
|
||||
'packages/core/src/tools/tool-names.ts':
|
||||
"export const UPDATE_TOPIC_TOOL_NAME = 'update_topic';",
|
||||
'.gemini/settings.json': JSON.stringify({
|
||||
experimental: {
|
||||
general: {
|
||||
topicUpdateNarration: true,
|
||||
},
|
||||
}),
|
||||
@@ -204,7 +204,7 @@ app.post('/users', (req, res) => {
|
||||
export default app;
|
||||
`,
|
||||
'.gemini/settings.json': JSON.stringify({
|
||||
experimental: {
|
||||
general: {
|
||||
topicUpdateNarration: true,
|
||||
},
|
||||
}),
|
||||
@@ -249,7 +249,7 @@ export default app;
|
||||
files: {
|
||||
'package.json': JSON.stringify({ name: 'test-project' }),
|
||||
'.gemini/settings.json': JSON.stringify({
|
||||
experimental: {
|
||||
general: {
|
||||
topicUpdateNarration: true,
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -135,10 +135,10 @@ export class InboxMemoryCommand implements Command {
|
||||
context: CommandContext,
|
||||
_: string[],
|
||||
): Promise<CommandExecutionResponse> {
|
||||
if (!context.agentContext.config.isMemoryManagerEnabled()) {
|
||||
if (!context.agentContext.config.isAutoMemoryEnabled()) {
|
||||
return {
|
||||
name: this.name,
|
||||
data: 'The memory inbox requires the experimental memory manager. Enable it with: experimental.memoryManager = true in settings.',
|
||||
data: 'The memory inbox requires Auto Memory. Enable it with: experimental.autoMemory = true in settings.',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -990,9 +990,12 @@ export async function loadCliConfig(
|
||||
disabledSkills: settings.skills?.disabled,
|
||||
experimentalJitContext: settings.experimental?.jitContext,
|
||||
experimentalMemoryManager: settings.experimental?.memoryManager,
|
||||
experimentalAutoMemory: settings.experimental?.autoMemory,
|
||||
contextManagement,
|
||||
modelSteering: settings.experimental?.modelSteering,
|
||||
topicUpdateNarration: settings.experimental?.topicUpdateNarration,
|
||||
topicUpdateNarration:
|
||||
settings.general?.topicUpdateNarration ??
|
||||
settings.experimental?.topicUpdateNarration,
|
||||
noBrowser: !!process.env['NO_BROWSER'],
|
||||
summarizeToolOutput: settings.model?.summarizeToolOutput,
|
||||
ideMode,
|
||||
|
||||
@@ -418,6 +418,16 @@ const SETTINGS_SCHEMA = {
|
||||
},
|
||||
description: 'Settings for automatic session cleanup.',
|
||||
},
|
||||
topicUpdateNarration: {
|
||||
type: 'boolean',
|
||||
label: 'Topic & Update Narration',
|
||||
category: 'General',
|
||||
requiresRestart: false,
|
||||
default: true,
|
||||
description:
|
||||
'Enable the Topic & Update communication model for reduced chattiness and structured progress reporting.',
|
||||
showInDialog: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
@@ -2203,6 +2213,16 @@ const SETTINGS_SCHEMA = {
|
||||
'Replace the built-in save_memory tool with a memory manager subagent that supports adding, removing, de-duplicating, and organizing memories.',
|
||||
showInDialog: true,
|
||||
},
|
||||
autoMemory: {
|
||||
type: 'boolean',
|
||||
label: 'Auto Memory',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: false,
|
||||
description:
|
||||
'Automatically extract reusable skills from past sessions in the background. Review results with /memory inbox.',
|
||||
showInDialog: true,
|
||||
},
|
||||
generalistProfile: {
|
||||
type: 'boolean',
|
||||
label: 'Use the generalist profile to manage agent contexts.',
|
||||
@@ -2228,9 +2248,8 @@ const SETTINGS_SCHEMA = {
|
||||
category: 'Experimental',
|
||||
requiresRestart: false,
|
||||
default: false,
|
||||
description:
|
||||
'Enable the experimental Topic & Update communication model for reduced chattiness and structured progress reporting.',
|
||||
showInDialog: true,
|
||||
description: 'Deprecated: Use general.topicUpdateNarration instead.',
|
||||
showInDialog: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -151,7 +151,7 @@ export async function startInteractiveUI(
|
||||
isScreenReaderEnabled: config.getScreenReader(),
|
||||
onRender: ({ renderTime }: { renderTime: number }) => {
|
||||
if (renderTime > SLOW_RENDER_MS) {
|
||||
recordSlowRender(config, renderTime);
|
||||
recordSlowRender(config, Math.round(renderTime));
|
||||
}
|
||||
profiler.reportFrameRendered();
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@ export const createMockConfig = (overrides: Partial<Config> = {}): Config =>
|
||||
fireSessionStartEvent: vi.fn().mockResolvedValue(undefined),
|
||||
})),
|
||||
isMemoryManagerEnabled: vi.fn(() => false),
|
||||
isAutoMemoryEnabled: vi.fn(() => false),
|
||||
getListExtensions: vi.fn(() => false),
|
||||
getExtensions: vi.fn(() => []),
|
||||
getListSessions: vi.fn(() => false),
|
||||
|
||||
@@ -486,8 +486,8 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
setConfigInitialized(true);
|
||||
startupProfiler.flush(config);
|
||||
|
||||
// Fire-and-forget memory service (skill extraction from past sessions)
|
||||
if (config.isMemoryManagerEnabled()) {
|
||||
// Fire-and-forget Auto Memory service (skill extraction from past sessions)
|
||||
if (config.isAutoMemoryEnabled()) {
|
||||
startMemoryService(config).catch((e) => {
|
||||
debugLogger.error('Failed to start memory service:', e);
|
||||
});
|
||||
@@ -977,6 +977,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
openAgentConfigDialog,
|
||||
openPermissionsDialog,
|
||||
quit: (messages: HistoryItem[]) => {
|
||||
closeThemeDialog();
|
||||
setQuittingMessages(messages);
|
||||
setTimeout(async () => {
|
||||
await runExitCleanup();
|
||||
@@ -1005,6 +1006,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
[
|
||||
setAuthState,
|
||||
openThemeDialog,
|
||||
closeThemeDialog,
|
||||
openEditorDialog,
|
||||
openSettingsDialog,
|
||||
openSessionBrowser,
|
||||
|
||||
@@ -473,7 +473,7 @@ describe('memoryCommand', () => {
|
||||
|
||||
const mockConfig = {
|
||||
reloadSkills: vi.fn(),
|
||||
isMemoryManagerEnabled: vi.fn().mockReturnValue(true),
|
||||
isAutoMemoryEnabled: vi.fn().mockReturnValue(true),
|
||||
};
|
||||
const context = createMockCommandContext({
|
||||
services: {
|
||||
@@ -491,11 +491,11 @@ describe('memoryCommand', () => {
|
||||
expect(result).toHaveProperty('component');
|
||||
});
|
||||
|
||||
it('should return info message when memory manager is disabled', () => {
|
||||
it('should return info message when auto memory is disabled', () => {
|
||||
if (!inboxCommand.action) throw new Error('Command has no action');
|
||||
|
||||
const mockConfig = {
|
||||
isMemoryManagerEnabled: vi.fn().mockReturnValue(false),
|
||||
isAutoMemoryEnabled: vi.fn().mockReturnValue(false),
|
||||
};
|
||||
const context = createMockCommandContext({
|
||||
services: {
|
||||
@@ -509,7 +509,7 @@ describe('memoryCommand', () => {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content:
|
||||
'The memory inbox requires the experimental memory manager. Enable it with: experimental.memoryManager = true in settings.',
|
||||
'The memory inbox requires Auto Memory. Enable it with: experimental.autoMemory = true in settings.',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -145,12 +145,12 @@ export const memoryCommand: SlashCommand = {
|
||||
};
|
||||
}
|
||||
|
||||
if (!config.isMemoryManagerEnabled()) {
|
||||
if (!config.isAutoMemoryEnabled()) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content:
|
||||
'The memory inbox requires the experimental memory manager. Enable it with: experimental.memoryManager = true in settings.',
|
||||
'The memory inbox requires Auto Memory. Enable it with: experimental.autoMemory = true in settings.',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,9 @@ export const GeneralistAgent = (
|
||||
return {
|
||||
systemPrompt: getCoreSystemPrompt(
|
||||
context.config,
|
||||
/*useMemory=*/ undefined,
|
||||
/*userMemory=*/ undefined,
|
||||
/*interactiveOverride=*/ false,
|
||||
/*topicUpdateNarrationOverride=*/ false,
|
||||
),
|
||||
query: '${request}',
|
||||
};
|
||||
|
||||
@@ -82,6 +82,7 @@ import { CompleteTaskTool } from '../tools/complete-task.js';
|
||||
import {
|
||||
COMPLETE_TASK_TOOL_NAME,
|
||||
ACTIVATE_SKILL_TOOL_NAME,
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
} from '../tools/definitions/base-declarations.js';
|
||||
|
||||
/** A callback function to report on agent activity. */
|
||||
@@ -189,6 +190,10 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tool.name === UPDATE_TOPIC_TOOL_NAME) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clone the tool, so it gets its own state and subagent messageBus
|
||||
const clonedTool = tool.clone(subagentMessageBus);
|
||||
agentToolRegistry.registerTool(clonedTool);
|
||||
|
||||
@@ -349,6 +349,27 @@ describe('Server Config (config.ts)', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should ignore properties that are explicitly undefined and preserve existing values', () => {
|
||||
const config = new Config(baseParams);
|
||||
|
||||
config.setShellExecutionConfig({
|
||||
terminalWidth: 80,
|
||||
showColor: true,
|
||||
});
|
||||
|
||||
expect(config.getShellExecutionConfig().terminalWidth).toBe(80);
|
||||
expect(config.getShellExecutionConfig().showColor).toBe(true);
|
||||
|
||||
// Provide undefined for terminalWidth, which should be ignored
|
||||
config.setShellExecutionConfig({
|
||||
terminalWidth: undefined,
|
||||
showColor: false,
|
||||
});
|
||||
|
||||
expect(config.getShellExecutionConfig().terminalWidth).toBe(80); // Should still be 80, not undefined
|
||||
expect(config.getShellExecutionConfig().showColor).toBe(false); // Should be updated
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -689,6 +710,59 @@ describe('Server Config (config.ts)', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('getProModelNoAccessSync', () => {
|
||||
it('should return experiment value for AuthType.LOGIN_WITH_GOOGLE', async () => {
|
||||
vi.mocked(getExperiments).mockResolvedValue({
|
||||
experimentIds: [],
|
||||
flags: {
|
||||
[ExperimentFlags.PRO_MODEL_NO_ACCESS]: {
|
||||
boolValue: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const config = new Config(baseParams);
|
||||
vi.mocked(createContentGeneratorConfig).mockResolvedValue({
|
||||
authType: AuthType.LOGIN_WITH_GOOGLE,
|
||||
});
|
||||
await config.refreshAuth(AuthType.LOGIN_WITH_GOOGLE);
|
||||
expect(config.getProModelNoAccessSync()).toBe(true);
|
||||
});
|
||||
|
||||
it('should return experiment value for AuthType.COMPUTE_ADC', async () => {
|
||||
vi.mocked(getExperiments).mockResolvedValue({
|
||||
experimentIds: [],
|
||||
flags: {
|
||||
[ExperimentFlags.PRO_MODEL_NO_ACCESS]: {
|
||||
boolValue: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const config = new Config(baseParams);
|
||||
vi.mocked(createContentGeneratorConfig).mockResolvedValue({
|
||||
authType: AuthType.COMPUTE_ADC,
|
||||
});
|
||||
await config.refreshAuth(AuthType.COMPUTE_ADC);
|
||||
expect(config.getProModelNoAccessSync()).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for other auth types even if experiment is true', async () => {
|
||||
vi.mocked(getExperiments).mockResolvedValue({
|
||||
experimentIds: [],
|
||||
flags: {
|
||||
[ExperimentFlags.PRO_MODEL_NO_ACCESS]: {
|
||||
boolValue: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const config = new Config(baseParams);
|
||||
vi.mocked(createContentGeneratorConfig).mockResolvedValue({
|
||||
authType: AuthType.USE_GEMINI,
|
||||
});
|
||||
await config.refreshAuth(AuthType.USE_GEMINI);
|
||||
expect(config.getProModelNoAccessSync()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRequestTimeoutMs', () => {
|
||||
it('should return undefined if the flag is not set', () => {
|
||||
const config = new Config(baseParams);
|
||||
@@ -3422,6 +3496,50 @@ describe('Config JIT Initialization', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAutoMemoryEnabled', () => {
|
||||
it('should default to false', () => {
|
||||
const params: ConfigParameters = {
|
||||
sessionId: 'test-session',
|
||||
targetDir: '/tmp/test',
|
||||
debugMode: false,
|
||||
model: 'test-model',
|
||||
cwd: '/tmp/test',
|
||||
};
|
||||
|
||||
config = new Config(params);
|
||||
expect(config.isAutoMemoryEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when experimentalAutoMemory is true', () => {
|
||||
const params: ConfigParameters = {
|
||||
sessionId: 'test-session',
|
||||
targetDir: '/tmp/test',
|
||||
debugMode: false,
|
||||
model: 'test-model',
|
||||
cwd: '/tmp/test',
|
||||
experimentalAutoMemory: true,
|
||||
};
|
||||
|
||||
config = new Config(params);
|
||||
expect(config.isAutoMemoryEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('should be independent of experimentalMemoryManager', () => {
|
||||
const params: ConfigParameters = {
|
||||
sessionId: 'test-session',
|
||||
targetDir: '/tmp/test',
|
||||
debugMode: false,
|
||||
model: 'test-model',
|
||||
cwd: '/tmp/test',
|
||||
experimentalMemoryManager: true,
|
||||
};
|
||||
|
||||
config = new Config(params);
|
||||
expect(config.isMemoryManagerEnabled()).toBe(true);
|
||||
expect(config.isAutoMemoryEnabled()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reloadSkills', () => {
|
||||
it('should refresh disabledSkills and re-register ActivateSkillTool when skills exist', async () => {
|
||||
const mockOnReload = vi.fn().mockResolvedValue({
|
||||
|
||||
@@ -701,6 +701,7 @@ export interface ConfigParameters {
|
||||
experimentalJitContext?: boolean;
|
||||
autoDistillation?: boolean;
|
||||
experimentalMemoryManager?: boolean;
|
||||
experimentalAutoMemory?: boolean;
|
||||
experimentalContextManagementConfig?: string;
|
||||
experimentalAgentHistoryTruncation?: boolean;
|
||||
experimentalAgentHistoryTruncationThreshold?: number;
|
||||
@@ -942,6 +943,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
private readonly adminSkillsEnabled: boolean;
|
||||
private readonly experimentalJitContext: boolean;
|
||||
private readonly experimentalMemoryManager: boolean;
|
||||
private readonly experimentalAutoMemory: boolean;
|
||||
private readonly experimentalContextManagementConfig?: string;
|
||||
private readonly memoryBoundaryMarkers: readonly string[];
|
||||
private readonly topicUpdateNarration: boolean;
|
||||
@@ -1154,6 +1156,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
|
||||
this.experimentalJitContext = params.experimentalJitContext ?? false;
|
||||
this.experimentalMemoryManager = params.experimentalMemoryManager ?? false;
|
||||
this.experimentalAutoMemory = params.experimentalAutoMemory ?? false;
|
||||
this.experimentalContextManagementConfig =
|
||||
params.experimentalContextManagementConfig;
|
||||
this.memoryBoundaryMarkers = params.memoryBoundaryMarkers ?? ['.git'];
|
||||
@@ -1196,7 +1199,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
},
|
||||
},
|
||||
};
|
||||
this.topicUpdateNarration = params.topicUpdateNarration ?? false;
|
||||
this.topicUpdateNarration = params.topicUpdateNarration ?? true;
|
||||
this.modelSteering = params.modelSteering ?? false;
|
||||
this.injectionService = new InjectionService(() =>
|
||||
this.isModelSteeringEnabled(),
|
||||
@@ -2486,6 +2489,10 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
return this.experimentalMemoryManager;
|
||||
}
|
||||
|
||||
isAutoMemoryEnabled(): boolean {
|
||||
return this.experimentalAutoMemory;
|
||||
}
|
||||
|
||||
getExperimentalContextManagementConfig(): string | undefined {
|
||||
return this.experimentalContextManagementConfig;
|
||||
}
|
||||
@@ -3153,7 +3160,10 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
* Note: This method should only be called after startup, once experiments have been loaded.
|
||||
*/
|
||||
getProModelNoAccessSync(): boolean {
|
||||
if (this.contentGeneratorConfig?.authType !== AuthType.LOGIN_WITH_GOOGLE) {
|
||||
if (
|
||||
this.contentGeneratorConfig?.authType !== AuthType.LOGIN_WITH_GOOGLE &&
|
||||
this.contentGeneratorConfig?.authType !== AuthType.COMPUTE_ADC
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
@@ -3396,20 +3406,23 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
return this.shellExecutionConfig;
|
||||
}
|
||||
|
||||
setShellExecutionConfig(config: ShellExecutionConfig): void {
|
||||
setShellExecutionConfig(config: Partial<ShellExecutionConfig>): void {
|
||||
const definedConfig: Partial<ShellExecutionConfig> = {};
|
||||
for (const [k, v] of Object.entries(config)) {
|
||||
// Only merge properties explicitly provided with a concrete value.
|
||||
// Filtering out `null` and `undefined` ensures existing system defaults
|
||||
// are preserved when an extension doesn't want to override them.
|
||||
if (v != null) {
|
||||
Object.assign(definedConfig, { [k]: v });
|
||||
}
|
||||
}
|
||||
|
||||
// Note: This performs a shallow merge. If the incoming config provides a nested
|
||||
// object (e.g., sandboxConfig), it will completely overwrite the existing
|
||||
// nested object rather than merging its individual properties.
|
||||
this.shellExecutionConfig = {
|
||||
...this.shellExecutionConfig,
|
||||
terminalWidth:
|
||||
config.terminalWidth ?? this.shellExecutionConfig.terminalWidth,
|
||||
terminalHeight:
|
||||
config.terminalHeight ?? this.shellExecutionConfig.terminalHeight,
|
||||
showColor: config.showColor ?? this.shellExecutionConfig.showColor,
|
||||
pager: config.pager ?? this.shellExecutionConfig.pager,
|
||||
sanitizationConfig:
|
||||
config.sanitizationConfig ??
|
||||
this.shellExecutionConfig.sanitizationConfig,
|
||||
sandboxManager:
|
||||
config.sandboxManager ?? this.shellExecutionConfig.sandboxManager,
|
||||
...definedConfig,
|
||||
};
|
||||
}
|
||||
getScreenReader(): boolean {
|
||||
|
||||
@@ -300,4 +300,78 @@ describe('ProjectRegistry', () => {
|
||||
'ProjectRegistry must be initialized before use',
|
||||
);
|
||||
});
|
||||
|
||||
it('retries on EBUSY during save', async () => {
|
||||
const registry = new ProjectRegistry(registryPath);
|
||||
await registry.initialize();
|
||||
|
||||
const originalRename = fs.promises.rename;
|
||||
const renameSpy = vi.spyOn(fs.promises, 'rename');
|
||||
let ebusyCount = 0;
|
||||
|
||||
renameSpy.mockImplementation(async (oldPath, newPath) => {
|
||||
// Only throw for the specific temporary file generated by save()
|
||||
if (oldPath.toString().includes('.tmp') && ebusyCount < 2) {
|
||||
ebusyCount++;
|
||||
const err = Object.assign(new Error('Resource busy or locked'), {
|
||||
code: 'EBUSY',
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
// On success, call the original native rename implementation
|
||||
return originalRename(oldPath, newPath);
|
||||
});
|
||||
|
||||
const projectPath = path.join(tempDir, 'ebusy-project');
|
||||
const shortId = await registry.getShortId(projectPath);
|
||||
expect(shortId).toBe('ebusy-project');
|
||||
expect(ebusyCount).toBe(2);
|
||||
|
||||
// Verify it actually saved properly after retries
|
||||
const data = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
||||
expect(data.projects[normalizePath(projectPath)]).toBe('ebusy-project');
|
||||
|
||||
renameSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('re-throws error if save ultimately fails after retries', async () => {
|
||||
const registry = new ProjectRegistry(registryPath);
|
||||
await registry.initialize();
|
||||
|
||||
const renameSpy = vi.spyOn(fs.promises, 'rename');
|
||||
const expectedError = Object.assign(new Error('Persistent EBUSY'), {
|
||||
code: 'EBUSY',
|
||||
});
|
||||
|
||||
// Mock rename to ALWAYS fail
|
||||
renameSpy.mockRejectedValue(expectedError);
|
||||
|
||||
const projectPath = path.join(tempDir, 'failing-project');
|
||||
await expect(registry.getShortId(projectPath)).rejects.toThrow(
|
||||
'Persistent EBUSY',
|
||||
);
|
||||
|
||||
renameSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('protects against data destruction by throwing on EACCES instead of resetting', async () => {
|
||||
// 1. Write valid registry data
|
||||
fs.writeFileSync(
|
||||
registryPath,
|
||||
JSON.stringify({ projects: { '/foo': 'bar' } }),
|
||||
);
|
||||
|
||||
const registry = new ProjectRegistry(registryPath);
|
||||
|
||||
// 2. Mock readFile to throw a permissions error
|
||||
const readFileSpy = vi.spyOn(fs.promises, 'readFile');
|
||||
readFileSpy.mockRejectedValue(
|
||||
Object.assign(new Error('Permission denied'), { code: 'EACCES' }),
|
||||
);
|
||||
|
||||
// 3. Initialization should NOT swallow the error
|
||||
await expect(registry.initialize()).rejects.toThrow('Permission denied');
|
||||
|
||||
readFileSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as path from 'node:path';
|
||||
import * as os from 'node:os';
|
||||
import { lock } from 'proper-lockfile';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import { isNodeError } from '../utils/errors.js';
|
||||
|
||||
export interface RegistryData {
|
||||
projects: Record<string, string>;
|
||||
@@ -54,18 +55,27 @@ export class ProjectRegistry {
|
||||
}
|
||||
|
||||
private async loadData(): Promise<RegistryData> {
|
||||
if (!fs.existsSync(this.registryPath)) {
|
||||
return { projects: {} };
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await fs.promises.readFile(this.registryPath, 'utf8');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return JSON.parse(content);
|
||||
} catch (e) {
|
||||
debugLogger.debug('Failed to load registry: ', e);
|
||||
// If the registry is corrupted, we'll start fresh to avoid blocking the CLI
|
||||
return { projects: {} };
|
||||
} catch (error: unknown) {
|
||||
if (isNodeError(error) && error.code === 'ENOENT') {
|
||||
return { projects: {} }; // Normal first run
|
||||
}
|
||||
if (error instanceof SyntaxError) {
|
||||
debugLogger.warn(
|
||||
'Failed to load registry (JSON corrupted), resetting to empty: ',
|
||||
error,
|
||||
);
|
||||
// Ownership markers on disk will allow self-healing when short IDs are requested.
|
||||
return { projects: {} };
|
||||
}
|
||||
|
||||
// If it's a real filesystem error (e.g. EACCES permission denied), DO NOT swallow it.
|
||||
// Swallowing read errors and overwriting the file would permanently destroy user data.
|
||||
debugLogger.error('Critical failure reading project registry:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,18 +92,54 @@ export class ProjectRegistry {
|
||||
if (!fs.existsSync(dir)) {
|
||||
await fs.promises.mkdir(dir, { recursive: true });
|
||||
}
|
||||
// Use a randomized tmp path to avoid ENOENT crashes when save() is called concurrently
|
||||
const tmpPath = this.registryPath + '.' + randomUUID() + '.tmp';
|
||||
let savedSuccessfully = false;
|
||||
|
||||
try {
|
||||
// Unconditionally ensure the directory exists; recursive ignores EEXIST.
|
||||
await fs.promises.mkdir(dir, { recursive: true });
|
||||
|
||||
const content = JSON.stringify(data, null, 2);
|
||||
// Use a randomized tmp path to avoid ENOENT crashes when save() is called concurrently
|
||||
const tmpPath = this.registryPath + '.' + randomUUID() + '.tmp';
|
||||
await fs.promises.writeFile(tmpPath, content, 'utf8');
|
||||
await fs.promises.rename(tmpPath, this.registryPath);
|
||||
|
||||
// Exponential backoff for OS-level file locks (EBUSY/EPERM) during rename
|
||||
const maxRetries = 5;
|
||||
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
||||
try {
|
||||
await fs.promises.rename(tmpPath, this.registryPath);
|
||||
savedSuccessfully = true;
|
||||
break; // Success, exit the retry loop
|
||||
} catch (error: unknown) {
|
||||
const code = isNodeError(error) ? error.code : '';
|
||||
const isRetryable = code === 'EBUSY' || code === 'EPERM';
|
||||
|
||||
if (!isRetryable || attempt === maxRetries - 1) {
|
||||
throw error; // Throw immediately on fatal error or final attempt
|
||||
}
|
||||
|
||||
const delayMs = Math.pow(2, attempt) * 50;
|
||||
debugLogger.debug(
|
||||
`Rename failed with ${code}, retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})...`,
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
debugLogger.error(
|
||||
`Failed to save project registry to ${this.registryPath}:`,
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
} finally {
|
||||
// Clean up the temporary file if it was left behind (e.g. if writeFile or rename failed)
|
||||
if (!savedSuccessfully) {
|
||||
try {
|
||||
await fs.promises.unlink(tmpPath);
|
||||
} catch {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,9 +159,18 @@ export class ProjectRegistry {
|
||||
if (!fs.existsSync(dir)) {
|
||||
await fs.promises.mkdir(dir, { recursive: true });
|
||||
}
|
||||
// Ensure the registry file exists so proper-lockfile can lock it
|
||||
// Ensure the registry file exists so proper-lockfile can lock it.
|
||||
// If it doesn't exist, we try to create it. If someone else creates it
|
||||
// between our check and our write, we just continue.
|
||||
if (!fs.existsSync(this.registryPath)) {
|
||||
await this.save({ projects: {} });
|
||||
try {
|
||||
await this.save({ projects: {} });
|
||||
} catch (e: unknown) {
|
||||
if (!fs.existsSync(this.registryPath)) {
|
||||
throw e; // File still doesn't exist and save failed, this is a real error.
|
||||
}
|
||||
// Someone else created it while we were trying to save. Continue to locking.
|
||||
}
|
||||
}
|
||||
|
||||
// Use proper-lockfile to prevent racy updates
|
||||
@@ -157,7 +212,13 @@ export class ProjectRegistry {
|
||||
await this.save(currentData);
|
||||
return shortId;
|
||||
} finally {
|
||||
await release();
|
||||
try {
|
||||
await release();
|
||||
} catch (e) {
|
||||
// Prevent proper-lockfile errors (e.g. if the lock dir was externally deleted)
|
||||
// from masking the original error thrown inside the try block.
|
||||
debugLogger.error('Failed to release project registry lock:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,20 +232,19 @@ export class ProjectRegistry {
|
||||
|
||||
for (const baseDir of this.baseDirs) {
|
||||
const markerPath = path.join(baseDir, slug, PROJECT_ROOT_FILE);
|
||||
if (fs.existsSync(markerPath)) {
|
||||
try {
|
||||
const owner = (await fs.promises.readFile(markerPath, 'utf8')).trim();
|
||||
if (this.normalizePath(owner) !== this.normalizePath(projectPath)) {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
debugLogger.debug(
|
||||
`Failed to read ownership marker ${markerPath}:`,
|
||||
e,
|
||||
);
|
||||
// If we can't read it, assume it's not ours or corrupted.
|
||||
try {
|
||||
const owner = (await fs.promises.readFile(markerPath, 'utf8')).trim();
|
||||
if (this.normalizePath(owner) !== this.normalizePath(projectPath)) {
|
||||
return false;
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
if (isNodeError(e) && e.code === 'ENOENT') {
|
||||
// Marker doesn't exist, this is fine, we just won't fail verification
|
||||
continue;
|
||||
}
|
||||
debugLogger.debug(`Failed to read ownership marker ${markerPath}:`, e);
|
||||
// If we can't read it for other reasons (perms, corrupted), assume not ours.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -276,10 +336,22 @@ export class ProjectRegistry {
|
||||
try {
|
||||
await this.ensureOwnershipMarkers(candidate, projectPath);
|
||||
return candidate;
|
||||
} catch {
|
||||
// Someone might have claimed it between our check and our write.
|
||||
// Try next candidate.
|
||||
continue;
|
||||
} catch (error: unknown) {
|
||||
// Only retry if it was a collision (someone else took the slug)
|
||||
// or a race condition during marker creation.
|
||||
const code = isNodeError(error) ? error.code : '';
|
||||
const isCollision =
|
||||
code === 'EEXIST' ||
|
||||
(error instanceof Error &&
|
||||
error.message.includes('already owned by'));
|
||||
|
||||
if (isCollision) {
|
||||
debugLogger.debug(`Slug collision for ${candidate}, trying next...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fatal error (Permission denied, Disk full, etc.)
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,13 +373,28 @@ export class ProjectRegistry {
|
||||
continue;
|
||||
}
|
||||
// Collision!
|
||||
throw new Error(`Slug ${slug} is already owned by ${owner}`);
|
||||
const error = Object.assign(
|
||||
new Error(`Slug ${slug} is already owned by ${owner}`),
|
||||
{ code: 'EEXIST' },
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
// Use flag: 'wx' to ensure atomic creation
|
||||
await fs.promises.writeFile(markerPath, normalizedProject, {
|
||||
encoding: 'utf8',
|
||||
flag: 'wx',
|
||||
});
|
||||
try {
|
||||
await fs.promises.writeFile(markerPath, normalizedProject, {
|
||||
encoding: 'utf8',
|
||||
flag: 'wx',
|
||||
});
|
||||
} catch (e: unknown) {
|
||||
if (isNodeError(e) && e.code === 'EEXIST') {
|
||||
// Re-verify ownership in case we just lost a race
|
||||
const owner = (await fs.promises.readFile(markerPath, 'utf8')).trim();
|
||||
if (this.normalizePath(owner) === normalizedProject) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,13 @@ export function getCoreSystemPrompt(
|
||||
config: Config,
|
||||
userMemory?: string | HierarchicalMemory,
|
||||
interactiveOverride?: boolean,
|
||||
topicUpdateNarrationOverride?: boolean,
|
||||
): string {
|
||||
return new PromptProvider().getCoreSystemPrompt(
|
||||
config,
|
||||
userMemory,
|
||||
interactiveOverride,
|
||||
topicUpdateNarrationOverride,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -277,6 +277,56 @@ describe('PromptProvider', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('topicUpdateNarrationOverride', () => {
|
||||
let provider: PromptProvider;
|
||||
|
||||
beforeEach(() => {
|
||||
provider = new PromptProvider();
|
||||
mockConfig.topicState.reset();
|
||||
(mockConfig.getToolRegistry as ReturnType<typeof vi.fn>).mockReturnValue({
|
||||
getAllToolNames: vi.fn().mockReturnValue([UPDATE_TOPIC_TOOL_NAME]),
|
||||
});
|
||||
(mockConfig.getAgentRegistry as ReturnType<typeof vi.fn>).mockReturnValue(
|
||||
{
|
||||
getAllDefinitions: vi.fn().mockReturnValue([]),
|
||||
getDefinition: vi.fn().mockReturnValue(undefined),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should disable topic update narration when override is false, even if config is true', () => {
|
||||
vi.mocked(mockConfig.isTopicUpdateNarrationEnabled).mockReturnValue(true);
|
||||
|
||||
const prompt = provider.getCoreSystemPrompt(
|
||||
mockConfig as unknown as Config,
|
||||
/*userMemory=*/ undefined,
|
||||
/*interactiveOverride=*/ undefined,
|
||||
/*topicUpdateNarrationOverride=*/ false,
|
||||
);
|
||||
|
||||
expect(prompt).not.toContain(
|
||||
`As you work, the user follows along by reading topic updates that you publish with ${UPDATE_TOPIC_TOOL_NAME}.`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should enable topic update narration when override is true, even if config is false', () => {
|
||||
vi.mocked(mockConfig.isTopicUpdateNarrationEnabled).mockReturnValue(
|
||||
false,
|
||||
);
|
||||
|
||||
const prompt = provider.getCoreSystemPrompt(
|
||||
mockConfig as unknown as Config,
|
||||
/*userMemory=*/ undefined,
|
||||
/*interactiveOverride=*/ undefined,
|
||||
/*topicUpdateNarrationOverride=*/ true,
|
||||
);
|
||||
|
||||
expect(prompt).toContain(
|
||||
`As you work, the user follows along by reading topic updates that you publish with ${UPDATE_TOPIC_TOOL_NAME}.`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Topic & Update Narration', () => {
|
||||
beforeEach(() => {
|
||||
mockConfig.topicState.reset();
|
||||
|
||||
@@ -44,6 +44,7 @@ export class PromptProvider {
|
||||
context: AgentLoopContext,
|
||||
userMemory?: string | HierarchicalMemory,
|
||||
interactiveOverride?: boolean,
|
||||
topicUpdateNarrationOverride?: boolean,
|
||||
): string {
|
||||
const systemMdResolution = resolvePathFromEnv(
|
||||
process.env['GEMINI_SYSTEM_MD'],
|
||||
@@ -57,6 +58,10 @@ export class PromptProvider {
|
||||
const isYoloMode = approvalMode === ApprovalMode.YOLO;
|
||||
const skills = context.config.getSkillManager().getSkills();
|
||||
const toolNames = context.toolRegistry.getAllToolNames();
|
||||
const isTopicUpdateNarrationEnabled =
|
||||
topicUpdateNarrationOverride ??
|
||||
context.config.isTopicUpdateNarrationEnabled();
|
||||
|
||||
const enabledToolNames = new Set(toolNames);
|
||||
|
||||
const approvedPlanPath = context.config.getApprovedPlanPath();
|
||||
@@ -139,7 +144,7 @@ export class PromptProvider {
|
||||
hasSkills: skills.length > 0,
|
||||
hasHierarchicalMemory,
|
||||
contextFilenames,
|
||||
topicUpdateNarration: context.config.isTopicUpdateNarrationEnabled(),
|
||||
topicUpdateNarration: isTopicUpdateNarrationEnabled,
|
||||
})),
|
||||
subAgents: this.withSection(
|
||||
'agentContexts',
|
||||
@@ -184,8 +189,7 @@ export class PromptProvider {
|
||||
? { path: approvedPlanPath }
|
||||
: undefined,
|
||||
taskTracker: trackerDir,
|
||||
topicUpdateNarration:
|
||||
context.config.isTopicUpdateNarrationEnabled(),
|
||||
topicUpdateNarration: isTopicUpdateNarrationEnabled,
|
||||
};
|
||||
},
|
||||
!isPlanMode,
|
||||
@@ -207,8 +211,7 @@ export class PromptProvider {
|
||||
enableShellEfficiency:
|
||||
context.config.getEnableShellOutputEfficiency(),
|
||||
interactiveShellEnabled: context.config.isInteractiveShellEnabled(),
|
||||
topicUpdateNarration:
|
||||
context.config.isTopicUpdateNarrationEnabled(),
|
||||
topicUpdateNarration: isTopicUpdateNarrationEnabled,
|
||||
memoryManagerEnabled: context.config.isMemoryManagerEnabled(),
|
||||
}),
|
||||
),
|
||||
@@ -251,7 +254,7 @@ export class PromptProvider {
|
||||
let sanitizedPrompt = finalPrompt.replace(/\n{3,}/g, '\n\n');
|
||||
|
||||
// Context Reinjection (Active Topic)
|
||||
if (context.config.isTopicUpdateNarrationEnabled()) {
|
||||
if (isTopicUpdateNarrationEnabled) {
|
||||
const activeTopic = context.config.topicState.getTopic();
|
||||
if (activeTopic) {
|
||||
const sanitizedTopic = activeTopic
|
||||
|
||||
@@ -209,6 +209,13 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"topicUpdateNarration": {
|
||||
"title": "Topic & Update Narration",
|
||||
"description": "Enable the Topic & Update communication model for reduced chattiness and structured progress reporting.",
|
||||
"markdownDescription": "Enable the Topic & Update communication model for reduced chattiness and structured progress reporting.\n\n- Category: `General`\n- Requires restart: `no`\n- Default: `true`",
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -2947,6 +2954,13 @@
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"autoMemory": {
|
||||
"title": "Auto Memory",
|
||||
"description": "Automatically extract reusable skills from past sessions in the background. Review results with /memory inbox.",
|
||||
"markdownDescription": "Automatically extract reusable skills from past sessions in the background. Review results with /memory inbox.\n\n- Category: `Experimental`\n- Requires restart: `yes`\n- Default: `false`",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"generalistProfile": {
|
||||
"title": "Use the generalist profile to manage agent contexts.",
|
||||
"description": "Suitable for general coding and software development tasks.",
|
||||
@@ -2963,8 +2977,8 @@
|
||||
},
|
||||
"topicUpdateNarration": {
|
||||
"title": "Topic & Update Narration",
|
||||
"description": "Enable the experimental Topic & Update communication model for reduced chattiness and structured progress reporting.",
|
||||
"markdownDescription": "Enable the experimental Topic & Update communication model for reduced chattiness and structured progress reporting.\n\n- Category: `Experimental`\n- Requires restart: `no`\n- Default: `false`",
|
||||
"description": "Deprecated: Use general.topicUpdateNarration instead.",
|
||||
"markdownDescription": "Deprecated: Use general.topicUpdateNarration instead.\n\n- Category: `Experimental`\n- Requires restart: `no`\n- Default: `false`",
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user