fix(cli): provide JSON output for AgentExecutionStopped in non-interactive mode (#26504)

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
cynthialong0-0
2026-05-05 17:33:31 -07:00
committed by GitHub
parent 80d2690540
commit 469092a72c
3 changed files with 155 additions and 0 deletions
@@ -2045,6 +2045,77 @@ describe('runNonInteractive', () => {
expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(1);
});
it('should write JSON output when AgentExecutionStopped event occurs', async () => {
vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
vi.spyOn(uiTelemetryService, 'getMetrics').mockReturnValue(
MOCK_SESSION_METRICS,
);
const events: ServerGeminiStreamEvent[] = [
{ type: GeminiEventType.Content, value: 'Partial content' },
{
type: GeminiEventType.AgentExecutionStopped,
value: { reason: 'Stopped by hook' },
},
];
mockGeminiClient.sendMessageStream.mockReturnValue(
createStreamFromEvents(events),
);
await runNonInteractive({
config: mockConfig,
settings: mockSettings,
input: 'test stop',
prompt_id: 'prompt-id-stop-json',
});
expect(processStdoutSpy).toHaveBeenCalledWith(
JSON.stringify(
{
session_id: 'test-session-id',
response: 'Partial content',
stats: MOCK_SESSION_METRICS,
warnings: ['Agent execution stopped: Stopped by hook'],
},
null,
2,
),
);
});
it('should emit result event when AgentExecutionStopped event occurs in streaming JSON mode', async () => {
vi.mocked(mockConfig.getOutputFormat).mockReturnValue(
OutputFormat.STREAM_JSON,
);
vi.spyOn(uiTelemetryService, 'getMetrics').mockReturnValue(
MOCK_SESSION_METRICS,
);
const events: ServerGeminiStreamEvent[] = [
{ type: GeminiEventType.Content, value: 'Partial content' },
{
type: GeminiEventType.AgentExecutionStopped,
value: { reason: 'Stopped by hook' },
},
];
mockGeminiClient.sendMessageStream.mockReturnValue(
createStreamFromEvents(events),
);
await runNonInteractive({
config: mockConfig,
settings: mockSettings,
input: 'test stop',
prompt_id: 'prompt-id-stop-stream',
});
const output = getWrittenOutput();
expect(output).toContain('"type":"result"');
expect(output).toContain('"status":"success"');
});
it('should handle AgentExecutionBlocked event', async () => {
const allEvents: ServerGeminiStreamEvent[] = [
{
+14
View File
@@ -400,6 +400,20 @@ export async function runNonInteractive(
durationMs,
),
});
} else if (config.getOutputFormat() === OutputFormat.JSON) {
const formatter = new JsonFormatter();
const stats = uiTelemetryService.getMetrics();
textOutput.write(
formatter.format(
config.getSessionId(),
responseText,
stats,
undefined,
[...warnings, stopMessage],
),
);
} else {
textOutput.ensureTrailingNewline(); // Ensure a final newline
}
return;
} else if (event.type === GeminiEventType.AgentExecutionBlocked) {
@@ -2208,6 +2208,76 @@ describe('runNonInteractive', () => {
expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(1);
});
it('should write JSON output when AgentExecutionStopped event occurs', async () => {
vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
vi.spyOn(uiTelemetryService, 'getMetrics').mockReturnValue(
MOCK_SESSION_METRICS,
);
const events: ServerGeminiStreamEvent[] = [
{ type: GeminiEventType.Content, value: 'Partial content' },
{
type: GeminiEventType.AgentExecutionStopped,
value: { reason: 'Stopped by hook' },
},
];
mockGeminiClient.sendMessageStream.mockReturnValue(
createStreamFromEvents(events),
);
await runNonInteractive({
config: mockConfig,
settings: mockSettings,
input: 'test stop',
prompt_id: 'prompt-id-stop-json',
});
expect(processStdoutSpy).toHaveBeenCalledWith(
JSON.stringify(
{
session_id: 'test-session-id',
response: 'Partial content',
stats: MOCK_SESSION_METRICS,
},
null,
2,
),
);
});
it('should emit result event when AgentExecutionStopped event occurs in streaming JSON mode', async () => {
vi.mocked(mockConfig.getOutputFormat).mockReturnValue(
OutputFormat.STREAM_JSON,
);
vi.spyOn(uiTelemetryService, 'getMetrics').mockReturnValue(
MOCK_SESSION_METRICS,
);
const events: ServerGeminiStreamEvent[] = [
{ type: GeminiEventType.Content, value: 'Partial content' },
{
type: GeminiEventType.AgentExecutionStopped,
value: { reason: 'Stopped by hook' },
},
];
mockGeminiClient.sendMessageStream.mockReturnValue(
createStreamFromEvents(events),
);
await runNonInteractive({
config: mockConfig,
settings: mockSettings,
input: 'test stop',
prompt_id: 'prompt-id-stop-stream',
});
const output = getWrittenOutput();
expect(output).toContain('"type":"result"');
expect(output).toContain('"status":"success"');
});
it('should handle AgentExecutionBlocked event', async () => {
const allEvents: ServerGeminiStreamEvent[] = [
{