diff --git a/packages/cli/src/ui/components/messages/ToolGroupDisplay.test.tsx b/packages/cli/src/ui/components/messages/ToolGroupDisplay.test.tsx
new file mode 100644
index 0000000000..96cefc0962
--- /dev/null
+++ b/packages/cli/src/ui/components/messages/ToolGroupDisplay.test.tsx
@@ -0,0 +1,304 @@
+/**
+ * @license
+ * Copyright 2026 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { describe, it, expect, vi, afterEach } from 'vitest';
+import { renderWithProviders } from '../../../test-utils/render.js';
+import { createMockSettings } from '../../../test-utils/settings.js';
+import { ToolGroupDisplay } from './ToolGroupDisplay.js';
+import {
+ CoreToolCallStatus,
+ UPDATE_TOPIC_DISPLAY_NAME,
+} from '@google/gemini-cli-core';
+import type {
+ HistoryItemToolDisplayGroup,
+ ToolDisplayItem,
+} from '../../types.js';
+
+describe('', () => {
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ const createToolItem = (
+ overrides: Partial = {},
+ ): ToolDisplayItem => ({
+ status: CoreToolCallStatus.Success,
+ name: 'test-tool',
+ description: 'Test description',
+ ...overrides,
+ });
+
+ const createHistoryItem = (
+ tools: ToolDisplayItem[],
+ overrides: Partial = {},
+ ): HistoryItemToolDisplayGroup => ({
+ type: 'tool_display_group',
+ tools,
+ borderColor: 'gray',
+ borderDimColor: true,
+ borderTop: true,
+ borderBottom: true,
+ ...overrides,
+ });
+
+ const fullVerbositySettings = createMockSettings({
+ ui: { errorVerbosity: 'full', compactToolOutput: false },
+ });
+ const compactSettings = createMockSettings({
+ ui: { compactToolOutput: true },
+ });
+
+ describe('Golden Snapshots', () => {
+ it('renders notices at the top (hoisting)', async () => {
+ const tools = [
+ createToolItem({ name: 'Tool A', format: 'box' }),
+ createToolItem({
+ name: UPDATE_TOPIC_DISPLAY_NAME,
+ description: 'New Topic',
+ format: 'notice',
+ }),
+ ];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ { settings: fullVerbositySettings },
+ );
+
+ const output = lastFrame();
+ // Notice should be before Tool A
+ expect(output.indexOf(UPDATE_TOPIC_DISPLAY_NAME)).toBeLessThan(
+ output.indexOf('Tool A'),
+ );
+ expect(output).toMatchSnapshot();
+ });
+
+ it('renders in compact mode (no box borders)', async () => {
+ const tools = [
+ createToolItem({ name: 'Tool A' }),
+ createToolItem({ name: 'Tool B' }),
+ ];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ { settings: compactSettings },
+ );
+
+ const output = lastFrame();
+ // Should not contain box drawing characters for the outer box
+ expect(output).not.toContain('╭');
+ expect(output).not.toContain('╰');
+ expect(output).toMatchSnapshot();
+ });
+
+ it('renders in boxed mode (full verbosity)', async () => {
+ const tools = [createToolItem({ name: 'Tool A' })];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ { settings: fullVerbositySettings },
+ );
+
+ const output = lastFrame();
+ expect(output).toContain('╭');
+ expect(output).toContain('╰');
+ expect(output).toMatchSnapshot();
+ });
+
+ it('renders standalone notices without a box', async () => {
+ const tools = [
+ createToolItem({
+ name: 'Notice Only',
+ format: 'notice',
+ }),
+ ];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ { settings: fullVerbositySettings },
+ );
+
+ const output = lastFrame();
+ expect(output).not.toContain('╭');
+ expect(output).toMatchSnapshot();
+ });
+
+ it('renders error message when display info is missing', async () => {
+ // Create an item that effectively has no display properties
+ const tools = [
+ {
+ status: CoreToolCallStatus.Executing,
+ originalRequestName: 'missing-tool',
+ } as ToolDisplayItem,
+ ];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ );
+
+ const output = lastFrame();
+ expect(output).toContain('Error: Tool display missing');
+ expect(output).toMatchSnapshot();
+ });
+
+ it('hides tools awaiting approval (confirming)', async () => {
+ const tools = [
+ createToolItem({
+ name: 'Confirming Tool',
+ status: CoreToolCallStatus.AwaitingApproval,
+ }),
+ ];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ );
+
+ // Should render nothing (null)
+ expect(lastFrame({ allowEmpty: true })).toBe('');
+ });
+ });
+
+ describe('Result Formatting', () => {
+ it('renders text results with summary below', async () => {
+ const tools = [
+ createToolItem({
+ result: { type: 'text', text: 'Detailed output' },
+ resultSummary: 'Short summary',
+ format: 'box',
+ }),
+ ];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ { settings: fullVerbositySettings },
+ );
+
+ const output = lastFrame();
+ expect(output).toContain('Detailed output');
+ expect(output).toContain('Short summary');
+ // Summary should be below detailed output
+ expect(output.indexOf('Detailed output')).toBeLessThan(
+ output.indexOf('Short summary'),
+ );
+ expect(output).toMatchSnapshot();
+ });
+
+ it('renders compact tools with summary on same line', async () => {
+ const tools = [
+ createToolItem({
+ resultSummary: 'Success summary',
+ format: 'compact',
+ }),
+ ];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ );
+
+ const output = lastFrame();
+ expect(output).toContain('→ Success summary');
+ expect(output).toMatchSnapshot();
+ });
+
+ it('renders placeholder for diff results', async () => {
+ const tools = [
+ createToolItem({
+ result: {
+ type: 'diff',
+ beforeText: 'old',
+ afterText: 'new',
+ path: 'file.ts',
+ },
+ }),
+ ];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ { settings: fullVerbositySettings },
+ );
+
+ const output = lastFrame();
+ expect(output).toContain('[Diff Display: 3 -> 3 chars]');
+ expect(output).toMatchSnapshot();
+ });
+
+ it('renders placeholder for terminal results', async () => {
+ const tools = [
+ createToolItem({
+ result: { type: 'terminal' },
+ }),
+ ];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ { settings: fullVerbositySettings },
+ );
+
+ expect(lastFrame()).toContain('[Terminal Output]');
+ });
+
+ it('renders placeholder for agent results', async () => {
+ const tools = [
+ createToolItem({
+ result: { type: 'agent', threadId: 'thread-123' },
+ }),
+ ];
+ const item = createHistoryItem(tools);
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ { settings: fullVerbositySettings },
+ );
+
+ expect(lastFrame()).toContain('[Subagent: thread-123]');
+ });
+ });
+
+ describe('Border & Margin Logic', () => {
+ it('forces top border on box when it follows a notice', async () => {
+ const tools = [
+ createToolItem({ name: 'Notice', format: 'notice' }),
+ createToolItem({ name: 'Tool in Box', format: 'box' }),
+ ];
+ // Even if item.borderTop is false (continuing a group),
+ // the box should have a top border because it follows a notice.
+ const item = createHistoryItem(tools, { borderTop: false });
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ { settings: fullVerbositySettings },
+ );
+
+ const output = lastFrame();
+ expect(output).toContain('Notice');
+ expect(output).toContain('╭'); // Top border for the box
+ expect(output).toMatchSnapshot();
+ });
+
+ it('applies bottom margin in compact mode when group is at boundary', async () => {
+ const tools = [createToolItem({ name: 'Compact Tool' })];
+ const item = createHistoryItem(tools, { borderBottom: true });
+
+ const { lastFrame } = await renderWithProviders(
+ ,
+ { settings: compactSettings },
+ );
+
+ // This is hard to assert via string check, but ensure match snapshot
+ // captures the vertical spacing.
+ expect(lastFrame()).toMatchSnapshot();
+ });
+ });
+});
diff --git a/packages/cli/src/ui/components/messages/ToolGroupDisplay.tsx b/packages/cli/src/ui/components/messages/ToolGroupDisplay.tsx
index 8e937964c5..137e6391d4 100644
--- a/packages/cli/src/ui/components/messages/ToolGroupDisplay.tsx
+++ b/packages/cli/src/ui/components/messages/ToolGroupDisplay.tsx
@@ -6,6 +6,7 @@
import type React from 'react';
import { Box, Text } from 'ink';
+import { CoreToolCallStatus } from '@google/gemini-cli-core';
import type {
HistoryItem,
HistoryItemWithoutId,
@@ -35,14 +36,23 @@ export const ToolGroupDisplay: React.FC = ({
const { tools, borderColor, borderDimColor, borderTop, borderBottom } =
item as HistoryItemToolDisplayGroup;
- const noticeTools = tools.filter((t) => t.format === 'notice');
- const otherTools = tools.filter(
+ const visibleTools = tools.filter(
+ (t) => t.status !== CoreToolCallStatus.AwaitingApproval,
+ );
+
+ const noticeTools = visibleTools.filter((t) => t.format === 'notice');
+ const otherTools = visibleTools.filter(
(t) => t.format !== 'notice' && t.format !== 'hidden',
);
const hasOtherTools = otherTools.length > 0;
const isClosingSlice = tools.length === 0 && borderBottom;
+ // If no tools are visible and it's not an explicit closing slice, hide the group
+ if (visibleTools.length === 0 && !isClosingSlice) {
+ return null;
+ }
+
// Standard view behavior: If compact mode is enabled, non-notice tools
// are typically rendered without an outer box.
const shouldShowBox =