feat: subagent feature wip

This commit is contained in:
tanzhenxin
2025-09-10 13:41:28 +08:00
parent 549f296eb5
commit 6b09aee32b
30 changed files with 329 additions and 239 deletions

View File

@@ -258,10 +258,8 @@ describe('TaskTool', () => {
beforeEach(() => {
mockSubagentScope = {
runNonInteractive: vi.fn().mockResolvedValue(undefined),
output: {
result: 'Task completed successfully',
terminate_reason: SubagentTerminateMode.GOAL,
},
result: 'Task completed successfully',
terminateMode: SubagentTerminateMode.GOAL,
getFinalText: vi.fn().mockReturnValue('Task completed successfully'),
formatCompactResult: vi
.fn()
@@ -305,6 +303,7 @@ describe('TaskTool', () => {
successfulToolCalls: 3,
failedToolCalls: 0,
}),
getTerminateMode: vi.fn().mockReturnValue(SubagentTerminateMode.GOAL),
} as unknown as SubAgentScope;
mockContextState = {
@@ -375,25 +374,6 @@ describe('TaskTool', () => {
expect(display.subagentName).toBe('non-existent');
});
it('should handle subagent execution failure', async () => {
mockSubagentScope.output.terminate_reason = SubagentTerminateMode.ERROR;
const params: TaskParams = {
description: 'Search files',
prompt: 'Find all TypeScript files',
subagent_type: 'file-search',
};
const invocation = (
taskTool as TaskToolWithProtectedMethods
).createInvocation(params);
const result = await invocation.execute();
const display = result.returnDisplay as TaskResultDisplay;
expect(display.status).toBe('failed');
expect(display.terminateReason).toBe('ERROR');
});
it('should handle execution errors gracefully', async () => {
vi.mocked(mockSubagentManager.createSubagentScope).mockRejectedValue(
new Error('Creation failed'),

View File

@@ -14,7 +14,7 @@ import {
} from './tools.js';
import { Config } from '../config/config.js';
import { SubagentManager } from '../subagents/subagent-manager.js';
import { SubagentConfig } from '../subagents/types.js';
import { SubagentConfig, SubagentTerminateMode } from '../subagents/types.js';
import { ContextState } from '../subagents/subagent.js';
import {
SubAgentEventEmitter,
@@ -409,21 +409,6 @@ class TaskToolInvocation extends BaseToolInvocation<TaskParams, ToolResult> {
// Set up event listeners for real-time updates
this.setupEventListeners(updateOutput);
if (signal) {
signal.addEventListener('abort', () => {
if (this.currentDisplay) {
this.updateDisplay(
{
status: 'failed',
terminateReason: 'CANCELLED',
result: 'Task was cancelled by user',
},
updateOutput,
);
}
});
}
// Send initial display
if (updateOutput) {
updateOutput(this.currentDisplay);
@@ -474,20 +459,31 @@ class TaskToolInvocation extends BaseToolInvocation<TaskParams, ToolResult> {
// Get the results
const finalText = subagentScope.getFinalText();
const terminateReason = subagentScope.output.terminate_reason;
const success = terminateReason === 'GOAL';
const terminateReason = subagentScope.getTerminateMode();
const success = terminateReason === SubagentTerminateMode.GOAL;
const executionSummary = subagentScope.getExecutionSummary();
// Update the final display state
this.updateDisplay(
{
status: success ? 'completed' : 'failed',
terminateReason,
result: finalText,
executionSummary,
},
updateOutput,
);
if (signal?.aborted) {
this.updateDisplay(
{
status: 'cancelled',
terminateReason: 'CANCELLED',
result: finalText || 'Task was cancelled by user',
executionSummary,
},
updateOutput,
);
} else {
this.updateDisplay(
{
status: success ? 'completed' : 'failed',
terminateReason,
result: finalText,
executionSummary,
},
updateOutput,
);
}
return {
llmContent: [{ text: finalText }],
@@ -500,7 +496,7 @@ class TaskToolInvocation extends BaseToolInvocation<TaskParams, ToolResult> {
const errorDisplay: TaskResultDisplay = {
...this.currentDisplay!,
status: 'failed' as const,
status: 'failed',
terminateReason: 'ERROR',
result: `Failed to run subagent: ${errorMessage}`,
};

View File

@@ -428,7 +428,7 @@ export interface TaskResultDisplay {
subagentColor?: string;
taskDescription: string;
taskPrompt: string;
status: 'running' | 'completed' | 'failed';
status: 'running' | 'completed' | 'failed' | 'cancelled';
terminateReason?: string;
result?: string;
executionSummary?: SubagentStatsSummary;