diff --git a/packages/cli/src/ui/components/messages/ToolGroupMessage.test.tsx b/packages/cli/src/ui/components/messages/ToolGroupMessage.test.tsx
index 8971d488d3..d5cbdabe60 100644
--- a/packages/cli/src/ui/components/messages/ToolGroupMessage.test.tsx
+++ b/packages/cli/src/ui/components/messages/ToolGroupMessage.test.tsx
@@ -69,6 +69,11 @@ describe('', () => {
ui: { errorVerbosity: 'full' },
},
});
+ const lowVerbositySettings = createMockSettings({
+ merged: {
+ ui: { errorVerbosity: 'low' },
+ },
+ });
describe('Golden Snapshots', () => {
it('renders single successful tool call', async () => {
@@ -721,6 +726,245 @@ describe('', () => {
expect(lastFrame({ allowEmpty: true })).toBe('');
unmount();
});
+
+ it('does not render a bottom-border fragment when all tools are filtered out', async () => {
+ const toolCalls = [
+ createToolCall({
+ callId: 'hidden-error-tool',
+ name: 'error-tool',
+ status: CoreToolCallStatus.Error,
+ resultDisplay: 'Hidden in low verbosity',
+ isClientInitiated: false,
+ }),
+ ];
+ const item = createItem(toolCalls);
+
+ const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
+ ,
+ {
+ config: baseMockConfig,
+ settings: lowVerbositySettings,
+ },
+ );
+
+ await waitUntilReady();
+ expect(lastFrame({ allowEmpty: true })).toBe('');
+ unmount();
+ });
+
+ it('still renders explicit closing slices for split static/pending groups', async () => {
+ const toolCalls: IndividualToolCallDisplay[] = [];
+ const item = createItem(toolCalls);
+
+ const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
+ ,
+ {
+ config: baseMockConfig,
+ settings: fullVerbositySettings,
+ },
+ );
+
+ await waitUntilReady();
+ expect(lastFrame({ allowEmpty: true })).not.toBe('');
+ unmount();
+ });
+
+ it('does not render a border fragment when plan-mode tools are filtered out', async () => {
+ const toolCalls = [
+ createToolCall({
+ callId: 'plan-write',
+ name: WRITE_FILE_DISPLAY_NAME,
+ approvalMode: ApprovalMode.PLAN,
+ status: CoreToolCallStatus.Success,
+ resultDisplay: 'Plan file written',
+ }),
+ ];
+ const item = createItem(toolCalls);
+
+ const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
+ ,
+ {
+ config: baseMockConfig,
+ settings: fullVerbositySettings,
+ },
+ );
+
+ await waitUntilReady();
+ expect(lastFrame({ allowEmpty: true })).toBe('');
+ unmount();
+ });
+
+ it('does not render a border fragment when only confirming tools are present', async () => {
+ const toolCalls = [
+ createToolCall({
+ callId: 'confirm-only',
+ status: CoreToolCallStatus.AwaitingApproval,
+ confirmationDetails: {
+ type: 'info',
+ title: 'Confirm',
+ prompt: 'Proceed?',
+ },
+ }),
+ ];
+ const item = createItem(toolCalls);
+
+ const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
+ ,
+ {
+ config: baseMockConfig,
+ settings: fullVerbositySettings,
+ },
+ );
+
+ await waitUntilReady();
+ expect(lastFrame({ allowEmpty: true })).toBe('');
+ unmount();
+ });
+
+ it('does not leave a border stub when transitioning from visible to fully filtered tools', async () => {
+ const visibleTools = [
+ createToolCall({
+ callId: 'visible-success',
+ name: 'visible-tool',
+ status: CoreToolCallStatus.Success,
+ resultDisplay: 'visible output',
+ }),
+ ];
+ const hiddenTools = [
+ createToolCall({
+ callId: 'hidden-error',
+ name: 'hidden-error-tool',
+ status: CoreToolCallStatus.Error,
+ resultDisplay: 'hidden output',
+ isClientInitiated: false,
+ }),
+ ];
+
+ const initialItem = createItem(visibleTools);
+ const hiddenItem = createItem(hiddenTools);
+
+ const firstRender = renderWithProviders(
+ ,
+ {
+ config: baseMockConfig,
+ settings: lowVerbositySettings,
+ },
+ );
+ await firstRender.waitUntilReady();
+ expect(firstRender.lastFrame()).toContain('visible-tool');
+ firstRender.unmount();
+
+ const secondRender = renderWithProviders(
+ ,
+ {
+ config: baseMockConfig,
+ settings: lowVerbositySettings,
+ },
+ );
+ await secondRender.waitUntilReady();
+ expect(secondRender.lastFrame({ allowEmpty: true })).toBe('');
+ secondRender.unmount();
+ });
+
+ it('keeps visible tools rendered with many filtered tools (stress case)', async () => {
+ const visibleTool = createToolCall({
+ callId: 'visible-tool',
+ name: 'visible-tool',
+ status: CoreToolCallStatus.Success,
+ resultDisplay: 'visible output',
+ });
+ const hiddenTools = Array.from({ length: 50 }, (_, index) =>
+ createToolCall({
+ callId: `hidden-${index}`,
+ name: `hidden-error-${index}`,
+ status: CoreToolCallStatus.Error,
+ resultDisplay: `hidden output ${index}`,
+ isClientInitiated: false,
+ }),
+ );
+ const toolCalls = [visibleTool, ...hiddenTools];
+ const item = createItem(toolCalls);
+
+ const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
+ ,
+ {
+ config: baseMockConfig,
+ settings: lowVerbositySettings,
+ },
+ );
+
+ await waitUntilReady();
+ const output = lastFrame();
+ expect(output).toContain('visible-tool');
+ expect(output).not.toContain('hidden-error-0');
+ expect(output).not.toContain('hidden-error-49');
+ unmount();
+ });
+
+ it('renders explicit closing slice even at very narrow terminal width', async () => {
+ const toolCalls: IndividualToolCallDisplay[] = [];
+ const item = createItem(toolCalls);
+
+ const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
+ ,
+ {
+ config: baseMockConfig,
+ settings: fullVerbositySettings,
+ },
+ );
+
+ await waitUntilReady();
+ expect(lastFrame({ allowEmpty: true })).not.toBe('');
+ unmount();
+ });
});
describe('Plan Mode Filtering', () => {
diff --git a/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx b/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
index 05f9984d69..01cec31727 100644
--- a/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
+++ b/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
@@ -141,11 +141,15 @@ export const ToolGroupMessage: React.FC = ({
const contentWidth = terminalWidth - TOOL_MESSAGE_HORIZONTAL_MARGIN;
- // If all tools are filtered out (e.g., in-progress AskUser tools, confirming tools),
- // only render if we need to close a border from previous
- // tool groups. borderBottomOverride=true means we must render the closing border;
- // undefined or false means there's nothing to display.
- if (visibleToolCalls.length === 0 && borderBottomOverride !== true) {
+ // If all tools are filtered out (e.g., in-progress AskUser tools, low-verbosity
+ // internal errors, plan-mode hidden write/edit), we should not emit standalone
+ // border fragments. The only case where an empty group should render is the
+ // explicit "closing slice" (tools: []) used to bridge static/pending sections.
+ const isExplicitClosingSlice = allToolCalls.length === 0;
+ if (
+ visibleToolCalls.length === 0 &&
+ (!isExplicitClosingSlice || borderBottomOverride !== true)
+ ) {
return null;
}