Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 10 additions & 24 deletions packages/core/src/agents/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,11 @@ describe('AgentRegistry', () => {
};

vi.mocked(tomlLoader.loadAgentsFromDirectory)
.mockResolvedValueOnce({ agents: [userAgent], errors: [] }) // User dir
.mockResolvedValueOnce({
agents: [projectAgent, uniqueProjectAgent],
errors: [],
}); // Project dir
}) // Project dir
.mockResolvedValueOnce({ agents: [userAgent], errors: [] }); // User dir

await registry.initialize();

Expand Down Expand Up @@ -1011,44 +1011,30 @@ 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);
expect(registry.getDefinition('MockAgent')?.description).toBe(
'Mock Description V1',
);

await registry.testRegisterAgent(MOCK_AGENT_V2);
expect(registry.getDefinition('MockAgent')?.description).toBe(
'Mock Description V2 (Updated)',
'Mock Description V1',
);
expect(registry.getAllDefinitions()).toHaveLength(1);
});

it('should log overwrites when in debug mode', async () => {
const debugConfig = makeMockedConfig({ debugMode: true });
const debugRegistry = new TestableAgentRegistry(debugConfig);
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')
it('should emit warning on duplicate agent definition', async () => {
const feedbackSpy = vi
.spyOn(coreEvents, 'emitFeedback')
.mockImplementation(() => {});

await registry.testRegisterAgent(MOCK_AGENT_V1);
await registry.testRegisterAgent(MOCK_AGENT_V2);

expect(debugLogSpy).not.toHaveBeenCalledWith(
`[AgentRegistry] Overriding agent 'MockAgent'`,
expect(feedbackSpy).toHaveBeenCalledWith(
'warning',
expect.stringContaining("Duplicate agent name 'MockAgent' detected"),
);
});

Expand Down
61 changes: 36 additions & 25 deletions packages/core/src/agents/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,31 +169,6 @@ export class AgentRegistry {
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)
const folderTrustEnabled = this.config.getFolderTrust();
const isTrustedFolder = this.config.isTrustedFolder();
Expand Down Expand Up @@ -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
for (const extension of this.config.getExtensions()) {
if (extension.isActive && extension.agents) {
Expand Down Expand Up @@ -336,6 +336,17 @@ export class AgentRegistry {
definition: AgentDefinition<TOutput>,
errors?: string[],
): Promise<void> {
const existing = this.agents.get(definition.name);
if (existing && existing !== definition) {
Comment thread
adamfweidman marked this conversation as resolved.
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') {
this.registerLocalAgent(definition);
} else if (definition.kind === 'remote') {
Expand Down
Loading