mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-24 10:42:37 -07:00
fix: resolve rebase compilation and test failures
This commit is contained in:
@@ -20,7 +20,6 @@ import {
|
||||
coreEvents,
|
||||
getErrorType,
|
||||
getErrorMessage,
|
||||
getErrorType,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { runSyncCleanup } from './cleanup.js';
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
describe('send', () => {
|
||||
it('returns streamId', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -163,7 +163,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('records the sent user message in the trajectory before send resolves', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -240,7 +240,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('returns streamId before emitting agent_start', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -280,7 +280,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
it('throws if send is called while a stream is active', async () => {
|
||||
let resolveHang: (() => void) | undefined;
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -308,7 +308,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('creates a new streamId after the previous stream completes', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock
|
||||
@@ -370,7 +370,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
describe('stream - basic flow', () => {
|
||||
it('emits agent_start, content messages, and agent_end', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -409,7 +409,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
describe('stream - tool calls', () => {
|
||||
it('handles a tool call round-trip', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
// First turn: model requests a tool
|
||||
@@ -436,7 +436,7 @@ describe('LegacyAgentSession', () => {
|
||||
]),
|
||||
);
|
||||
|
||||
const scheduleMock = deps.scheduler!.schedule as ReturnType<typeof vi.fn>;
|
||||
const scheduleMock = deps.scheduler.schedule as ReturnType<typeof vi.fn>;
|
||||
scheduleMock.mockResolvedValueOnce([
|
||||
makeCompletedToolCall('call-1', 'read_file', 'file contents'),
|
||||
]);
|
||||
@@ -469,7 +469,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('handles tool errors and sends error message in content', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValueOnce(
|
||||
@@ -506,7 +506,7 @@ describe('LegacyAgentSession', () => {
|
||||
},
|
||||
} as CompletedToolCall;
|
||||
|
||||
const scheduleMock = deps.scheduler!.schedule as ReturnType<typeof vi.fn>;
|
||||
const scheduleMock = deps.scheduler.schedule as ReturnType<typeof vi.fn>;
|
||||
scheduleMock.mockResolvedValueOnce([errorToolCall]);
|
||||
|
||||
const session = new LegacyAgentSession(deps);
|
||||
@@ -527,7 +527,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('stops on STOP_EXECUTION tool error', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValueOnce(
|
||||
@@ -555,7 +555,7 @@ describe('LegacyAgentSession', () => {
|
||||
},
|
||||
} as CompletedToolCall;
|
||||
|
||||
const scheduleMock = deps.scheduler!.schedule as ReturnType<typeof vi.fn>;
|
||||
const scheduleMock = deps.scheduler.schedule as ReturnType<typeof vi.fn>;
|
||||
scheduleMock.mockResolvedValueOnce([stopToolCall]);
|
||||
|
||||
const session = new LegacyAgentSession(deps);
|
||||
@@ -571,7 +571,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('treats fatal tool errors as tool_response followed by agent_end failed', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValueOnce(
|
||||
@@ -599,7 +599,7 @@ describe('LegacyAgentSession', () => {
|
||||
},
|
||||
} as CompletedToolCall;
|
||||
|
||||
const scheduleMock = deps.scheduler!.schedule as ReturnType<typeof vi.fn>;
|
||||
const scheduleMock = deps.scheduler.schedule as ReturnType<typeof vi.fn>;
|
||||
scheduleMock.mockResolvedValueOnce([fatalToolCall]);
|
||||
|
||||
const session = new LegacyAgentSession(deps);
|
||||
@@ -628,7 +628,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
describe('stream - terminal events', () => {
|
||||
it('handles AgentExecutionStopped', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -652,7 +652,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('handles AgentExecutionBlocked as non-terminal and continues the stream', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -699,7 +699,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('handles Error events', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -723,7 +723,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('handles LoopDetected as non-terminal warning event', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
// LoopDetected followed by more content — stream continues
|
||||
@@ -777,7 +777,7 @@ describe('LegacyAgentSession', () => {
|
||||
>;
|
||||
configMock.mockReturnValue(0);
|
||||
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -803,7 +803,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('treats GeminiClient MaxSessionTurns as a terminal max_turns stream end', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -832,7 +832,7 @@ describe('LegacyAgentSession', () => {
|
||||
describe('abort', () => {
|
||||
it('treats abort before the first model event as aborted without fatal error', async () => {
|
||||
let releaseAbort: (() => void) | undefined;
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -871,7 +871,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('aborts the stream', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
// Stream that yields content then checks abort signal via a deferred
|
||||
@@ -914,7 +914,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
it('treats abort during pending scheduler work as aborted without fatal error', async () => {
|
||||
let resolveSchedule: ((value: CompletedToolCall[]) => void) | undefined;
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -930,7 +930,7 @@ describe('LegacyAgentSession', () => {
|
||||
]),
|
||||
);
|
||||
|
||||
const scheduleMock = deps.scheduler!.schedule as ReturnType<typeof vi.fn>;
|
||||
const scheduleMock = deps.scheduler.schedule as ReturnType<typeof vi.fn>;
|
||||
scheduleMock.mockReturnValue(
|
||||
new Promise<CompletedToolCall[]>((resolve) => {
|
||||
resolveSchedule = resolve;
|
||||
@@ -966,7 +966,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
describe('events property', () => {
|
||||
it('accumulates all events', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -990,7 +990,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
describe('subscription and stream scoping', () => {
|
||||
it('subscribe receives live events for the next stream', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -1021,7 +1021,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('subscribe is live-only and does not replay old history when idle', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock
|
||||
@@ -1073,7 +1073,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('streams only the requested streamId', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock
|
||||
@@ -1131,7 +1131,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('resumes from eventId within the same stream only', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock
|
||||
@@ -1192,7 +1192,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
describe('agent_end ordering', () => {
|
||||
it('agent_end is always the final event yielded', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -1214,7 +1214,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('agent_end is final even after error events', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValue(
|
||||
@@ -1236,7 +1236,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
describe('intermediate Finished events', () => {
|
||||
it('does NOT emit agent_end when tool calls are pending', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
// First turn: tool request + Finished (should NOT produce agent_end)
|
||||
@@ -1269,7 +1269,7 @@ describe('LegacyAgentSession', () => {
|
||||
]),
|
||||
);
|
||||
|
||||
const scheduleMock = deps.scheduler!.schedule as ReturnType<typeof vi.fn>;
|
||||
const scheduleMock = deps.scheduler.schedule as ReturnType<typeof vi.fn>;
|
||||
scheduleMock.mockResolvedValueOnce([
|
||||
makeCompletedToolCall('call-1', 'read_file', 'data'),
|
||||
]);
|
||||
@@ -1285,7 +1285,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('emits usage for intermediate Finished events', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockReturnValueOnce(
|
||||
@@ -1316,7 +1316,7 @@ describe('LegacyAgentSession', () => {
|
||||
]),
|
||||
);
|
||||
|
||||
const scheduleMock = deps.scheduler!.schedule as ReturnType<typeof vi.fn>;
|
||||
const scheduleMock = deps.scheduler.schedule as ReturnType<typeof vi.fn>;
|
||||
scheduleMock.mockResolvedValueOnce([
|
||||
makeCompletedToolCall('call-1', 'read_file', 'contents'),
|
||||
]);
|
||||
@@ -1337,7 +1337,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
describe('error handling in runLoop', () => {
|
||||
it('catches thrown errors and emits error + agent_end', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
sendMock.mockImplementation(() => {
|
||||
@@ -1363,7 +1363,7 @@ describe('LegacyAgentSession', () => {
|
||||
|
||||
describe('_emitErrorAndAgentEnd metadata', () => {
|
||||
it('preserves exitCode and code in _meta for FatalError', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
// Simulate a FatalError being thrown
|
||||
@@ -1386,7 +1386,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('preserves exitCode for non-FatalError errors that carry one', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
const exitCodeError = new Error('custom exit');
|
||||
@@ -1406,7 +1406,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('preserves code in _meta for errors with code property', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
const codedError = new Error('ENOENT');
|
||||
@@ -1426,7 +1426,7 @@ describe('LegacyAgentSession', () => {
|
||||
});
|
||||
|
||||
it('preserves status in _meta for errors with status property', async () => {
|
||||
const sendMock = deps.client!.sendMessageStream as ReturnType<
|
||||
const sendMock = deps.client.sendMessageStream as ReturnType<
|
||||
typeof vi.fn
|
||||
>;
|
||||
const statusError = new Error('rate limited');
|
||||
|
||||
@@ -14,12 +14,10 @@ import type { Part } from '@google/genai';
|
||||
import type { GeminiClient } from '../core/client.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import type { ToolCallRequestInfo } from '../scheduler/types.js';
|
||||
import { Scheduler } from '../scheduler/scheduler.js';
|
||||
import type { Scheduler } from '../scheduler/scheduler.js';
|
||||
import { recordToolCallInteractions } from '../code_assist/telemetry.js';
|
||||
import { ToolErrorType, isFatalToolError } from '../tools/tool-error.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import { MessageBusType } from '../confirmation-bus/types.js';
|
||||
import type { ToolCallsUpdateMessage } from '../confirmation-bus/types.js';
|
||||
import {
|
||||
buildToolResponseData,
|
||||
contentPartsToGeminiParts,
|
||||
@@ -46,18 +44,15 @@ function isAbortLikeError(err: unknown): boolean {
|
||||
return err instanceof Error && err.name === 'AbortError';
|
||||
}
|
||||
|
||||
import type { EditorType } from '../utils/editor.js';
|
||||
|
||||
export interface LegacyAgentSessionDeps {
|
||||
client: GeminiClient;
|
||||
scheduler: Scheduler;
|
||||
config: Config;
|
||||
client?: GeminiClient;
|
||||
scheduler?: Scheduler;
|
||||
promptId?: string;
|
||||
promptId: string;
|
||||
streamId?: string;
|
||||
getPreferredEditor?: () => EditorType | undefined;
|
||||
}
|
||||
|
||||
export class LegacyAgentProtocol implements AgentProtocol {
|
||||
class LegacyAgentProtocol implements AgentProtocol {
|
||||
private _events: AgentEvent[] = [];
|
||||
private _subscribers = new Set<(event: AgentEvent) => void>();
|
||||
private _translationState: TranslationState;
|
||||
@@ -74,16 +69,10 @@ export class LegacyAgentProtocol implements AgentProtocol {
|
||||
constructor(deps: LegacyAgentSessionDeps) {
|
||||
this._translationState = createTranslationState(deps.streamId);
|
||||
this._nextStreamIdOverride = deps.streamId;
|
||||
this._client = deps.client;
|
||||
this._scheduler = deps.scheduler;
|
||||
this._config = deps.config;
|
||||
this._client = deps.client ?? deps.config.getGeminiClient();
|
||||
this._promptId = deps.promptId ?? deps.config.promptId ?? '';
|
||||
this._scheduler =
|
||||
deps.scheduler ??
|
||||
new Scheduler({
|
||||
context: deps.config,
|
||||
schedulerId: 'legacy-agent-scheduler',
|
||||
getPreferredEditor: deps.getPreferredEditor ?? (() => undefined),
|
||||
});
|
||||
this._promptId = deps.promptId;
|
||||
}
|
||||
|
||||
get events(): readonly AgentEvent[] {
|
||||
@@ -174,223 +163,142 @@ export class LegacyAgentProtocol implements AgentProtocol {
|
||||
let turnCount = 0;
|
||||
const maxTurns = this._config.getMaxSessionTurns();
|
||||
|
||||
const handleToolCallsUpdate = (event: ToolCallsUpdateMessage) => {
|
||||
const toolUpdates: AgentEvent[] = [];
|
||||
for (const tc of event.toolCalls) {
|
||||
if (tc.status === 'awaiting_approval') {
|
||||
this._emit([
|
||||
this._makeToolResponseEvent({
|
||||
requestId: tc.request.callId,
|
||||
name: tc.request.name,
|
||||
content: [
|
||||
{ type: 'text', text: 'Tool approvals not yet implemented.' },
|
||||
],
|
||||
isError: true,
|
||||
displayContent: [{ type: 'text', text: 'Approval required' }],
|
||||
}),
|
||||
this._makeErrorEvent({
|
||||
status: 'UNIMPLEMENTED',
|
||||
message:
|
||||
'TODO: Tool approvals not yet implemented, please switch to YOLO mode to test.',
|
||||
fatal: true,
|
||||
}),
|
||||
]);
|
||||
void this.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (tc.status === 'executing') {
|
||||
toolUpdates.push(
|
||||
this._makeToolUpdateEvent({
|
||||
requestId: tc.request.callId,
|
||||
displayContent: toolResultDisplayToContentParts(tc.liveOutput),
|
||||
data: {
|
||||
progressMessage: tc.progressMessage,
|
||||
progress: tc.progress,
|
||||
progressTotal: tc.progressTotal,
|
||||
pid: tc.pid,
|
||||
},
|
||||
_meta: {
|
||||
description: tc.invocation.getDescription(),
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
while (true) {
|
||||
turnCount++;
|
||||
if (maxTurns >= 0 && turnCount > maxTurns) {
|
||||
this._finishStream('max_turns', {
|
||||
code: 'MAX_TURNS_EXCEEDED',
|
||||
maxTurns,
|
||||
turnCount: turnCount - 1,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._emit(toolUpdates);
|
||||
};
|
||||
|
||||
this._config
|
||||
.getMessageBus()
|
||||
.subscribe(MessageBusType.TOOL_CALLS_UPDATE, handleToolCallsUpdate);
|
||||
const toolCallRequests: ToolCallRequestInfo[] = [];
|
||||
const responseStream = this._client.sendMessageStream(
|
||||
currentParts,
|
||||
this._abortController.signal,
|
||||
this._promptId,
|
||||
undefined,
|
||||
false,
|
||||
currentDisplayContent,
|
||||
);
|
||||
currentDisplayContent = undefined;
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
turnCount++;
|
||||
if (maxTurns >= 0 && turnCount > maxTurns) {
|
||||
this._finishStream('max_turns', {
|
||||
code: 'MAX_TURNS_EXCEEDED',
|
||||
maxTurns,
|
||||
turnCount: turnCount - 1,
|
||||
});
|
||||
for await (const event of responseStream) {
|
||||
if (this._abortController.signal.aborted) {
|
||||
this._finishStream('aborted');
|
||||
return;
|
||||
}
|
||||
|
||||
const toolCallRequests: ToolCallRequestInfo[] = [];
|
||||
const responseStream = this._client.sendMessageStream(
|
||||
currentParts,
|
||||
this._abortController.signal,
|
||||
this._promptId,
|
||||
undefined,
|
||||
false,
|
||||
currentDisplayContent,
|
||||
);
|
||||
currentDisplayContent = undefined;
|
||||
if (event.type === GeminiEventType.ToolCallRequest) {
|
||||
toolCallRequests.push(event.value);
|
||||
}
|
||||
|
||||
for await (const event of responseStream) {
|
||||
if (this._abortController.signal.aborted) {
|
||||
this._finishStream('aborted');
|
||||
this._emit(translateEvent(event, this._translationState));
|
||||
|
||||
switch (event.type) {
|
||||
case GeminiEventType.Error:
|
||||
case GeminiEventType.InvalidStream:
|
||||
case GeminiEventType.ContextWindowWillOverflow:
|
||||
this._finishStream('failed');
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type === GeminiEventType.ToolCallRequest) {
|
||||
toolCallRequests.push(event.value);
|
||||
}
|
||||
|
||||
const translatedEvents = translateEvent(
|
||||
event,
|
||||
this._translationState,
|
||||
);
|
||||
|
||||
for (const ev of translatedEvents) {
|
||||
if (ev.type === 'tool_request') {
|
||||
const tool = this._config.getToolRegistry().getTool(ev.name);
|
||||
const invocation = tool?.build(ev.args);
|
||||
ev._meta = {
|
||||
legacyState: {
|
||||
displayName: tool?.displayName ?? ev.name,
|
||||
description:
|
||||
invocation?.getDescription() ?? tool?.description ?? '',
|
||||
isOutputMarkdown: tool?.isOutputMarkdown ?? false,
|
||||
kind: tool?.kind,
|
||||
},
|
||||
};
|
||||
case GeminiEventType.Finished:
|
||||
if (toolCallRequests.length === 0) {
|
||||
this._finishStream(mapFinishReason(event.value.reason));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._emit(translatedEvents);
|
||||
|
||||
switch (event.type) {
|
||||
case GeminiEventType.Error:
|
||||
case GeminiEventType.InvalidStream:
|
||||
case GeminiEventType.ContextWindowWillOverflow:
|
||||
this._finishStream('failed');
|
||||
return;
|
||||
case GeminiEventType.Finished:
|
||||
if (toolCallRequests.length === 0) {
|
||||
this._finishStream(mapFinishReason(event.value.reason));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case GeminiEventType.AgentExecutionStopped:
|
||||
case GeminiEventType.UserCancelled:
|
||||
case GeminiEventType.MaxSessionTurns:
|
||||
this._clearActiveStream();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GeminiEventType.AgentExecutionStopped:
|
||||
case GeminiEventType.UserCancelled:
|
||||
case GeminiEventType.MaxSessionTurns:
|
||||
this._clearActiveStream();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (this._abortController.signal.aborted) {
|
||||
this._finishStream('aborted');
|
||||
return;
|
||||
}
|
||||
|
||||
if (toolCallRequests.length === 0) {
|
||||
this._finishStream('completed');
|
||||
return;
|
||||
}
|
||||
|
||||
const completedToolCalls = await this._scheduler.schedule(
|
||||
toolCallRequests,
|
||||
this._abortController.signal,
|
||||
);
|
||||
|
||||
if (this._abortController.signal.aborted) {
|
||||
this._finishStream('aborted');
|
||||
return;
|
||||
}
|
||||
|
||||
const toolResponseParts: Part[] = [];
|
||||
for (const tc of completedToolCalls) {
|
||||
const response = tc.response;
|
||||
const request = tc.request;
|
||||
const content: ContentPart[] = response.error
|
||||
? [{ type: 'text', text: response.error.message }]
|
||||
: geminiPartsToContentParts(response.responseParts);
|
||||
const displayContent = toolResultDisplayToContentParts(
|
||||
response.resultDisplay,
|
||||
);
|
||||
const data = buildToolResponseData(response);
|
||||
|
||||
this._emit([
|
||||
this._makeToolResponseEvent({
|
||||
requestId: request.callId,
|
||||
name: request.name,
|
||||
content,
|
||||
isError: response.error !== undefined,
|
||||
...(displayContent ? { displayContent } : {}),
|
||||
...(data ? { data } : {}),
|
||||
_meta: {
|
||||
resultDisplay: response.resultDisplay,
|
||||
outputFile: response.outputFile,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
if (response.responseParts) {
|
||||
toolResponseParts.push(...response.responseParts);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const currentModel =
|
||||
this._client.getCurrentSequenceModel() ?? this._config.getModel();
|
||||
this._client
|
||||
.getChat()
|
||||
.recordCompletedToolCalls(currentModel, completedToolCalls);
|
||||
await recordToolCallInteractions(this._config, completedToolCalls);
|
||||
} catch (error) {
|
||||
debugLogger.error(
|
||||
`Error recording completed tool call information: ${error}`,
|
||||
);
|
||||
}
|
||||
|
||||
const stopTool = completedToolCalls.find(
|
||||
(tc) =>
|
||||
tc.response.errorType === ToolErrorType.STOP_EXECUTION &&
|
||||
tc.response.error !== undefined,
|
||||
);
|
||||
if (stopTool) {
|
||||
this._finishStream('completed');
|
||||
return;
|
||||
}
|
||||
|
||||
const fatalTool = completedToolCalls.find((tc) =>
|
||||
isFatalToolError(tc.response.errorType),
|
||||
);
|
||||
if (fatalTool) {
|
||||
this._finishStream('failed');
|
||||
return;
|
||||
}
|
||||
|
||||
currentParts = toolResponseParts;
|
||||
}
|
||||
} finally {
|
||||
this._config
|
||||
.getMessageBus()
|
||||
.unsubscribe(MessageBusType.TOOL_CALLS_UPDATE, handleToolCallsUpdate);
|
||||
|
||||
if (this._abortController.signal.aborted) {
|
||||
this._finishStream('aborted');
|
||||
return;
|
||||
}
|
||||
|
||||
if (toolCallRequests.length === 0) {
|
||||
this._finishStream('completed');
|
||||
return;
|
||||
}
|
||||
|
||||
const completedToolCalls = await this._scheduler.schedule(
|
||||
toolCallRequests,
|
||||
this._abortController.signal,
|
||||
);
|
||||
|
||||
if (this._abortController.signal.aborted) {
|
||||
this._finishStream('aborted');
|
||||
return;
|
||||
}
|
||||
|
||||
const toolResponseParts: Part[] = [];
|
||||
for (const tc of completedToolCalls) {
|
||||
const response = tc.response;
|
||||
const request = tc.request;
|
||||
const content: ContentPart[] = response.error
|
||||
? [{ type: 'text', text: response.error.message }]
|
||||
: geminiPartsToContentParts(response.responseParts);
|
||||
const displayContent = toolResultDisplayToContentParts(
|
||||
response.resultDisplay,
|
||||
);
|
||||
const data = buildToolResponseData(response);
|
||||
|
||||
this._emit([
|
||||
this._makeToolResponseEvent({
|
||||
requestId: request.callId,
|
||||
name: request.name,
|
||||
content,
|
||||
isError: response.error !== undefined,
|
||||
...(displayContent ? { displayContent } : {}),
|
||||
...(data ? { data } : {}),
|
||||
}),
|
||||
]);
|
||||
|
||||
if (response.responseParts) {
|
||||
toolResponseParts.push(...response.responseParts);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const currentModel =
|
||||
this._client.getCurrentSequenceModel() ?? this._config.getModel();
|
||||
this._client
|
||||
.getChat()
|
||||
.recordCompletedToolCalls(currentModel, completedToolCalls);
|
||||
await recordToolCallInteractions(this._config, completedToolCalls);
|
||||
} catch (error) {
|
||||
debugLogger.error(
|
||||
`Error recording completed tool call information: ${error}`,
|
||||
);
|
||||
}
|
||||
|
||||
const stopTool = completedToolCalls.find(
|
||||
(tc) =>
|
||||
tc.response.errorType === ToolErrorType.STOP_EXECUTION &&
|
||||
tc.response.error !== undefined,
|
||||
);
|
||||
if (stopTool) {
|
||||
this._finishStream('completed');
|
||||
return;
|
||||
}
|
||||
|
||||
const fatalTool = completedToolCalls.find((tc) =>
|
||||
isFatalToolError(tc.response.errorType),
|
||||
);
|
||||
if (fatalTool) {
|
||||
this._finishStream('failed');
|
||||
return;
|
||||
}
|
||||
|
||||
currentParts = toolResponseParts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,20 +434,6 @@ export class LegacyAgentProtocol implements AgentProtocol {
|
||||
return event;
|
||||
}
|
||||
|
||||
private _makeToolUpdateEvent(
|
||||
payload: Omit<
|
||||
AgentEvent<'tool_update'>,
|
||||
'id' | 'timestamp' | 'streamId' | 'type'
|
||||
>,
|
||||
): AgentEvent<'tool_update'> {
|
||||
const event = {
|
||||
...this._nextEventFields(),
|
||||
type: 'tool_update',
|
||||
...payload,
|
||||
} satisfies AgentEvent<'tool_update'>;
|
||||
return event;
|
||||
}
|
||||
|
||||
private _makeAgentStartEvent(): AgentEvent<'agent_start'> {
|
||||
const event = {
|
||||
...this._nextEventFields(),
|
||||
|
||||
@@ -681,18 +681,11 @@ export interface ConfigParameters {
|
||||
adminSkillsEnabled?: boolean;
|
||||
experimentalJitContext?: boolean;
|
||||
experimentalMemoryManager?: boolean;
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
useAgentProtocol?: boolean;
|
||||
=======
|
||||
experimentalAgentHistoryTruncation?: boolean;
|
||||
experimentalAgentHistoryTruncationThreshold?: number;
|
||||
experimentalAgentHistoryRetainedMessages?: number;
|
||||
experimentalAgentHistorySummarization?: boolean;
|
||||
>>>>>>> 320c8aba4 (feat(core): Land `AgentHistoryProvider`. (#23978))
|
||||
=======
|
||||
useAgentProtocol?: boolean;
|
||||
>>>>>>> 792d1c88d (feat: add experimental useAgentProtocol flag)
|
||||
topicUpdateNarration?: boolean;
|
||||
toolOutputMasking?: Partial<ToolOutputMaskingConfig>;
|
||||
disableLLMCorrection?: boolean;
|
||||
@@ -1135,10 +1128,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
|
||||
this.experimentalJitContext = params.experimentalJitContext ?? true;
|
||||
this.experimentalMemoryManager = params.experimentalMemoryManager ?? false;
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
this.useAgentProtocol = params.useAgentProtocol ?? false;
|
||||
=======
|
||||
this.experimentalAgentHistoryTruncation =
|
||||
params.experimentalAgentHistoryTruncation ?? false;
|
||||
this.experimentalAgentHistoryTruncationThreshold =
|
||||
@@ -1147,10 +1137,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
params.experimentalAgentHistoryRetainedMessages ?? 15;
|
||||
this.experimentalAgentHistorySummarization =
|
||||
params.experimentalAgentHistorySummarization ?? false;
|
||||
>>>>>>> 320c8aba4 (feat(core): Land `AgentHistoryProvider`. (#23978))
|
||||
=======
|
||||
this.useAgentProtocol = params.useAgentProtocol ?? false;
|
||||
>>>>>>> 792d1c88d (feat: add experimental useAgentProtocol flag)
|
||||
this.topicUpdateNarration = params.topicUpdateNarration ?? false;
|
||||
this.modelSteering = params.modelSteering ?? false;
|
||||
this.injectionService = new InjectionService(() =>
|
||||
@@ -2332,7 +2319,6 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
}
|
||||
|
||||
getExperimentalUseAgentProtocol(): boolean {
|
||||
<<<<<<< HEAD
|
||||
return (
|
||||
this.useAgentProtocol ||
|
||||
process.env['GEMINI_CLI_USE_AGENT_PROTOCOL'] === 'true'
|
||||
@@ -2353,9 +2339,6 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
|
||||
isExperimentalAgentHistorySummarizationEnabled(): boolean {
|
||||
return this.experimentalAgentHistorySummarization;
|
||||
=======
|
||||
return this.useAgentProtocol;
|
||||
>>>>>>> 792d1c88d (feat: add experimental useAgentProtocol flag)
|
||||
}
|
||||
|
||||
isTopicUpdateNarrationEnabled(): boolean {
|
||||
@@ -3645,9 +3628,3 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
}
|
||||
// Export model constants for use in CLI
|
||||
export { DEFAULT_GEMINI_FLASH_MODEL };
|
||||
er.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Export model constants for use in CLI
|
||||
export { DEFAULT_GEMINI_FLASH_MODEL };
|
||||
|
||||
Reference in New Issue
Block a user