mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-18 18:11:02 -07:00
feat: Add enableSubagents configuration and wire up subagent registration (#9988)
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
import type { AgentDefinition } from './types.js';
|
||||
import { LSTool } from '../tools/ls.js';
|
||||
import { ReadFileTool } from '../tools/read-file.js';
|
||||
import { GlobTool } from '../tools/glob.js';
|
||||
import { GLOB_TOOL_NAME } from '../tools/tool-names.js';
|
||||
import { GrepTool } from '../tools/grep.js';
|
||||
import { DEFAULT_GEMINI_MODEL } from '../config/models.js';
|
||||
|
||||
@@ -104,7 +104,7 @@ ${CODEBASE_REPORT_MARKDOWN}
|
||||
|
||||
toolConfig: {
|
||||
// Grant access only to read-only tools.
|
||||
tools: [LSTool.Name, ReadFileTool.Name, GlobTool.Name, GrepTool.Name],
|
||||
tools: [LSTool.Name, ReadFileTool.Name, GLOB_TOOL_NAME, GrepTool.Name],
|
||||
},
|
||||
|
||||
promptConfig: {
|
||||
|
||||
@@ -370,44 +370,46 @@ export class AgentExecutor {
|
||||
return true;
|
||||
});
|
||||
|
||||
const toolPromises = validatedFunctionCalls.map(async (functionCall) => {
|
||||
const callId = functionCall.id ?? `${functionCall.name}-${Date.now()}`;
|
||||
const args = functionCall.args ?? {};
|
||||
const toolPromises = validatedFunctionCalls.map(
|
||||
async (functionCall, index) => {
|
||||
const callId = functionCall.id ?? `${promptId}-${index}`;
|
||||
const args = functionCall.args ?? {};
|
||||
|
||||
this.emitActivity('TOOL_CALL_START', {
|
||||
name: functionCall.name,
|
||||
args,
|
||||
});
|
||||
|
||||
const requestInfo: ToolCallRequestInfo = {
|
||||
callId,
|
||||
name: functionCall.name as string,
|
||||
args: args as Record<string, unknown>,
|
||||
isClientInitiated: true,
|
||||
prompt_id: promptId,
|
||||
};
|
||||
|
||||
const toolResponse = await executeToolCall(
|
||||
this.runtimeContext,
|
||||
requestInfo,
|
||||
signal,
|
||||
);
|
||||
|
||||
if (toolResponse.error) {
|
||||
this.emitActivity('ERROR', {
|
||||
context: 'tool_call',
|
||||
this.emitActivity('TOOL_CALL_START', {
|
||||
name: functionCall.name,
|
||||
error: toolResponse.error.message,
|
||||
args,
|
||||
});
|
||||
} else {
|
||||
this.emitActivity('TOOL_CALL_END', {
|
||||
name: functionCall.name,
|
||||
output: toolResponse.resultDisplay,
|
||||
});
|
||||
}
|
||||
|
||||
return toolResponse;
|
||||
});
|
||||
const requestInfo: ToolCallRequestInfo = {
|
||||
callId,
|
||||
name: functionCall.name as string,
|
||||
args: args as Record<string, unknown>,
|
||||
isClientInitiated: true,
|
||||
prompt_id: promptId,
|
||||
};
|
||||
|
||||
const toolResponse = await executeToolCall(
|
||||
this.runtimeContext,
|
||||
requestInfo,
|
||||
signal,
|
||||
);
|
||||
|
||||
if (toolResponse.error) {
|
||||
this.emitActivity('ERROR', {
|
||||
context: 'tool_call',
|
||||
name: functionCall.name,
|
||||
error: toolResponse.error.message,
|
||||
});
|
||||
} else {
|
||||
this.emitActivity('TOOL_CALL_END', {
|
||||
name: functionCall.name,
|
||||
output: toolResponse.resultDisplay,
|
||||
});
|
||||
}
|
||||
|
||||
return toolResponse;
|
||||
},
|
||||
);
|
||||
|
||||
const toolResponses = await Promise.all(toolPromises);
|
||||
const toolResponseParts: Part[] = toolResponses
|
||||
|
||||
@@ -16,6 +16,7 @@ import { AgentTerminateMode } from './types.js';
|
||||
import { makeFakeConfig } from '../test-utils/config.js';
|
||||
import { ToolErrorType } from '../tools/tool-error.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
|
||||
vi.mock('./executor.js');
|
||||
|
||||
@@ -52,6 +53,21 @@ describe('SubagentInvocation', () => {
|
||||
MockAgentExecutor.create.mockResolvedValue(mockExecutorInstance);
|
||||
});
|
||||
|
||||
it('should pass the messageBus to the parent constructor', () => {
|
||||
const mockMessageBus = {} as MessageBus;
|
||||
const params = { task: 'Analyze data' };
|
||||
const invocation = new SubagentInvocation(
|
||||
params,
|
||||
testDefinition,
|
||||
mockConfig,
|
||||
mockMessageBus,
|
||||
);
|
||||
|
||||
// Access the protected messageBus property by casting to any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect((invocation as any).messageBus).toBe(mockMessageBus);
|
||||
});
|
||||
|
||||
describe('getDescription', () => {
|
||||
it('should format the description with inputs', () => {
|
||||
const params = { task: 'Analyze data', priority: 5 };
|
||||
|
||||
@@ -14,6 +14,7 @@ import type {
|
||||
AgentInputs,
|
||||
SubagentActivityEvent,
|
||||
} from './types.js';
|
||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
|
||||
const INPUT_PREVIEW_MAX_LENGTH = 50;
|
||||
const DESCRIPTION_MAX_LENGTH = 200;
|
||||
@@ -36,13 +37,15 @@ export class SubagentInvocation extends BaseToolInvocation<
|
||||
* @param params The validated input parameters for the agent.
|
||||
* @param definition The definition object that configures the agent.
|
||||
* @param config The global runtime configuration.
|
||||
* @param messageBus Optional message bus for policy enforcement.
|
||||
*/
|
||||
constructor(
|
||||
params: AgentInputs,
|
||||
private readonly definition: AgentDefinition,
|
||||
private readonly config: Config,
|
||||
messageBus?: MessageBus,
|
||||
) {
|
||||
super(params);
|
||||
super(params, messageBus);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ import { makeFakeConfig } from '../test-utils/config.js';
|
||||
import type { AgentDefinition, AgentInputs } from './types.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import { Kind } from '../tools/tools.js';
|
||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
|
||||
// Mock dependencies to isolate the SubagentToolWrapper class
|
||||
vi.mock('./invocation.js');
|
||||
@@ -119,6 +120,26 @@ describe('SubagentToolWrapper', () => {
|
||||
params,
|
||||
mockDefinition,
|
||||
mockConfig,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass the messageBus to the SubagentInvocation constructor', () => {
|
||||
const mockMessageBus = {} as MessageBus;
|
||||
const wrapper = new SubagentToolWrapper(
|
||||
mockDefinition,
|
||||
mockConfig,
|
||||
mockMessageBus,
|
||||
);
|
||||
const params: AgentInputs = { goal: 'Test the invocation', priority: 1 };
|
||||
|
||||
wrapper.build(params);
|
||||
|
||||
expect(MockedSubagentInvocation).toHaveBeenCalledWith(
|
||||
params,
|
||||
mockDefinition,
|
||||
mockConfig,
|
||||
mockMessageBus,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import type { Config } from '../config/config.js';
|
||||
import type { AgentDefinition, AgentInputs } from './types.js';
|
||||
import { convertInputConfigToJsonSchema } from './schema-utils.js';
|
||||
import { SubagentInvocation } from './invocation.js';
|
||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
|
||||
/**
|
||||
* A tool wrapper that dynamically exposes a subagent as a standard,
|
||||
@@ -31,10 +32,12 @@ export class SubagentToolWrapper extends BaseDeclarativeTool<
|
||||
*
|
||||
* @param definition The `AgentDefinition` of the subagent to wrap.
|
||||
* @param config The runtime configuration, passed down to the subagent.
|
||||
* @param messageBus Optional message bus for policy enforcement.
|
||||
*/
|
||||
constructor(
|
||||
private readonly definition: AgentDefinition,
|
||||
private readonly config: Config,
|
||||
messageBus?: MessageBus,
|
||||
) {
|
||||
// Dynamically generate the JSON schema required for the tool definition.
|
||||
const parameterSchema = convertInputConfigToJsonSchema(
|
||||
@@ -49,6 +52,7 @@ export class SubagentToolWrapper extends BaseDeclarativeTool<
|
||||
parameterSchema,
|
||||
/* isOutputMarkdown */ true,
|
||||
/* canUpdateOutput */ true,
|
||||
messageBus,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,6 +68,11 @@ export class SubagentToolWrapper extends BaseDeclarativeTool<
|
||||
protected createInvocation(
|
||||
params: AgentInputs,
|
||||
): ToolInvocation<AgentInputs, ToolResult> {
|
||||
return new SubagentInvocation(params, this.definition, this.config);
|
||||
return new SubagentInvocation(
|
||||
params,
|
||||
this.definition,
|
||||
this.config,
|
||||
this.messageBus,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user