File size: 4,109 Bytes
52efc7b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | /**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {
type ToolCall,
type SerializableConfirmationDetails,
type ToolResultDisplay,
debugLogger,
CoreToolCallStatus,
type SubagentActivityItem,
} from '@google/gemini-cli-core';
import {
type HistoryItemToolGroup,
type IndividualToolCallDisplay,
} from '../types.js';
function hasSubagentHistory(
call: ToolCall,
): call is ToolCall & { subagentHistory: SubagentActivityItem[] } {
return 'subagentHistory' in call && call.subagentHistory !== undefined;
}
/**
* 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;
borderColor?: string;
borderDimColor?: boolean;
} = {},
): HistoryItemToolGroup {
const toolCalls = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];
const { borderTop, borderBottom, borderColor, borderDimColor } = 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,
parentCallId: call.request.parentCallId,
name: displayName,
args: call.request.args,
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;
let progressMessage: string | undefined = undefined;
let progress: number | undefined = undefined;
let progressTotal: number | 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;
progressMessage = call.progressMessage;
progress = call.progress;
progressTotal = call.progressTotal;
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,
isClientInitiated: !!call.request.isClientInitiated,
kind: call.tool?.kind,
resultDisplay,
confirmationDetails,
outputFile,
ptyId,
correlationId,
progressMessage,
progress,
progressTotal,
approvalMode: call.approvalMode,
originalRequestName: call.request.originalRequestName,
subagentHistory: hasSubagentHistory(call)
? call.subagentHistory
: undefined,
};
});
return {
type: 'tool_group',
tools: toolDisplays,
borderTop,
borderBottom,
borderColor,
borderDimColor,
};
}
|