mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-14 05:42:54 -07:00
feat(core): change agent registration to first-wins and prioritize project (#26953)
This commit is contained in:
@@ -250,11 +250,11 @@ describe('AgentRegistry', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
vi.mocked(tomlLoader.loadAgentsFromDirectory)
|
vi.mocked(tomlLoader.loadAgentsFromDirectory)
|
||||||
.mockResolvedValueOnce({ agents: [userAgent], errors: [] }) // User dir
|
|
||||||
.mockResolvedValueOnce({
|
.mockResolvedValueOnce({
|
||||||
agents: [projectAgent, uniqueProjectAgent],
|
agents: [projectAgent, uniqueProjectAgent],
|
||||||
errors: [],
|
errors: [],
|
||||||
}); // Project dir
|
}) // Project dir
|
||||||
|
.mockResolvedValueOnce({ agents: [userAgent], errors: [] }); // User dir
|
||||||
|
|
||||||
await registry.initialize();
|
await registry.initialize();
|
||||||
|
|
||||||
@@ -1011,7 +1011,7 @@ describe('AgentRegistry', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should overwrite an existing agent definition', async () => {
|
it('should NOT overwrite an existing agent definition', async () => {
|
||||||
await registry.testRegisterAgent(MOCK_AGENT_V1);
|
await registry.testRegisterAgent(MOCK_AGENT_V1);
|
||||||
expect(registry.getDefinition('MockAgent')?.description).toBe(
|
expect(registry.getDefinition('MockAgent')?.description).toBe(
|
||||||
'Mock Description V1',
|
'Mock Description V1',
|
||||||
@@ -1019,36 +1019,22 @@ describe('AgentRegistry', () => {
|
|||||||
|
|
||||||
await registry.testRegisterAgent(MOCK_AGENT_V2);
|
await registry.testRegisterAgent(MOCK_AGENT_V2);
|
||||||
expect(registry.getDefinition('MockAgent')?.description).toBe(
|
expect(registry.getDefinition('MockAgent')?.description).toBe(
|
||||||
'Mock Description V2 (Updated)',
|
'Mock Description V1',
|
||||||
);
|
);
|
||||||
expect(registry.getAllDefinitions()).toHaveLength(1);
|
expect(registry.getAllDefinitions()).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log overwrites when in debug mode', async () => {
|
it('should emit warning on duplicate agent definition', async () => {
|
||||||
const debugConfig = makeMockedConfig({ debugMode: true });
|
const feedbackSpy = vi
|
||||||
const debugRegistry = new TestableAgentRegistry(debugConfig);
|
.spyOn(coreEvents, 'emitFeedback')
|
||||||
const debugLogSpy = vi
|
|
||||||
.spyOn(debugLogger, 'log')
|
|
||||||
.mockImplementation(() => {});
|
|
||||||
|
|
||||||
await debugRegistry.testRegisterAgent(MOCK_AGENT_V1);
|
|
||||||
await debugRegistry.testRegisterAgent(MOCK_AGENT_V2);
|
|
||||||
|
|
||||||
expect(debugLogSpy).toHaveBeenCalledWith(
|
|
||||||
`[AgentRegistry] Overriding agent 'MockAgent'`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not log overwrites when not in debug mode', async () => {
|
|
||||||
const debugLogSpy = vi
|
|
||||||
.spyOn(debugLogger, 'log')
|
|
||||||
.mockImplementation(() => {});
|
.mockImplementation(() => {});
|
||||||
|
|
||||||
await registry.testRegisterAgent(MOCK_AGENT_V1);
|
await registry.testRegisterAgent(MOCK_AGENT_V1);
|
||||||
await registry.testRegisterAgent(MOCK_AGENT_V2);
|
await registry.testRegisterAgent(MOCK_AGENT_V2);
|
||||||
|
|
||||||
expect(debugLogSpy).not.toHaveBeenCalledWith(
|
expect(feedbackSpy).toHaveBeenCalledWith(
|
||||||
`[AgentRegistry] Overriding agent 'MockAgent'`,
|
'warning',
|
||||||
|
expect.stringContaining("Duplicate agent name 'MockAgent' detected"),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -169,31 +169,6 @@ export class AgentRegistry {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load user-level agents: ~/.gemini/agents/
|
|
||||||
const userAgentsDir = Storage.getUserAgentsDir();
|
|
||||||
const userAgents = await loadAgentsFromDirectory(userAgentsDir);
|
|
||||||
for (const error of userAgents.errors) {
|
|
||||||
debugLogger.warn(
|
|
||||||
`[AgentRegistry] Error loading user agent: ${error.message}`,
|
|
||||||
);
|
|
||||||
const msg = `Agent loading error: ${error.message}`;
|
|
||||||
errors?.push(msg);
|
|
||||||
coreEvents.emitFeedback('error', msg);
|
|
||||||
}
|
|
||||||
await Promise.allSettled(
|
|
||||||
userAgents.agents.map(async (agent) => {
|
|
||||||
try {
|
|
||||||
this.ensureRemoteAgentHash(agent);
|
|
||||||
await this.registerAgent(agent, errors);
|
|
||||||
} catch (e) {
|
|
||||||
const msg = `Error registering user agent "${agent.name}": ${e instanceof Error ? e.message : String(e)}`;
|
|
||||||
debugLogger.warn(`[AgentRegistry] ${msg}`, e);
|
|
||||||
errors?.push(msg);
|
|
||||||
coreEvents.emitFeedback('error', msg);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load project-level agents: .gemini/agents/ (relative to Project Root)
|
// Load project-level agents: .gemini/agents/ (relative to Project Root)
|
||||||
const folderTrustEnabled = this.config.getFolderTrust();
|
const folderTrustEnabled = this.config.getFolderTrust();
|
||||||
const isTrustedFolder = this.config.isTrustedFolder();
|
const isTrustedFolder = this.config.isTrustedFolder();
|
||||||
@@ -256,6 +231,31 @@ export class AgentRegistry {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load user-level agents: ~/.gemini/agents/
|
||||||
|
const userAgentsDir = Storage.getUserAgentsDir();
|
||||||
|
const userAgents = await loadAgentsFromDirectory(userAgentsDir);
|
||||||
|
for (const error of userAgents.errors) {
|
||||||
|
debugLogger.warn(
|
||||||
|
`[AgentRegistry] Error loading user agent: ${error.message}`,
|
||||||
|
);
|
||||||
|
const msg = `Agent loading error: ${error.message}`;
|
||||||
|
errors?.push(msg);
|
||||||
|
coreEvents.emitFeedback('error', msg);
|
||||||
|
}
|
||||||
|
await Promise.allSettled(
|
||||||
|
userAgents.agents.map(async (agent) => {
|
||||||
|
try {
|
||||||
|
this.ensureRemoteAgentHash(agent);
|
||||||
|
await this.registerAgent(agent, errors);
|
||||||
|
} catch (e) {
|
||||||
|
const msg = `Error registering user agent "${agent.name}": ${e instanceof Error ? e.message : String(e)}`;
|
||||||
|
debugLogger.warn(`[AgentRegistry] ${msg}`, e);
|
||||||
|
errors?.push(msg);
|
||||||
|
coreEvents.emitFeedback('error', msg);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
// Load agents from extensions
|
// Load agents from extensions
|
||||||
for (const extension of this.config.getExtensions()) {
|
for (const extension of this.config.getExtensions()) {
|
||||||
if (extension.isActive && extension.agents) {
|
if (extension.isActive && extension.agents) {
|
||||||
@@ -336,6 +336,17 @@ export class AgentRegistry {
|
|||||||
definition: AgentDefinition<TOutput>,
|
definition: AgentDefinition<TOutput>,
|
||||||
errors?: string[],
|
errors?: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const existing = this.agents.get(definition.name);
|
||||||
|
if (existing && existing !== definition) {
|
||||||
|
coreEvents.emitFeedback(
|
||||||
|
'warning',
|
||||||
|
`Duplicate agent name '${definition.name}' detected. ` +
|
||||||
|
`The later definition will be ignored. ` +
|
||||||
|
`Rename one of the agents to avoid this conflict.`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (definition.kind === 'local') {
|
if (definition.kind === 'local') {
|
||||||
this.registerLocalAgent(definition);
|
this.registerLocalAgent(definition);
|
||||||
} else if (definition.kind === 'remote') {
|
} else if (definition.kind === 'remote') {
|
||||||
|
|||||||
Reference in New Issue
Block a user