This commit is contained in:
Adam Weidman
2026-03-29 17:43:07 -04:00
parent bb459defe9
commit cd34c9666e
5 changed files with 73 additions and 55 deletions
+65 -7
View File
@@ -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;
}
}
-1
View File
@@ -20,7 +20,6 @@ import {
coreEvents,
getErrorType,
getErrorMessage,
getErrorType,
} from '@google/gemini-cli-core';
import { runSyncCleanup } from './cleanup.js';
@@ -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
@@ -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 };
}
+4 -6
View File
@@ -46,12 +46,10 @@ type RequireExactlyOne<T> = {
}[keyof T];
interface AgentSendPayloads {
message:
| ContentPart[]
| {
content: ContentPart[];
displayContent?: string;
};
message: {
content: ContentPart[];
displayContent?: string;
};
elicitations: ElicitationResponse[];
update: { title?: string; model?: string; config?: Record<string, unknown> };
action: { type: string; data: unknown };