Merge branch 'main' into M-DEV-1/clean-unsafe-return-suppressions

This commit is contained in:
mahadevan 2026-04-19 02:52:27 +05:30 committed by GitHub
commit 35bd39a31f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 239 additions and 12 deletions

143
docs/cli/auto-memory.md Normal file
View file

@ -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).

View file

@ -169,6 +169,7 @@ they appear in the UI.
| 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` |

View file

@ -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.

View file

@ -1729,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`

View file

@ -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" },
{

View file

@ -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,
};

View file

@ -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.',
};
}

View file

@ -990,6 +990,7 @@ 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:

View file

@ -2213,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.',

View file

@ -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),

View file

@ -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);
});

View file

@ -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.',
});
});

View file

@ -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.',
};
}

View file

@ -3443,6 +3443,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({

View file

@ -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'];
@ -2486,6 +2489,10 @@ export class Config implements McpContext, AgentLoopContext {
return this.experimentalMemoryManager;
}
isAutoMemoryEnabled(): boolean {
return this.experimentalAutoMemory;
}
getExperimentalContextManagementConfig(): string | undefined {
return this.experimentalContextManagementConfig;
}

View file

@ -2954,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.",