mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
feat(core): Improve validation logic for subagents.
This commit is contained in:
@@ -121,19 +121,22 @@ describe('DelegateToAgentTool', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw helpful error when agent_name does not exist', async () => {
|
it('should throw helpful error when agent_name does not exist', () => {
|
||||||
// We allow validation to pass now, checking happens in execute.
|
expect(() =>
|
||||||
const invocation = tool.build({
|
tool.build({
|
||||||
agent_name: 'non_existent_agent',
|
agent_name: 'non_existent_agent',
|
||||||
} as DelegateParams);
|
} as DelegateParams),
|
||||||
|
).toThrow(
|
||||||
await expect(() =>
|
|
||||||
invocation.execute(new AbortController().signal),
|
|
||||||
).rejects.toThrow(
|
|
||||||
"Agent 'non_existent_agent' not found. Available agents are: 'test_agent' (A test agent), 'remote_agent' (A remote agent). Please choose a valid agent_name.",
|
"Agent 'non_existent_agent' not found. Available agents are: 'test_agent' (A test agent), 'remote_agent' (A remote agent). Please choose a valid agent_name.",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw error when agent_name is missing', () => {
|
||||||
|
expect(() => tool.build({} as DelegateParams)).toThrow(
|
||||||
|
"Missing 'agent_name' parameter. Please specify the agent you want to delegate to.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should validate correct arguments', async () => {
|
it('should validate correct arguments', async () => {
|
||||||
const invocation = tool.build({
|
const invocation = tool.build({
|
||||||
agent_name: 'test_agent',
|
agent_name: 'test_agent',
|
||||||
@@ -152,28 +155,24 @@ describe('DelegateToAgentTool', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw helpful error for missing required argument', async () => {
|
it('should throw helpful error for missing required argument', () => {
|
||||||
const invocation = tool.build({
|
expect(() =>
|
||||||
agent_name: 'test_agent',
|
tool.build({
|
||||||
arg2: 123,
|
agent_name: 'test_agent',
|
||||||
} as DelegateParams);
|
arg2: 123,
|
||||||
|
} as DelegateParams),
|
||||||
await expect(() =>
|
).toThrow(
|
||||||
invocation.execute(new AbortController().signal),
|
|
||||||
).rejects.toThrow(
|
|
||||||
`Invalid arguments for agent 'test_agent': params must have required property 'arg1'. Input schema: ${JSON.stringify(mockAgentDef.inputConfig.inputSchema)}.`,
|
`Invalid arguments for agent 'test_agent': params must have required property 'arg1'. Input schema: ${JSON.stringify(mockAgentDef.inputConfig.inputSchema)}.`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw helpful error for invalid argument type', async () => {
|
it('should throw helpful error for invalid argument type', () => {
|
||||||
const invocation = tool.build({
|
expect(() =>
|
||||||
agent_name: 'test_agent',
|
tool.build({
|
||||||
arg1: 123,
|
agent_name: 'test_agent',
|
||||||
} as DelegateParams);
|
arg1: 123,
|
||||||
|
} as DelegateParams),
|
||||||
await expect(() =>
|
).toThrow(
|
||||||
invocation.execute(new AbortController().signal),
|
|
||||||
).rejects.toThrow(
|
|
||||||
`Invalid arguments for agent 'test_agent': params/arg1 must be string. Input schema: ${JSON.stringify(mockAgentDef.inputConfig.inputSchema)}.`,
|
`Invalid arguments for agent 'test_agent': params/arg1 must be string. Input schema: ${JSON.stringify(mockAgentDef.inputConfig.inputSchema)}.`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -121,11 +121,33 @@ export class DelegateToAgentTool extends BaseDeclarativeTool<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
override validateToolParams(_params: DelegateParams): string | null {
|
override validateToolParams(params: DelegateParams): string | null {
|
||||||
// We override the default schema validation because the generic JSON schema validation
|
if (!params.agent_name) {
|
||||||
// produces poor error messages for discriminated unions (anyOf).
|
return "Missing 'agent_name' parameter. Please specify the agent you want to delegate to.";
|
||||||
// Instead, we perform detailed, agent-specific validation in the `execute` method
|
}
|
||||||
// to provide rich error messages that help the LLM self-heal.
|
|
||||||
|
const definition = this.registry.getDefinition(params.agent_name);
|
||||||
|
if (!definition) {
|
||||||
|
const availableAgents = this.registry
|
||||||
|
.getAllDefinitions()
|
||||||
|
.map((def) => `'${def.name}' (${def.description})`)
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
|
return `Agent '${params.agent_name}' not found. Available agents are: ${availableAgents}. Please choose a valid agent_name.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { agent_name: _agent_name, ...agentArgs } = params;
|
||||||
|
|
||||||
|
// Validate specific agent arguments here using SchemaValidator to generate helpful error messages.
|
||||||
|
const validationError = SchemaValidator.validate(
|
||||||
|
definition.inputConfig.inputSchema,
|
||||||
|
agentArgs,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (validationError) {
|
||||||
|
return `Invalid arguments for agent '${definition.name}': ${validationError}. Input schema: ${JSON.stringify(definition.inputConfig.inputSchema)}.`;
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,8 +195,8 @@ class DelegateInvocation extends BaseToolInvocation<
|
|||||||
override async shouldConfirmExecute(
|
override async shouldConfirmExecute(
|
||||||
abortSignal: AbortSignal,
|
abortSignal: AbortSignal,
|
||||||
): Promise<ToolCallConfirmationDetails | false> {
|
): Promise<ToolCallConfirmationDetails | false> {
|
||||||
const definition = this.registry.getDefinition(this.params.agent_name);
|
const definition = this.registry.getDefinition(this.params.agent_name)!;
|
||||||
if (!definition || definition.kind !== 'remote') {
|
if (definition.kind !== 'remote') {
|
||||||
// Local agents should execute without confirmation. Inner tool calls will bubble up their own confirmations to the user.
|
// Local agents should execute without confirmation. Inner tool calls will bubble up their own confirmations to the user.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -191,32 +213,9 @@ class DelegateInvocation extends BaseToolInvocation<
|
|||||||
signal: AbortSignal,
|
signal: AbortSignal,
|
||||||
updateOutput?: (output: string | AnsiOutput) => void,
|
updateOutput?: (output: string | AnsiOutput) => void,
|
||||||
): Promise<ToolResult> {
|
): Promise<ToolResult> {
|
||||||
const definition = this.registry.getDefinition(this.params.agent_name);
|
const definition = this.registry.getDefinition(this.params.agent_name)!;
|
||||||
if (!definition) {
|
|
||||||
const availableAgents = this.registry
|
|
||||||
.getAllDefinitions()
|
|
||||||
.map((def) => `'${def.name}' (${def.description})`)
|
|
||||||
.join(', ');
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`Agent '${this.params.agent_name}' not found. Available agents are: ${availableAgents}. Please choose a valid agent_name.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { agent_name: _agent_name, ...agentArgs } = this.params;
|
const { agent_name: _agent_name, ...agentArgs } = this.params;
|
||||||
|
|
||||||
// Validate specific agent arguments here using SchemaValidator to generate helpful error messages.
|
|
||||||
const validationError = SchemaValidator.validate(
|
|
||||||
definition.inputConfig.inputSchema,
|
|
||||||
agentArgs,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (validationError) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid arguments for agent '${definition.name}': ${validationError}. Input schema: ${JSON.stringify(definition.inputConfig.inputSchema)}.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const invocation = this.buildSubInvocation(
|
const invocation = this.buildSubInvocation(
|
||||||
definition,
|
definition,
|
||||||
agentArgs as AgentInputs,
|
agentArgs as AgentInputs,
|
||||||
|
|||||||
Reference in New Issue
Block a user