mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-17 05:20:23 -07:00
feat(cli): support 'notice' format and refine declarative tool rendering
Key enhancements: - Updated `UpdateTopicTool` to provide declarative 'notice' display info, using dynamic descriptions for high-fidelity output. - Refined `ToolGroupDisplay` to 'hoist' notice-format tools to the top of the group. - Implemented conditional boxing in `ToolGroupDisplay`: borders are now suppressed in compact mode, matching the standard CLI view. - Added support for `resultSummary` rendering at the bottom of text results in boxed mode. - Improved `useAgentStream` to wait for turn completion before pushing tools to history, ensuring all notices for a turn are correctly grouped and hoisted together. - Fixed margin and border logic to handle seamless transitions between notices and tool boxes.
This commit is contained in:
@@ -77,6 +77,8 @@ export const useAgentStream = ({
|
||||
useStateAndRef<Set<string>>(new Set());
|
||||
const [_isFirstToolInGroup, isFirstToolInGroupRef, setIsFirstToolInGroup] =
|
||||
useStateAndRef<boolean>(true);
|
||||
const [_hasEmittedBoxInTurn, hasEmittedBoxInTurnRef, setHasEmittedBoxInTurn] =
|
||||
useStateAndRef<boolean>(false);
|
||||
|
||||
const { startNewPrompt } = useSessionStats();
|
||||
|
||||
@@ -381,32 +383,27 @@ export const useAgentStream = ({
|
||||
|
||||
// Push completed tools to history
|
||||
useEffect(() => {
|
||||
const toolsToPush: IndividualToolCallDisplay[] = [];
|
||||
for (let i = 0; i < trackedTools.length; i++) {
|
||||
const tc = trackedTools[i];
|
||||
if (pushedToolCallIdsRef.current.has(tc.callId)) continue;
|
||||
if (trackedTools.length === 0) return;
|
||||
|
||||
if (
|
||||
// We only push to history once all currently known tools in the turn are terminal.
|
||||
// This allows ToolGroupDisplay to correctly hoist ALL notices (topics) for the turn.
|
||||
const allTerminal = trackedTools.every(
|
||||
(tc) =>
|
||||
tc.status === 'success' ||
|
||||
tc.status === 'error' ||
|
||||
tc.status === 'cancelled'
|
||||
) {
|
||||
toolsToPush.push(tc);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tc.status === 'cancelled',
|
||||
);
|
||||
|
||||
if (toolsToPush.length > 0) {
|
||||
const toolsToPush = trackedTools.filter(
|
||||
(tc) => !pushedToolCallIdsRef.current.has(tc.callId),
|
||||
);
|
||||
|
||||
if (allTerminal && toolsToPush.length > 0) {
|
||||
const newPushed = new Set(pushedToolCallIdsRef.current);
|
||||
for (const tc of toolsToPush) {
|
||||
newPushed.add(tc.callId);
|
||||
}
|
||||
|
||||
const isLastInBatch =
|
||||
toolsToPush[toolsToPush.length - 1] ===
|
||||
trackedTools[trackedTools.length - 1];
|
||||
|
||||
const appearance = getToolGroupBorderAppearance(
|
||||
{ type: 'tool_group', tools: trackedTools },
|
||||
activePtyId,
|
||||
@@ -415,6 +412,13 @@ export const useAgentStream = ({
|
||||
backgroundTasks,
|
||||
);
|
||||
|
||||
const hasBoxInBatch = toolsToPush.some(
|
||||
(tc) => tc.display?.format !== 'notice',
|
||||
);
|
||||
const shouldStartNewBlock =
|
||||
isFirstToolInGroupRef.current ||
|
||||
(!hasEmittedBoxInTurnRef.current && hasBoxInBatch);
|
||||
|
||||
const historyItem: HistoryItemToolDisplayGroup = {
|
||||
type: 'tool_display_group',
|
||||
tools: toolsToPush.map((tc) => ({
|
||||
@@ -424,21 +428,27 @@ export const useAgentStream = ({
|
||||
status: tc.status,
|
||||
originalRequestName: tc.originalRequestName,
|
||||
})),
|
||||
borderTop: isFirstToolInGroupRef.current,
|
||||
borderBottom: isLastInBatch,
|
||||
borderTop: shouldStartNewBlock,
|
||||
borderBottom: true,
|
||||
...appearance,
|
||||
};
|
||||
|
||||
addItem(historyItem);
|
||||
setPushedToolCallIds(newPushed);
|
||||
|
||||
if (hasBoxInBatch) {
|
||||
setHasEmittedBoxInTurn(true);
|
||||
}
|
||||
setIsFirstToolInGroup(false);
|
||||
}
|
||||
}, [
|
||||
trackedTools,
|
||||
pushedToolCallIdsRef,
|
||||
isFirstToolInGroupRef,
|
||||
hasEmittedBoxInTurnRef,
|
||||
setPushedToolCallIds,
|
||||
setIsFirstToolInGroup,
|
||||
setHasEmittedBoxInTurn,
|
||||
addItem,
|
||||
activePtyId,
|
||||
isShellFocused,
|
||||
@@ -447,7 +457,7 @@ export const useAgentStream = ({
|
||||
|
||||
const pendingToolGroupItems = useMemo((): HistoryItemWithoutId[] => {
|
||||
const remainingTools = trackedTools.filter(
|
||||
(tc) => !pushedToolCallIds.has(tc.callId),
|
||||
(tc) => !pushedToolCallIdsRef.current.has(tc.callId),
|
||||
);
|
||||
|
||||
const items: HistoryItemWithoutId[] = [];
|
||||
@@ -461,6 +471,13 @@ export const useAgentStream = ({
|
||||
);
|
||||
|
||||
if (remainingTools.length > 0) {
|
||||
const hasBoxInPending = remainingTools.some(
|
||||
(tc) => tc.display?.format !== 'notice',
|
||||
);
|
||||
const shouldStartNewBlock =
|
||||
pushedToolCallIds.size === 0 ||
|
||||
(!hasEmittedBoxInTurnRef.current && hasBoxInPending);
|
||||
|
||||
items.push({
|
||||
type: 'tool_display_group',
|
||||
tools: remainingTools.map((tc) => ({
|
||||
@@ -470,7 +487,7 @@ export const useAgentStream = ({
|
||||
status: tc.status,
|
||||
originalRequestName: tc.originalRequestName,
|
||||
})),
|
||||
borderTop: pushedToolCallIds.size === 0,
|
||||
borderTop: shouldStartNewBlock,
|
||||
borderBottom: false,
|
||||
...appearance,
|
||||
});
|
||||
@@ -510,6 +527,8 @@ export const useAgentStream = ({
|
||||
}, [
|
||||
trackedTools,
|
||||
pushedToolCallIds,
|
||||
pushedToolCallIdsRef,
|
||||
hasEmittedBoxInTurnRef,
|
||||
activePtyId,
|
||||
isShellFocused,
|
||||
backgroundTasks,
|
||||
|
||||
Reference in New Issue
Block a user