mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-12 04:17:15 -07:00
feat: enable subagents (#22386)
This commit is contained in:
@@ -1246,7 +1246,7 @@ describe('Server Config (config.ts)', () => {
|
||||
const config = new Config(params);
|
||||
|
||||
const mockAgentDefinition = {
|
||||
name: 'codebase-investigator',
|
||||
name: 'codebase_investigator',
|
||||
description: 'Agent 1',
|
||||
instructions: 'Inst 1',
|
||||
};
|
||||
@@ -1294,7 +1294,7 @@ describe('Server Config (config.ts)', () => {
|
||||
it('should register subagents as tools even when they are not in allowedTools', async () => {
|
||||
const params: ConfigParameters = {
|
||||
...baseParams,
|
||||
allowedTools: ['read_file'], // codebase-investigator is NOT here
|
||||
allowedTools: ['read_file'], // codebase_investigator is NOT here
|
||||
agents: {
|
||||
overrides: {
|
||||
codebase_investigator: { enabled: true },
|
||||
@@ -1304,7 +1304,7 @@ describe('Server Config (config.ts)', () => {
|
||||
const config = new Config(params);
|
||||
|
||||
const mockAgentDefinition = {
|
||||
name: 'codebase-investigator',
|
||||
name: 'codebase_investigator',
|
||||
description: 'Agent 1',
|
||||
instructions: 'Inst 1',
|
||||
};
|
||||
|
||||
@@ -948,7 +948,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
this.model = params.model;
|
||||
this.disableLoopDetection = params.disableLoopDetection ?? false;
|
||||
this._activeModel = params.model;
|
||||
this.enableAgents = params.enableAgents ?? false;
|
||||
this.enableAgents = params.enableAgents ?? true;
|
||||
this.agents = params.agents ?? {};
|
||||
this.disableLLMCorrection = params.disableLLMCorrection ?? true;
|
||||
this.planEnabled = params.plan ?? true;
|
||||
@@ -3147,22 +3147,23 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
*/
|
||||
private registerSubAgentTools(registry: ToolRegistry): void {
|
||||
const agentsOverrides = this.getAgentsSettings().overrides ?? {};
|
||||
if (
|
||||
this.isAgentsEnabled() ||
|
||||
agentsOverrides['codebase_investigator']?.enabled !== false ||
|
||||
agentsOverrides['cli_help']?.enabled !== false
|
||||
) {
|
||||
const definitions = this.agentRegistry.getAllDefinitions();
|
||||
const definitions = this.agentRegistry.getAllDefinitions();
|
||||
|
||||
for (const definition of definitions) {
|
||||
try {
|
||||
const tool = new SubagentTool(definition, this, this.messageBus);
|
||||
registry.registerTool(tool);
|
||||
} catch (e: unknown) {
|
||||
debugLogger.warn(
|
||||
`Failed to register tool for agent ${definition.name}: ${getErrorMessage(e)}`,
|
||||
);
|
||||
for (const definition of definitions) {
|
||||
try {
|
||||
if (
|
||||
!this.isAgentsEnabled() ||
|
||||
agentsOverrides[definition.name]?.enabled === false
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tool = new SubagentTool(definition, this, this.messageBus);
|
||||
registry.registerTool(tool);
|
||||
} catch (e: unknown) {
|
||||
debugLogger.warn(
|
||||
`Failed to register tool for agent ${definition.name}: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ describe('FolderTrustDiscoveryService', () => {
|
||||
await fs.mkdir(path.join(skillsDir, 'test-skill'), { recursive: true });
|
||||
await fs.writeFile(path.join(skillsDir, 'test-skill', 'SKILL.md'), 'body');
|
||||
|
||||
// Mock agents
|
||||
const agentsDir = path.join(geminiDir, 'agents');
|
||||
await fs.mkdir(agentsDir);
|
||||
await fs.writeFile(path.join(agentsDir, 'test-agent.md'), 'body');
|
||||
|
||||
// Mock settings (MCPs, Hooks, and general settings)
|
||||
const settings = {
|
||||
mcpServers: {
|
||||
@@ -62,6 +67,7 @@ describe('FolderTrustDiscoveryService', () => {
|
||||
|
||||
expect(results.commands).toContain('test-cmd');
|
||||
expect(results.skills).toContain('test-skill');
|
||||
expect(results.agents).toContain('test-agent');
|
||||
expect(results.mcps).toContain('test-mcp');
|
||||
expect(results.hooks).toContain('test-hook');
|
||||
expect(results.settings).toContain('general');
|
||||
@@ -79,9 +85,6 @@ describe('FolderTrustDiscoveryService', () => {
|
||||
allowed: ['git'],
|
||||
sandbox: false,
|
||||
},
|
||||
experimental: {
|
||||
enableAgents: true,
|
||||
},
|
||||
security: {
|
||||
folderTrust: {
|
||||
enabled: false,
|
||||
@@ -98,9 +101,6 @@ describe('FolderTrustDiscoveryService', () => {
|
||||
expect(results.securityWarnings).toContain(
|
||||
'This project auto-approves certain tools (tools.allowed).',
|
||||
);
|
||||
expect(results.securityWarnings).toContain(
|
||||
'This project enables autonomous agents (enableAgents).',
|
||||
);
|
||||
expect(results.securityWarnings).toContain(
|
||||
'This project attempts to disable folder trust (security.folderTrust.enabled).',
|
||||
);
|
||||
@@ -158,4 +158,20 @@ describe('FolderTrustDiscoveryService', () => {
|
||||
expect(results.discoveryErrors).toHaveLength(0);
|
||||
expect(results.settings).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should flag security warning for custom agents', async () => {
|
||||
const geminiDir = path.join(tempDir, GEMINI_DIR);
|
||||
await fs.mkdir(geminiDir, { recursive: true });
|
||||
|
||||
const agentsDir = path.join(geminiDir, 'agents');
|
||||
await fs.mkdir(agentsDir);
|
||||
await fs.writeFile(path.join(agentsDir, 'test-agent.md'), 'body');
|
||||
|
||||
const results = await FolderTrustDiscoveryService.discover(tempDir);
|
||||
|
||||
expect(results.agents).toContain('test-agent');
|
||||
expect(results.securityWarnings).toContain(
|
||||
'This project contains custom agents.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface FolderDiscoveryResults {
|
||||
mcps: string[];
|
||||
hooks: string[];
|
||||
skills: string[];
|
||||
agents: string[];
|
||||
settings: string[];
|
||||
securityWarnings: string[];
|
||||
discoveryErrors: string[];
|
||||
@@ -37,6 +38,7 @@ export class FolderTrustDiscoveryService {
|
||||
mcps: [],
|
||||
hooks: [],
|
||||
skills: [],
|
||||
agents: [],
|
||||
settings: [],
|
||||
securityWarnings: [],
|
||||
discoveryErrors: [],
|
||||
@@ -50,6 +52,7 @@ export class FolderTrustDiscoveryService {
|
||||
await Promise.all([
|
||||
this.discoverCommands(geminiDir, results),
|
||||
this.discoverSkills(geminiDir, results),
|
||||
this.discoverAgents(geminiDir, results),
|
||||
this.discoverSettings(geminiDir, results),
|
||||
]);
|
||||
|
||||
@@ -99,6 +102,34 @@ export class FolderTrustDiscoveryService {
|
||||
}
|
||||
}
|
||||
|
||||
private static async discoverAgents(
|
||||
geminiDir: string,
|
||||
results: FolderDiscoveryResults,
|
||||
) {
|
||||
const agentsDir = path.join(geminiDir, 'agents');
|
||||
if (await this.exists(agentsDir)) {
|
||||
try {
|
||||
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (
|
||||
entry.isFile() &&
|
||||
entry.name.endsWith('.md') &&
|
||||
!entry.name.startsWith('_')
|
||||
) {
|
||||
results.agents.push(path.basename(entry.name, '.md'));
|
||||
}
|
||||
}
|
||||
if (results.agents.length > 0) {
|
||||
results.securityWarnings.push('This project contains custom agents.');
|
||||
}
|
||||
} catch (e) {
|
||||
results.discoveryErrors.push(
|
||||
`Failed to discover agents: ${e instanceof Error ? e.message : String(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async discoverSettings(
|
||||
geminiDir: string,
|
||||
results: FolderDiscoveryResults,
|
||||
@@ -119,7 +150,7 @@ export class FolderTrustDiscoveryService {
|
||||
(key) => !['mcpServers', 'hooks', '$schema'].includes(key),
|
||||
);
|
||||
|
||||
results.securityWarnings = this.collectSecurityWarnings(settings);
|
||||
results.securityWarnings.push(...this.collectSecurityWarnings(settings));
|
||||
|
||||
const mcpServers = settings['mcpServers'];
|
||||
if (this.isRecord(mcpServers)) {
|
||||
@@ -159,10 +190,6 @@ export class FolderTrustDiscoveryService {
|
||||
? settings['tools']
|
||||
: undefined;
|
||||
|
||||
const experimental = this.isRecord(settings['experimental'])
|
||||
? settings['experimental']
|
||||
: undefined;
|
||||
|
||||
const security = this.isRecord(settings['security'])
|
||||
? settings['security']
|
||||
: undefined;
|
||||
@@ -179,10 +206,6 @@ export class FolderTrustDiscoveryService {
|
||||
condition: Array.isArray(allowedTools) && allowedTools.length > 0,
|
||||
message: 'This project auto-approves certain tools (tools.allowed).',
|
||||
},
|
||||
{
|
||||
condition: experimental?.['enableAgents'] === true,
|
||||
message: 'This project enables autonomous agents (enableAgents).',
|
||||
},
|
||||
{
|
||||
condition: folderTrust?.['enabled'] === false,
|
||||
message:
|
||||
|
||||
Reference in New Issue
Block a user