fix(core,cli): handle structured tool display properly and prevent metadata overwrite

This addresses PR feedback by:
- Creating a `renderDisplayDiff` utility to render `DisplayDiff` objects.
- Creating a `displayContentToString` utility to safely extract text from any `DisplayContent`.
- Updating non-interactive CLI to use `displayContentToString` to avoid data loss on non-text output.
- Updating `useAgentStream` to use `displayContentToString` to avoid stale UI state for non-text output.
- Shallow merging the `display` object in `useAgentStream` rather than replacing it, preventing loss of display metadata.
This commit is contained in:
Michael Bleigh
2026-04-10 11:42:37 -07:00
parent 7f7e69e6c6
commit fae963ff0e
4 changed files with 105 additions and 18 deletions

View File

@@ -37,6 +37,7 @@ import {
LegacyAgentSession,
ToolErrorType,
geminiPartsToContentParts,
displayContentToString,
debugLogger,
} from '@google/gemini-cli-core';
@@ -470,10 +471,8 @@ export async function runNonInteractive({
case 'tool_response': {
textOutput.ensureTrailingNewline();
if (streamFormatter) {
const displayText =
event.display?.result?.type === 'text'
? event.display.result.text
: undefined;
const display = event.display?.result;
const displayText = displayContentToString(display);
const errorMsg = getTextContent(event.content) ?? 'Tool error';
streamFormatter.emitEvent({
type: JsonStreamEventType.TOOL_RESULT,
@@ -493,10 +492,8 @@ export async function runNonInteractive({
});
}
if (event.isError) {
const displayText =
event.display?.result?.type === 'text'
? event.display.result.text
: undefined;
const display = event.display?.result;
const displayText = displayContentToString(display);
const errorMsg = getTextContent(event.content) ?? 'Tool error';
if (event.data?.['errorType'] === ToolErrorType.STOP_EXECUTION) {

View File

@@ -10,6 +10,7 @@ import {
MessageSenderType,
debugLogger,
geminiPartsToContentParts,
displayContentToString,
parseThought,
CoreToolCallStatus,
type ApprovalMode,
@@ -223,10 +224,9 @@ export const useAgentStream = ({
else if (evtStatus === 'success')
status = CoreToolCallStatus.Success;
const display = event.display?.result;
const liveOutput =
event.display?.result?.type === 'text'
? event.display.result.text
: tc.resultDisplay;
displayContentToString(display) ?? tc.resultDisplay;
const progressMessage =
legacyState?.progressMessage ?? tc.progressMessage;
const progress = legacyState?.progress ?? tc.progress;
@@ -238,7 +238,9 @@ export const useAgentStream = ({
return {
...tc,
status,
display: event.display ?? tc.display,
display: event.display
? { ...tc.display, ...event.display }
: tc.display,
resultDisplay: liveOutput,
progressMessage,
progress,
@@ -257,17 +259,18 @@ export const useAgentStream = ({
const legacyState = event._meta?.legacyState;
const outputFile = legacyState?.outputFile;
const display = event.display?.result;
const resultDisplay =
event.display?.result?.type === 'text'
? event.display.result.text
: tc.resultDisplay;
displayContentToString(display) ?? tc.resultDisplay;
return {
...tc,
status: event.isError
? CoreToolCallStatus.Error
: CoreToolCallStatus.Success,
display: event.display ?? tc.display,
display: event.display
? { ...tc.display, ...event.display }
: tc.display,
resultDisplay,
outputFile,
};