mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-30 04:30:48 +00:00
refactor(core,cli)!: rename SubAgentScope to AgentHeadless
- Rename SubAgentScope → AgentHeadless and runNonInteractive → execute - Move agents-collab/ into agents/ with new runtime/ subdirectory - Split subagent.ts into agent-core.ts and agent-headless.ts - Update all event types, emitters, and statistics classes BREAKING CHANGE: SubAgentScope renamed to AgentHeadless; runNonInteractive() renamed to execute() Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
193bc438bd
commit
e968483a8a
43 changed files with 1589 additions and 1303 deletions
|
|
@ -10,26 +10,26 @@ import type { SessionContext } from './types.js';
|
|||
import type {
|
||||
Config,
|
||||
ToolRegistry,
|
||||
SubAgentEventEmitter,
|
||||
SubAgentToolCallEvent,
|
||||
SubAgentToolResultEvent,
|
||||
SubAgentApprovalRequestEvent,
|
||||
SubAgentStreamTextEvent,
|
||||
AgentEventEmitter,
|
||||
AgentToolCallEvent,
|
||||
AgentToolResultEvent,
|
||||
AgentApprovalRequestEvent,
|
||||
AgentStreamTextEvent,
|
||||
ToolEditConfirmationDetails,
|
||||
ToolInfoConfirmationDetails,
|
||||
} from '@qwen-code/qwen-code-core';
|
||||
import {
|
||||
SubAgentEventType,
|
||||
AgentEventType,
|
||||
ToolConfirmationOutcome,
|
||||
TodoWriteTool,
|
||||
} from '@qwen-code/qwen-code-core';
|
||||
import type * as acp from '../acp.js';
|
||||
import { EventEmitter } from 'node:events';
|
||||
|
||||
// Helper to create a mock SubAgentToolCallEvent with required fields
|
||||
// Helper to create a mock AgentToolCallEvent with required fields
|
||||
function createToolCallEvent(
|
||||
overrides: Partial<SubAgentToolCallEvent> & { name: string; callId: string },
|
||||
): SubAgentToolCallEvent {
|
||||
overrides: Partial<AgentToolCallEvent> & { name: string; callId: string },
|
||||
): AgentToolCallEvent {
|
||||
return {
|
||||
subagentId: 'test-subagent',
|
||||
round: 1,
|
||||
|
|
@ -40,14 +40,14 @@ function createToolCallEvent(
|
|||
};
|
||||
}
|
||||
|
||||
// Helper to create a mock SubAgentToolResultEvent with required fields
|
||||
// Helper to create a mock AgentToolResultEvent with required fields
|
||||
function createToolResultEvent(
|
||||
overrides: Partial<SubAgentToolResultEvent> & {
|
||||
overrides: Partial<AgentToolResultEvent> & {
|
||||
name: string;
|
||||
callId: string;
|
||||
success: boolean;
|
||||
},
|
||||
): SubAgentToolResultEvent {
|
||||
): AgentToolResultEvent {
|
||||
return {
|
||||
subagentId: 'test-subagent',
|
||||
round: 1,
|
||||
|
|
@ -56,15 +56,15 @@ function createToolResultEvent(
|
|||
};
|
||||
}
|
||||
|
||||
// Helper to create a mock SubAgentApprovalRequestEvent with required fields
|
||||
// Helper to create a mock AgentApprovalRequestEvent with required fields
|
||||
function createApprovalEvent(
|
||||
overrides: Partial<SubAgentApprovalRequestEvent> & {
|
||||
overrides: Partial<AgentApprovalRequestEvent> & {
|
||||
name: string;
|
||||
callId: string;
|
||||
confirmationDetails: SubAgentApprovalRequestEvent['confirmationDetails'];
|
||||
respond: SubAgentApprovalRequestEvent['respond'];
|
||||
confirmationDetails: AgentApprovalRequestEvent['confirmationDetails'];
|
||||
respond: AgentApprovalRequestEvent['respond'];
|
||||
},
|
||||
): SubAgentApprovalRequestEvent {
|
||||
): AgentApprovalRequestEvent {
|
||||
return {
|
||||
subagentId: 'test-subagent',
|
||||
round: 1,
|
||||
|
|
@ -102,10 +102,10 @@ function createInfoConfirmation(
|
|||
};
|
||||
}
|
||||
|
||||
// Helper to create a mock SubAgentStreamTextEvent with required fields
|
||||
// Helper to create a mock AgentStreamTextEvent with required fields
|
||||
function createStreamTextEvent(
|
||||
overrides: Partial<SubAgentStreamTextEvent> & { text: string },
|
||||
): SubAgentStreamTextEvent {
|
||||
overrides: Partial<AgentStreamTextEvent> & { text: string },
|
||||
): AgentStreamTextEvent {
|
||||
return {
|
||||
subagentId: 'test-subagent',
|
||||
round: 1,
|
||||
|
|
@ -120,7 +120,7 @@ describe('SubAgentTracker', () => {
|
|||
let sendUpdateSpy: ReturnType<typeof vi.fn>;
|
||||
let requestPermissionSpy: ReturnType<typeof vi.fn>;
|
||||
let tracker: SubAgentTracker;
|
||||
let eventEmitter: SubAgentEventEmitter;
|
||||
let eventEmitter: AgentEventEmitter;
|
||||
let abortController: AbortController;
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
@ -151,7 +151,7 @@ describe('SubAgentTracker', () => {
|
|||
'parent-call-123',
|
||||
'test-subagent',
|
||||
);
|
||||
eventEmitter = new EventEmitter() as unknown as SubAgentEventEmitter;
|
||||
eventEmitter = new EventEmitter() as unknown as AgentEventEmitter;
|
||||
abortController = new AbortController();
|
||||
});
|
||||
|
||||
|
|
@ -169,19 +169,19 @@ describe('SubAgentTracker', () => {
|
|||
tracker.setup(eventEmitter, abortController.signal);
|
||||
|
||||
expect(onSpy).toHaveBeenCalledWith(
|
||||
SubAgentEventType.TOOL_CALL,
|
||||
AgentEventType.TOOL_CALL,
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(onSpy).toHaveBeenCalledWith(
|
||||
SubAgentEventType.TOOL_RESULT,
|
||||
AgentEventType.TOOL_RESULT,
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(onSpy).toHaveBeenCalledWith(
|
||||
SubAgentEventType.TOOL_WAITING_APPROVAL,
|
||||
AgentEventType.TOOL_WAITING_APPROVAL,
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(onSpy).toHaveBeenCalledWith(
|
||||
SubAgentEventType.STREAM_TEXT,
|
||||
AgentEventType.STREAM_TEXT,
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
|
@ -193,19 +193,19 @@ describe('SubAgentTracker', () => {
|
|||
cleanups[0]();
|
||||
|
||||
expect(offSpy).toHaveBeenCalledWith(
|
||||
SubAgentEventType.TOOL_CALL,
|
||||
AgentEventType.TOOL_CALL,
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(offSpy).toHaveBeenCalledWith(
|
||||
SubAgentEventType.TOOL_RESULT,
|
||||
AgentEventType.TOOL_RESULT,
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(offSpy).toHaveBeenCalledWith(
|
||||
SubAgentEventType.TOOL_WAITING_APPROVAL,
|
||||
AgentEventType.TOOL_WAITING_APPROVAL,
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(offSpy).toHaveBeenCalledWith(
|
||||
SubAgentEventType.STREAM_TEXT,
|
||||
AgentEventType.STREAM_TEXT,
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
|
@ -222,7 +222,7 @@ describe('SubAgentTracker', () => {
|
|||
description: 'Reading file',
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_CALL, event);
|
||||
eventEmitter.emit(AgentEventType.TOOL_CALL, event);
|
||||
|
||||
// Allow async operations to complete
|
||||
await vi.waitFor(() => {
|
||||
|
|
@ -258,7 +258,7 @@ describe('SubAgentTracker', () => {
|
|||
args: { todos: [] },
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_CALL, event);
|
||||
eventEmitter.emit(AgentEventType.TOOL_CALL, event);
|
||||
|
||||
// Give time for any async operation
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
|
@ -276,7 +276,7 @@ describe('SubAgentTracker', () => {
|
|||
args: {},
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_CALL, event);
|
||||
eventEmitter.emit(AgentEventType.TOOL_CALL, event);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
|
|
@ -290,7 +290,7 @@ describe('SubAgentTracker', () => {
|
|||
|
||||
// First emit tool call to store state
|
||||
eventEmitter.emit(
|
||||
SubAgentEventType.TOOL_CALL,
|
||||
AgentEventType.TOOL_CALL,
|
||||
createToolCallEvent({
|
||||
name: 'read_file',
|
||||
callId: 'call-123',
|
||||
|
|
@ -306,7 +306,7 @@ describe('SubAgentTracker', () => {
|
|||
resultDisplay: 'File contents',
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_RESULT, resultEvent);
|
||||
eventEmitter.emit(AgentEventType.TOOL_RESULT, resultEvent);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(sendUpdateSpy).toHaveBeenCalledWith(
|
||||
|
|
@ -334,7 +334,7 @@ describe('SubAgentTracker', () => {
|
|||
resultDisplay: undefined,
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_RESULT, resultEvent);
|
||||
eventEmitter.emit(AgentEventType.TOOL_RESULT, resultEvent);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(sendUpdateSpy).toHaveBeenCalledWith(
|
||||
|
|
@ -356,7 +356,7 @@ describe('SubAgentTracker', () => {
|
|||
|
||||
// Store args via tool call
|
||||
eventEmitter.emit(
|
||||
SubAgentEventType.TOOL_CALL,
|
||||
AgentEventType.TOOL_CALL,
|
||||
createToolCallEvent({
|
||||
name: TodoWriteTool.Name,
|
||||
callId: 'call-todo',
|
||||
|
|
@ -377,7 +377,7 @@ describe('SubAgentTracker', () => {
|
|||
}),
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_RESULT, resultEvent);
|
||||
eventEmitter.emit(AgentEventType.TOOL_RESULT, resultEvent);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(sendUpdateSpy).toHaveBeenCalledWith({
|
||||
|
|
@ -393,7 +393,7 @@ describe('SubAgentTracker', () => {
|
|||
tracker.setup(eventEmitter, abortController.signal);
|
||||
|
||||
eventEmitter.emit(
|
||||
SubAgentEventType.TOOL_CALL,
|
||||
AgentEventType.TOOL_CALL,
|
||||
createToolCallEvent({
|
||||
name: 'test_tool',
|
||||
callId: 'call-cleanup',
|
||||
|
|
@ -402,7 +402,7 @@ describe('SubAgentTracker', () => {
|
|||
);
|
||||
|
||||
eventEmitter.emit(
|
||||
SubAgentEventType.TOOL_RESULT,
|
||||
AgentEventType.TOOL_RESULT,
|
||||
createToolResultEvent({
|
||||
name: 'test_tool',
|
||||
callId: 'call-cleanup',
|
||||
|
|
@ -413,7 +413,7 @@ describe('SubAgentTracker', () => {
|
|||
// Emit another result for same callId - should not have stored args
|
||||
sendUpdateSpy.mockClear();
|
||||
eventEmitter.emit(
|
||||
SubAgentEventType.TOOL_RESULT,
|
||||
AgentEventType.TOOL_RESULT,
|
||||
createToolResultEvent({
|
||||
name: 'test_tool',
|
||||
callId: 'call-cleanup',
|
||||
|
|
@ -447,7 +447,7 @@ describe('SubAgentTracker', () => {
|
|||
respond: respondSpy,
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_WAITING_APPROVAL, event);
|
||||
eventEmitter.emit(AgentEventType.TOOL_WAITING_APPROVAL, event);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(requestPermissionSpy).toHaveBeenCalled();
|
||||
|
|
@ -483,7 +483,7 @@ describe('SubAgentTracker', () => {
|
|||
respond: respondSpy,
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_WAITING_APPROVAL, event);
|
||||
eventEmitter.emit(AgentEventType.TOOL_WAITING_APPROVAL, event);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(respondSpy).toHaveBeenCalledWith(
|
||||
|
|
@ -504,7 +504,7 @@ describe('SubAgentTracker', () => {
|
|||
respond: respondSpy,
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_WAITING_APPROVAL, event);
|
||||
eventEmitter.emit(AgentEventType.TOOL_WAITING_APPROVAL, event);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(respondSpy).toHaveBeenCalledWith(ToolConfirmationOutcome.Cancel);
|
||||
|
|
@ -525,7 +525,7 @@ describe('SubAgentTracker', () => {
|
|||
respond: respondSpy,
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_WAITING_APPROVAL, event);
|
||||
eventEmitter.emit(AgentEventType.TOOL_WAITING_APPROVAL, event);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(respondSpy).toHaveBeenCalledWith(ToolConfirmationOutcome.Cancel);
|
||||
|
|
@ -548,7 +548,7 @@ describe('SubAgentTracker', () => {
|
|||
respond: vi.fn(),
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.TOOL_WAITING_APPROVAL, event);
|
||||
eventEmitter.emit(AgentEventType.TOOL_WAITING_APPROVAL, event);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(requestPermissionSpy).toHaveBeenCalled();
|
||||
|
|
@ -572,7 +572,7 @@ describe('SubAgentTracker', () => {
|
|||
text: 'Hello, this is a response from the model.',
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.STREAM_TEXT, event);
|
||||
eventEmitter.emit(AgentEventType.STREAM_TEXT, event);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(sendUpdateSpy).toHaveBeenCalled();
|
||||
|
|
@ -593,15 +593,15 @@ describe('SubAgentTracker', () => {
|
|||
tracker.setup(eventEmitter, abortController.signal);
|
||||
|
||||
eventEmitter.emit(
|
||||
SubAgentEventType.STREAM_TEXT,
|
||||
AgentEventType.STREAM_TEXT,
|
||||
createStreamTextEvent({ text: 'First chunk ' }),
|
||||
);
|
||||
eventEmitter.emit(
|
||||
SubAgentEventType.STREAM_TEXT,
|
||||
AgentEventType.STREAM_TEXT,
|
||||
createStreamTextEvent({ text: 'Second chunk ' }),
|
||||
);
|
||||
eventEmitter.emit(
|
||||
SubAgentEventType.STREAM_TEXT,
|
||||
AgentEventType.STREAM_TEXT,
|
||||
createStreamTextEvent({ text: 'Third chunk' }),
|
||||
);
|
||||
|
||||
|
|
@ -640,7 +640,7 @@ describe('SubAgentTracker', () => {
|
|||
text: 'This should not be emitted',
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.STREAM_TEXT, event);
|
||||
eventEmitter.emit(AgentEventType.STREAM_TEXT, event);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
|
|
@ -655,7 +655,7 @@ describe('SubAgentTracker', () => {
|
|||
thought: true,
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.STREAM_TEXT, event);
|
||||
eventEmitter.emit(AgentEventType.STREAM_TEXT, event);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(sendUpdateSpy).toHaveBeenCalled();
|
||||
|
|
@ -680,7 +680,7 @@ describe('SubAgentTracker', () => {
|
|||
thought: false,
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.STREAM_TEXT, event);
|
||||
eventEmitter.emit(AgentEventType.STREAM_TEXT, event);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(sendUpdateSpy).toHaveBeenCalled();
|
||||
|
|
@ -705,7 +705,7 @@ describe('SubAgentTracker', () => {
|
|||
text: 'Default behavior text.',
|
||||
});
|
||||
|
||||
eventEmitter.emit(SubAgentEventType.STREAM_TEXT, event);
|
||||
eventEmitter.emit(AgentEventType.STREAM_TEXT, event);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(sendUpdateSpy).toHaveBeenCalled();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue