Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 36 additions & 22 deletions packages/cli/src/ui/components/messages/ShellToolMessage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,49 +191,63 @@ describe('<ShellToolMessage />', () => {
10,
8,
false,
true,
],
[
'uses ACTIVE_SHELL_MAX_LINES when availableTerminalHeight is large',
100,
ACTIVE_SHELL_MAX_LINES - 3,
false,
true,
],
[
'uses full availableTerminalHeight when focused in alternate buffer mode',
100,
98, // 100 - 2
true,
false,
],
[
'defaults to ACTIVE_SHELL_MAX_LINES in alternate buffer when availableTerminalHeight is undefined',
undefined,
ACTIVE_SHELL_MAX_LINES - 3,
false,
false,
],
])('%s', async (_, availableTerminalHeight, expectedMaxLines, focused) => {
const { lastFrame, waitUntilReady, unmount } = renderShell(
{
resultDisplay: LONG_OUTPUT,
renderOutputAsMarkdown: false,
availableTerminalHeight,
ptyId: 1,
status: CoreToolCallStatus.Executing,
},
{
useAlternateBuffer: true,
uiState: {
activePtyId: focused ? 1 : 2,
embeddedShellFocused: focused,
])(
'%s',
async (
_,
availableTerminalHeight,
expectedMaxLines,
focused,
constrainHeight,
) => {
const { lastFrame, waitUntilReady, unmount } = renderShell(
{
resultDisplay: LONG_OUTPUT,
renderOutputAsMarkdown: false,
availableTerminalHeight,
ptyId: 1,
status: CoreToolCallStatus.Executing,
},
},
);
{
useAlternateBuffer: true,
uiState: {
activePtyId: focused ? 1 : 2,
embeddedShellFocused: focused,
constrainHeight,
},
},
);

await waitUntilReady();
const frame = lastFrame();
expect(frame.match(/Line \d+/g)?.length).toBe(expectedMaxLines);
expect(frame).toMatchSnapshot();
unmount();
});
await waitUntilReady();
const frame = lastFrame();
expect(frame.match(/Line \d+/g)?.length).toBe(expectedMaxLines);
expect(frame).toMatchSnapshot();
unmount();
},
);

it('fully expands in standard mode when availableTerminalHeight is undefined', async () => {
const { lastFrame, unmount } = renderShell(
Expand Down
101 changes: 100 additions & 1 deletion packages/cli/src/ui/components/messages/ToolMessage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { ToolMessage, type ToolMessageProps } from './ToolMessage.js';
import { describe, it, expect, vi } from 'vitest';
import { StreamingState } from '../../types.js';
import { Text } from 'ink';
import { type AnsiOutput, CoreToolCallStatus } from '@google/gemini-cli-core';
import {
type AnsiOutput,
CoreToolCallStatus,
Kind,
} from '@google/gemini-cli-core';
import { renderWithProviders } from '../../../test-utils/render.js';
import { tryParseJSON } from '../../../utils/jsonoutput.js';

Expand Down Expand Up @@ -435,4 +439,99 @@ describe('<ToolMessage />', () => {
expect(output).toMatchSnapshot();
unmount();
});

describe('Truncation', () => {
it('applies truncation for Kind.Agent when availableTerminalHeight is provided', async () => {
const multilineString = Array.from(
{ length: 30 },
(_, i) => `Line ${i + 1}`,
).join('\n');

const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolMessage
{...baseProps}
kind={Kind.Agent}
resultDisplay={multilineString}
renderOutputAsMarkdown={false}
availableTerminalHeight={40}
/>,
{
uiActions,
uiState: {
streamingState: StreamingState.Idle,
constrainHeight: true,
},
width: 80,
useAlternateBuffer: false,
},
);
await waitUntilReady();
const output = lastFrame();

// Since kind=Kind.Agent and availableTerminalHeight is provided, it should truncate to SUBAGENT_MAX_LINES (15)
// and show the FIRST lines (overflowDirection='bottom')
expect(output).toContain('Line 1');
expect(output).toContain('Line 14');
expect(output).not.toContain('Line 16');
expect(output).not.toContain('Line 30');
unmount();
});

it('does NOT apply truncation for Kind.Agent when availableTerminalHeight is undefined', async () => {
const multilineString = Array.from(
{ length: 30 },
(_, i) => `Line ${i + 1}`,
).join('\n');

const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolMessage
{...baseProps}
kind={Kind.Agent}
resultDisplay={multilineString}
renderOutputAsMarkdown={false}
availableTerminalHeight={undefined}
/>,
{
uiActions,
uiState: { streamingState: StreamingState.Idle },
width: 80,
useAlternateBuffer: false,
},
);
await waitUntilReady();
const output = lastFrame();

expect(output).toContain('Line 1');
expect(output).toContain('Line 30');
unmount();
});

it('does NOT apply truncation for Kind.Read', async () => {
const multilineString = Array.from(
{ length: 30 },
(_, i) => `Line ${i + 1}`,
).join('\n');

const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolMessage
{...baseProps}
kind={Kind.Read}
resultDisplay={multilineString}
renderOutputAsMarkdown={false}
/>,
{
uiActions,
uiState: { streamingState: StreamingState.Idle },
width: 80,
useAlternateBuffer: false,
},
);
await waitUntilReady();
const output = lastFrame();

expect(output).toContain('Line 1');
expect(output).toContain('Line 30');
unmount();
});
});
});
10 changes: 9 additions & 1 deletion packages/cli/src/ui/components/messages/ToolMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import {
useFocusHint,
FocusHint,
} from './ToolShared.js';
import { type Config, CoreToolCallStatus } from '@google/gemini-cli-core';
import { type Config, CoreToolCallStatus, Kind } from '@google/gemini-cli-core';
import { ShellInputPrompt } from '../ShellInputPrompt.js';
import { SUBAGENT_MAX_LINES } from '../../constants.js';

export type { TextEmphasis };

Expand All @@ -45,6 +46,7 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
description,
resultDisplay,
status,
kind,
availableTerminalHeight,
terminalWidth,
emphasis = 'medium',
Expand Down Expand Up @@ -133,6 +135,12 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
terminalWidth={terminalWidth}
renderOutputAsMarkdown={renderOutputAsMarkdown}
hasFocus={isThisShellFocused}
maxLines={
kind === Kind.Agent && availableTerminalHeight !== undefined
? SUBAGENT_MAX_LINES
: undefined
}
overflowDirection={kind === Kind.Agent ? 'bottom' : 'top'}
/>
{isThisShellFocused && config && (
<Box paddingLeft={STATUS_INDICATOR_WIDTH} marginTop={1}>
Expand Down
Loading
Loading