From ca184a386e57adb382f359cecd7e04438e309c6d Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 6 Mar 2026 20:28:23 +0000 Subject: [PATCH] continuous session --- evals/continuous_session.eval.ts | 49 ++++++ packages/cli/src/config/config.ts | 1 + packages/cli/src/config/settingsSchema.ts | 9 ++ .../continuousSession.test.tsx | 51 ++++++ packages/cli/src/test-utils/AppRig.tsx | 3 +- .../fixtures/continuous_session.responses | 5 + packages/cli/src/ui/hooks/useGeminiStream.ts | 18 ++- .../core/src/agents/local-executor.test.ts | 2 +- packages/core/src/agents/local-executor.ts | 2 +- packages/core/src/config/config.ts | 38 +++++ .../core/__snapshots__/prompts.test.ts.snap | 90 +++++++++++ packages/core/src/core/auth-types.ts | 14 ++ packages/core/src/core/client.test.ts | 71 ++++++++- packages/core/src/core/client.ts | 39 ++++- packages/core/src/core/compression-status.ts | 31 ++++ packages/core/src/core/geminiChat.ts | 55 ++++++- packages/core/src/core/turn.ts | 27 +--- packages/core/src/index.ts | 2 + packages/core/src/prompts/snippets.ts | 13 ++ packages/core/src/scheduler/tool-executor.ts | 2 + packages/core/src/scheduler/types.ts | 14 +- .../services/chatCompressionService.test.ts | 2 +- .../src/services/chatCompressionService.ts | 5 +- .../services/continuityCompressionService.ts | 55 +++++++ packages/core/src/tools/checkpoint-state.ts | 105 ++++++++++++ packages/core/src/tools/compress.ts | 150 ++++++++++++++++++ .../tools/definitions/base-declarations.ts | 8 + .../core/src/tools/definitions/coreTools.ts | 18 +++ .../model-family-sets/default-legacy.ts | 35 ++++ .../definitions/model-family-sets/gemini-3.ts | 35 ++++ packages/core/src/tools/definitions/types.ts | 2 + packages/core/src/tools/tool-names.ts | 12 ++ packages/core/src/tools/tools.ts | 13 +- packages/core/src/types/continuity.ts | 8 + packages/core/src/utils/getFolderStructure.ts | 16 +- 35 files changed, 947 insertions(+), 53 deletions(-) create mode 100644 evals/continuous_session.eval.ts create mode 100644 packages/cli/src/integration-tests/continuousSession.test.tsx create mode 100644 packages/cli/src/test-utils/fixtures/continuous_session.responses create mode 100644 packages/core/src/core/auth-types.ts create mode 100644 packages/core/src/core/compression-status.ts create mode 100644 packages/core/src/services/continuityCompressionService.ts create mode 100644 packages/core/src/tools/checkpoint-state.ts create mode 100644 packages/core/src/tools/compress.ts create mode 100644 packages/core/src/types/continuity.ts diff --git a/evals/continuous_session.eval.ts b/evals/continuous_session.eval.ts new file mode 100644 index 0000000000..205aa1e00f --- /dev/null +++ b/evals/continuous_session.eval.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { describe, expect } from 'vitest'; +import { appEvalTest } from './app-test-helper.js'; + +describe('Continuous Session Behavioral Evals', () => { + appEvalTest('ALWAYS_PASSES', { + name: 'Continuous Session: Model preserves technical state across manual compression', + configOverrides: { + excludeTools: ['run_shell_command', 'write_file'], + modelSteering: true, + }, + files: { + 'src/offender.ts': `// Line 1 +// Line 2 +const x = process.env.SECRET_KEY; // Line 3 +// Line 4`, + }, + prompt: + 'Audit src/ for process.env usage. When you find one, checkpoint your state with the line number and then manually compress the context to clear your history before giving me the final report.', + setup: async (rig) => { + // Pause on our new tools to observe the workflow + rig.setBreakpoint(['checkpoint_state', 'compress']); + }, + assert: async (rig) => { + // 1. Wait for Checkpoint + await rig.waitForPendingConfirmation('checkpoint_state', 45000); + await rig.resolveAwaitedTool(); + + // 2. Wait for Compression + await rig.waitForPendingConfirmation('compress', 45000); + await rig.resolveAwaitedTool(); + + // 3. Final Verification + // The model should report the finding even though the initial read_file + // is gone from history due to compression. + await rig.waitForOutput(/process\.env\.SECRET_KEY/i, 60000); + //await rig.waitForOutput(/Line 3/i, 60000); + await rig.waitForIdle(30000); + + const output = rig.getStaticOutput(); + expect(output).toContain('SECRET_KEY'); + }, + }); +}); diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index a1ce5b7d1c..10bb18b249 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -777,6 +777,7 @@ export async function loadCliConfig( skillsSupport: settings.skills?.enabled ?? true, disabledSkills: settings.skills?.disabled, experimentalJitContext: settings.experimental?.jitContext, + continuousSession: settings.experimental?.continuousSession, modelSteering: settings.experimental?.modelSteering, toolOutputMasking: settings.experimental?.toolOutputMasking, noBrowser: !!process.env['NO_BROWSER'], diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index fbc50e8b39..287071e2bf 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -1846,6 +1846,15 @@ const SETTINGS_SCHEMA = { 'Enable model steering (user hints) to guide the model during tool execution.', showInDialog: true, }, + continuousSession: { + type: 'boolean', + label: 'Continuous Session', + category: 'Experimental', + requiresRestart: false, + default: false, + description: 'Enables tool and prompts for a continuous session effect.', + showInDialog: true, + }, directWebFetch: { type: 'boolean', label: 'Direct Web Fetch', diff --git a/packages/cli/src/integration-tests/continuousSession.test.tsx b/packages/cli/src/integration-tests/continuousSession.test.tsx new file mode 100644 index 0000000000..1f231cd26d --- /dev/null +++ b/packages/cli/src/integration-tests/continuousSession.test.tsx @@ -0,0 +1,51 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { describe, it, afterEach } from 'vitest'; +import { AppRig } from '../test-utils/AppRig.js'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { PolicyDecision } from '@google/gemini-cli-core'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('Continuous Session Integration', () => { + let rig: AppRig | undefined; + + afterEach(async () => { + await rig?.unmount(); + }); + + it('should handle checkpoint_state and manual compress tools correctly', async () => { + const fakeResponsesPath = path.join( + __dirname, + '../test-utils/fixtures/continuous_session.responses', + ); + rig = new AppRig({ + fakeResponsesPath, + }); + await rig.initialize(); + rig.render(); + await rig.waitForIdle(); + + // Set policies to AUTO so it proceeds without asking user + rig.setToolPolicy('checkpoint_state', PolicyDecision.ALLOW); + rig.setToolPolicy('compress', PolicyDecision.ALLOW); + + // Start the quest + await rig.type('Start the mission'); + await rig.pressEnter(); + + // 1. Wait for CheckpointState tool call + await rig.waitForOutput('CheckpointState'); + + // 2. Wait for Compress tool call + await rig.waitForOutput('Compress'); + + // 3. Wait for final model response after compression + await rig.waitForOutput('Compression successful.'); + }); +}); diff --git a/packages/cli/src/test-utils/AppRig.tsx b/packages/cli/src/test-utils/AppRig.tsx index 3ff65c4067..f2aab488ab 100644 --- a/packages/cli/src/test-utils/AppRig.tsx +++ b/packages/cli/src/test-utils/AppRig.tsx @@ -680,9 +680,10 @@ export class AppRig { await this.waitUntil( () => { const frame = this.lastFrame; - return typeof pattern === 'string' + const matched = typeof pattern === 'string' ? frame.includes(pattern) : pattern.test(frame); + return matched; }, { timeout, diff --git a/packages/cli/src/test-utils/fixtures/continuous_session.responses b/packages/cli/src/test-utils/fixtures/continuous_session.responses new file mode 100644 index 0000000000..b1de5df98c --- /dev/null +++ b/packages/cli/src/test-utils/fixtures/continuous_session.responses @@ -0,0 +1,5 @@ +{"method":"generateContentStream","response":[{"candidates":[{"content":{"role":"model","parts":[{"text":"I will now checkpoint our progress."},{"functionCall":{"name":"checkpoint_state","args":{"summary":"GOAL: Implementation of session continuity.\nPROGRESS: Tools implemented.\nCONSTRAINT: Use high-fidelity summary."}}}]},"finishReason":"STOP"}]}]} +{"method":"generateContentStream","response":[{"candidates":[{"content":{"role":"model","parts":[{"text":"Checkpoint created. Now I will trigger compression to clear the context."},{"functionCall":{"name":"compress","args":{"force":true}}}]},"finishReason":"STOP"}]}]} +{"method":"generateContent","response":{"candidates":[{"content":{"role":"model","parts":[{"text":"\nImplement session continuity\nUse high-fidelity summary\nTools implemented: checkpoint_state, compress\n1. [DONE] Implement tools\n2. [IN PROGRESS] Verify continuity\n\n"}]}}],"finishReason":"STOP"}} +{"method":"generateContent","response":{"candidates":[{"content":{"role":"model","parts":[{"text":"The is accurate and preserves all critical details."}]},"finishReason":"STOP"}]}} +{"method":"generateContentStream","response":[{"candidates":[{"content":{"role":"model","parts":[{"text":"Compression successful. I have clear context and I remember our mission."}]},"finishReason":"STOP"}]}]} diff --git a/packages/cli/src/ui/hooks/useGeminiStream.ts b/packages/cli/src/ui/hooks/useGeminiStream.ts index 630566090b..99f195b85a 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.ts +++ b/packages/cli/src/ui/hooks/useGeminiStream.ts @@ -1278,10 +1278,22 @@ export const useGeminiStream = ( case ServerGeminiEventType.ChatCompressed: handleChatCompressionEvent(event.value, userMessageTimestamp); break; - case ServerGeminiEventType.ToolCallConfirmation: - case ServerGeminiEventType.ToolCallResponse: - // do nothing + case ServerGeminiEventType.ToolCallResponse: { + const response = event.value; + if (response.resultDisplay) { + addItem( + { + type: MessageType.INFO, + text: typeof response.resultDisplay === 'string' + ? response.resultDisplay + : 'Tool execution completed.', + }, + userMessageTimestamp, + ); + } break; + } + case ServerGeminiEventType.ToolCallConfirmation: case ServerGeminiEventType.MaxSessionTurns: handleMaxSessionTurnsEvent(); break; diff --git a/packages/core/src/agents/local-executor.test.ts b/packages/core/src/agents/local-executor.test.ts index 50eb30da76..e4867b1aa3 100644 --- a/packages/core/src/agents/local-executor.test.ts +++ b/packages/core/src/agents/local-executor.test.ts @@ -63,7 +63,7 @@ import { } from './types.js'; import type { AnyDeclarativeTool, AnyToolInvocation } from '../tools/tools.js'; import type { ToolCallRequestInfo } from '../scheduler/types.js'; -import { CompressionStatus } from '../core/turn.js'; +import { CompressionStatus } from '../core/compression-status.js'; import { ChatCompressionService } from '../services/chatCompressionService.js'; import type { ModelConfigKey, diff --git a/packages/core/src/agents/local-executor.ts b/packages/core/src/agents/local-executor.ts index 7bbecdac7c..1b7ea08250 100644 --- a/packages/core/src/agents/local-executor.ts +++ b/packages/core/src/agents/local-executor.ts @@ -17,7 +17,7 @@ import { } from '@google/genai'; import { ToolRegistry } from '../tools/tool-registry.js'; import { DiscoveredMCPTool } from '../tools/mcp-tool.js'; -import { CompressionStatus } from '../core/turn.js'; +import { CompressionStatus } from '../core/compression-status.js'; import { type ToolCallRequestInfo } from '../scheduler/types.js'; import { ChatCompressionService } from '../services/chatCompressionService.js'; import { getDirectoryContextString } from '../utils/environmentContext.js'; diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index e4c0fef6eb..17a0ce25ca 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -33,6 +33,8 @@ import { WebFetchTool } from '../tools/web-fetch.js'; import { MemoryTool, setGeminiMdFilename } from '../tools/memoryTool.js'; import { WebSearchTool } from '../tools/web-search.js'; import { AskUserTool } from '../tools/ask-user.js'; +import { CheckpointStateTool } from '../tools/checkpoint-state.js'; +import { CompressTool } from '../tools/compress.js'; import { ExitPlanModeTool } from '../tools/exit-plan-mode.js'; import { EnterPlanModeTool } from '../tools/enter-plan-mode.js'; import { GeminiClient } from '../core/client.js'; @@ -104,6 +106,8 @@ import { } from '../services/modelConfigService.js'; import { DEFAULT_MODEL_CONFIGS } from './defaultModelConfigs.js'; import { ContextManager } from '../services/contextManager.js'; +import { ChatCompressionService } from '../services/chatCompressionService.js'; + import { TrackerService } from '../services/trackerService.js'; import type { GenerateContentParameters } from '@google/genai'; @@ -155,6 +159,7 @@ import { CheckerRunner } from '../safety/checker-runner.js'; import { ContextBuilder } from '../safety/context-builder.js'; import { CheckerRegistry } from '../safety/registry.js'; import { ConsecaSafetyChecker } from '../safety/conseca/conseca.js'; +import { ContinuityCompressionService } from '../services/continuityCompressionService.js' export interface AccessibilitySettings { /** @deprecated Use ui.loadingPhrases instead. */ @@ -507,6 +512,7 @@ export interface ConfigParameters { customIgnoreFilePaths?: string[]; }; checkpointing?: boolean; + continuousSession?: boolean; proxy?: string; cwd: string; fileDiscoveryService?: FileDiscoveryService; @@ -663,6 +669,7 @@ export class Config implements McpContext { private fileDiscoveryService: FileDiscoveryService | null = null; private gitService: GitService | undefined = undefined; private readonly checkpointing: boolean; + private readonly continuousSession: boolean; private readonly proxy: string | undefined; private readonly cwd: string; private readonly bugCommand: BugCommandSettings | undefined; @@ -798,6 +805,8 @@ export class Config implements McpContext { private readonly planModeRoutingEnabled: boolean; private readonly modelSteering: boolean; private contextManager?: ContextManager; + private chatCompressionService?: ChatCompressionService; + private continuityCompressionService?: ContinuityCompressionService; private terminalBackground: string | undefined = undefined; private remoteAdminSettings: AdminControlsSettings | undefined; private latestApiRequest: GenerateContentParameters | undefined; @@ -874,6 +883,7 @@ export class Config implements McpContext { customIgnoreFilePaths: params.fileFiltering?.customIgnoreFilePaths ?? [], }; this.checkpointing = params.checkpointing ?? false; + this.continuousSession = params.continuousSession ?? false; this.proxy = params.proxy; this.cwd = params.cwd ?? process.cwd(); this.fileDiscoveryService = params.fileDiscoveryService ?? null; @@ -1198,6 +1208,8 @@ export class Config implements McpContext { await this.contextManager.refresh(); } + this.chatCompressionService = new ChatCompressionService(); + await this.geminiClient.initialize(); this.initialized = true; } @@ -1897,6 +1909,22 @@ export class Config implements McpContext { return this.contextManager; } + getChatCompressionService(): ChatCompressionService { + if (!this.chatCompressionService) { + this.chatCompressionService = new ChatCompressionService(); + } + return this.chatCompressionService; + } + + getContinuityCompressionService(): ContinuityCompressionService { + if (!this.continuityCompressionService) { + this.continuityCompressionService = new ContinuityCompressionService( + this.getBaseLlmClient(), + ); + } + return this.continuityCompressionService; + } + isJitContextEnabled(): boolean { return this.experimentalJitContext; } @@ -2196,6 +2224,10 @@ export class Config implements McpContext { return []; } + getContinuousSessionEnabled(): boolean { + return this.continuousSession; + } + getCheckpointingEnabled(): boolean { return this.checkpointing; } @@ -2843,6 +2875,12 @@ export class Config implements McpContext { maybeRegister(AskUserTool, () => registry.registerTool(new AskUserTool(this.messageBus)), ); + maybeRegister(CheckpointStateTool, () => + registry.registerTool(new CheckpointStateTool(this, this.messageBus)), + ); + maybeRegister(CompressTool, () => + registry.registerTool(new CompressTool(this, this.messageBus)), + ); if (this.getUseWriteTodos()) { maybeRegister(WriteTodosTool, () => registry.registerTool(new WriteTodosTool(this.messageBus)), diff --git a/packages/core/src/core/__snapshots__/prompts.test.ts.snap b/packages/core/src/core/__snapshots__/prompts.test.ts.snap index 699503c23f..e4fcb3f9f7 100644 --- a/packages/core/src/core/__snapshots__/prompts.test.ts.snap +++ b/packages/core/src/core/__snapshots__/prompts.test.ts.snap @@ -39,6 +39,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -207,6 +212,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -494,6 +504,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -662,6 +677,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -830,6 +850,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -952,6 +977,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -1547,6 +1577,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -1711,6 +1746,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -1866,6 +1906,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -2021,6 +2066,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -2172,6 +2222,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -2323,6 +2378,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -2466,6 +2526,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -2616,6 +2681,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -3008,6 +3078,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -3159,6 +3234,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -3422,6 +3502,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -3573,6 +3658,11 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the \`checkpoint_state\` tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the \`compress\` tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in \`GEMINI.md\` files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. diff --git a/packages/core/src/core/auth-types.ts b/packages/core/src/core/auth-types.ts new file mode 100644 index 0000000000..b988c87f2d --- /dev/null +++ b/packages/core/src/core/auth-types.ts @@ -0,0 +1,14 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export enum AuthType { + LOGIN_WITH_GOOGLE = 'oauth-personal', + USE_GEMINI = 'gemini-api-key', + USE_VERTEX_AI = 'vertex-ai', + LEGACY_CLOUD_SHELL = 'cloud-shell', + COMPUTE_ADC = 'compute-default-credentials', + GATEWAY = 'gateway', +} diff --git a/packages/core/src/core/client.test.ts b/packages/core/src/core/client.test.ts index 059b72437f..939fe08b6a 100644 --- a/packages/core/src/core/client.test.ts +++ b/packages/core/src/core/client.test.ts @@ -24,12 +24,14 @@ import { import { GeminiChat } from './geminiChat.js'; import type { Config } from '../config/config.js'; import { - CompressionStatus, GeminiEventType, Turn, - type ChatCompressionInfo, type ServerGeminiStreamEvent, } from './turn.js'; +import { + CompressionStatus, + type ChatCompressionInfo, +} from './compression-status.js'; import { getCoreSystemPrompt } from './prompts.js'; import { DEFAULT_GEMINI_MODEL_AUTO } from '../config/models.js'; import { FileDiscoveryService } from '../services/fileDiscoveryService.js'; @@ -51,6 +53,7 @@ import * as policyCatalog from '../availability/policyCatalog.js'; import { LlmRole, LoopType } from '../telemetry/types.js'; import { partToString } from '../utils/partUtils.js'; import { coreEvents } from '../utils/events.js'; +import { COMPRESS_TOOL_NAME } from '../tools/tool-names.js'; // Mock fs module to prevent actual file system operations during tests const mockFileSystem = new Map(); @@ -244,6 +247,13 @@ describe('Gemini Client (client.ts)', () => { getSkipNextSpeakerCheck: vi.fn().mockReturnValue(false), getShowModelInfoInChat: vi.fn().mockReturnValue(false), getContinueOnFailedApiCall: vi.fn(), + getChatCompressionService: vi + .fn() + .mockReturnValue(new ChatCompressionService()), + getContinuityCompressionService: vi.fn().mockResolvedValue({ + generateSnapshot: vi.fn().mockResolvedValue('Mock Snapshot'), + }), + getContinuousSessionEnabled: vi.fn().mockReturnValue(false), getProjectRoot: vi.fn().mockReturnValue('/test/project/root'), getIncludeDirectoryTree: vi.fn().mockReturnValue(true), storage: { @@ -251,6 +261,9 @@ describe('Gemini Client (client.ts)', () => { }, getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator), getBaseLlmClient: vi.fn().mockReturnValue({ + generateContent: vi.fn().mockResolvedValue({ + candidates: [{ content: { parts: [{ text: '{"key": "value"}' }] } }], + }), generateJson: vi.fn().mockResolvedValue({ next_speaker: 'user', reasoning: 'test', @@ -390,11 +403,15 @@ describe('Gemini Client (client.ts)', () => { })); client['chat'] = { - getHistory: mockGetHistory, + getHistory: mockGetHistory.mockReturnValue([]), addHistory: vi.fn(), setHistory: vi.fn(), setTools: vi.fn(), getLastPromptTokenCount: vi.fn(), + getChatRecordingService: vi.fn().mockReturnValue({ + getConversation: vi.fn().mockReturnValue(null), + getConversationFilePath: vi.fn().mockReturnValue(null), + }), } as unknown as GeminiChat; }); @@ -3216,6 +3233,54 @@ ${JSON.stringify( ); }); }); + + it('detects a manual compression request from the agent', async () => { + // Arrange + mockTurnRunFn.mockReturnValue( + (async function* () { + yield { type: 'content', value: 'Hello' }; + })(), + ); + + vi.spyOn(ChatCompressionService.prototype, 'compress').mockResolvedValue({ + newHistory: [{ role: 'user', parts: [{ text: 'Summary' }] }], + info: { + originalTokenCount: 1000, + newTokenCount: 500, + compressionStatus: CompressionStatus.COMPRESSED, + }, + }); + + const compressResponse = [ + { + functionResponse: { + name: COMPRESS_TOOL_NAME, + response: { output: 'Compression requested.' }, + }, + }, + ]; + + // Act + const stream = client.sendMessageStream( + compressResponse, + new AbortController().signal, + 'prompt-id-1', + ); + + await fromAsync(stream); + + // Assert + // Verify that ChatCompressionService.compress was called with force=true + expect(ChatCompressionService.prototype.compress).toHaveBeenCalledWith( + expect.anything(), + 'prompt-id-1', + false, // force + 'test-model', + expect.anything(), + false, // hasFailedCompressionAttempt + ); + + }); }); describe('generateContent', () => { diff --git a/packages/core/src/core/client.ts b/packages/core/src/core/client.ts index 14e2f42bc3..5d66259ed4 100644 --- a/packages/core/src/core/client.ts +++ b/packages/core/src/core/client.ts @@ -18,12 +18,14 @@ import { getInitialChatHistory, } from '../utils/environmentContext.js'; import { - CompressionStatus, Turn, GeminiEventType, type ServerGeminiStreamEvent, - type ChatCompressionInfo, } from './turn.js'; +import { + CompressionStatus, + type ChatCompressionInfo, +} from './compression-status.js'; import type { Config } from '../config/config.js'; import { getCoreSystemPrompt } from './prompts.js'; import { checkNextSpeaker } from '../utils/nextSpeakerChecker.js'; @@ -42,7 +44,8 @@ import type { } from '../services/chatRecordingService.js'; import type { ContentGenerator } from './contentGenerator.js'; import { LoopDetectionService } from '../services/loopDetectionService.js'; -import { ChatCompressionService } from '../services/chatCompressionService.js'; +import type { ChatCompressionService } from '../services/chatCompressionService.js'; +import type { ContinuityCompressionService } from '../services/continuityCompressionService.js'; import { ideContextStore } from '../ide/ideContext.js'; import { logContentRetryFailure, @@ -92,7 +95,6 @@ export class GeminiClient { private sessionTurnCount = 0; private readonly loopDetector: LoopDetectionService; - private readonly compressionService: ChatCompressionService; private readonly toolOutputMaskingService: ToolOutputMaskingService; private lastPromptId: string; private currentSequenceModel: string | null = null; @@ -107,13 +109,20 @@ export class GeminiClient { constructor(private readonly config: Config) { this.loopDetector = new LoopDetectionService(config); - this.compressionService = new ChatCompressionService(); this.toolOutputMaskingService = new ToolOutputMaskingService(); this.lastPromptId = this.config.getSessionId(); coreEvents.on(CoreEvent.ModelChanged, this.handleModelChanged); } + get compressionService(): ChatCompressionService { + return this.config.getChatCompressionService(); + } + + get continuityCompressionService(): ContinuityCompressionService { + return this.config.getContinuityCompressionService(); + } + private handleModelChanged = () => { this.currentSequenceModel = null; }; @@ -735,6 +744,22 @@ export class GeminiClient { if (event.type === GeminiEventType.Error) { isError = true; } + + if (event.type === GeminiEventType.ToolCallResponse) { + const toolResponse = event.value; + if (toolResponse.newHistory) { + this.getChat().replaceHistory(toolResponse.newHistory); + // Yield the event so UI knows compression happened + yield { + type: GeminiEventType.ChatCompressed, + value: toolResponse.compressionInfo ?? { + originalTokenCount: 0, + newTokenCount: 0, + compressionStatus: CompressionStatus.COMPRESSED, + }, + }; + } + } } if (loopDetectedAbort) { @@ -835,7 +860,8 @@ export class GeminiClient { } } } - return turn; + const turnResult = turn; + return turnResult; } async *sendMessageStream( @@ -1116,6 +1142,7 @@ export class GeminiClient { this.hasFailedCompressionAttempt, ); + if ( info.compressionStatus === CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT diff --git a/packages/core/src/core/compression-status.ts b/packages/core/src/core/compression-status.ts new file mode 100644 index 0000000000..fc66463b33 --- /dev/null +++ b/packages/core/src/core/compression-status.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export enum CompressionStatus { + /** The compression was not necessary and no action was taken */ + NOOP, + + /** The compression was successful */ + COMPRESSED, + + /** The compression failed due to the compression inflating the token count */ + COMPRESSION_FAILED_INFLATED_TOKEN_COUNT, + + /** The compression failed due to an error counting tokens */ + COMPRESSION_FAILED_TOKEN_COUNT_ERROR, + + /** The compression failed because the summary was empty */ + COMPRESSION_FAILED_EMPTY_SUMMARY, + + /** The compression was skipped due to previous failure, but content was truncated to budget */ + CONTENT_TRUNCATED, +} + +export interface ChatCompressionInfo { + originalTokenCount: number; + newTokenCount: number; + compressionStatus: CompressionStatus; +} diff --git a/packages/core/src/core/geminiChat.ts b/packages/core/src/core/geminiChat.ts index ae5f46db37..d506babaf3 100644 --- a/packages/core/src/core/geminiChat.ts +++ b/packages/core/src/core/geminiChat.ts @@ -261,6 +261,16 @@ export class GeminiChat { ); } + private continuityAnchor?: string; + + setContinuityAnchor(anchor: string) { + this.continuityAnchor = anchor; + } + + getContinuityAnchor(): string | undefined { + return this.continuityAnchor; + } + setSystemInstruction(sysInstr: string) { this.systemInstruction = sysInstr; } @@ -687,17 +697,48 @@ export class GeminiChat { * @return History contents alternating between user and model for the entire * chat session. */ + /** + * Replaces the entire conversation history. Use with caution. + * This is primarily used for context compression and history restoration. + */ + replaceHistory(newHistory: Content[]): void { + validateHistory(newHistory); + this.history = [...newHistory]; + this.lastPromptTokenCount = estimateTokenCountSync( + this.history.flatMap((c) => c.parts || []), + ); + } + getHistory(curated: boolean = false): Content[] { const history = curated ? extractCuratedHistory(this.history) : this.history; - // Return a shallow copy of the array to prevent callers from mutating - // the internal history array (push/pop/splice). Content objects are - // shared references — callers MUST NOT mutate them in place. - // This replaces a prior structuredClone() which deep-copied the entire - // conversation on every call, causing O(n) memory pressure per turn - // that compounded into OOM crashes in long-running sessions. - return [...history]; + + if (!this.continuityAnchor) { + return [...history]; + } + + const anchorText = `\n${this.continuityAnchor}\n`; + const baseHistory = [...history]; + + if (baseHistory.length > 0 && baseHistory[0].role === 'user') { + // Merge into the first user message to preserve role alternation + const firstMessage = { ...baseHistory[0] }; + // Ensure we don't accidentally mutate the original parts array + firstMessage.parts = [ + { text: anchorText }, + ...(firstMessage.parts || []), + ]; + baseHistory[0] = firstMessage; + } else { + // Prepend as a new user message + baseHistory.unshift({ + role: 'user', + parts: [{ text: anchorText }], + }); + } + + return baseHistory; } /** diff --git a/packages/core/src/core/turn.ts b/packages/core/src/core/turn.ts index 9c0e536c48..659660b349 100644 --- a/packages/core/src/core/turn.ts +++ b/packages/core/src/core/turn.ts @@ -34,6 +34,7 @@ import { type ToolCallRequestInfo, type ToolCallResponseInfo, } from '../scheduler/types.js'; +import { type ChatCompressionInfo } from './compression-status.js'; export interface ServerTool { name: string; @@ -164,32 +165,6 @@ export type ServerGeminiErrorEvent = { value: GeminiErrorEventValue; }; -export enum CompressionStatus { - /** The compression was successful */ - COMPRESSED = 1, - - /** The compression failed due to the compression inflating the token count */ - COMPRESSION_FAILED_INFLATED_TOKEN_COUNT, - - /** The compression failed due to an error counting tokens */ - COMPRESSION_FAILED_TOKEN_COUNT_ERROR, - - /** The compression failed because the summary was empty */ - COMPRESSION_FAILED_EMPTY_SUMMARY, - - /** The compression was not necessary and no action was taken */ - NOOP, - - /** The compression was skipped due to previous failure, but content was truncated to budget */ - CONTENT_TRUNCATED, -} - -export interface ChatCompressionInfo { - originalTokenCount: number; - newTokenCount: number; - compressionStatus: CompressionStatus; -} - export type ServerGeminiChatCompressedEvent = { type: GeminiEventType.ChatCompressed; value: ChatCompressionInfo | null; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c4a9965e41..526692684a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -39,6 +39,7 @@ export * from './core/logger.js'; export * from './core/prompts.js'; export * from './core/tokenLimits.js'; export * from './core/turn.js'; +export * from './core/compression-status.js'; export * from './core/geminiRequest.js'; export * from './core/coreToolScheduler.js'; export * from './scheduler/scheduler.js'; @@ -118,6 +119,7 @@ export * from './services/fileDiscoveryService.js'; export * from './services/gitService.js'; export * from './services/FolderTrustDiscoveryService.js'; export * from './services/chatRecordingService.js'; +export * from './services/chatCompressionService.js'; export * from './services/fileSystemService.js'; export * from './services/sessionSummaryUtils.js'; export * from './services/contextManager.js'; diff --git a/packages/core/src/prompts/snippets.ts b/packages/core/src/prompts/snippets.ts index 2e889dac6d..91c3c989fe 100644 --- a/packages/core/src/prompts/snippets.ts +++ b/packages/core/src/prompts/snippets.ts @@ -17,6 +17,8 @@ import { SHELL_TOOL_NAME, WRITE_FILE_TOOL_NAME, WRITE_TODOS_TOOL_NAME, + CHECKPOINT_STATE_TOOL_NAME, + COMPRESS_TOOL_NAME, GREP_PARAM_TOTAL_MAX_MATCHES, GREP_PARAM_INCLUDE_PATTERN, GREP_PARAM_EXCLUDE_PATTERN, @@ -206,6 +208,15 @@ Use the following guidelines to optimize your search and read patterns. - **Navigating:** read the minimum required to not require additional turns spent reading the file. +## Intentional Continuity & Context Management +- **Semantic Checkpointing:** Periodically use the ${formatToolName( + CHECKPOINT_STATE_TOOL_NAME, + )} tool to "park" complex threads with high-fidelity summaries. This ensures that your technical rationale, discovered constraints, and progress are preserved with maximum signal during compression. +- **Agentic Compression:** If you feel the context window is becoming cluttered or you have just finished a significant sub-task, use the ${formatToolName( + COMPRESS_TOOL_NAME, + )} tool to manually trigger a compression event. This clears the history while persisting your latest checkpoints. +- **Context Awareness:** You are responsible for maintaining your context health. As history grows, technical noise (like long file reads or repetitive searches) creates **entropy** that degrades your reasoning precision and self-awareness. Signs of degradation include repeating redundant steps, circular reasoning, or experiencing subtle tool parameter errors. You MUST proactively use the "Save-Point" strategy: \`checkpoint_state\` to lock in critical progress and \`compress\` to restore clarity. It is always better to compress "too soon" than to struggle through a foggy context. Do not wait for an overflow; clear the deck as soon as a major sub-task is finished or noise begins to accumulate. + ## Engineering Standards - **Contextual Precedence:** Instructions found in ${formattedFilenames} files are foundational mandates. They take absolute precedence over the general workflows and tool defaults described in this system prompt. - **Conventions & Style:** Rigorously adhere to existing workspace conventions, architectural patterns, and style (naming, formatting, typing, commenting). During the research phase, analyze surrounding files, tests, and configuration to ensure your changes are seamless, idiomatic, and consistent with the local context. Never compromise idiomatic quality or completeness (e.g., proper declarations, type safety, documentation) to minimize tool calls; all supporting changes required by local conventions are part of a surgical update. @@ -718,6 +729,8 @@ When the conversation history grows too large, you will be invoked to distill th First, you will think through the entire history in a private . Review the user's overall goal, the agent's actions, tool outputs, file modifications, and any unresolved questions. Identify every piece of information for future actions. +**AGENT CHECKPOINTS:** The history may contain one or more \`\` blocks. These were explicitly written by the agent as high-fidelity hand-offs. You MUST prioritize the information in these checkpoints and ensure their technical details, rationale, and intent are preserved in your final snapshot. + After your reasoning is complete, generate the final XML object. Be incredibly dense with information. Omit any irrelevant conversational filler. The structure MUST be as follows: diff --git a/packages/core/src/scheduler/tool-executor.ts b/packages/core/src/scheduler/tool-executor.ts index 8269f1fc41..032b107c80 100644 --- a/packages/core/src/scheduler/tool-executor.ts +++ b/packages/core/src/scheduler/tool-executor.ts @@ -370,6 +370,8 @@ export class ToolExecutor { outputFile, contentLength: typeof content === 'string' ? content.length : undefined, data: toolResult.data, + newHistory: toolResult.newHistory, + compressionInfo: toolResult.compressionInfo, }; const startTime = 'startTime' in call ? call.startTime : undefined; diff --git a/packages/core/src/scheduler/types.ts b/packages/core/src/scheduler/types.ts index 9fedd48f41..eadbc0580e 100644 --- a/packages/core/src/scheduler/types.ts +++ b/packages/core/src/scheduler/types.ts @@ -4,7 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { Part } from '@google/genai'; +import type { Part, Content } from '@google/genai'; +import { type ChatCompressionInfo } from '../core/compression-status.js'; import type { AnyDeclarativeTool, AnyToolInvocation, @@ -61,6 +62,17 @@ export interface ToolCallResponseInfo { * Optional data payload for passing structured information back to the caller. */ data?: Record; + + /** + * Optional new conversation history to replace the current one. + * Used for context compression and history restoration. + */ + newHistory?: Content[]; + + /** + * Optional compression metrics if the tool performed context compression. + */ + compressionInfo?: ChatCompressionInfo; } /** Request to execute another tool immediately after a completed one. */ diff --git a/packages/core/src/services/chatCompressionService.test.ts b/packages/core/src/services/chatCompressionService.test.ts index 2911119a25..61f2046dbd 100644 --- a/packages/core/src/services/chatCompressionService.test.ts +++ b/packages/core/src/services/chatCompressionService.test.ts @@ -11,7 +11,7 @@ import { modelStringToModelConfigAlias, } from './chatCompressionService.js'; import type { Content, GenerateContentResponse } from '@google/genai'; -import { CompressionStatus } from '../core/turn.js'; +import { CompressionStatus } from '../core/compression-status.js'; import type { BaseLlmClient } from '../core/baseLlmClient.js'; import type { GeminiChat } from '../core/geminiChat.js'; import type { Config } from '../config/config.js'; diff --git a/packages/core/src/services/chatCompressionService.ts b/packages/core/src/services/chatCompressionService.ts index 5303a1a82a..d49f480e6d 100644 --- a/packages/core/src/services/chatCompressionService.ts +++ b/packages/core/src/services/chatCompressionService.ts @@ -7,7 +7,10 @@ import type { Content } from '@google/genai'; import type { Config } from '../config/config.js'; import type { GeminiChat } from '../core/geminiChat.js'; -import { type ChatCompressionInfo, CompressionStatus } from '../core/turn.js'; +import { + type ChatCompressionInfo, + CompressionStatus, +} from '../core/compression-status.js'; import { tokenLimit } from '../core/tokenLimits.js'; import { getCompressionPrompt } from '../core/prompts.js'; import { getResponseText } from '../utils/partUtils.js'; diff --git a/packages/core/src/services/continuityCompressionService.ts b/packages/core/src/services/continuityCompressionService.ts new file mode 100644 index 0000000000..d280e44248 --- /dev/null +++ b/packages/core/src/services/continuityCompressionService.ts @@ -0,0 +1,55 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type { Content } from '@google/genai'; +import type { BaseLlmClient } from '../core/baseLlmClient.js'; +import { LlmRole } from '../telemetry/llmRole.js'; +import { getResponseText } from '../utils/partUtils.js'; + +/** + * ContinuityCompressionService is a specialized summarizer for the "Mythical Continuous Session". + * + * Unlike the standard compression service which attempts to summarize the entire conversation, + * this service prioritizes specific `` blocks (Continuity Anchors) + * provided by the agent. It ensures that critical intent and progress are preserved + * with high fidelity while discarding "noisy" technical details (like large file reads) + * that are no longer needed. + */ +export class ContinuityCompressionService { + constructor( + private readonly llmClient: BaseLlmClient, + ) {} + + /** + * Generates a high-fidelity snapshot of the conversation state. + * It specifically looks for the most recent checkpoint and uses it as the anchor. + */ + async generateSnapshot(history: Content[], model: string, promptId: string): Promise { + const prompt = ` +You are a context compression engine for a long-running software engineering session. +Your goal is to produce a dense, high-fidelity that will replace the current history. + +CRITICAL INSTRUCTIONS: +1. Identify the most recent in the history. This is the ground truth of the agent's progress. +2. Incorporate any critical new information found *after* the last checkpoint. +3. Remove all redundant tool outputs, file contents, and intermediate research steps. +4. Output ONLY the new block. + +Current History to compress: +${JSON.stringify(history)} + `.trim(); + + const result = await this.llmClient.generateContent({ + modelConfigKey: { model }, + contents: [{ role: 'user', parts: [{ text: prompt }] }], + promptId, + role: LlmRole.UTILITY_COMPRESSOR, + abortSignal: new AbortController().signal + }); + + return getResponseText(result)?.trim() ?? ''; + } +} diff --git a/packages/core/src/tools/checkpoint-state.ts b/packages/core/src/tools/checkpoint-state.ts new file mode 100644 index 0000000000..b56ed974a7 --- /dev/null +++ b/packages/core/src/tools/checkpoint-state.ts @@ -0,0 +1,105 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + BaseDeclarativeTool, + BaseToolInvocation, + Kind, + type ToolInvocation, + type ToolResult, +} from './tools.js'; +import { + CHECKPOINT_STATE_TOOL_NAME, + CHECKPOINT_STATE_PARAM_SUMMARY, +} from './tool-names.js'; +import { CHECKPOINT_STATE_DEFINITION } from './definitions/coreTools.js'; +import type { MessageBus } from '../confirmation-bus/message-bus.js'; +import type { Config } from '../config/config.js'; +import type { GeminiChat } from '../core/geminiChat.js'; + +interface CheckpointStateParams { + [CHECKPOINT_STATE_PARAM_SUMMARY]: string; +} + +class CheckpointStateInvocation extends BaseToolInvocation< + CheckpointStateParams, + ToolResult +> { + constructor( + params: CheckpointStateParams, + messageBus: MessageBus, + toolName: string, + toolDisplayName: string, + private readonly chat: GeminiChat, + ) { + super(params, messageBus, toolName, toolDisplayName); + } + + override getDescription(): string { + return 'Parks the current state of the conversation with a high-fidelity summary.'; + } + + override async execute(): Promise { + const summary = this.params[CHECKPOINT_STATE_PARAM_SUMMARY]; + const previousSummary = this.chat.getContinuityAnchor(); + + // Atomically update the chat session's continuity anchor. + // This anchor will be used as a "hard hand-off" during the next compression event. + this.chat.setContinuityAnchor(summary); + + const llmContent = `\n${summary}\n\n\n${ + previousSummary + ? 'Previous checkpoint summary replaced. Use the `previous_summary` in the result data for reconciliation if needed.' + : 'First checkpoint created. No previous summary found.' + }`; + + return { + llmContent, + returnDisplay: '', + data: { + previous_summary: previousSummary || null, + }, + }; + } +} + +/** + * A tool that allows the agent to "park" a thread with a high-fidelity summary. + */ +export class CheckpointStateTool extends BaseDeclarativeTool< + CheckpointStateParams, + ToolResult +> { + static readonly Name = CHECKPOINT_STATE_TOOL_NAME; + + constructor( + private readonly config: Config, + messageBus: MessageBus, + ) { + super( + CHECKPOINT_STATE_TOOL_NAME, + 'CheckpointState', + CHECKPOINT_STATE_DEFINITION.base.description ?? '', + Kind.Think, + CHECKPOINT_STATE_DEFINITION.base.parametersJsonSchema, + messageBus, + ); + } + + override createInvocation( + params: CheckpointStateParams, + ): ToolInvocation { + const chat = this.config.getGeminiClient().getChat(); + + return new CheckpointStateInvocation( + params, + this.messageBus, + this.name, + this.displayName, + chat, + ); + } +} diff --git a/packages/core/src/tools/compress.ts b/packages/core/src/tools/compress.ts new file mode 100644 index 0000000000..2b9db7c73e --- /dev/null +++ b/packages/core/src/tools/compress.ts @@ -0,0 +1,150 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + BaseDeclarativeTool, + BaseToolInvocation, + Kind, + type ToolInvocation, + type ToolResult, +} from './tools.js'; +import { ToolErrorType } from './tool-error.js'; +import { COMPRESS_TOOL_NAME, COMPRESS_PARAM_FORCE } from './tool-names.js'; +import { COMPRESS_DEFINITION } from './definitions/coreTools.js'; +import type { MessageBus } from '../confirmation-bus/message-bus.js'; +import type { Config } from '../config/config.js'; +import type { GeminiChat } from '../core/geminiChat.js'; +import { CompressionStatus } from '../core/compression-status.js'; + +interface CompressParams { + [COMPRESS_PARAM_FORCE]?: boolean; +} + +class CompressInvocation extends BaseToolInvocation< + CompressParams, + ToolResult +> { + constructor( + params: CompressParams, + messageBus: MessageBus, + toolName: string, + toolDisplayName: string, + private readonly config: Config, + private readonly chat: GeminiChat, + private readonly promptId: string, + ) { + super(params, messageBus, toolName, toolDisplayName); + } + + override getDescription(): string { + return 'Manually triggers a context compression event.'; + } + + override async execute(): Promise { + const force = this.params[COMPRESS_PARAM_FORCE] !== false; + + if (this.config.getContinuousSessionEnabled()) { + const continuityService = await this.config.getContinuityCompressionService(); + const snapshot = await continuityService.generateSnapshot( + this.chat.getHistory(), + this.config.getModel(), + this.promptId, + ); + + const newHistory = [ + { + role: 'user', + parts: [{ text: snapshot }], + }, + { + role: 'model', + parts: [{ text: 'Got it. Thanks for the additional context!' }], + }, + ]; + + this.chat.setContinuityAnchor(''); + + return { + llmContent: `Compression completed. Status: 1`, + returnDisplay: '', + newHistory, + compressionInfo: { + originalTokenCount: 0, + newTokenCount: 0, + compressionStatus: CompressionStatus.COMPRESSED, + }, + }; + } + + const { newHistory, info } = await this.config.getChatCompressionService().compress( + this.chat, + this.promptId, + force, + this.config.getModel(), + this.config, + false, // Manual compression + ); + + if (newHistory) { + return { + llmContent: `Compression completed. Status: ${info.compressionStatus}`, + returnDisplay: '', + newHistory, + compressionInfo: info, + }; + } + + return { + llmContent: `Compression failed. Status: ${info.compressionStatus}`, + returnDisplay: `Context compression failed: ${info.compressionStatus}`, + error: { + message: `Context compression failed: ${info.compressionStatus}`, + type: ToolErrorType.EXECUTION_FAILED, + }, + }; + } +} + +/** + * A tool that allows the agent to manually trigger a context compression event. + */ +export class CompressTool extends BaseDeclarativeTool< + CompressParams, + ToolResult +> { + static readonly Name = COMPRESS_TOOL_NAME; + + constructor( + private readonly config: Config, + messageBus: MessageBus, + ) { + super( + COMPRESS_TOOL_NAME, + 'Compress', + COMPRESS_DEFINITION.base.description ?? '', + Kind.Think, + COMPRESS_DEFINITION.base.parametersJsonSchema, + messageBus, + ); + } + + override createInvocation( + params: CompressParams, + ): ToolInvocation { + const chat = this.config.getGeminiClient().getChat(); + const promptId = this.config.getSessionId(); // Best guess for current promptId in this context + + return new CompressInvocation( + params, + this.messageBus, + this.name, + this.displayName, + this.config, + chat, + promptId, + ); + } +} diff --git a/packages/core/src/tools/definitions/base-declarations.ts b/packages/core/src/tools/definitions/base-declarations.ts index b39dc42286..c1d7940f90 100644 --- a/packages/core/src/tools/definitions/base-declarations.ts +++ b/packages/core/src/tools/definitions/base-declarations.ts @@ -122,3 +122,11 @@ export const EXIT_PLAN_PARAM_PLAN_PATH = 'plan_path'; // -- enter_plan_mode -- export const ENTER_PLAN_MODE_TOOL_NAME = 'enter_plan_mode'; export const PLAN_MODE_PARAM_REASON = 'reason'; + +// -- checkpoint_state -- +export const CHECKPOINT_STATE_TOOL_NAME = 'checkpoint_state'; +export const CHECKPOINT_STATE_PARAM_SUMMARY = 'summary'; + +// -- compress -- +export const COMPRESS_TOOL_NAME = 'compress'; +export const COMPRESS_PARAM_FORCE = 'force'; diff --git a/packages/core/src/tools/definitions/coreTools.ts b/packages/core/src/tools/definitions/coreTools.ts index b5121ca5d2..43566134e7 100644 --- a/packages/core/src/tools/definitions/coreTools.ts +++ b/packages/core/src/tools/definitions/coreTools.ts @@ -91,6 +91,10 @@ export { PLAN_MODE_PARAM_REASON, EXIT_PLAN_PARAM_PLAN_PATH, SKILL_PARAM_NAME, + CHECKPOINT_STATE_TOOL_NAME, + CHECKPOINT_STATE_PARAM_SUMMARY, + COMPRESS_TOOL_NAME, + COMPRESS_PARAM_FORCE, } from './base-declarations.js'; // Re-export sets for compatibility @@ -221,6 +225,20 @@ export const ENTER_PLAN_MODE_DEFINITION: ToolDefinition = { overrides: (modelId) => getToolSet(modelId).enter_plan_mode, }; +export const CHECKPOINT_STATE_DEFINITION: ToolDefinition = { + get base() { + return DEFAULT_LEGACY_SET.checkpoint_state; + }, + overrides: (modelId) => getToolSet(modelId).checkpoint_state, +}; + +export const COMPRESS_DEFINITION: ToolDefinition = { + get base() { + return DEFAULT_LEGACY_SET.compress; + }, + overrides: (modelId) => getToolSet(modelId).compress, +}; + // ============================================================================ // DYNAMIC TOOL DEFINITIONS (LEGACY EXPORTS) // ============================================================================ diff --git a/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts b/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts index 3309fcc5ba..9a0c9b88ca 100644 --- a/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts +++ b/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts @@ -73,6 +73,10 @@ import { ASK_USER_OPTION_PARAM_LABEL, ASK_USER_OPTION_PARAM_DESCRIPTION, PLAN_MODE_PARAM_REASON, + CHECKPOINT_STATE_TOOL_NAME, + CHECKPOINT_STATE_PARAM_SUMMARY, + COMPRESS_TOOL_NAME, + COMPRESS_PARAM_FORCE, } from '../base-declarations.js'; import { getShellDeclaration, @@ -734,4 +738,35 @@ The agent did not use the todo list because this task could be completed by a ti exit_plan_mode: (plansDir) => getExitPlanModeDeclaration(plansDir), activate_skill: (skillNames) => getActivateSkillDeclaration(skillNames), + + checkpoint_state: { + name: CHECKPOINT_STATE_TOOL_NAME, + description: `Parks the current state of the conversation with a high-fidelity summary provided by you. This summary will be preserved through future compression events, ensuring that critical intent, progress, and technical details are not lost. Use this tool when you have completed a significant sub-task or want to "save" the current context before it gets potentially compressed. Returns the previous summary (if any) to allow for reconciliation.`, + parametersJsonSchema: { + type: 'object', + properties: { + [CHECKPOINT_STATE_PARAM_SUMMARY]: { + type: 'string', + description: + 'A detailed, structured summary of the current session state, including goals, progress, constraints, and key discoveries.', + }, + }, + required: [CHECKPOINT_STATE_PARAM_SUMMARY], + }, + }, + + compress: { + name: COMPRESS_TOOL_NAME, + description: `Manually triggers a context compression event. Use this after calling '${CHECKPOINT_STATE_TOOL_NAME}' to ensure your summary is persisted and the context window is cleared of unnecessary details.`, + parametersJsonSchema: { + type: 'object', + properties: { + [COMPRESS_PARAM_FORCE]: { + type: 'boolean', + description: 'Whether to force compression even if under the threshold.', + default: true, + }, + }, + }, + }, }; diff --git a/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts b/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts index 2c0375baa3..0c3232acc3 100644 --- a/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts +++ b/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts @@ -73,6 +73,10 @@ import { ASK_USER_OPTION_PARAM_LABEL, ASK_USER_OPTION_PARAM_DESCRIPTION, PLAN_MODE_PARAM_REASON, + CHECKPOINT_STATE_TOOL_NAME, + CHECKPOINT_STATE_PARAM_SUMMARY, + COMPRESS_TOOL_NAME, + COMPRESS_PARAM_FORCE, } from '../base-declarations.js'; import { getShellDeclaration, @@ -709,4 +713,35 @@ The agent did not use the todo list because this task could be completed by a ti exit_plan_mode: (plansDir) => getExitPlanModeDeclaration(plansDir), activate_skill: (skillNames) => getActivateSkillDeclaration(skillNames), + + checkpoint_state: { + name: CHECKPOINT_STATE_TOOL_NAME, + description: `Parks the current state of the conversation with a high-fidelity summary provided by you. This summary will be preserved through future compression events, ensuring that critical intent, progress, and technical details are not lost. Use this tool when you have completed a significant sub-task or want to "save" the current context before it gets potentially compressed. Returns the previous summary (if any) to allow for reconciliation.`, + parametersJsonSchema: { + type: 'object', + properties: { + [CHECKPOINT_STATE_PARAM_SUMMARY]: { + type: 'string', + description: + 'A detailed, structured summary of the current session state, including goals, progress, constraints, and key discoveries.', + }, + }, + required: [CHECKPOINT_STATE_PARAM_SUMMARY], + }, + }, + + compress: { + name: COMPRESS_TOOL_NAME, + description: `Manually triggers a context compression event. Use this after calling '${CHECKPOINT_STATE_TOOL_NAME}' to ensure your summary is persisted and the context window is cleared of unnecessary details.`, + parametersJsonSchema: { + type: 'object', + properties: { + [COMPRESS_PARAM_FORCE]: { + type: 'boolean', + description: 'Whether to force compression even if under the threshold.', + default: true, + }, + }, + }, + }, }; diff --git a/packages/core/src/tools/definitions/types.ts b/packages/core/src/tools/definitions/types.ts index a9bd3d85d7..cf4b98e553 100644 --- a/packages/core/src/tools/definitions/types.ts +++ b/packages/core/src/tools/definitions/types.ts @@ -49,4 +49,6 @@ export interface CoreToolSet { enter_plan_mode: FunctionDeclaration; exit_plan_mode: (plansDir: string) => FunctionDeclaration; activate_skill: (skillNames: string[]) => FunctionDeclaration; + checkpoint_state: FunctionDeclaration; + compress: FunctionDeclaration; } diff --git a/packages/core/src/tools/tool-names.ts b/packages/core/src/tools/tool-names.ts index c539532fd1..516827006d 100644 --- a/packages/core/src/tools/tool-names.ts +++ b/packages/core/src/tools/tool-names.ts @@ -75,6 +75,10 @@ import { PLAN_MODE_PARAM_REASON, EXIT_PLAN_PARAM_PLAN_PATH, SKILL_PARAM_NAME, + CHECKPOINT_STATE_TOOL_NAME, + CHECKPOINT_STATE_PARAM_SUMMARY, + COMPRESS_TOOL_NAME, + COMPRESS_PARAM_FORCE, } from './definitions/coreTools.js'; export { @@ -148,6 +152,10 @@ export { PLAN_MODE_PARAM_REASON, EXIT_PLAN_PARAM_PLAN_PATH, SKILL_PARAM_NAME, + CHECKPOINT_STATE_TOOL_NAME, + CHECKPOINT_STATE_PARAM_SUMMARY, + COMPRESS_TOOL_NAME, + COMPRESS_PARAM_FORCE, }; export const LS_TOOL_NAME_LEGACY = 'list_directory'; // Just to be safe if anything used the old exported name directly @@ -229,6 +237,8 @@ export const ALL_BUILTIN_TOOL_NAMES = [ GET_INTERNAL_DOCS_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, + CHECKPOINT_STATE_TOOL_NAME, + COMPRESS_TOOL_NAME, ] as const; /** @@ -244,6 +254,8 @@ export const PLAN_MODE_TOOLS = [ WEB_SEARCH_TOOL_NAME, ASK_USER_TOOL_NAME, ACTIVATE_SKILL_TOOL_NAME, + CHECKPOINT_STATE_TOOL_NAME, + COMPRESS_TOOL_NAME, ] as const; /** diff --git a/packages/core/src/tools/tools.ts b/packages/core/src/tools/tools.ts index 0a82cc1510..814151d3f4 100644 --- a/packages/core/src/tools/tools.ts +++ b/packages/core/src/tools/tools.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { FunctionDeclaration, PartListUnion } from '@google/genai'; +import type { Content, FunctionDeclaration, PartListUnion } from '@google/genai'; import { ToolErrorType } from './tool-error.js'; import type { DiffUpdateResult } from '../ide/ide-client.js'; import type { ShellExecutionConfig } from '../services/shellExecutionService.js'; @@ -595,6 +595,17 @@ export interface ToolResult { name: string; args: Record; }; + + /** + * Optional new conversation history to replace the current one. + * Used for context compression and history restoration. + */ + newHistory?: Content[]; + + /** + * Optional compression metrics if the tool performed context compression. + */ + compressionInfo?: import('../core/compression-status.js').ChatCompressionInfo; } /** diff --git a/packages/core/src/types/continuity.ts b/packages/core/src/types/continuity.ts new file mode 100644 index 0000000000..cba4cdc5a3 --- /dev/null +++ b/packages/core/src/types/continuity.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export const CONTINUITY_CHECKPOINT_TAG = 'state_checkpoint'; +export const CONTINUITY_SNAPSHOT_TAG = 'state_snapshot'; diff --git a/packages/core/src/utils/getFolderStructure.ts b/packages/core/src/utils/getFolderStructure.ts index 6e1814cd90..fa6cbdc30c 100644 --- a/packages/core/src/utils/getFolderStructure.ts +++ b/packages/core/src/utils/getFolderStructure.ts @@ -292,6 +292,10 @@ function formatStructure( // --- Main Exported Function --- +// --- Caching --- +const structureCache = new Map(); +const CACHE_TTL_MS = 10000; // 10 seconds + /** * Generates a string representation of a directory's structure, * limiting the number of items displayed. Ignored folders are shown @@ -306,6 +310,14 @@ export async function getFolderStructure( options?: FolderStructureOptions, ): Promise { const resolvedPath = path.resolve(directory); + const cacheKey = `${resolvedPath}:${JSON.stringify(options || {})}`; + const now = Date.now(); + + const cached = structureCache.get(cacheKey); + if (cached && now - cached.timestamp < CACHE_TTL_MS) { + return cached.result; + } + const mergedOptions: MergedFolderStructureOptions = { maxItems: options?.maxItems ?? MAX_ITEMS, ignoredFolders: options?.ignoredFolders ?? DEFAULT_IGNORED_FOLDERS, @@ -347,7 +359,9 @@ export async function getFolderStructure( summary += ` Folders or files indicated with ${TRUNCATION_INDICATOR} contain more items not shown, were ignored, or the display limit (${mergedOptions.maxItems} items) was reached.`; } - return `${summary}\n\n${resolvedPath}${path.sep}\n${structureLines.join('\n')}`; + const finalResult = `${summary}\n\n${resolvedPath}${path.sep}\n${structureLines.join('\n')}`; + structureCache.set(cacheKey, { result: finalResult, timestamp: now }); + return finalResult; } catch (error: unknown) { debugLogger.warn( `Error getting folder structure for ${resolvedPath}:`,