Fix: TaskTool Dynamic Updates (#697)

This commit is contained in:
tanzhenxin
2025-09-25 19:11:55 +08:00
committed by GitHub
parent 673854b446
commit c405434c41
5 changed files with 106 additions and 24 deletions

View File

@@ -40,11 +40,29 @@ const AGENT_CONFIG_DIR = 'agents';
export class SubagentManager {
private readonly validator: SubagentValidator;
private subagentsCache: Map<SubagentLevel, SubagentConfig[]> | null = null;
private readonly changeListeners: Set<() => void> = new Set();
constructor(private readonly config: Config) {
this.validator = new SubagentValidator();
}
addChangeListener(listener: () => void): () => void {
this.changeListeners.add(listener);
return () => {
this.changeListeners.delete(listener);
};
}
private notifyChangeListeners(): void {
for (const listener of this.changeListeners) {
try {
listener();
} catch (error) {
console.warn('Subagent change listener threw an error:', error);
}
}
}
/**
* Creates a new subagent configuration.
*
@@ -93,8 +111,8 @@ export class SubagentManager {
try {
await fs.writeFile(filePath, content, 'utf8');
// Clear cache after successful creation
this.clearCache();
// Refresh cache after successful creation
await this.refreshCache();
} catch (error) {
throw new SubagentError(
`Failed to write subagent file: ${error instanceof Error ? error.message : 'Unknown error'}`,
@@ -183,8 +201,8 @@ export class SubagentManager {
try {
await fs.writeFile(existing.filePath, content, 'utf8');
// Clear cache after successful update
this.clearCache();
// Refresh cache after successful update
await this.refreshCache();
} catch (error) {
throw new SubagentError(
`Failed to update subagent file: ${error instanceof Error ? error.message : 'Unknown error'}`,
@@ -242,8 +260,8 @@ export class SubagentManager {
);
}
// Clear cache after successful deletion
this.clearCache();
// Refresh cache after successful deletion
await this.refreshCache();
}
/**
@@ -327,21 +345,17 @@ export class SubagentManager {
* @private
*/
private async refreshCache(): Promise<void> {
this.subagentsCache = new Map();
const subagentsCache = new Map();
const levels: SubagentLevel[] = ['project', 'user', 'builtin'];
for (const level of levels) {
const levelSubagents = await this.listSubagentsAtLevel(level);
this.subagentsCache.set(level, levelSubagents);
subagentsCache.set(level, levelSubagents);
}
}
/**
* Clears the subagents cache, forcing the next listSubagents call to reload from disk.
*/
clearCache(): void {
this.subagentsCache = null;
this.subagentsCache = subagentsCache;
this.notifyChangeListeners();
}
/**

View File

@@ -41,12 +41,14 @@ import type {
ToolConfig,
} from './types.js';
import { SubagentTerminateMode } from './types.js';
import { GeminiClient } from '../core/client.js';
vi.mock('../core/geminiChat.js');
vi.mock('../core/contentGenerator.js');
vi.mock('../utils/environmentContext.js');
vi.mock('../core/nonInteractiveToolExecutor.js');
vi.mock('../ide/ide-client.js');
vi.mock('../core/client.js');
async function createMockConfig(
toolRegistryMocks = {},
@@ -194,6 +196,28 @@ describe('subagent.ts', () => {
}) as unknown as GeminiChat,
);
// Mock GeminiClient constructor to return a properly mocked client
const mockGeminiChat = {
setTools: vi.fn(),
getHistory: vi.fn().mockReturnValue([]),
setHistory: vi.fn(),
sendMessageStream: vi.fn(),
};
const mockGeminiClient = {
getChat: vi.fn().mockReturnValue(mockGeminiChat),
setTools: vi.fn().mockResolvedValue(undefined),
isInitialized: vi.fn().mockReturnValue(true),
getHistory: vi.fn().mockReturnValue([]),
initialize: vi.fn().mockResolvedValue(undefined),
setHistory: vi.fn(),
};
// Mock the GeminiClient constructor
vi.mocked(GeminiClient).mockImplementation(
() => mockGeminiClient as unknown as GeminiClient,
);
// Default mock for executeToolCall
vi.mocked(executeToolCall).mockResolvedValue({
callId: 'default-call',