mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 08:47:44 +00:00
feat: sdk subagent support
This commit is contained in:
@@ -613,6 +613,12 @@ export class Config {
|
||||
}
|
||||
this.promptRegistry = new PromptRegistry();
|
||||
this.subagentManager = new SubagentManager(this);
|
||||
|
||||
// Load session subagents if they were provided before initialization
|
||||
if (this.sessionSubagents.length > 0) {
|
||||
this.subagentManager.loadSessionSubagents(this.sessionSubagents);
|
||||
}
|
||||
|
||||
this.toolRegistry = await this.createToolRegistry();
|
||||
|
||||
await this.geminiClient.initialize();
|
||||
@@ -874,13 +880,6 @@ export class Config {
|
||||
this.sessionSubagents = subagents;
|
||||
}
|
||||
|
||||
addSessionSubagents(subagents: SubagentConfig[]): void {
|
||||
if (this.initialized) {
|
||||
throw new Error('Cannot modify sessionSubagents after initialization');
|
||||
}
|
||||
this.sessionSubagents = [...this.sessionSubagents, ...subagents];
|
||||
}
|
||||
|
||||
getSdkMode(): boolean {
|
||||
return this.sdkMode;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ You are a helpful assistant.
|
||||
it('should parse valid markdown content', () => {
|
||||
const config = manager.parseSubagentContent(
|
||||
validMarkdown,
|
||||
validConfig.filePath,
|
||||
validConfig.filePath!,
|
||||
'project',
|
||||
);
|
||||
|
||||
@@ -207,7 +207,7 @@ You are a helpful assistant.
|
||||
|
||||
const config = manager.parseSubagentContent(
|
||||
markdownWithTools,
|
||||
validConfig.filePath,
|
||||
validConfig.filePath!,
|
||||
'project',
|
||||
);
|
||||
|
||||
@@ -228,7 +228,7 @@ You are a helpful assistant.
|
||||
|
||||
const config = manager.parseSubagentContent(
|
||||
markdownWithModel,
|
||||
validConfig.filePath,
|
||||
validConfig.filePath!,
|
||||
'project',
|
||||
);
|
||||
|
||||
@@ -249,7 +249,7 @@ You are a helpful assistant.
|
||||
|
||||
const config = manager.parseSubagentContent(
|
||||
markdownWithRun,
|
||||
validConfig.filePath,
|
||||
validConfig.filePath!,
|
||||
'project',
|
||||
);
|
||||
|
||||
@@ -267,7 +267,7 @@ You are a helpful assistant.
|
||||
|
||||
const config = manager.parseSubagentContent(
|
||||
markdownWithNumeric,
|
||||
validConfig.filePath,
|
||||
validConfig.filePath!,
|
||||
'project',
|
||||
);
|
||||
|
||||
@@ -288,7 +288,7 @@ You are a helpful assistant.
|
||||
|
||||
const config = manager.parseSubagentContent(
|
||||
markdownWithBoolean,
|
||||
validConfig.filePath,
|
||||
validConfig.filePath!,
|
||||
'project',
|
||||
);
|
||||
|
||||
@@ -324,7 +324,7 @@ Just content`;
|
||||
expect(() =>
|
||||
manager.parseSubagentContent(
|
||||
invalidMarkdown,
|
||||
validConfig.filePath,
|
||||
validConfig.filePath!,
|
||||
'project',
|
||||
),
|
||||
).toThrow(SubagentError);
|
||||
@@ -341,7 +341,7 @@ You are a helpful assistant.
|
||||
expect(() =>
|
||||
manager.parseSubagentContent(
|
||||
markdownWithoutName,
|
||||
validConfig.filePath,
|
||||
validConfig.filePath!,
|
||||
'project',
|
||||
),
|
||||
).toThrow(SubagentError);
|
||||
@@ -358,7 +358,7 @@ You are a helpful assistant.
|
||||
expect(() =>
|
||||
manager.parseSubagentContent(
|
||||
markdownWithoutDescription,
|
||||
validConfig.filePath,
|
||||
validConfig.filePath!,
|
||||
'project',
|
||||
),
|
||||
).toThrow(SubagentError);
|
||||
@@ -438,7 +438,7 @@ You are a helpful assistant.
|
||||
await manager.createSubagent(validConfig, { level: 'project' });
|
||||
|
||||
expect(fs.mkdir).toHaveBeenCalledWith(
|
||||
path.normalize(path.dirname(validConfig.filePath)),
|
||||
path.normalize(path.dirname(validConfig.filePath!)),
|
||||
{ recursive: true },
|
||||
);
|
||||
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||
|
||||
@@ -159,7 +159,14 @@ export class SubagentManager {
|
||||
return this.findSubagentByNameAtLevel(name, level);
|
||||
}
|
||||
|
||||
// Try project level first
|
||||
// Try session level first (highest priority for runtime)
|
||||
const sessionSubagents = this.subagentsCache?.get('session') || [];
|
||||
const sessionConfig = sessionSubagents.find((agent) => agent.name === name);
|
||||
if (sessionConfig) {
|
||||
return sessionConfig;
|
||||
}
|
||||
|
||||
// Try project level
|
||||
const projectConfig = await this.findSubagentByNameAtLevel(name, 'project');
|
||||
if (projectConfig) {
|
||||
return projectConfig;
|
||||
@@ -220,6 +227,15 @@ export class SubagentManager {
|
||||
// Validate the updated configuration
|
||||
this.validator.validateOrThrow(updatedConfig);
|
||||
|
||||
// Ensure filePath exists for file-based agents
|
||||
if (!existing.filePath) {
|
||||
throw new SubagentError(
|
||||
`Cannot update subagent "${name}": no file path available`,
|
||||
SubagentErrorCode.FILE_ERROR,
|
||||
name,
|
||||
);
|
||||
}
|
||||
|
||||
// Write the updated configuration
|
||||
const content = this.serializeSubagent(updatedConfig);
|
||||
|
||||
@@ -302,11 +318,6 @@ export class SubagentManager {
|
||||
|
||||
// In SDK mode, only load session-level subagents
|
||||
if (this.config.getSdkMode()) {
|
||||
const sessionSubagents = this.config.getSessionSubagents();
|
||||
if (sessionSubagents && sessionSubagents.length > 0) {
|
||||
this.loadSessionSubagents(sessionSubagents);
|
||||
}
|
||||
|
||||
const levelsToCheck: SubagentLevel[] = options.level
|
||||
? [options.level]
|
||||
: ['session'];
|
||||
|
||||
@@ -42,8 +42,8 @@ export interface SubagentConfig {
|
||||
/** Storage level - determines where the configuration file is stored */
|
||||
level: SubagentLevel;
|
||||
|
||||
/** Absolute path to the configuration file */
|
||||
filePath: string;
|
||||
/** Absolute path to the configuration file. Optional for session subagents. */
|
||||
filePath?: string;
|
||||
|
||||
/**
|
||||
* Optional model configuration. If not provided, uses defaults.
|
||||
|
||||
Reference in New Issue
Block a user