Compare commits

...

1 Commits

Author SHA1 Message Date
LaZzyMan
6cb4371373 test: fix nonInteractive tool output test assertions
Update test to match new tool output format that includes tool displayName and description in text mode.
2026-01-13 16:05:02 +08:00
2 changed files with 65 additions and 6 deletions

View File

@@ -1826,7 +1826,7 @@ describe('runNonInteractive', () => {
);
});
it('should print tool output to console in text mode (non-Task tools)', async () => {
it('should print tool description and output to console in text mode (non-Task tools)', async () => {
// Test that tool output is printed to stdout in text mode
const toolCallEvent: ServerGeminiStreamEvent = {
type: GeminiEventType.ToolCallRequest,
@@ -1839,6 +1839,21 @@ describe('runNonInteractive', () => {
},
};
// Mock the tool registry to return a tool with displayName and build method
const mockTool = {
displayName: 'Shell',
build: (args: Record<string, unknown>) => {
// @ts-expect-error - accessing indexed property for test mock
const command: string = args.command || '';
return {
getDescription: () => String(command),
};
},
};
vi.mocked(mockToolRegistry.getTool).mockReturnValue(
mockTool as unknown as ReturnType<typeof mockToolRegistry.getTool>,
);
// Mock tool execution with outputUpdateHandler being called
mockCoreExecuteToolCall.mockImplementation(
async (_config, _request, _signal, options) => {
@@ -1901,8 +1916,15 @@ describe('runNonInteractive', () => {
);
// Verify tool output was written to stdout
expect(processStdoutSpy).toHaveBeenCalledWith('Package outdated\n');
expect(processStdoutSpy).toHaveBeenCalledWith('npm@1.0.0 -> npm@2.0.0\n');
// First call should be tool description
expect(processStdoutSpy).toHaveBeenCalledWith('Shell: npm outdated');
expect(processStdoutSpy).toHaveBeenCalledWith('\n');
// Then the actual tool output
expect(processStdoutSpy).toHaveBeenCalledWith('Package outdated');
expect(processStdoutSpy).toHaveBeenCalledWith('npm@1.0.0 -> npm@2.0.0');
// Final newline after tool execution
expect(processStdoutSpy).toHaveBeenCalledWith('\n');
// And the model's response
expect(processStdoutSpy).toHaveBeenCalledWith('Dependencies checked');
});
});

View File

@@ -351,19 +351,51 @@ export async function runNonInteractive(
const taskToolProgressHandler = taskToolProgress?.handler;
// Create output handler for non-Task tools in text mode (for console output)
const toolOutputLines: string[] = [];
const nonTaskOutputHandler =
!isTaskTool && !adapter
? (callId: string, outputChunk: ToolResultDisplay) => {
const toolRegistry = config.getToolRegistry();
const tool = toolRegistry.getTool(finalRequestInfo.name);
if (tool) {
try {
const invocation = tool.build(finalRequestInfo.args);
const description = invocation.getDescription();
toolOutputLines.push(
`${tool.displayName}: ${description}`,
);
toolOutputLines.push('\n');
} catch {
// If we can't build invocation, just show tool name
toolOutputLines.push(`${tool.displayName}`);
toolOutputLines.push('\n');
}
}
// Print tool output to console in text mode
if (typeof outputChunk === 'string') {
process.stdout.write(outputChunk);
// Indent output lines to show they're part of the tool execution
const lines = outputChunk.split('\n');
for (let i = 0; i < lines.length; i++) {
if (i === lines.length - 1 && lines[i] === '') {
// Skip trailing empty line
continue;
}
toolOutputLines.push(lines[i]);
}
} else if (
outputChunk &&
typeof outputChunk === 'object' &&
'ansiOutput' in outputChunk
) {
// Handle ANSI output - just print as string for now
process.stdout.write(String(outputChunk.ansiOutput));
// Handle ANSI output - indent it similarly
const ansiStr = String(outputChunk.ansiOutput);
const lines = ansiStr.split('\n');
for (let i = 0; i < lines.length; i++) {
if (i === lines.length - 1 && lines[i] === '') {
continue;
}
toolOutputLines.push(lines[i]);
}
}
}
: undefined;
@@ -386,6 +418,11 @@ export async function runNonInteractive(
: undefined,
);
if (toolOutputLines.length > 0) {
toolOutputLines.forEach((line) => process.stdout.write(line));
process.stdout.write('\n');
}
// Note: In JSON mode, subagent messages are automatically added to the main
// adapter's messages array and will be output together on emitResult()