diff --git a/packages/cli/src/nonInteractiveCli.ts b/packages/cli/src/nonInteractiveCli.ts index bae45e1e32..bf1f4e4d6d 100644 --- a/packages/cli/src/nonInteractiveCli.ts +++ b/packages/cli/src/nonInteractiveCli.ts @@ -15,7 +15,14 @@ import { isSlashCommand } from './ui/utils/commandUtils.js'; import type { LoadedSettings } from './config/settings.js'; import { convertSessionToClientHistory, + FatalError, + FatalAuthenticationError, FatalInputError, + FatalSandboxError, + FatalConfigError, + FatalTurnLimitedError, + FatalToolExecutionError, + FatalCancellationError, promptIdContext, OutputFormat, JsonFormatter, @@ -341,20 +348,60 @@ export async function runNonInteractive({ }; const reconstructFatalError = (event: AgentEvent<'error'>): Error => { - const errToThrow = new Error(event.message); const errorMeta = event._meta; + const name = + typeof errorMeta?.['errorName'] === 'string' + ? errorMeta['errorName'] + : undefined; + + let errToThrow: Error; + switch (name) { + case 'FatalAuthenticationError': + errToThrow = new FatalAuthenticationError(event.message); + break; + case 'FatalInputError': + errToThrow = new FatalInputError(event.message); + break; + case 'FatalSandboxError': + errToThrow = new FatalSandboxError(event.message); + break; + case 'FatalConfigError': + errToThrow = new FatalConfigError(event.message); + break; + case 'FatalTurnLimitedError': + errToThrow = new FatalTurnLimitedError(event.message); + break; + case 'FatalToolExecutionError': + errToThrow = new FatalToolExecutionError(event.message); + break; + case 'FatalCancellationError': + errToThrow = new FatalCancellationError(event.message); + break; + case 'FatalError': + errToThrow = new FatalError( + event.message, + typeof errorMeta?.['exitCode'] === 'number' + ? errorMeta['exitCode'] + : 1, + ); + break; + default: + errToThrow = new Error(event.message); + if (name) { + Object.defineProperty(errToThrow, 'name', { + value: name, + enumerable: true, + }); + } + break; + } + if (errorMeta?.['exitCode'] !== undefined) { Object.defineProperty(errToThrow, 'exitCode', { value: errorMeta['exitCode'], enumerable: true, }); } - if (errorMeta?.['errorName'] !== undefined) { - Object.defineProperty(errToThrow, 'name', { - value: errorMeta['errorName'], - enumerable: true, - }); - } if (errorMeta?.['code'] !== undefined) { Object.defineProperty(errToThrow, 'code', { value: errorMeta['code'], @@ -555,7 +602,18 @@ export async function runNonInteractive({ streamEnded = true; break; } + case 'initialize': + case 'session_update': + case 'agent_start': + case 'tool_update': + case 'elicitation_request': + case 'elicitation_response': + case 'usage': + case 'custom': + // Explicitly ignore these non-interactive events + break; default: + event satisfies never; break; } } diff --git a/packages/cli/src/utils/errors.ts b/packages/cli/src/utils/errors.ts index 5e48abed99..774d9e994c 100644 --- a/packages/cli/src/utils/errors.ts +++ b/packages/cli/src/utils/errors.ts @@ -20,7 +20,6 @@ import { coreEvents, getErrorType, getErrorMessage, - getErrorType, } from '@google/gemini-cli-core'; import { runSyncCleanup } from './cleanup.js'; diff --git a/packages/core/src/agent/legacy-agent-session.test.ts b/packages/core/src/agent/legacy-agent-session.test.ts index 8cd92ca08d..38bea34910 100644 --- a/packages/core/src/agent/legacy-agent-session.test.ts +++ b/packages/core/src/agent/legacy-agent-session.test.ts @@ -200,40 +200,6 @@ describe('LegacyAgentSession', () => { await collectEvents(session, { streamId: streamId ?? undefined }); }); - it('accepts legacy message-array sends without displayContent', async () => { - const sendMock = deps.client.sendMessageStream as ReturnType< - typeof vi.fn - >; - sendMock.mockReturnValue( - makeStream([ - { - type: GeminiEventType.Finished, - value: { reason: FinishReason.STOP, usageMetadata: undefined }, - }, - ]), - ); - - const session = new LegacyAgentSession(deps); - const { streamId } = await session.send({ - message: [{ type: 'text', text: 'hi' }], - }); - - const userMessage = session.events.find( - (e): e is AgentEvent<'message'> => - e.type === 'message' && e.role === 'user' && e.streamId === streamId, - ); - expect(userMessage?.content).toEqual([{ type: 'text', text: 'hi' }]); - await vi.advanceTimersByTimeAsync(0); - expect(sendMock).toHaveBeenCalledWith( - [{ text: 'hi' }], - expect.any(AbortSignal), - 'test-prompt', - undefined, - false, - undefined, - ); - }); - it('returns streamId before emitting agent_start', async () => { const sendMock = deps.client.sendMessageStream as ReturnType< typeof vi.fn diff --git a/packages/core/src/agent/legacy-agent-session.ts b/packages/core/src/agent/legacy-agent-session.ts index fa1d652eb2..667c85f5ed 100644 --- a/packages/core/src/agent/legacy-agent-session.ts +++ b/packages/core/src/agent/legacy-agent-session.ts @@ -93,9 +93,6 @@ class LegacyAgentProtocol implements AgentProtocol { 'LegacyAgentSession.send() only supports message sends for the moment.', ); } - const normalizedMessage = Array.isArray(message) - ? { content: message, displayContent: undefined } - : message; if (this._activeStreamId) { // TODO: Interactive may eventually allow selected in-stream sends such as @@ -108,16 +105,16 @@ class LegacyAgentProtocol implements AgentProtocol { this._beginNewStream(); const streamId = this._translationState.streamId; - const parts = contentPartsToGeminiParts(normalizedMessage.content); + const parts = contentPartsToGeminiParts(message.content); const userMessage = this._makeUserMessageEvent( - normalizedMessage.content, - normalizedMessage.displayContent, + message.content, + message.displayContent, payload._meta, ); this._emit([userMessage]); - this._scheduleRunLoop(parts, normalizedMessage.displayContent); + this._scheduleRunLoop(parts, message.displayContent); return { streamId }; } diff --git a/packages/core/src/agent/types.ts b/packages/core/src/agent/types.ts index 512a8c9507..9bc3e81e0f 100644 --- a/packages/core/src/agent/types.ts +++ b/packages/core/src/agent/types.ts @@ -46,12 +46,10 @@ type RequireExactlyOne = { }[keyof T]; interface AgentSendPayloads { - message: - | ContentPart[] - | { - content: ContentPart[]; - displayContent?: string; - }; + message: { + content: ContentPart[]; + displayContent?: string; + }; elicitations: ElicitationResponse[]; update: { title?: string; model?: string; config?: Record }; action: { type: string; data: unknown };