Compare commits

...

3 Commits

Author SHA1 Message Date
Aishanee Shah 45df40c513 feat: enable thin harness by default on this branch 2026-03-17 14:34:54 +00:00
Aishanee Shah 7bcb1280ad feat: restrict tools to core only in thin harness mode 2026-03-17 14:33:27 +00:00
Aishanee Shah a39866a7cd feat: implement thin harness mode 2026-03-17 14:07:44 +00:00
4 changed files with 103 additions and 27 deletions
+9
View File
@@ -104,6 +104,11 @@ export class AgentRegistry {
private async loadAgents(): Promise<void> {
this.agents.clear();
this.allDefinitions.clear();
if (this.config.isThinHarness()) {
return;
}
this.loadBuiltInAgents();
if (!this.config.isAgentsEnabled()) {
@@ -240,6 +245,10 @@ export class AgentRegistry {
}
private loadBuiltInAgents(): void {
if (this.config.isThinHarness()) {
return;
}
this.registerLocalAgent(CodebaseInvestigatorAgent(this.config));
this.registerLocalAgent(CliHelpAgent(this.config));
this.registerLocalAgent(GeneralistAgent(this.config));
+70 -22
View File
@@ -633,6 +633,7 @@ export interface ConfigParameters {
tracker?: boolean;
planSettings?: PlanSettings;
modelSteering?: boolean;
thinHarness?: boolean;
onModelChange?: (model: string) => void;
mcpEnabled?: boolean;
extensionsEnabled?: boolean;
@@ -853,6 +854,7 @@ export class Config implements McpContext, AgentLoopContext {
private readonly planEnabled: boolean;
private readonly trackerEnabled: boolean;
private readonly planModeRoutingEnabled: boolean;
private readonly thinHarness: boolean;
private readonly modelSteering: boolean;
private contextManager?: ContextManager;
private terminalBackground: string | undefined = undefined;
@@ -996,6 +998,8 @@ export class Config implements McpContext, AgentLoopContext {
modelConfigServiceConfig ?? DEFAULT_MODEL_CONFIGS,
);
this.thinHarness =
params.thinHarness ?? process.env['GEMINI_THIN_HARNESS'] !== 'false';
this.experimentalJitContext = params.experimentalJitContext ?? true;
this.topicUpdateNarration = params.topicUpdateNarration ?? false;
this.modelSteering = params.modelSteering ?? false;
@@ -1236,30 +1240,35 @@ export class Config implements McpContext, AgentLoopContext {
this._toolRegistry = await this.createToolRegistry();
discoverToolsHandle?.end();
this.mcpClientManager = new McpClientManager(
this.clientVersion,
this._toolRegistry,
this,
this.eventEmitter,
);
// We do not await this promise so that the CLI can start up even if
// MCP servers are slow to connect.
this.mcpInitializationPromise = Promise.allSettled([
this.mcpClientManager.startConfiguredMcpServers(),
this.getExtensionLoader().start(this),
]).then((results) => {
for (const result of results) {
if (result.status === 'rejected') {
debugLogger.error('Error initializing MCP clients:', result.reason);
if (this.thinHarness) {
this.mcpInitializationPromise = Promise.resolve();
} else {
this.mcpClientManager = new McpClientManager(
this.clientVersion,
this._toolRegistry,
this,
this.eventEmitter,
);
// We do not await this promise so that the CLI can start up even if
// MCP servers are slow to connect.
this.mcpInitializationPromise = Promise.allSettled([
this.mcpClientManager.startConfiguredMcpServers(),
this.getExtensionLoader().start(this),
]).then((results) => {
for (const result of results) {
if (result.status === 'rejected') {
debugLogger.error('Error initializing MCP clients:', result.reason);
}
}
}
});
});
}
if (!this.interactive || this.acpMode) {
await this.mcpInitializationPromise;
}
if (this.skillsSupport) {
if (this.skillsSupport && !this.thinHarness) {
this.getSkillManager().setAdminSettings(this.adminSkillsEnabled);
if (this.adminSkillsEnabled) {
await this.getSkillManager().discoverSkills(
@@ -2024,6 +2033,10 @@ export class Config implements McpContext, AgentLoopContext {
}
getUserMemory(): string | HierarchicalMemory {
if (this.thinHarness) {
return { global: '', extension: '', project: '' };
}
if (this.experimentalJitContext && this.contextManager) {
return {
global: this.contextManager.getGlobalMemory(),
@@ -2120,6 +2133,9 @@ export class Config implements McpContext, AgentLoopContext {
}
getGeminiMdFileCount(): number {
if (this.thinHarness) {
return 0;
}
if (this.experimentalJitContext && this.contextManager) {
return this.contextManager.getLoadedPaths().size;
}
@@ -2131,6 +2147,9 @@ export class Config implements McpContext, AgentLoopContext {
}
getGeminiMdFilePaths(): string[] {
if (this.thinHarness) {
return [];
}
if (this.experimentalJitContext && this.contextManager) {
return Array.from(this.contextManager.getLoadedPaths());
}
@@ -2493,7 +2512,7 @@ export class Config implements McpContext, AgentLoopContext {
}
isAgentsEnabled(): boolean {
return this.enableAgents;
return this.enableAgents && !this.thinHarness;
}
isEventDrivenSchedulerEnabled(): boolean {
@@ -2786,14 +2805,14 @@ export class Config implements McpContext, AgentLoopContext {
}
isSkillsSupportEnabled(): boolean {
return this.skillsSupport;
return this.skillsSupport && !this.thinHarness;
}
/**
* Reloads skills by re-discovering them from extensions and local directories.
*/
async reloadSkills(): Promise<void> {
if (!this.skillsSupport) {
if (!this.skillsSupport || this.thinHarness) {
return;
}
@@ -2843,6 +2862,10 @@ export class Config implements McpContext, AgentLoopContext {
}
}
isThinHarness(): boolean {
return this.thinHarness;
}
isInteractive(): boolean {
return this.interactive;
}
@@ -3041,6 +3064,25 @@ export class Config implements McpContext, AgentLoopContext {
);
}
if (this.thinHarness) {
const allowedTools = [
'LSTool',
'ReadFileTool',
'RipGrepTool',
'GrepTool',
'GlobTool',
'EditTool',
'WriteFileTool',
'WebFetchTool',
'ShellTool',
'WebSearchTool',
'AskUserTool',
];
if (!allowedTools.includes(normalizedClassName)) {
isEnabled = false;
}
}
if (isEnabled) {
registerFn();
}
@@ -3144,7 +3186,9 @@ export class Config implements McpContext, AgentLoopContext {
// Register Subagents as Tools
this.registerSubAgentTools(registry);
await registry.discoverAllTools();
if (!this.thinHarness) {
await registry.discoverAllTools();
}
registry.sortTools();
return registry;
}
@@ -3153,6 +3197,10 @@ export class Config implements McpContext, AgentLoopContext {
* Registers SubAgentTools for all available agents.
*/
private registerSubAgentTools(registry: ToolRegistry): void {
if (this.thinHarness) {
return;
}
const agentsOverrides = this.getAgentsSettings().overrides ?? {};
const definitions = this.agentRegistry.getAllDefinitions();
+12 -5
View File
@@ -44,6 +44,11 @@ export class PromptProvider {
userMemory?: string | HierarchicalMemory,
interactiveOverride?: boolean,
): string {
const config = context.config;
if (config.isThinHarness()) {
return 'You are Gemini CLI, a helpful assistant specializing in software engineering tasks.';
}
const systemMdResolution = resolvePathFromEnv(
process.env['GEMINI_SYSTEM_MD'],
);
@@ -216,11 +221,13 @@ export class PromptProvider {
}
// --- Finalization (Shell) ---
const finalPrompt = activeSnippets.renderFinalShell(
basePrompt,
userMemory,
contextFilenames,
);
const finalPrompt = context.config.isThinHarness()
? basePrompt
: activeSnippets.renderFinalShell(
basePrompt,
userMemory,
contextFilenames,
);
// Sanitize erratic newlines from composition
const sanitizedPrompt = finalPrompt.replace(/\n{3,}/g, '\n\n');
@@ -699,6 +699,18 @@ export async function loadServerHierarchicalMemory(
* Returns the result of the call to `loadHierarchicalGeminiMemory`.
*/
export async function refreshServerHierarchicalMemory(config: Config) {
if (config.isThinHarness()) {
const emptyResult: LoadServerHierarchicalMemoryResponse = {
memoryContent: { global: '', extension: '', project: '' },
fileCount: 0,
filePaths: [],
};
config.setUserMemory(emptyResult.memoryContent);
config.setGeminiMdFileCount(0);
config.setGeminiMdFilePaths([]);
return emptyResult;
}
const result = await loadServerHierarchicalMemory(
config.getWorkingDir(),
config.shouldLoadMemoryFromIncludeDirectories()