Compare commits

...

6 Commits

10 changed files with 30 additions and 29 deletions
@@ -76,7 +76,7 @@ export const DetailedMessagesDisplay: React.FC<
> >
<Box marginBottom={1}> <Box marginBottom={1}>
<Text bold color={theme.text.primary}> <Text bold color={theme.text.primary}>
Debug Console <Text color={theme.text.secondary}>(F12 to close)</Text> Debug console <Text color={theme.text.secondary}>(F12 to close)</Text>
</Text> </Text>
</Box> </Box>
<Box height={maxHeight} width={width - borderAndPadding}> <Box height={maxHeight} width={width - borderAndPadding}>
@@ -120,7 +120,7 @@ describe('<LoadingIndicator />', () => {
unmount(); unmount();
}); });
it('should display the elapsedTime correctly in human-readable format', async () => { it('should display the elapsedTime correctly in minutes and OMIT seconds for > 1m', async () => {
const props = { const props = {
currentLoadingPhrase: 'Thinking...', currentLoadingPhrase: 'Thinking...',
elapsedTime: 125, elapsedTime: 125,
@@ -130,7 +130,9 @@ describe('<LoadingIndicator />', () => {
StreamingState.Responding, StreamingState.Responding,
); );
await waitUntilReady(); await waitUntilReady();
expect(lastFrame()).toContain('(esc to cancel, 2m 5s)'); const output = lastFrame();
expect(output).toContain('(esc to cancel, 2m)');
expect(output).not.toContain('5s');
unmount(); unmount();
}); });
@@ -258,15 +260,13 @@ describe('<LoadingIndicator />', () => {
const output = lastFrame(); const output = lastFrame();
expect(output).toBeDefined(); expect(output).toBeDefined();
if (output) { if (output) {
// Should NOT contain "Thinking... " prefix because the subject already starts with "Thinking" expect(output).not.toContain('Gemini is thinking');
expect(output).not.toContain('Thinking... Thinking');
expect(output).toContain('Thinking about something...'); expect(output).toContain('Thinking about something...');
expect(output).not.toContain('and other stuff.');
} }
unmount(); unmount();
}); });
it('should NOT prepend "Thinking... " even if the subject does not start with "Thinking"', async () => { it('should NOT prepend "Thinking... " if a subject is provided', async () => {
const props = { const props = {
thought: { thought: {
subject: 'Planning the response...', subject: 'Planning the response...',
@@ -11,7 +11,6 @@ import { theme } from '../semantic-colors.js';
import { useStreamingContext } from '../contexts/StreamingContext.js'; import { useStreamingContext } from '../contexts/StreamingContext.js';
import { StreamingState } from '../types.js'; import { StreamingState } from '../types.js';
import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js'; import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
import { formatDuration } from '../utils/formatters.js';
import { useTerminalSize } from '../hooks/useTerminalSize.js'; import { useTerminalSize } from '../hooks/useTerminalSize.js';
import { isNarrowWidth } from '../utils/isNarrowWidth.js'; import { isNarrowWidth } from '../utils/isNarrowWidth.js';
import { INTERACTIVE_SHELL_WAITING_PHRASE } from '../hooks/usePhraseCycler.js'; import { INTERACTIVE_SHELL_WAITING_PHRASE } from '../hooks/usePhraseCycler.js';
@@ -74,7 +73,11 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
const cancelAndTimerContent = const cancelAndTimerContent =
showCancelAndTimer && showCancelAndTimer &&
streamingState !== StreamingState.WaitingForConfirmation streamingState !== StreamingState.WaitingForConfirmation
? `(esc to cancel, ${elapsedTime < 60 ? `${elapsedTime}s` : formatDuration(elapsedTime * 1000)})` ? `(esc to cancel, ${
elapsedTime < 60
? `${elapsedTime}s`
: `${Math.floor(elapsedTime / 60)}m`
})`
: null; : null;
const wittyPhraseNode = const wittyPhraseNode =
@@ -3,7 +3,7 @@
exports[`DetailedMessagesDisplay > renders message counts 1`] = ` exports[`DetailedMessagesDisplay > renders message counts 1`] = `
" "
╭──────────────────────────────────────────────────────────────────────────────╮ ╭──────────────────────────────────────────────────────────────────────────────╮
│ Debug Console (F12 to close) │ │ Debug console (F12 to close) │
│ │ │ │
Repeated message (x5) │ Repeated message (x5) │
│ │ │ │
@@ -18,7 +18,7 @@ exports[`DetailedMessagesDisplay > renders message counts 1`] = `
exports[`DetailedMessagesDisplay > renders messages correctly 1`] = ` exports[`DetailedMessagesDisplay > renders messages correctly 1`] = `
" "
╭──────────────────────────────────────────────────────────────────────────────╮ ╭──────────────────────────────────────────────────────────────────────────────╮
│ Debug Console (F12 to close) │ │ Debug console (F12 to close) │
│ │ │ │
Log message │ Log message │
│ ⚠ Warning message │ │ ⚠ Warning message │
@@ -1048,7 +1048,7 @@ describe('useGeminiStream', () => {
).toBe(false); ).toBe(false);
expect( expect(
infoTexts.some((text) => infoTexts.some((text) =>
text.includes('This request failed. Press F12 for diagnostics'), text.includes('Request failed. Press F12 for diagnostics'),
), ),
).toBe(false); ).toBe(false);
}); });
@@ -1108,15 +1108,13 @@ describe('useGeminiStream', () => {
([item]) => (item as { text?: string }).text ?? '', ([item]) => (item as { text?: string }).text ?? '',
); );
const noteIndex = infoTexts.findIndex((text) => const noteIndex = infoTexts.findIndex((text) =>
text.includes( text.includes('Previous tool attempts failed'),
'Some internal tool attempts failed before this final error',
),
); );
const stopIndex = infoTexts.findIndex((text) => const stopIndex = infoTexts.findIndex((text) =>
text.includes('Agent execution stopped: Stop reason from hook'), text.includes('Agent execution stopped: Stop reason from hook'),
); );
const failureHintIndex = infoTexts.findIndex((text) => const failureHintIndex = infoTexts.findIndex((text) =>
text.includes('This request failed. Press F12 for diagnostics'), text.includes('Request failed. Press F12 for diagnostics'),
); );
expect(noteIndex).toBeGreaterThanOrEqual(0); expect(noteIndex).toBeGreaterThanOrEqual(0);
expect(stopIndex).toBeGreaterThanOrEqual(0); expect(stopIndex).toBeGreaterThanOrEqual(0);
+2 -2
View File
@@ -110,9 +110,9 @@ enum StreamProcessingStatus {
} }
const SUPPRESSED_TOOL_ERRORS_NOTE = const SUPPRESSED_TOOL_ERRORS_NOTE =
'Some internal tool attempts failed before this final error. Press F12 for diagnostics, or run /settings and change "Error Verbosity" to full for details.'; 'Previous tool attempts failed. Press F12 for diagnostics or set ui.errorVerbosity=full for details';
const LOW_VERBOSITY_FAILURE_NOTE = const LOW_VERBOSITY_FAILURE_NOTE =
'This request failed. Press F12 for diagnostics, or run /settings and change "Error Verbosity" to full for full details.'; 'Request failed. Press F12 for diagnostics or set ui.errorVerbosity=full for details';
function getBackgroundedToolInfo( function getBackgroundedToolInfo(
toolCall: TrackedCompletedToolCall | TrackedCancelledToolCall, toolCall: TrackedCompletedToolCall | TrackedCancelledToolCall,
@@ -237,7 +237,7 @@ describe('useLoadingIndicator', () => {
retryStatus, retryStatus,
); );
expect(result.current.currentLoadingPhrase).toContain('Trying to reach'); expect(result.current.currentLoadingPhrase).toContain('Retrying');
expect(result.current.currentLoadingPhrase).toContain('Attempt 3/3'); expect(result.current.currentLoadingPhrase).toContain('Attempt 3/3');
}); });
@@ -258,7 +258,7 @@ describe('useLoadingIndicator', () => {
); );
expect(result.current.currentLoadingPhrase).not.toBe( expect(result.current.currentLoadingPhrase).not.toBe(
"This is taking a bit longer, we're still on it.", 'System busy. Retrying...',
); );
}); });
@@ -279,7 +279,7 @@ describe('useLoadingIndicator', () => {
); );
expect(result.current.currentLoadingPhrase).toBe( expect(result.current.currentLoadingPhrase).toBe(
"This is taking a bit longer, we're still on it.", 'System busy. Retrying...',
); );
}); });
@@ -82,9 +82,9 @@ export const useLoadingIndicator = ({
const retryPhrase = retryStatus const retryPhrase = retryStatus
? errorVerbosity === 'low' ? errorVerbosity === 'low'
? retryStatus.attempt >= LOW_VERBOSITY_RETRY_HINT_ATTEMPT_THRESHOLD ? retryStatus.attempt >= LOW_VERBOSITY_RETRY_HINT_ATTEMPT_THRESHOLD
? "This is taking a bit longer, we're still on it." ? 'System busy. Retrying...'
: null : null
: `Trying to reach ${getDisplayString(retryStatus.model)} (Attempt ${retryStatus.attempt + 1}/${retryStatus.maxAttempts})` : `Retrying ${getDisplayString(retryStatus.model)} (Attempt ${retryStatus.attempt + 1}/${retryStatus.maxAttempts})`
: null; : null;
return { return {
+3 -3
View File
@@ -11,8 +11,8 @@ import { AuthType } from '../core/contentGenerator.js';
import type { StructuredError } from '../core/turn.js'; import type { StructuredError } from '../core/turn.js';
describe('parseAndFormatApiError', () => { describe('parseAndFormatApiError', () => {
const vertexMessage = 'request a quota increase through Vertex'; const vertexMessage = 'request a quota increase in Vertex';
const geminiMessage = 'request a quota increase through AI Studio'; const geminiMessage = 'request a quota increase in AI Studio';
it('should format a valid API error JSON', () => { it('should format a valid API error JSON', () => {
const errorMessage = const errorMessage =
@@ -34,7 +34,7 @@ describe('parseAndFormatApiError', () => {
); );
expect(result).toContain('[API Error: Rate limit exceeded'); expect(result).toContain('[API Error: Rate limit exceeded');
expect(result).toContain( expect(result).toContain(
'Possible quota limitations in place or slow response times detected. Switching to the gemini-2.5-flash model', 'System busy or quota limit reached. Switching to gemini-2.5-flash for the rest of this session.',
); );
}); });
+3 -3
View File
@@ -10,13 +10,13 @@ import type { UserTierId } from '../code_assist/types.js';
import { AuthType } from '../core/contentGenerator.js'; import { AuthType } from '../core/contentGenerator.js';
const RATE_LIMIT_ERROR_MESSAGE_USE_GEMINI = const RATE_LIMIT_ERROR_MESSAGE_USE_GEMINI =
'\nPlease wait and try again later. To increase your limits, request a quota increase through AI Studio, or switch to another /auth method'; '\nRate limit reached. Try again later, request a quota increase in AI Studio, or switch /auth method';
const RATE_LIMIT_ERROR_MESSAGE_VERTEX = const RATE_LIMIT_ERROR_MESSAGE_VERTEX =
'\nPlease wait and try again later. To increase your limits, request a quota increase through Vertex, or switch to another /auth method'; '\nRate limit reached. Try again later, request a quota increase in Vertex, or switch /auth method';
const getRateLimitErrorMessageDefault = ( const getRateLimitErrorMessageDefault = (
fallbackModel: string = DEFAULT_GEMINI_FLASH_MODEL, fallbackModel: string = DEFAULT_GEMINI_FLASH_MODEL,
) => ) =>
`\nPossible quota limitations in place or slow response times detected. Switching to the ${fallbackModel} model for the rest of this session.`; `\nSystem busy or quota limit reached. Switching to ${fallbackModel} for the rest of this session.`;
function getRateLimitMessage( function getRateLimitMessage(
authType?: AuthType, authType?: AuthType,