feat(core, ui): Add /agents refresh command. (#16204)

This commit is contained in:
joshualitt
2026-01-09 09:33:59 -08:00
committed by GitHub
parent c1401682ed
commit 041463d112
7 changed files with 154 additions and 6 deletions

View File

@@ -162,6 +162,20 @@ describe('A2AClientManager', () => {
"[A2AClientManager] Loaded agent 'TestAgent' from http://test.agent/card",
);
});
it('should clear the cache', async () => {
await manager.loadAgent('TestAgent', 'http://test.agent/card');
expect(manager.getAgentCard('TestAgent')).toBeDefined();
expect(manager.getClient('TestAgent')).toBeDefined();
manager.clearCache();
expect(manager.getAgentCard('TestAgent')).toBeUndefined();
expect(manager.getClient('TestAgent')).toBeUndefined();
expect(debugLogger.debug).toHaveBeenCalledWith(
'[A2AClientManager] Cache cleared.',
);
});
});
describe('sendMessage', () => {

View File

@@ -104,6 +104,15 @@ export class A2AClientManager {
return agentCard;
}
/**
* Invalidates all cached clients and agent cards.
*/
clearCache(): void {
this.clients.clear();
this.agentCards.clear();
debugLogger.debug('[A2AClientManager] Cache cleared.');
}
/**
* Sends a message to a loaded agent.
* @param agentName The name of the agent to send the message to.

View File

@@ -99,7 +99,7 @@ describe('AgentRegistry', () => {
const agentCount = debugRegistry.getAllDefinitions().length;
expect(debugLogSpy).toHaveBeenCalledWith(
`[AgentRegistry] Initialized with ${agentCount} agents.`,
`[AgentRegistry] Loaded with ${agentCount} agents.`,
);
});
@@ -444,6 +444,37 @@ describe('AgentRegistry', () => {
});
});
describe('reload', () => {
it('should clear existing agents and reload from directories', async () => {
const config = makeFakeConfig({ enableAgents: true });
const registry = new TestableAgentRegistry(config);
const initialAgent = { ...MOCK_AGENT_V1, name: 'InitialAgent' };
await registry.testRegisterAgent(initialAgent);
expect(registry.getDefinition('InitialAgent')).toBeDefined();
const newAgent = { ...MOCK_AGENT_V1, name: 'NewAgent' };
vi.mocked(tomlLoader.loadAgentsFromDirectory).mockResolvedValue({
agents: [newAgent],
errors: [],
});
const clearCacheSpy = vi.fn();
vi.mocked(A2AClientManager.getInstance).mockReturnValue({
clearCache: clearCacheSpy,
} as unknown as A2AClientManager);
const emitSpy = vi.spyOn(coreEvents, 'emitAgentsRefreshed');
await registry.reload();
expect(clearCacheSpy).toHaveBeenCalled();
expect(registry.getDefinition('InitialAgent')).toBeUndefined();
expect(registry.getDefinition('NewAgent')).toBeDefined();
expect(emitSpy).toHaveBeenCalled();
});
});
describe('inheritance and refresh', () => {
it('should resolve "inherit" to the current model from configuration', async () => {
const config = makeFakeConfig({ model: 'current-model' });

View File

@@ -46,8 +46,6 @@ export class AgentRegistry {
* Discovers and loads agents.
*/
async initialize(): Promise<void> {
this.loadBuiltInAgents();
coreEvents.on(CoreEvent.ModelChanged, () => {
this.refreshAgents().catch((e) => {
debugLogger.error(
@@ -57,6 +55,22 @@ export class AgentRegistry {
});
});
await this.loadAgents();
}
/**
* Clears the current registry and re-scans for agents.
*/
async reload(): Promise<void> {
A2AClientManager.getInstance().clearCache();
this.agents.clear();
await this.loadAgents();
coreEvents.emitAgentsRefreshed();
}
private async loadAgents(): Promise<void> {
this.loadBuiltInAgents();
if (!this.config.isAgentsEnabled()) {
return;
}
@@ -99,7 +113,7 @@ export class AgentRegistry {
if (this.config.getDebugMode()) {
debugLogger.log(
`[AgentRegistry] Initialized with ${this.agents.size} agents.`,
`[AgentRegistry] Loaded with ${this.agents.size} agents.`,
);
}
}

View File

@@ -107,6 +107,7 @@ export enum CoreEvent {
SettingsChanged = 'settings-changed',
HookStart = 'hook-start',
HookEnd = 'hook-end',
AgentsRefreshed = 'agents-refreshed',
}
export interface CoreEvents {
@@ -119,6 +120,7 @@ export interface CoreEvents {
[CoreEvent.SettingsChanged]: never[];
[CoreEvent.HookStart]: [HookStartPayload];
[CoreEvent.HookEnd]: [HookEndPayload];
[CoreEvent.AgentsRefreshed]: never[];
}
type EventBacklogItem = {
@@ -220,6 +222,13 @@ export class CoreEventEmitter extends EventEmitter<CoreEvents> {
this.emit(CoreEvent.HookEnd, payload);
}
/**
* Notifies subscribers that agents have been refreshed.
*/
emitAgentsRefreshed(): void {
this.emit(CoreEvent.AgentsRefreshed);
}
/**
* Flushes buffered messages. Call this immediately after primary UI listener
* subscribes.