Files
gemini-cli/packages/cli/src/ui/hooks/toolMapping.ts

109 lines
3.0 KiB
TypeScript

/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {
type ToolCall,
type SerializableConfirmationDetails,
type ToolResultDisplay,
debugLogger,
CoreToolCallStatus,
} from '@google/gemini-cli-core';
import {
type HistoryItemToolGroup,
type IndividualToolCallDisplay,
} from '../types.js';
/**
* Transforms `ToolCall` objects into `HistoryItemToolGroup` objects for UI
* display. This is a pure projection layer and does not track interaction
* state.
*/
export function mapToDisplay(
toolOrTools: ToolCall[] | ToolCall,
options: { borderTop?: boolean; borderBottom?: boolean } = {},
): HistoryItemToolGroup {
const toolCalls = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];
const { borderTop, borderBottom } = options;
const toolDisplays = toolCalls.map((call): IndividualToolCallDisplay => {
let description: string;
let renderOutputAsMarkdown = false;
const displayName = call.tool?.displayName ?? call.request.name;
if (call.status === CoreToolCallStatus.Error) {
description = JSON.stringify(call.request.args);
} else {
description = call.invocation.getDescription();
renderOutputAsMarkdown = call.tool.isOutputMarkdown;
}
const baseDisplayProperties = {
callId: call.request.callId,
name: displayName,
description,
renderOutputAsMarkdown,
};
let resultDisplay: ToolResultDisplay | undefined = undefined;
let confirmationDetails: SerializableConfirmationDetails | undefined =
undefined;
let outputFile: string | undefined = undefined;
let ptyId: number | undefined = undefined;
let correlationId: string | undefined = undefined;
switch (call.status) {
case CoreToolCallStatus.Success:
resultDisplay = call.response.resultDisplay;
outputFile = call.response.outputFile;
break;
case CoreToolCallStatus.Error:
case CoreToolCallStatus.Cancelled:
resultDisplay = call.response.resultDisplay;
break;
case CoreToolCallStatus.AwaitingApproval:
correlationId = call.correlationId;
// Pass through details. Context handles dispatch (callback vs bus).
confirmationDetails = call.confirmationDetails;
break;
case CoreToolCallStatus.Executing:
resultDisplay = call.liveOutput;
ptyId = call.pid;
break;
case CoreToolCallStatus.Scheduled:
case CoreToolCallStatus.Validating:
break;
default: {
const exhaustiveCheck: never = call;
debugLogger.warn(
`Unhandled tool call status in mapper: ${
(exhaustiveCheck as ToolCall).status
}`,
);
break;
}
}
return {
...baseDisplayProperties,
status: call.status,
resultDisplay,
confirmationDetails,
outputFile,
ptyId,
correlationId,
approvalMode: call.approvalMode,
};
});
return {
type: 'tool_group',
tools: toolDisplays,
borderTop,
borderBottom,
};
}